diff --git a/.dockerignore b/.dockerignore index da96199c..2f2b517c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,8 +5,8 @@ .factory Dockerfile .dockerignore -hawk_bin -hawk_test_bin +graycode_bin +graycode_test_bin coverage.out coverage.html go.work diff --git a/.env.example b/.env.example index fff29ad5..c8c98f50 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,10 @@ -# hawk daemon environment variables — copy to .env and fill in -HAWK_DAEMON_API_KEY= -HAWK_DAEMON_PORT=4590 -HAWK_DAEMON_HOST=127.0.0.1 -# Eyrie connection (LLM provider runtime) -EYRIE_API_KEY= -EYRIE_BASE_URL=http://localhost:8080 -# Harrier connection (memory service) -HARRIER_API_KEY= -HARRIER_ADDR=127.0.0.1:3456 +# graycode daemon environment variables — copy to .env and fill in. +# Only GRAYCODE_DAEMON_API_KEY is consumed from the environment. +GRAYCODE_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`) +# 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 a1938081..474819e4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ -# CODEOWNERS for hawk +# CODEOWNERS for graycode # # 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 bd16c3cb..e0a4255c 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/hawk/issues) to avoid duplicates. + - Search [existing issues](https://github.com/GrayCodeAI/graycode-cli/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 `hawk ...`, I expected X but got Y. + placeholder: When I run `graycode ...`, 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 `hawk ...` + 1. Run `graycode ...` 2. Type `...` 3. See error `...` validations: @@ -44,10 +44,10 @@ body: required: true - type: input - id: hawk-version + id: graycode-version attributes: - label: hawk version - description: Output of `hawk version` (or `hawk --version`). + label: graycode version + description: Output of `graycode version` (or `graycode --version`). placeholder: "0.1.0" validations: required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index a1885141..4a730ef6 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/hawk/security/advisories/new + url: https://github.com/GrayCodeAI/graycode-cli/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/hawk/discussions + url: https://github.com/GrayCodeAI/graycode-cli/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 6f62dab5..62e4245d 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 hawk. +description: Suggest an improvement or new capability for graycode. title: "feat: " labels: ["enhancement", "triage"] @@ -7,13 +7,13 @@ body: - type: markdown attributes: value: | - Thanks for proposing a feature. hawk is built **for developers first** + Thanks for proposing a feature. graycode 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/hawk/issues) to avoid duplicates. + - Search [existing issues](https://github.com/GrayCodeAI/graycode-cli/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, hawk makes me do Y, which is painful because Z. + placeholder: When I'm doing X, graycode 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 hawk to behave? CLI flags, output, config, etc. + description: How would you like graycode to behave? CLI flags, output, config, etc. validations: required: true @@ -55,7 +55,7 @@ body: id: principles attributes: label: Developer fit - description: hawk avoids enterprise scope for now. Confirm this feature respects that. + description: graycode 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-eyrie/action.yml b/.github/actions/checkout-eyrie/action.yml index 6207a5eb..987441e3 100644 --- a/.github/actions/checkout-eyrie/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 hawk's go.work +description: Clone graycode-eco sibling repos into the workspace parent for graycode's go.work inputs: ref: @@ -18,15 +18,22 @@ runs: INPUT_REF: ${{ inputs.ref }} run: | set -euo pipefail - # The generated workspace references sibling bird-codename repos. + # The generated workspace references sibling repos from ecosystem.yaml. + # Repositories that no longer resolve are skipped with a warning so a + # single-repo checkout still yields a working CI run (expected during + # ecosystem migration; downstream guards skip absent checkouts too). ws_parent="$(cd "${GITHUB_WORKSPACE}/.." && pwd)" while IFS= read -r repo; do - [ "$repo" = hawk ] && continue + [ "$repo" = graycode-cli ] && continue dest="${ws_parent}/${repo}" if [ -d "$dest/.git" ]; then echo "$repo already present at $dest" continue fi + if ! git ls-remote "https://github.com/GrayCodeAI/${repo}.git" HEAD >/dev/null 2>&1; then + echo "WARNING: GrayCodeAI/${repo} is not reachable; skipping checkout" + continue + fi if git ls-remote --heads "https://github.com/GrayCodeAI/${repo}.git" "$INPUT_REF" | grep -q .; then echo "Cloning $repo at branch $INPUT_REF" git clone --depth=1 --branch "$INPUT_REF" "https://github.com/GrayCodeAI/${repo}.git" "$dest" diff --git a/.github/actions/hawk/action.yml b/.github/actions/graycode/action.yml similarity index 59% rename from .github/actions/hawk/action.yml rename to .github/actions/graycode/action.yml index 42f5c803..f60cae4f 100644 --- a/.github/actions/hawk/action.yml +++ b/.github/actions/graycode/action.yml @@ -1,7 +1,7 @@ -name: Hawk +name: Graycode description: >- - Run the Hawk coding agent headlessly in a GitHub workflow. Hawk is + Run the Graycode coding agent headlessly in a GitHub workflow. Graycode 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 Hawk to execute. Mutually exclusive with prompt-file. + description: The instruction for Graycode 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 Hawk step and never written to the log. + it is exported only for the Graycode 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 Hawk step. Leave the action + api-key, that variable is exported only for the Graycode 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 `hawk exec --model`. + description: Model id to use for the run. Passed to `graycode exec --model`. required: false default: "" auto: @@ -54,19 +54,19 @@ inputs: default: supervised agent: description: >- - Agent persona to use (from Hawk user state). Maps to `hawk exec --agent`. + Agent persona to use (from Graycode user state). Maps to `graycode exec --agent`. required: false default: "" cwd: description: >- - Working directory override. Maps to `hawk exec --cwd`. + Working directory override. Maps to `graycode 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 `hawk exec --ephemeral`. + Maps to `graycode exec --ephemeral`. required: false default: "false" worktree: @@ -80,7 +80,7 @@ inputs: default: text post-to: description: >- - Optional destination for a run summary after Hawk finishes: `pr-comment` + Optional destination for a run summary after Graycode 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 Hawk in. Defaults to ${{ github.workspace }}. + description: Directory to run Graycode in. Defaults to ${{ github.workspace }}. required: false default: ${{ github.workspace }} - hawk-version: + graycode-version: description: >- - Hawk release version/tag to install (e.g. v1.2.3 or latest). + Graycode release version/tag to install (e.g. v1.2.3 or latest). Defaults to the ref this action was resolved at. required: false default: "" - hawk-repo: - description: Repository to install Hawk from (owner/repo). + graycode-repo: + description: Repository to install Graycode from (owner/repo). required: false - default: GrayCodeAI/hawk + default: GrayCodeAI/graycode-cli outputs: exit-code: - description: The exit code Hawk returned (0 success, 2 usage, 3 provider, non-zero otherwise). + description: The exit code Graycode returned (0 success, 2 usage, 3 provider, non-zero otherwise). value: ${{ steps.run.outputs.exit-code }} output-file: - description: Path to the captured Hawk stdout (the raw output-format stream). + description: Path to the captured Graycode 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 Hawk + - name: Install Graycode id: install shell: bash env: - HAWK_REPO: ${{ inputs.hawk-repo }} - HAWK_VERSION_INPUT: ${{ inputs.hawk-version }} + GRAYCODE_REPO: ${{ inputs.graycode-repo }} + GRAYCODE_VERSION_INPUT: ${{ inputs.graycode-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 Hawk action currently supports Linux and macOS runners only." >&2 + echo "::error::The Graycode 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="${HAWK_VERSION_INPUT}" + version="${GRAYCODE_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}/hawk-bin" - src_dir="${RUNNER_TEMP}/hawk-src" + install_dir="${RUNNER_TEMP}/graycode-bin" + src_dir="${RUNNER_TEMP}/graycode-src" mkdir -p "${install_dir}" "${src_dir}" - # Download the Hawk source archive and extract it + # Download the Graycode source archive and extract it if [ "${version}" = "latest" ]; then - tarball_url=$(curl -sL "https://api.github.com/repos/${HAWK_REPO}/releases/latest" \ + tarball_url=$(curl -sL "https://api.github.com/repos/${GRAYCODE_REPO}/releases/latest" \ | grep '"tarball_url":' | sed 's/.*"\([^"]*\)".*/\1/') else - tarball_url="https://github.com/${HAWK_REPO}/archive/refs/tags/v${version}.tar.gz" + tarball_url="https://github.com/${GRAYCODE_REPO}/archive/refs/tags/v${version}.tar.gz" fi curl -sL "${tarball_url}" | tar -xz -C "${src_dir}" --strip-components=1 - # Build the Hawk binary from source (Go must be available on the runner) + # Build the Graycode 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}/hawk" "${src_dir}/cmd/hawk" - chmod +x "${install_dir}/hawk" + go build -o "${install_dir}/graycode" "${src_dir}/cmd/graycode" + chmod +x "${install_dir}/graycode" echo "${install_dir}" >> "${GITHUB_PATH}" - echo "Installed Hawk from ${HAWK_REPO}@${version}" + echo "Installed Graycode from ${GRAYCODE_REPO}@${version}" - - name: Run Hawk + - name: Run Graycode 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. - 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 }} + 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 }} 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 "${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 + 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 echo "::error::api-key-env must be a valid environment variable name." >&2 exit 2 fi - export "${HAWK_INPUT_API_KEY_ENV}=${HAWK_INPUT_API_KEY}" + export "${GRAYCODE_INPUT_API_KEY_ENV}=${GRAYCODE_INPUT_API_KEY}" fi - if [ -n "${HAWK_INPUT_PROVIDER}" ]; then - export HAWK_PROVIDER="${HAWK_INPUT_PROVIDER}" + if [ -n "${GRAYCODE_INPUT_PROVIDER}" ]; then + export GRAYCODE_PROVIDER="${GRAYCODE_INPUT_PROVIDER}" fi args=(exec) - if [ -n "${HAWK_INPUT_PROMPT_FILE}" ] && [ -n "${HAWK_INPUT_PROMPT}" ]; then + if [ -n "${GRAYCODE_INPUT_PROMPT_FILE}" ] && [ -n "${GRAYCODE_INPUT_PROMPT}" ]; then echo "::error::The 'prompt' and 'prompt-file' inputs are mutually exclusive." >&2 exit 2 fi - if [ -n "${HAWK_INPUT_PROMPT_FILE}" ]; then - args+=(--file "${HAWK_INPUT_PROMPT_FILE}") - elif [ -n "${HAWK_INPUT_PROMPT}" ]; then - args+=("${HAWK_INPUT_PROMPT}") + if [ -n "${GRAYCODE_INPUT_PROMPT_FILE}" ]; then + args+=(--file "${GRAYCODE_INPUT_PROMPT_FILE}") + elif [ -n "${GRAYCODE_INPUT_PROMPT}" ]; then + args+=("${GRAYCODE_INPUT_PROMPT}") else echo "::error::Provide either the 'prompt' or 'prompt-file' input." >&2 exit 2 fi - [ -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 + [ -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 args+=(--ephemeral) fi - if [ "${HAWK_INPUT_WORKTREE}" = "true" ]; then + if [ "${GRAYCODE_INPUT_WORKTREE}" = "true" ]; then args+=(--worktree) fi - [ -n "${HAWK_INPUT_OUTPUT_FORMAT}" ] && args+=(--output-format "${HAWK_INPUT_OUTPUT_FORMAT}") + [ -n "${GRAYCODE_INPUT_OUTPUT_FORMAT}" ] && args+=(--output-format "${GRAYCODE_INPUT_OUTPUT_FORMAT}") - output_file="${RUNNER_TEMP}/hawk-output.txt" + output_file="${RUNNER_TEMP}/graycode-output.txt" - # Run Hawk. Capture stdout to a file AND echo it to the log. Surface the + # Run Graycode. Capture stdout to a file AND echo it to the log. Surface the # real exit code as the step's status. set +e - hawk "${args[@]}" | tee "${output_file}" + graycode "${args[@]}" | tee "${output_file}" code=${PIPESTATUS[0]} set -e @@ -255,14 +255,14 @@ runs: # Best-effort one-line summary if [ "${code}" = "0" ]; then - summary="Hawk run succeeded" + summary="Graycode run succeeded" else - summary="Hawk run failed (exit ${code})" + summary="Graycode 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 }} - HAWK_EXIT: ${{ steps.run.outputs.exit-code }} - HAWK_SUMMARY: ${{ steps.run.outputs.summary }} + GRAYCODE_EXIT: ${{ steps.run.outputs.exit-code }} + GRAYCODE_SUMMARY: ${{ steps.run.outputs.summary }} run: | set -euo pipefail status="succeeded" - [ "${HAWK_EXIT}" != "0" ] && status="failed (exit ${HAWK_EXIT})" - body="$(printf 'Hawk run %s.\n\n%s' "${status}" "${HAWK_SUMMARY}")" + [ "${GRAYCODE_EXIT}" != "0" ] && status="failed (exit ${GRAYCODE_EXIT})" + body="$(printf 'Graycode run %s.\n\n%s' "${status}" "${GRAYCODE_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 }} - HAWK_EXIT: ${{ steps.run.outputs.exit-code }} - HAWK_SUMMARY: ${{ steps.run.outputs.summary }} + GRAYCODE_EXIT: ${{ steps.run.outputs.exit-code }} + GRAYCODE_SUMMARY: ${{ steps.run.outputs.summary }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | set -euo pipefail status="succeeded" - [ "${HAWK_EXIT}" != "0" ] && status="failed (exit ${HAWK_EXIT})" - text="Hawk run ${status} for ${GITHUB_REPOSITORY}@${GITHUB_REF_NAME}. ${HAWK_SUMMARY} ${RUN_URL}" + [ "${GRAYCODE_EXIT}" != "0" ] && status="failed (exit ${GRAYCODE_EXIT})" + text="Graycode run ${status} for ${GITHUB_REPOSITORY}@${GITHUB_REF_NAME}. ${GRAYCODE_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 e3559e36..80610ede 100644 --- a/.github/actions/setup-deps/action.yml +++ b/.github/actions/setup-deps/action.yml @@ -28,8 +28,17 @@ runs: ws_parent="$(cd "${GITHUB_WORKSPACE}/.." && pwd)" mkdir -p "$ws_parent" while IFS= read -r repo; do - [ "$repo" = hawk ] && continue - clone_with_retry "$repo" "$ws_parent/$repo" main + [ "$repo" = graycode-cli ] && continue + dest="$ws_parent/$repo" + if [ -d "$dest/.git" ]; then + echo "$repo already present at $dest" + continue + fi + if ! git ls-remote "https://x-access-token:${GH_TOKEN}@github.com/GrayCodeAI/${repo}.git" HEAD >/dev/null 2>&1; then + echo "WARNING: GrayCodeAI/${repo} is not reachable; skipping checkout" + continue + fi + clone_with_retry "$repo" "$dest" main done < <(./scripts/ecosystem-manifest.sh list workspace) - name: Set up Go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 768cafd4..db807c47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,12 +20,12 @@ concurrency: env: GO_VERSION: "1.26.6" - # GrayCodeAI sibling modules are resolved from the workspace ../ checkouts via - # go.work; their go.mod require versions intentionally do not match the - # frozen public proxy/sumdb snapshot, so bypass the proxy + checksum DB for them. - GOPRIVATE: "github.com/GrayCodeAI/*" - GONOSUMDB: "github.com/GrayCodeAI/*" - GONOSUMCHECK: "1" + # Module resolution: the committed third_party/modproxy serves GrayCodeAI pins + # whose repositories no longer exist (and which the public proxy never cached, + # so it 404s them). Public modules resolve from proxy.golang.org as usual; + # direct-VCS fallback is retained for anything else. Do NOT set GOPRIVATE for + # GrayCodeAI paths: it would force direct git and bypass the committed proxy. + GOPROXY: "file://${{ github.workspace }}/third_party/modproxy,https://proxy.golang.org,direct" # CI must resolve the dependency versions pinned in go.mod/go.sum, not whatever # happens to sit in the sibling checkouts that the committed go.work points at. # Go's reference is explicit that a committed go.work "may cause a continuous @@ -33,7 +33,7 @@ env: # module's dependencies" and that "CI systems should generally not be allowed to # 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 eagle pin that no longer matches). + # 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 # go.work content checks genuinely require the workspace. GOWORK: "off" @@ -83,7 +83,7 @@ jobs: run: bash ./scripts/check-eyrie-engine-boundary.sh # ------------------------------------------------------------------------- - # 2. Module hygiene — tidy, verify Hawk plus the sibling Go modules via go.work. + # 2. Module hygiene — tidy, verify Graycode plus the sibling Go modules via go.work. # ------------------------------------------------------------------------- module: name: module hygiene @@ -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/hawk + go build -mod=readonly -o /dev/null ./cmd/graycode 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" = hawk ] && continue + [ "$module" = graycode ] && continue if ! grep -q "./${module}" "$workspace_file"; then echo "::error::go.work must include ./${module}." cat "$workspace_file" @@ -141,8 +141,6 @@ jobs: (cd "$dir" && GOWORK=off go mod verify) (cd "$dir" && GOWORK=off go test ./... -count=1 -timeout=300s -skip='TestDefaultSkillDirsCrossAgent|TestCopySelectionE2E') done < <(find . -name go.mod -not -path './.git/*' -print | sort) - - name: contracts parity guard - run: bash ./scripts/check-contracts-parity.sh public-modules: name: public module graph @@ -164,7 +162,7 @@ jobs: run: | go mod download go mod verify - go build -mod=readonly ./cmd/hawk + go build -mod=readonly ./cmd/graycode go test ./... -count=1 -timeout=300s -skip='TestDefaultSkillDirsCrossAgent|TestCopySelectionE2E' release-parity: @@ -202,8 +200,8 @@ jobs: cache: true - name: go vet run: go vet ./... - - name: hawk harness evaluation audit - run: go run ./cmd/hawk harness --out-dir .hawk/harness + - name: graycode harness evaluation audit + run: go run ./cmd/graycode harness --out-dir .graycode/harness - name: support repo coupling guard run: bash ./scripts/check-support-repo-coupling.sh @@ -238,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 hawk PR #155, where + # historically masked real test regressions (see graycode 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. @@ -302,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/hawk/issues/123" + echo " // TODO: https://github.com/GrayCodeAI/graycode-cli/issues/123" echo " t.Skip(\"not yet implemented\")" fi @@ -501,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/hawk-bin ./cmd/hawk && wc -c < /tmp/hawk-bin) + size=$(go build -trimpath -ldflags="-s -w" -o /tmp/graycode-bin ./cmd/graycode && wc -c < /tmp/graycode-bin) size_mb=$((size / 1024 / 1024)) echo "Binary size: ${size_mb}MB" # Threshold history: 110MB → 80MB (binary was ~76MB) → 98MB. @@ -515,7 +513,7 @@ jobs: if [ "$size_mb" -gt 98 ]; then echo "::warning::Binary size ${size_mb}MB exceeds 98MB threshold" fi - rm -f /tmp/hawk-bin + rm -f /tmp/graycode-bin # ------------------------------------------------------------------------- # Fuzz — short corpus runs to catch panics in fuzz targets. @@ -545,7 +543,7 @@ jobs: go test -fuzz=FuzzParseSessionMeta -fuzztime=60s ./internal/session/... || true # ------------------------------------------------------------------------- - # 10. Smoke — build hawk and verify ecosystem CLI wiring. + # 10. Smoke — build graycode and verify ecosystem CLI wiring. # ------------------------------------------------------------------------- smoke: name: smoke @@ -562,5 +560,5 @@ jobs: with: go-version: ${{ env.GO_VERSION }} cache: true - - name: smoke-hawk.sh - run: ./scripts/smoke-hawk.sh + - name: smoke-graycode.sh + run: ./scripts/smoke-graycode.sh diff --git a/.github/workflows/daemon-image.yml b/.github/workflows/daemon-image.yml index 71fdf0c6..4bf4100f 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/hawk-daemon.service" + - "packaging/systemd/graycode-daemon.service" - "internal/**" - "cmd/**" - "go.mod" @@ -21,7 +21,7 @@ permissions: env: REGISTRY: ghcr.io - IMAGE_NAME: graycodeai/hawk-daemon + IMAGE_NAME: graycodeai/graycode-daemon jobs: build: @@ -51,8 +51,8 @@ jobs: push: false load: true tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:scan - cache-from: type=gha,scope=hawk-daemon - cache-to: type=gha,mode=max,scope=hawk-daemon + cache-from: type=gha,scope=graycode-daemon + cache-to: type=gha,mode=max,scope=graycode-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=hawk-daemon - cache-to: type=gha,mode=max,scope=hawk-daemon + cache-from: type=gha,scope=graycode-daemon + cache-to: type=gha,mode=max,scope=graycode-daemon build-args: | VERSION=${{ github.ref_name }} COMMIT=${{ github.sha }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index b6630429..d0c85a8b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -21,7 +21,7 @@ permissions: env: REGISTRY: ghcr.io - IMAGE_NAME: graycodeai/hawk + IMAGE_NAME: graycodeai/graycode 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=hawk-amd64 - cache-to: type=gha,mode=max,scope=hawk-amd64 + cache-from: type=gha,scope=graycode-amd64 + cache-to: type=gha,mode=max,scope=graycode-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=hawk-amd64 - cache-to: type=gha,mode=max,scope=hawk-amd64 + cache-from: type=gha,scope=graycode-amd64 + cache-to: type=gha,mode=max,scope=graycode-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=hawk-arm64 - cache-to: type=gha,mode=max,scope=hawk-arm64 + cache-from: type=gha,scope=graycode-arm64 + cache-to: type=gha,mode=max,scope=graycode-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=hawk-arm64 - cache-to: type=gha,mode=max,scope=hawk-arm64 + cache-from: type=gha,scope=graycode-arm64 + cache-to: type=gha,mode=max,scope=graycode-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/hawk-sandbox:scan - cache-from: type=gha,scope=hawk-sandbox - cache-to: type=gha,mode=max,scope=hawk-sandbox + tags: ${{ env.REGISTRY }}/graycodeai/graycode-sandbox:scan + cache-from: type=gha,scope=graycode-sandbox + cache-to: type=gha,mode=max,scope=graycode-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/hawk-sandbox:scan + ${{ env.REGISTRY }}/graycodeai/graycode-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/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 + ${{ 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 # 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 c0c4a76d..c84872ba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,6 +16,9 @@ permissions: jobs: goreleaser: runs-on: ubuntu-latest + env: + # Deleted-repos pins resolve from the committed file proxy (see ci.yml). + GOPROXY: "file://${{ github.workspace }}/third_party/modproxy,https://proxy.golang.org,direct" steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.gitignore b/.gitignore index 314e07a1..ddd214f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ # Binaries -/hawk +/graycode # Ignore build output in bin/, but track committed source scripts like bin/buf. bin/* !bin/buf -hawk_bin +graycode_bin *.exe *.dll *.so @@ -12,7 +12,7 @@ hawk_bin .claude/ .codegraph/ .commandcode/ -.hawk/ +.graycode/ .harrier/ .openclaude/ .pi/ @@ -45,8 +45,8 @@ go.work.sum # Lefthook-generated git hook wrappers .githooks/ __pycache__/ -hawk_bin +graycode_bin # Logs *.log -hawk-sec105b.log +graycode-sec105b.log diff --git a/.golangci.yml b/.golangci.yml index 9358dd36..7259c974 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 — hawk reads user-supplied file paths by design (CLI agent) + # G304 — graycode 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 0afb1d9f..d07766c4 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -2,17 +2,17 @@ # Source of truth: .shared-templates/.goreleaser.yml.tmpl # # Placeholders rendered per repo: -# hawk — short repo name (e.g. hawk, harrier, swift) +# graycode — short repo name (e.g. graycode, 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/hawk/internal/version) +# (e.g. main or github.com/GrayCodeAI/graycode-cli/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: hawk +project_name: graycode # --------------------------------------------------------------------------- # Pre-build hooks — keep go.mod tidy and verified. @@ -27,9 +27,9 @@ before: # rather than the build host's clock. # --------------------------------------------------------------------------- builds: - - id: hawk - main: ./cmd/hawk - binary: hawk + - id: graycode + main: ./cmd/graycode + binary: graycode 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/hawk`. Requires HOMEBREW_TAP_TOKEN or +# `brew install graycodeai/tap/graycode`. 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/hawk" + homepage: "https://github.com/GrayCodeAI/graycode-cli" description: "AI coding agent — reads, writes, and runs code in your terminal" license: "MIT" install: | - bin.install "hawk" + bin.install "graycode" test: | - system "#{bin}/hawk", "--version" + system "#{bin}/graycode", "--version" # --------------------------------------------------------------------------- # Release — auto-detect prereleases (rc/beta tags). Created on the repo @@ -158,9 +158,9 @@ release: prerelease: auto name_template: "v{{ .Version }}" header: | - ## hawk v{{ .Version }} + ## graycode v{{ .Version }} AI coding agent — reads, writes, and runs code in your terminal footer: | - **Full changelog:** https://github.com/GrayCodeAI/hawk/compare/{{ .PreviousTag }}...{{ .Tag }} + **Full changelog:** https://github.com/GrayCodeAI/graycode-cli/compare/{{ .PreviousTag }}...{{ .Tag }} diff --git a/.shared-templates/.goreleaser.yml.tmpl b/.shared-templates/.goreleaser.yml.tmpl index 9edda1bb..a8b12a43 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. hawk, harrier, swift) +# {{NAME}} — short repo name (e.g. graycode, 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/hawk/internal/version) +# (e.g. main or github.com/GrayCodeAI/graycode-cli/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 f9403f07..6728120c 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}}. # -# hawk is currently the only Go binary repo in the ecosystem, and its real -# Makefile extends this core with hawk-specific targets (workspace `setup`, +# graycode is currently the only Go binary repo in the ecosystem, and its real +# Makefile extends this core with graycode-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 -# hawk/Makefile. +# graycode/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/hawk/blob/main/docs/versioning.md. +# See https://github.com/GrayCodeAI/graycode-cli/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 hawk/Makefile for the real, extended reference implementation. +# See graycode/Makefile for the real, extended reference implementation. # --------------------------------------------------------------------------- diff --git a/.shared-templates/Makefile.library.tmpl b/.shared-templates/Makefile.library.tmpl index 3ed3921a..f93961e1 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/hawk/blob/main/VERSIONING.md. +# See https://github.com/GrayCodeAI/graycode-cli/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 76e029d4..2dfd5f3c 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 Hawk private packages. +boundary-guard: ## Fail if the SDK references support engines or Graycode 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 1a3617a9..fffa3875 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 `hawk`, because -`hawk` is the one repo every consumer already depends on or references — +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 — there is no separate monorepo at the workspace root to hold it. -**This directory is not built or run by hawk itself.** It is a template +**This directory is not built or run by graycode 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 `hawk`) +- `Makefile.binary.tmpl` — Go binary repos (currently only `graycode`) - `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 `hawk`'s `external/` submodule pins (hawk-specific, not templated elsewhere) +- `scripts/sync-external.sh` — read-only drift report for `graycode`'s `external/` submodule pins (graycode-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. `hawk`'s own `Makefile`/`lefthook.yml`/CI predate this directory +per repo. `graycode`'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 `hawk`'s +`Makefile.binary.tmpl`, which documents the real deltas against `graycode`'s Makefile). ## Boundary rule diff --git a/.shared-templates/docs/coverage-matrix.md b/.shared-templates/docs/coverage-matrix.md index 81afac8b..620e9eec 100644 --- a/.shared-templates/docs/coverage-matrix.md +++ b/.shared-templates/docs/coverage-matrix.md @@ -8,7 +8,7 @@ repo's threshold, update both the CI file and this table in the same PR.** | Repo | Threshold | Mechanism | |---|---|---| -| `hawk` | 60% | inline `bc` check in `ci.yml` | +| `graycode` | 60% | inline `bc` check in `ci.yml` | | `eyrie` | 60% | inline `bc` check in `ci.yml` | | `harrier` | 49% | `THRESHOLD=` in `ci.yml` (go-ci.yml.tmpl) | | `shrike` | 38% | `THRESHOLD=` in `ci.yml` (go-ci.yml.tmpl) | @@ -17,7 +17,6 @@ repo's threshold, update both the CI file and this table in the same PR.** | `merlin` | 76% | `THRESHOLD=` in `ci.yml` (go-ci.yml.tmpl) | | `sparrow` | 80% | `THRESHOLD=` in `ci.yml` (go-ci.yml.tmpl) | | `robin` | 78% | `--cov-fail-under=` in `ci.yml` (python-ci.yml.tmpl) | -| `eagle` | none enforced | leaf library; add one before it grows past a handful of files | | `falcon` | none enforced | leaf library; add one before it grows past a handful of files | | `starling` | n/a | no Go/Python test suite (skill/content registry) | diff --git a/.shared-templates/lefthook.yml.tmpl b/.shared-templates/lefthook.yml.tmpl index d177327d..80d30873 100644 --- a/.shared-templates/lefthook.yml.tmpl +++ b/.shared-templates/lefthook.yml.tmpl @@ -139,7 +139,7 @@ prepare-commit-msg: sed '/^[Cc]o-[Aa]uthored-[Bb]y:/d' "{1}" > "{1}.tmp" && mv "{1}.tmp" "{1}" # --------------------------------------------------------------------------- -# Notes for foundation repos (eagle, falcon): these have +# Notes for foundation repos (falcon): these have # no graycode-eco dependencies at all, so `pre-push.commands.boundaries` checks # for *zero* GrayCodeAI/* imports rather than checking against a peer-engine # allowlist. The command line above is identical either way — only diff --git a/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl b/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl index 3f500da5..732ab550 100644 --- a/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl +++ b/.shared-templates/scripts/check-ecosystem-boundaries.sh.tmpl @@ -13,28 +13,28 @@ cd "$ROOT_DIR" # ============================================================================= # VARIANT 1 — Support engine (eyrie, harrier, shrike, swift, kestrel, merlin). -# Engines are peers: they may depend on eagle and falcon, -# but never on hawk/internal/* or another engine. +# Engines are peers: they may depend on falcon, +# but never on graycode/internal/* or another engine. # ============================================================================= -FORBIDDEN_HAWK='github\.com/GrayCodeAI/hawk/(internal/|shared/types)' +FORBIDDEN_GRAYCODE='github\.com/GrayCodeAI/graycode-cli/(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_HAWK" --glob '*.go' . || true)" + violations="$(rg -n "$FORBIDDEN_GRAYCODE" --glob '*.go' . || true)" engine_violations="$(rg -n "$FORBIDDEN_ENGINES" --glob '*.go' . || true)" else - violations="$(grep -rn --include='*.go' -E "$FORBIDDEN_HAWK" . || true)" + violations="$(grep -rn --include='*.go' -E "$FORBIDDEN_GRAYCODE" . || true)" engine_violations="$(grep -rn --include='*.go' -E "$FORBIDDEN_ENGINES" . || true)" fi if [[ -n "${violations}" ]]; then - echo "forbidden Hawk imports found:" + echo "forbidden Graycode imports found:" echo "${violations}" echo - echo "support repos must use eagle or local contracts, not hawk/internal or removed hawk/shared/types" + echo "support repos must use local contracts, not graycode/internal or removed graycode/shared/types" exit_code=1 fi @@ -53,7 +53,7 @@ fi echo "ecosystem boundary guard passed" # ============================================================================= -# VARIANT 2 — Foundation repo (eagle, falcon). +# VARIANT 2 — Foundation repo (falcon). # Foundation repos sit below everything: zero graycode-eco dependencies at all. # ============================================================================= # @@ -70,7 +70,7 @@ 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 hawk, engines, or any other GrayCodeAI/* package" +# echo "{{OWN_MODULE}} is a foundation repo — it must not depend on graycode, engines, or any other GrayCodeAI/* package" # exit 1 # fi # @@ -78,12 +78,12 @@ echo "ecosystem boundary guard passed" # ============================================================================= # VARIANT 3 — SDK / skills consumer (sparrow, robin, -# starling). Consumers may depend on hawk's public surfaces -# only — never on a support engine directly, and never on hawk/internal. +# starling). Consumers may depend on graycode's public surfaces +# only — never on a support engine directly, and never on graycode/internal. # ============================================================================= # # FORBIDDEN_ENGINES='github\.com/GrayCodeAI/(eyrie|harrier|shrike|swift|kestrel|merlin)(/|")' -# FORBIDDEN_INTERNAL='github\.com/GrayCodeAI/hawk/internal' +# FORBIDDEN_INTERNAL='github\.com/GrayCodeAI/graycode-cli/internal' # # (same rg/grep + report pattern as Variant 1, naming "SDKs/skills must go -# through hawk's public API, not engines directly" as the violation message) +# through graycode'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 8f6fc20c..55157c93 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 hawk to bump the external/tok pin. This +# sync-submodules` + a commit in graycode 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 hawk after confirming the sibling repos are what you expect, then commit the updated external/ pins" + echo "drift detected — run 'make sync-submodules' in graycode 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 5182755c..aaf63c9f 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 hawk/docs/compatibility.md. +# Cross-repo compatibility matrix check, referenced from graycode/docs/compatibility.md. # Source of truth: .shared-templates/workflows/compatibility-test.yml.tmpl # -# Lives (and runs) in hawk only — hawk owns testdata/compatibility-matrix.json +# Lives (and runs) in graycode only — graycode 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-ci.yml.tmpl b/.shared-templates/workflows/go-ci.yml.tmpl index 676a7945..f3674249 100644 --- a/.shared-templates/workflows/go-ci.yml.tmpl +++ b/.shared-templates/workflows/go-ci.yml.tmpl @@ -228,7 +228,7 @@ jobs: run: go build ./... # ----------------------------------------------------------------------------- -# Foundation repos (eagle, falcon) have zero graycode-eco +# Foundation repos (falcon) have zero graycode-eco # dependencies, so they drop the GOPROXY/GOPRIVATE/GONOSUMDB env block above # and every "Clone " step that other repos may add for local # workspace deps — there is nothing to clone. diff --git a/.shared-templates/workflows/go-release.yml.tmpl b/.shared-templates/workflows/go-release.yml.tmpl index cd043fec..acf7509b 100644 --- a/.shared-templates/workflows/go-release.yml.tmpl +++ b/.shared-templates/workflows/go-release.yml.tmpl @@ -23,7 +23,7 @@ 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 (hawk itself clones eyrie this way via + # graycode-eco repos at build time (graycode itself clones eyrie this way via # ./.github/actions/checkout-eyrie). Omit for repos with none. - name: Set up Go diff --git a/.shared-templates/workflows/python-ci.yml.tmpl b/.shared-templates/workflows/python-ci.yml.tmpl index 8a62864a..1e470fcd 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. hawk) +# {{PACKAGE}} — importable package name for --cov (e.g. graycode) # {{THRESHOLD}} — minimum coverage percentage, kept in sync with # docs/coverage-matrix.md diff --git a/.trivyignore b/.trivyignore index 8dca84a9..297bfc82 100644 --- a/.trivyignore +++ b/.trivyignore @@ -1,10 +1,10 @@ -# Trivy OS-package ignore list for Hawk Docker images. +# Trivy OS-package ignore list for Graycode 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). 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 +# 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 # 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 a6ae8d4f..8e469916 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,15 +1,15 @@ --- -description: Extending hawk — how to write AGENTS.md files, custom specialists, skills, hooks, MCP servers, and plugins. +description: Extending graycode — 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 hawk +# Extending graycode -hawk is an open-source code intelligence platform. It lives in the `graycode-eco` -workspace alongside the ecosystem repos that power it (`eyrie`, `eagle`, +graycode 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 -hawk with custom tools, skills, hooks, and integrations. +graycode 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 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)`). +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)`). 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. hawk 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. graycode 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. hawk caps the **total** across all matched files at 32 KiB; everything past the cap is dropped. +- Keep each file under ~8 KiB. graycode 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`). hawk 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`). graycode 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: `~/.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. +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. 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 hawk's sub-agents. Three scopes, in priority order: +Specialists are graycode's sub-agents. Three scopes, in priority order: | Scope | Path | Shared? | | --- | --- | --- | -| Built-in | compiled into hawk | yes | -| User | `~/.hawk/specialists/*.md` | no — your machine only | +| Built-in | compiled into graycode | yes | +| User | `~/.graycode/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 -hawk specialist list -hawk specialist show api-reviewer -hawk specialist create api-reviewer \ +graycode specialist list +graycode specialist show api-reviewer +graycode specialist create api-reviewer \ --project \ --description "Reviews API changes" \ --tools read-only,plan \ --prompt "$(cat api-reviewer.md)" -hawk specialist edit api-reviewer --project -hawk specialist delete api-reviewer --project -hawk specialist path # prints the resolved specialists directory +graycode specialist edit api-reviewer --project +graycode specialist delete api-reviewer --project +graycode specialist path # prints the resolved specialists directory ``` ## 3. Skills -hawk ships **no bundled skills** by default. Skills are markdown instruction +graycode ships **no bundled skills** by default. Skills are markdown instruction files that extend agent capabilities, sourced from the separate `GrayCodeAI/starling` repo and installed on demand: ```bash -hawk skills search # find skills in starling -hawk skills install [skill-name] # install after user approval -hawk skills list # list installed skills -hawk skills remove +graycode skills search # find skills in starling +graycode skills install [skill-name] # install after user approval +graycode skills list # list installed skills +graycode skills remove ``` Installed skills live in user or project scope: -- User-scoped: `~/.hawk/skills/` +- User-scoped: `~/.graycode/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 -hawk hook add beforeReview --command "lint-check" -hawk hook remove beforeReview -hawk hook list +graycode hook add beforeReview --command "lint-check" +graycode hook remove beforeReview +graycode hook list ``` ## 5. MCP integration -MCP (Model Context Protocol) servers can expose tools to hawk: +MCP (Model Context Protocol) servers can expose tools to graycode: ```bash -hawk mcp add --name server --url http://localhost:8080 -hawk mcp remove server -hawk mcp list +graycode mcp add --name server --url http://localhost:8080 +graycode mcp remove server +graycode mcp list ``` ## 6. Plugins -Plugins extend hawk with custom tools and capabilities: +Plugins extend graycode with custom tools and capabilities: ```bash -hawk plugin add --name my-plugin --path ./my-plugin -hawk plugin remove my-plugin -hawk plugin list +graycode plugin add --name my-plugin --path ./my-plugin +graycode plugin remove my-plugin +graycode plugin list ``` ## 7. Verification -hawk includes a self-verification system to validate local changes before contributing: +graycode includes a self-verification system to validate local changes before contributing: ```bash -hawk verify -hawk verify --fix +graycode verify +graycode verify --fix ``` ## Development ```bash make lint -hawk verify +graycode verify ``` ### Architecture note: cross-repo contracts -Legacy `hawk/shared/types` has been removed. Cross-repo severity and finding contracts now live in `github.com/GrayCodeAI/eagle` (`eagle/types`) — extensions and support repos must import that module instead of Hawk internals. +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. ### Architecture note: provider ownership Implement provider protocols, adapters, catalog metadata, credential mappings, and -provider contract tests in `../eyrie` (the eyrie sibling repo) first. Hawk consumes providers only -through Eyrie's stable engine facade; Hawk changes should be limited to host UX +provider contract tests in `../graycode-router` (the eyrie engine's repo) first. Graycode consumes providers only +through Eyrie's stable engine facade; Graycode 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 **hawk** (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 **graycode** (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 **hawk** (97743 symbols, 322940 relations | Resource | Use for | |----------|---------| -| `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 swift | +| `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 swift | ## CLI @@ -245,10 +245,10 @@ This project is indexed by GitNexus as **hawk** (97743 symbols, 322940 relations ### Workspace workflow (sibling repos) -hawk depends on ecosystem repos (`eyrie`, `eagle`, 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. +graycode depends on ecosystem repos (`eyrie`, 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. -1. Edit + test in `../` — run its tests, run `make test` in hawk +1. Edit + test in `../` — run its tests, run `make test` in graycode 2. Push from the sibling: `git push origin ` 3. Open a PR in the sibling repo → merge to `main` -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) +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) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3ec7ef8..aba41a96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.2.0] — 2026-07-13 ### Changed -- **Hawk/Eyrie production boundary completed**: Hawk owns the product face, +- **Graycode/Eyrie production boundary completed**: Graycode owns the product face, sessions, tools, permissions, and public schemas while Eyrie v0.2.1 owns credentials, catalog resolution, provider transport, resilience, and usage telemetry behind the stable `eyrie/engine` facade. @@ -40,18 +40,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Permission system unified into two independent axes**: the old `PermissionMode` (`default`/`acceptEdits`/`bypassPermissions`/`dontAsk`/`plan`) is removed. `/autonomy` now controls the 5-tier trust ladder (`Always Ask`/`Scout`/`Builder`/`Operator`/`Autonomous`, bare `/autonomy` opens a picker), and `/spec` controls an independent, orthogonal spec-driven workflow gate (`Specify → Plan → Tasks → ApproveImplementation`, bare `/spec` opens a picker) that blocks Write/Edit/Bash regardless of trust tier — including at Autonomous. Fixes a real bug where the old Plan Mode's write-block could be silently bypassed at high autonomy tiers, since tier and mode were checked independently with no ordering guarantee. - **Fixed `PermissionService.SetAutonomy`/`Autonomy()`**: previously wrote to/read from a shadow field the permission engine's `CheckTool` never consulted, meaning autonomy tier changes may not have reliably taken effect. Now both read/write the same `PermissionEngine.Autonomy` field the check logic uses. - **`--permission-mode` CLI flag removed**; `--dangerously-skip-permissions` unchanged (now maps to the Autonomous tier). New `--dry-run` flag added as an unconditional kill switch (deny every tool call, regardless of tier or spec stage) — replaces `dontAsk`'s hard-lockout role. -- **Version re-baselined to `0.1.0`** across `cmd/hawk/main.go`, `cmd/daemon.go`, - `flake.nix`, `.github/workflows/release.yml`, and the `update`/daemon test suites, aligning hawk +- **Version re-baselined to `0.1.0`** across `cmd/graycode/main.go`, `cmd/daemon.go`, + `flake.nix`, `.github/workflows/release.yml`, and the `update`/daemon test suites, aligning graycode with the rest of the GrayCodeAI ecosystem (`eyrie`, `shrike`, `harrier`, `kestrel`, `merlin`). -- **Architecture boundary hardening**: Hawk now owns runtime request/response DTOs, transport config/provider seams, and review/verification product-boundary contracts, with `eyrie/client` usage restricted to internal adapters and guarded in CI. -- **`shared/types` removed**: Hawk no longer ships the old shared type path, and local boundary checks now block any attempt to reintroduce it. +- **Architecture boundary hardening**: Graycode now owns runtime request/response DTOs, transport config/provider seams, and review/verification product-boundary contracts, with `eyrie/client` usage restricted to internal adapters and guarded in CI. +- **`shared/types` removed**: Graycode no longer ships the old shared type path, and local boundary checks now block any attempt to reintroduce it. ### Added -- **Spec-driven workflow (`/spec`)**: independent, orthogonal permission gate that walks the model through `Specify → Plan → Tasks`, writing real `spec.md`/`plan.md`/`tasks.md` files to `.hawk/specs//`, and requires explicit `ApproveImplementation` approval (always prompts, at any trust tier) before Write/Edit/Bash unlock. The approval prompt shows the actual written content, not a blind yes/no. +- **Spec-driven workflow (`/spec`)**: independent, orthogonal permission gate that walks the model through `Specify → Plan → Tasks`, writing real `spec.md`/`plan.md`/`tasks.md` files to `.graycode/specs//`, and requires explicit `ApproveImplementation` approval (always prompts, at any trust tier) before Write/Edit/Bash unlock. The approval prompt shows the actual written content, not a blind yes/no. - **`/autonomy` and `/spec` picker overlays**: bare `/autonomy` or `/spec` opens an arrow-key-navigable, filterable picker (Esc/Enter) instead of requiring subcommand syntax; typed subcommands (`/autonomy tier scout`, `/spec status`, etc.) still work. - **Watch mode (`--watch`)**: file-watcher loop that acts on `AI!` (do-now) and `AI?` (answer) code comments. Off by default. -- **GitHub Action** (`.github/actions/hawk`): interactive mode on `@hawk` mentions, automation mode on labeled issues/PRs, and skill dispatch for `/`-prefixed prompts. -- **Messaging gateways**: opt-in Telegram, Discord, and Slack gateways on the daemon for chatting with hawk from messaging apps. +- **GitHub Action** (`.github/actions/graycode`): interactive mode on `@graycode` mentions, automation mode on labeled issues/PRs, and skill dispatch for `/`-prefixed prompts. +- **Messaging gateways**: opt-in Telegram, Discord, and Slack gateways on the daemon for chatting with graycode from messaging apps. - **AST repo-map** (`internal/context/repomap`): structural repository map for richer model context. - **Auto codebase analysis on first run** (`internal/autoinit`): opt-in seeding of project context. - **Auto-lint / auto-fix cycle**: runs the matching linter after edits and iterates on fixes with bounded retries (opt-in). @@ -73,7 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added — Round 2 ecosystem improvements (2026-06-01) - **Cavecrew personas** (`internal/multiagent/agents`): three new - built-in personas built into GrayCode Hawk + built-in personas built into GrayCode Graycode (`cavecrew-investigator`, `cavecrew-builder`, `cavecrew-reviewer`). Each enforces a strict output format so downstream agents can parse outputs mechanically: @@ -112,7 +112,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 and a command label. API: `Record`, `AggregateForSession`, `ListForSession`, `PruneForSession`. Scoped to a single session so callers can selectively compact their own history. Companion to - shrike's `internal/tracking.Tracker` (shrike tracks globally, hawk tracks + shrike's `internal/tracking.Tracker` (shrike tracks globally, graycode tracks per-session). ### Added — Production Hardening (top-50 OSS parity) @@ -147,16 +147,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.4.0] — 2026-05-05 ### Added -- **Exec Subcommand**: `hawk exec "prompt"` — full engine non-interactive mode with `--output-format json`, `--auto` autonomy levels, `--worktree` isolation, `--agent` personas, `--session-id` resume, stdin piping -- **Daemon Server**: `hawk daemon start/stop/status` — background HTTP server with JSON + SSE streaming on `/v1/chat`, `/v1/health`, `/v1/sessions` -- **Mission Mode**: `hawk mission "prompt"` — multi-agent orchestration decomposing work into parallel features executed in isolated git worktrees. `--dry-run` for planning only -- **Session Search**: `hawk search "query"` — full-text search across all saved sessions with `--json` output -- **Custom Agents**: `hawk agent list/create/show/remove` — markdown persona definitions in `~/.hawk/agents/` with YAML frontmatter (name, description, model) -- **Snapshot System**: Shadow git tracking of every file change. `hawk snapshot list/restore/diff` + `/snapshot` slash command. Auto-snapshots on every Write/Edit tool call +- **Exec Subcommand**: `graycode exec "prompt"` — full engine non-interactive mode with `--output-format json`, `--auto` autonomy levels, `--worktree` isolation, `--agent` personas, `--session-id` resume, stdin piping +- **Daemon Server**: `graycode daemon start/stop/status` — background HTTP server with JSON + SSE streaming on `/v1/chat`, `/v1/health`, `/v1/sessions` +- **Mission Mode**: `graycode mission "prompt"` — multi-agent orchestration decomposing work into parallel features executed in isolated git worktrees. `--dry-run` for planning only +- **Session Search**: `graycode search "query"` — full-text search across all saved sessions with `--json` output +- **Custom Agents**: `graycode agent list/create/show/remove` — markdown persona definitions in `~/.graycode/agents/` with YAML frontmatter (name, description, model) +- **Snapshot System**: Shadow git tracking of every file change. `graycode snapshot list/restore/diff` + `/snapshot` slash command. Auto-snapshots on every Write/Edit tool call - **Waza Workflows**: `/think` (plan before code), `/hunt` (root-cause diagnosis), `/check` (pre-ship review with auto-fix), `/design` (screenshot-driven UI iteration) - **Structured Compaction**: Summary template with Goal/Constraints/Progress/Files/Decisions/Errors/Instructions/Next sections for better intent preservation - **Doom Loop Detection**: Lowered threshold to 3 (from 4). Two-tier escalation: first detection injects redirect prompt, doom loop hard-stops with "ask user for help" -- **Session Persistence for exec**: All exec runs saved to `~/.hawk/sessions/` and searchable via `hawk search` +- **Session Persistence for exec**: All exec runs saved to `~/.graycode/sessions/` and searchable via `graycode search` ### Packages Added - `mission/` — Multi-agent orchestration with worktree-based parallel workers diff --git a/CLAUDE.md b/CLAUDE.md index a24445bd..40345896 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,7 +1,7 @@ # GitNexus — Code Intelligence -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. +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. > 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 **hawk** (97743 symbols, 322940 relations | Resource | Use for | |----------|---------| -| `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 swift | +| `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 swift | ## CLI diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index bc0e9e3a..3bfa3f3f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,7 +2,7 @@ ## Our pledge -We — the maintainers and contributors of the hawk project — pledge to +We — the maintainers and contributors of the graycode 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 5c1db48e..ac602c81 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,8 +1,8 @@ -# Contributing to hawk +# Contributing to graycode 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 `hawk` -version (`hawk --version` for binaries, `hawk.Version` for +Open an issue using the bug-report template. Include the `graycode` +version (`graycode --version` for binaries, `graycode.Version` for libraries — see this repo's `VERSION` file), reproduction steps, expected behaviour, and actual behaviour. diff --git a/Dockerfile b/Dockerfile index cd4c5d2c..f6fc28e9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,17 +9,17 @@ RUN apk upgrade --no-cache && \ WORKDIR /build # GrayCodeAI engine modules are published and pinned in go.mod at tagged or -# commit-pseudo versions, resolved from the module proxy. The committed +# commit-pseudo versions. Pins whose repositories no longer exist resolve from +# the committed third_party/modproxy (copied in via COPY . . below); everything +# else resolves from the public proxy with direct-VCS fallback. The committed # go.work (which references sibling checkouts ../) is excluded from the # build context, so build in module mode (no go.work) against those pins. -ENV GOPRIVATE=github.com/GrayCodeAI/* \ - GONOSUMDB=github.com/GrayCodeAI/* \ - GONOSUMCHECK=1 +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/hawk/main.go ldflags fallbacks. +# runnable image — matching the cmd/graycode/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 hawk ./cmd/hawk + -o graycode ./cmd/graycode -# Runtime stage — Alpine (hawk requires git + bash for workspace operations; distroless excluded) +# Runtime stage — Alpine (graycode 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/hawk hawk + adduser -D -u 1000 -h /home/graycode graycode -COPY --from=builder /build/hawk /usr/local/bin/hawk +COPY --from=builder /build/graycode /usr/local/bin/graycode COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo -USER hawk +USER graycode WORKDIR /workspace -ENTRYPOINT ["tini", "--", "hawk"] +ENTRYPOINT ["tini", "--", "graycode"] CMD ["--help"] diff --git a/Dockerfile.daemon b/Dockerfile.daemon index 1b6d132c..e975f10f 100644 --- a/Dockerfile.daemon +++ b/Dockerfile.daemon @@ -1,9 +1,9 @@ -# Dockerfile for the Hawk daemon (background HTTP server). +# Dockerfile for the Graycode 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 hawk-daemon . -# Run: docker run -p 4590:4590 -e HAWK_DAEMON_API_KEY=... hawk-daemon +# Build: docker build -f Dockerfile.daemon -t graycode-daemon . +# Run: docker run -p 4590:4590 -e GRAYCODE_DAEMON_API_KEY=... graycode-daemon FROM golang:1.26.6-alpine@sha256:af8d6740070b8906d12eae1c3e3ea0957fb63f492051ea05e354c38ef9fe88df AS builder RUN apk upgrade --no-cache && \ @@ -11,9 +11,8 @@ RUN apk upgrade --no-cache && \ WORKDIR /build -ENV GOPRIVATE=github.com/GrayCodeAI/* \ - GONOSUMDB=github.com/GrayCodeAI/* \ - GONOSUMCHECK=1 +# Module resolution is file-proxy-first (see Dockerfile); no GOPRIVATE override. +ENV GOPROXY=file:///build/third_party/modproxy,https://proxy.golang.org,direct ARG VERSION=dev ARG COMMIT=none @@ -29,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 hawk ./cmd/hawk + -o graycode ./cmd/graycode 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/hawk hawk + adduser -D -u 1000 -h /home/graycode graycode # Create state directory for daemon logs, API key, and audit log. -RUN mkdir -p /home/hawk/.hawk/state && \ - chown -R hawk:hawk /home/hawk/.hawk +RUN mkdir -p /home/graycode/.graycode/state && \ + chown -R graycode:graycode /home/graycode/.graycode -COPY --from=builder /build/hawk /usr/local/bin/hawk +COPY --from=builder /build/graycode /usr/local/bin/graycode COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo -COPY packaging/systemd/hawk-daemon.service /etc/systemd/system/hawk-daemon.service +COPY packaging/systemd/graycode-daemon.service /etc/systemd/system/graycode-daemon.service -USER hawk +USER graycode WORKDIR /workspace EXPOSE 4590 @@ -54,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", "--", "hawk", "daemon", "start"] +ENTRYPOINT ["tini", "--", "graycode", "daemon", "start"] CMD ["--host", "0.0.0.0", "--port", "4590"] diff --git a/Makefile b/Makefile index 1dcde635..75a936b8 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: hawk, .. +# Placeholders rendered per repo: graycode, .. # --------------------------------------------------------------------------- # Project metadata # --------------------------------------------------------------------------- -NAME := hawk -MAIN_PKG := ./cmd/hawk +NAME := graycode +MAIN_PKG := ./cmd/graycode # --------------------------------------------------------------------------- # Versioning — sourced from VERSION file; falls back to git describe. -# See https://github.com/GrayCodeAI/hawk/blob/main/docs/versioning.md. +# See https://github.com/GrayCodeAI/graycode-cli/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 contracts-parity 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 \ +.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,10 +112,10 @@ fmt: ## Format source files (gofumpt + goimports). vet: ## Run go vet. go vet ./... -contracts-guard: ## Fail on any legacy imports of removed hawk/shared/types. +contracts-guard: ## Fail on any legacy imports of removed graycode/shared/types. bash ./scripts/check-shared-types-imports.sh -ecosystem-guard: ## Fail if external ecosystem repos import hawk/internal or removed hawk/shared/types. +ecosystem-guard: ## Fail if external ecosystem repos import graycode/internal or removed graycode/shared/types. bash ./scripts/check-ecosystem-boundaries.sh eyrie-client-guard: ## Fail on any production eyrie/client import. @@ -124,19 +124,16 @@ eyrie-client-guard: ## Fail on any production eyrie/client import. 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 Hawk contracts. +peer-guard: ## Fail if support engines import each other instead of depending only on Graycode contracts. bash ./scripts/check-support-repo-coupling.sh -internal-layers-guard: ## Enforce one-way dependencies across stable Hawk internal layers. +internal-layers-guard: ## Enforce one-way dependencies across stable Graycode 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 -contracts-parity: ## Fail if ecosystem repos pin different Eagle versions (see ecosystem.yaml). - bash ./scripts/check-contracts-parity.sh - -boundaries: manifest-guard check-replace contracts-guard contracts-parity 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). +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 @@ -164,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-hawk.sh + ./scripts/smoke-graycode.sh path: ## Verify developer path (setup, security, milestone tests). ./scripts/verify-developer-path.sh @@ -188,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 hawk development environment ===" + @echo "=== Setting up graycode development environment ===" @echo "✓ go.work generated and synced from ecosystem.yaml" @echo "" @echo "=== Environment check ===" @@ -214,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 (hawk-specific extension to the canonical template). +# Compatibility matrix (graycode-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. # --------------------------------------------------------------------------- @@ -226,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 Hawk and sibling repositories. Never fails. +compat-drift: ## Advisory: report pin drift between Graycode 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 1b29118e..949b58e7 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,9 @@

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

@@ -24,13 +24,13 @@ --- -## Why hawk +## Why graycode -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. +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. -**Developer path:** one machine, keychain credentials, local memory. Run `hawk path` to check readiness. +**Developer path:** one machine, keychain credentials, local memory. Run `graycode path` to check readiness. -- **Model-agnostic** — supports 28 first-class providers through [eyrie](https://github.com/GrayCodeAI/eyrie), including Anthropic, OpenAI, Gemini, Fireworks AI, Concentrate AI (pay-as-you-go), DeepSeek, and Ollama +- **Model-agnostic** — supports 28 first-class providers through [eyrie](https://github.com/GrayCodeAI/graycode-router), 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 @@ -39,59 +39,59 @@ hawk is an AI-powered coding agent that lives in your terminal. It reads your co ## Status -**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. +**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. -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). +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). ## Install (60 seconds) -Pick one — all install the same `hawk` binary (versioned into `~/.hawk/bin`, symlinked as `hawk`): +Pick one — all install the same `graycode` binary (versioned into `~/.graycode/bin`, symlinked as `graycode`): ```bash # 1. Script (any shell, verifies checksum; cosign signature when available) -curl -fsSL https://raw.githubusercontent.com/GrayCodeAI/hawk/main/install.sh | sh +curl -fsSL https://raw.githubusercontent.com/GrayCodeAI/graycode-cli/main/install.sh | sh # 2. Homebrew (macOS / Linuxbrew) — after the next tagged release -brew install graycodeai/tap/hawk +brew install graycodeai/tap/graycode # 3. npm (wraps the same release binaries) -npm install -g @graycodeai/hawk +npm install -g @graycodeai/graycode ``` -If `~/.hawk/bin` is not on your `PATH`, add it to your shell profile. +If `~/.graycode/bin` is not on your `PATH`, add it to your shell profile. Then: ```bash -hawk # interactive REPL (/config on first run: API key + model) -hawk path # verify readiness +graycode # interactive REPL (/config on first run: API key + model) +graycode path # verify readiness ``` ## Quick Start (contributors — from source) ```bash -git clone https://github.com/GrayCodeAI/hawk && cd hawk +git clone https://github.com/GrayCodeAI/graycode-cli && cd graycode-cli make setup # generates go.work referencing sibling support repos in the graycode-eco workspace -go build -o hawk ./cmd/hawk -./hawk +go build -o graycode ./cmd/graycode +./graycode # First run — paste API key in /config (stored in macOS Keychain / Linux keyring) # Verify readiness -./hawk path +./graycode path ``` Docker is required for agent command execution. Start the Docker daemon before -launching Hawk; there is no host-execution fallback. Hawk automatically uses -the versioned public `graycodeai/hawk-sandbox` image. When the image is not -local, Hawk pulls it anonymously; if the registry is unavailable, Hawk builds +launching Graycode; there is no host-execution fallback. Graycode automatically uses +the versioned public `graycodeai/graycode-sandbox` image. When the image is not +local, Graycode pulls it anonymously; if the registry is unavailable, Graycode builds the bundled sandbox image locally through Docker. -See [docs/SECURITY-DEVELOPER.md](docs/SECURITY-DEVELOPER.md) for the credential model. Do not put API keys in shell env or `.env` for hawk. +See [docs/SECURITY-DEVELOPER.md](docs/SECURITY-DEVELOPER.md) for the credential model. Do not put API keys in shell env or `.env` for graycode. Optional for contributors: ```bash -go install github.com/GrayCodeAI/hawk/cmd/hawk@latest +go install github.com/GrayCodeAI/graycode-cli/cmd/graycode@latest ``` ## Features @@ -114,26 +114,26 @@ Built with [Bubble Tea](https://github.com/charmbracelet/bubbletea) for a smooth ### Portable Execution Graph -Export the latest or a selected Hawk session as validated graph nodes, edges, +Export the latest or a selected Graycode session as validated graph nodes, edges, and lifecycle events: ```bash -hawk graph export -hawk graph export -hawk graph export --swift-checkpoint abc123def456 -hawk graph export --mission-dir /path/to/mission +graycode graph export +graycode graph export +graycode graph export --swift-checkpoint abc123def456 +graycode graph export --mission-dir /path/to/mission # Explicitly privacy-normalize and sync the graph for a connected cloud project -hawk cloud graph sync -hawk cloud graph sync --mission-dir /path/to/mission +graycode cloud graph sync +graycode cloud graph sync --mission-dir /path/to/mission ``` The export contains metadata and hashes, not prompts, tool arguments/results, policy reasons, verification evidence, or runtime output. Swift remains -available separately as `hawk swift graph export`. Persisted chat sessions +available separately as `graycode 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 Hawk code-index chunks actually selected for inference are also +subgraphs and Graycode 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 @@ -146,7 +146,7 @@ explicitly with the `--mission-dir` variants above. For larger tasks, decompose work into parallel feature branches (power-user / future team workflows): ```bash -hawk mission "Add auth, rate limiting, and logging" +graycode mission "Add auth, rate limiting, and logging" ``` Each sub-agent runs in its own git worktree with full autonomy. @@ -156,14 +156,14 @@ Each sub-agent runs in its own git worktree with full autonomy. Discover and install modular instruction packages for specialized workflows: ```bash -hawk skills search api # Search community registry -hawk skills install go-review # Install from GitHub -hawk skills audit # Security scan installed skills +graycode skills search api # Search community registry +graycode skills install go-review # Install from GitHub +graycode skills audit # Security scan installed skills ``` ### Permission Center -hawk exposes two independent chat command centers — trust tier and the +graycode exposes two independent chat command centers — trust tier and the spec-driven workflow gate — rather than one merged permission mode: ```text @@ -200,7 +200,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 `.hawk/specs//`, and blocks Write/Edit/Bash + writing real files to `.graycode/specs//`, and blocks Write/Edit/Bash until you approve moving to implementation — at **any** trust tier, including Autonomous. @@ -214,23 +214,23 @@ Connect external tools via [Model Context Protocol](https://modelcontextprotocol ### Watch Mode (AI-comment loop) -`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. +`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. ### CI / GitHub Action -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`). +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`). ### Messaging Gateways (opt-in) -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. +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. ### 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, hawk 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, graycode can auto-analyze the codebase to seed context (default-off, opt-in). ### Auto-Lint / Auto-Fix Cycle -After edits, hawk can run the matching linter and iterate on fixes (bounded retries) before handing back. Opt-in; preserves existing behavior when disabled. +After edits, graycode can run the matching linter and iterate on fixes (bounded retries) before handing back. Opt-in; preserves existing behavior when disabled. ### Image / Multimodal Context @@ -266,16 +266,16 @@ Features adopted from open-source agent projects. All are off by default unless | Feature | Flag / Command | What it does | |---|---|---| -| 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) | +| 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) | | Structural code match | `CodeMatch` tool | Tree-sitter query search over Go/Python/TS/TSX | -| Composable toolsets | `hawk toolset [name]` + `Toolset` tool | Named tool groups (research, dev, ops, full_stack) | +| Composable toolsets | `graycode 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 `eyrie/client` (`ImageClient`), wired by the host (boundary-guarded — hawk routes through the eyrie facade) | +| 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 — graycode 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 | @@ -287,28 +287,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 `hawk 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 `graycode 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 `hawk 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 `graycode 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 `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 | +| 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 | ## Usage ### Interactive Mode ```bash -hawk # Start REPL -hawk -r abc123 # Resume session -hawk -c # Continue latest session -hawk --provider openai --model gpt-4o # Override provider +graycode # Start REPL +graycode -r abc123 # Resume session +graycode -c # Continue latest session +graycode --provider openai --model gpt-4o # Override provider ``` ### Permission Examples @@ -328,40 +328,40 @@ hawk --provider openai --model gpt-4o # Override provider ### Non-Interactive Mode ```bash -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 +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 ``` ### Diagnostics & ecosystem ```bash -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 +graycode path # Developer path readiness (setup + security + sandbox) +graycode doctor # Full health report (eyrie + 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 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 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 all 15 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 -hawk daemon start # Background HTTP server on port 4590 -hawk daemon status # Check if running -hawk daemon stop # Graceful shutdown +graycode daemon start # Background HTTP server on port 4590 +graycode daemon status # Check if running +graycode daemon stop # Graceful shutdown ``` Endpoints: `GET /v1/health`, `GET /v1/ready` (dependency-aware readiness), `POST /v1/chat` (JSON or SSE streaming) @@ -369,15 +369,15 @@ Endpoints: `GET /v1/health`, `GET /v1/ready` (dependency-aware readiness), `POST ### Mission Mode ```bash -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 +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 ``` ## Providers -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. +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. | Provider | ID | Key (via `/config`) | |---|---|---| @@ -395,20 +395,20 @@ hawk works with any LLM provider. **Developer path:** paste keys in `/config` (s | 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 [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 +Provider routing, model resolution, and retries are handled by [eyrie](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 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 +instead of changing Graycode. In chat, run `/refresh-model-catalog` to fetch the latest deployment-aware catalog into `~/.eyrie/model_catalog.json`. ## Architecture -hawk is built in Go with a modular, layered architecture: +graycode is built in Go with a modular, layered architecture: ``` -hawk/ -├── bin/ # Built binaries (hawk, hawk_bin) +graycode/ +├── bin/ # Built binaries (graycode, graycode_bin) ├── cmd/ # CLI entry point (Cobra + Bubble Tea TUI) ├── internal/ │ ├── engine/ # Agent loop, compaction, self-improvement @@ -437,60 +437,54 @@ hawk/ Ecosystem sibling repos (independent Git repos in the `graycode-eco` parent folder): ├── eyrie/ # LLM provider runtime -├── eagle/ # Shared cross-repo contracts -├── merlin/ # Merlin security audit library -├── kestrel/ # Kestrel diff-based code review -├── shrike/ # Shrike tokenizer, compression, secrets scanning -├── swift/ # Swift session capture and replay -└── harrier/ # Harrier graph-based persistent memory ``` ### Ecosystem -hawk is the main CLI/product and integrates these GrayCodeAI repositories in +graycode is the main CLI/product and integrates these GrayCodeAI repositories in three runtime layers plus optional tooling/platform services: -- **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:** **eagle** holds neutral cross-repo types and **falcon** +- **Primary product:** **graycode** is the only end-user product surface in this ecosystem. +- **Support engines mounted by Graycode:** **eyrie**, **harrier**, **shrike**, **swift**, **kestrel**, **merlin**. Graycode imports or shells into these engines behind its own command surface. +- **Shared foundations:** **falcon** provides shared MCP server scaffolding. - **API consumers/extensions:** **sparrow**, **robin**, and **wren** consume - Hawk's daemon API; **starling** provides Hawk skills. + Graycode's daemon API; **starling** provides Graycode skills. - **Tooling/platform:** **owl** visualizes the generated ecosystem graph; - **graycode-platform** contains the optional web/BFF/Hawk Cloud plane and is - outside the Hawk Go runtime graph. + **graycode-platform** contains the optional web/BFF/Graycode Cloud plane and is + outside the Graycode Go runtime graph. Local development uses: -- **`go.mod` modules:** pinned requirements for the support engines and `eagle` +- **`go.mod` modules:** pinned requirements for the support engines - **Workspace + `go.work`:** sibling support repos are cloned in the `graycode-eco` workspace (as `../`); `go.work` resolves the module paths to those local checkouts - **Module-mode builds:** standalone / Docker builds resolve the pinned `go.mod` versions from the module proxy (no workspace) -Cross-repo contracts now live in **`github.com/GrayCodeAI/eagle`** so support repos do not depend on Hawk internals. The old `hawk/shared/types` path has been removed; use `eagle/types` for shared severity and finding contracts. +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; +external consumers should vendor the needed DTOs from `internal/contracts` +until a published contracts module exists. -Current contract packages: +Current contract packages (`internal/contracts/`): -- `eagle/types` — severity, findings -- `eagle/review` — normalized review findings, comments, stats, results -- `eagle/verify` — normalized verification findings, stats, reports -- `eagle/tools` — tool call/result contracts -- `eagle/events` — normalized tool/swift events -- `eagle/policy` — permission and policy verdict contracts +- `types` — severity, findings +- `graph` — portable graph vocabulary: nodes, edges, events, provenance +- `agent` — typed subagent spawn DTOs and hook events +- `policy` — permission and policy verdict contracts +- `contracts/review` — normalized review findings, comments, stats, results +- `contracts/verify` — normalized verification findings, stats, reports +- `events` — tool, trace, and usage events +- `harness` — harness evaluation reports and dimension scores You may keep a **personal** parent **`go.work`** that lists alternate clones on disk for multi-repo development. | Component | Repository | Purpose | |---|---|---| -| **hawk** | This repo | AI coding agent | -| **eyrie** | [GrayCodeAI/eyrie](https://github.com/GrayCodeAI/eyrie) | LLM provider runtime | -| **kestrel** | [GrayCodeAI/kestrel](https://github.com/GrayCodeAI/kestrel) | Diff-based code review (`hawk kestrel`) | -| **merlin** | [GrayCodeAI/merlin](https://github.com/GrayCodeAI/merlin) | Site audit library | -| **shrike** | [GrayCodeAI/shrike](https://github.com/GrayCodeAI/shrike) | Compression, redaction, token/cost budgets, and privacy-safe runtime graph facts | -| **harrier** | [GrayCodeAI/harrier](https://github.com/GrayCodeAI/harrier) | Graph-based memory | -| **swift** | [GrayCodeAI/swift](https://github.com/GrayCodeAI/swift) | Session capture and replay engine mounted as `hawk swift ...` | -| **eagle** | [GrayCodeAI/eagle](https://github.com/GrayCodeAI/eagle) | Shared contracts and neutral cross-repo vocabulary | - -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). +| **graycode** | This repo | AI coding agent | +| **eyrie** | [GrayCodeAI/graycode-router](https://github.com/GrayCodeAI/graycode-router) | LLM provider runtime | + +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 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). @@ -504,7 +498,7 @@ and the Swift correlation contract, see ### Build & Test ```bash -go build ./cmd/hawk # Build binary +go build ./cmd/graycode # 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 @@ -512,7 +506,7 @@ make cover # Generate coverage report ### Project Structure -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. +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. ## Contributing diff --git a/SECURITY.md b/SECURITY.md index 543fb1a7..17b0d5bf 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,4 +1,4 @@ -# Security Policy — hawk +# Security Policy — graycode ## Supported versions @@ -7,14 +7,14 @@ 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/hawk/blob/main/docs/versioning.md) +file at the repo root. See [`docs/versioning.md`](https://github.com/GrayCodeAI/graycode-cli/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/hawk/security/advisories/new), **or** +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. Include in your report: @@ -63,44 +63,53 @@ 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 hawk integrates with (report to the +- LLM provider services that graycode integrates with (report to the provider). - Local filesystem misuse where an attacker already has shell access (out of threat model). -For hawk-specific threat-model notes, see the README and any docs in +For graycode-specific threat-model notes, see the README and any docs in this repo. ## Config security model ### Clone-and-load attack defense -Project-level `.hawk/settings.json` can be committed to a git repository. +Project-level `.graycode/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 hawk in that directory. +arbitrary commands when a developer clones and runs graycode in that directory. -**Mitigation:** Project-level MCP servers are blocked by default. They are -only loaded when the user explicitly passes `--allow-project-mcp` on the -command line. Global MCP servers (from `~/.hawk/settings.json`) are always -loaded. +**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 +`internal/trust/store.go`). There is no `--allow-project-mcp` flag. +Global MCP servers (from `~/.graycode/settings.json`) are always loaded. ### Security-sensitive fields -The following settings **cannot** be overridden by project-level config: -- MCP servers (blocked by default, require explicit `--allow-project-mcp` flag) +The following settings **cannot** be set by project-level config (stripped by +`projectSafeSettings`): +- `model`, `provider` (selection stays in global config) +- `auto_allow`, `allowed_tools`, `disallowed_tools`, `never_allow` (permissions) +- MCP servers, custom providers, `deployment_routing`, thinking flags - API keys (never stored in settings.json; use OS secret store via `/config`) -The following settings **can** be overridden by project config: -- `model`, `provider` (convenience, not a security risk) -- `theme`, `auto_allow`, `allowed_tools`, `disallowed_tools` -- `max_budget_usd`, `sandbox`, `autonomy` +The following settings **can** be set by project config (anything not stripped): +- `theme`, `autonomy`, `sandbox`, `max_budget_usd`, and other + repository-local behavior ### Config merge precedence -1. Global `~/.hawk/settings.json` (lowest priority) -2. Project `.hawk/settings.json` (overrides global) -3. CLI `--settings` flag (overrides both) -4. Environment variables (highest priority for specific keys) +Highest priority first (`LoadSettings` / `LoadSettingsWithOverride` in +`internal/config/settings.go`, per-command flag resolution in `cmd/options.go`): + +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` +6. Built-in defaults (lowest priority) Project-level config CANNOT escalate permissions beyond what global config allows. The `MergeSettings` function in `internal/config/settings.go` diff --git a/VERSION b/VERSION index 0ea3a944..8acdd82b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.0 +0.0.1 diff --git a/api/openapi.yaml b/api/openapi.yaml index e52930c4..569f1db2 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -1,19 +1,19 @@ openapi: "3.1.0" info: - title: Hawk Daemon API + title: Graycode Daemon API description: | - HTTP API served by the hawk daemon on port 4590. + HTTP API served by the graycode daemon on port 4590. Used by sparrow, robin, and external integrations. - The daemon must be running (`hawk daemon start`) before calling these endpoints. + The daemon must be running (`graycode 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/hawk/blob/main/LICENSE + url: https://github.com/GrayCodeAI/graycode-cli/blob/main/LICENSE contact: - url: https://github.com/GrayCodeAI/hawk + url: https://github.com/GrayCodeAI/graycode-cli 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 Hawk agent persona to apply. Continuations inherit the persisted persona when omitted. + description: Named Graycode 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: [hawk.graph/v1] + enum: [graycode.graph/v1] generated_at: type: string format: date-time @@ -457,7 +457,7 @@ components: properties: status: type: string - description: Output of `hawk review status` + description: Output of `graycode review status` Error: type: object @@ -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-Hawk-Session-ID` header identify a retrievable `/v1/sessions/{id}`. + `X-Graycode-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-Hawk-Session-ID: + X-Graycode-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 Hawk's privacy-safe, read-only `hawk.graph/v1` projection. + Returns Graycode's privacy-safe, read-only `graycode.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 Hawk. + Swift session correlation performed by Graycode. parameters: - name: id in: path diff --git a/cmd/acp.go b/cmd/acp.go index f0cc3f3c..16e88220 100644 --- a/cmd/acp.go +++ b/cmd/acp.go @@ -7,19 +7,19 @@ import ( "path/filepath" "syscall" - "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/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/spf13/cobra" ) var acpCmd = &cobra.Command{ Use: "acp", - 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 " + + 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 " + "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 := hawkconfig.LoadSettings() - newSession := newConfiguredHawkSessionFactory(settings, logger.New(io.Discard, logger.Error)) + settings := graycodeconfig.LoadSettings() + newSession := newConfiguredGraycodeSessionFactory(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 1e26c13f..4e4c6549 100644 --- a/cmd/agent.go +++ b/cmd/agent.go @@ -7,7 +7,7 @@ import ( "path/filepath" "text/tabwriter" - "github.com/GrayCodeAI/hawk/internal/multiagent/agents" + "github.com/GrayCodeAI/graycode-cli/internal/multiagent/agents" "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 Hawk user state.", + Long: "Create, list, and manage custom agent personas stored in Graycode user state.", } var agentListJSON bool @@ -81,7 +81,7 @@ func runAgentList(cmd *cobra.Command, _ []string) error { return nil } if len(all) == 0 { - fmt.Printf("No agents found. Create one with: hawk agent create \n") + fmt.Printf("No agents found. Create one with: graycode agent create \n") fmt.Printf("Agent directory: %s\n", agents.DefaultDir()) return nil } diff --git a/cmd/agent_grid.go b/cmd/agent_grid.go index 30cacfc1..d5fddb4a 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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(hawkColor).Padding(0, 1) + agentActiveStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder()).BorderForeground(graycodeColor).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 8ece3017..699822ce 100644 --- a/cmd/agent_grid_test.go +++ b/cmd/agent_grid_test.go @@ -3,7 +3,7 @@ package cmd import ( "testing" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) func TestAgentStateString(t *testing.T) { diff --git a/cmd/agent_test.go b/cmd/agent_test.go index ef4bad43..3a498907 100644 --- a/cmd/agent_test.go +++ b/cmd/agent_test.go @@ -7,7 +7,7 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/hawk/internal/multiagent/agents" + "github.com/GrayCodeAI/graycode-cli/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("HAWK_STATE_DIR", filepath.Join(dir, "state")) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", stateDir) + t.Setenv("GRAYCODE_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 82b203fb..8cd66252 100644 --- a/cmd/ai_comments.go +++ b/cmd/ai_comments.go @@ -9,7 +9,7 @@ import ( "sort" "strings" - "github.com/GrayCodeAI/hawk/internal/fsutil" + "github.com/GrayCodeAI/graycode-cli/internal/fsutil" ) // AIDirective represents a found AI comment directive in a source file. diff --git a/cmd/audit.go b/cmd/audit.go index 7b90adae..695235f8 100644 --- a/cmd/audit.go +++ b/cmd/audit.go @@ -9,8 +9,8 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/hooks/audit" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/hooks/audit" + "github.com/GrayCodeAI/graycode-cli/internal/storage" "github.com/spf13/cobra" ) @@ -29,7 +29,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 hawk would have caught with current policies enabled, +Reports what graycode would have caught with current policies enabled, plus audit-only detectors that identify optimization opportunities.`, RunE: runAudit, } @@ -179,9 +179,9 @@ func discoverSessions(days int, projectFilter string) ([]SessionInfo, error) { cutoff := time.Now().AddDate(0, 0, -days) var sessions []SessionInfo - // Scan hawk sessions directory - hawkDir := storage.SessionsDir() - entries, err := os.ReadDir(hawkDir) + // Scan graycode sessions directory + graycodeDir := storage.SessionsDir() + entries, err := os.ReadDir(graycodeDir) if err != nil && !os.IsNotExist(err) { return nil, err } @@ -190,7 +190,7 @@ func discoverSessions(days int, projectFilter string) ([]SessionInfo, error) { if e.IsDir() || !strings.HasSuffix(e.Name(), ".jsonl") { continue } - path := filepath.Join(hawkDir, e.Name()) + path := filepath.Join(graycodeDir, e.Name()) info, err := os.Stat(path) if err != nil { continue @@ -255,7 +255,7 @@ func printAuditText(cmd *cobra.Command, result AuditResult) { _, _ = fmt.Fprintf(w, "\n") _, _ = fmt.Fprintf(w, "═══════════════════════════════════════════════════════════════\n") - _, _ = fmt.Fprintf(w, " Hawk Audit Report\n") + _, _ = fmt.Fprintf(w, " Graycode Audit Report\n") _, _ = fmt.Fprintf(w, "═══════════════════════════════════════════════════════════════\n") _, _ = fmt.Fprintf(w, "\n") _, _ = fmt.Fprintf(w, " Scanned: %d sessions (last %d days)\n", result.Sessions, result.Days) diff --git a/cmd/autoinit.go b/cmd/autoinit.go index ddff08eb..29e826e9 100644 --- a/cmd/autoinit.go +++ b/cmd/autoinit.go @@ -8,8 +8,8 @@ import ( "path/filepath" "time" - "github.com/GrayCodeAI/hawk/internal/autoinit" - "github.com/GrayCodeAI/hawk/internal/intelligence/repomap" + "github.com/GrayCodeAI/graycode-cli/internal/autoinit" + "github.com/GrayCodeAI/graycode-cli/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 / HAWK.md +// - It is a no-op unless the project has NO context file (AGENTS.md / GRAYCODE.md // / CLAUDE.md / CONTEXT.md), no auto-init marker exists yet, and the -// HAWK_DISABLE_AUTO_INIT kill switch is unset — all enforced by +// GRAYCODE_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 / `hawk init` without coupling to the agentic flow: a cheap, +// init-deep skill / `graycode 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 hawk on first run. Edit freely or delete to regenerate. -> Set HAWK_DISABLE_AUTO_INIT=1 to opt out of automatic context generation. +> 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. ## Package map diff --git a/cmd/autoinit_test.go b/cmd/autoinit_test.go index 12bb0977..ff7f35e7 100644 --- a/cmd/autoinit_test.go +++ b/cmd/autoinit_test.go @@ -6,14 +6,14 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/hawk/internal/autoinit" + "github.com/GrayCodeAI/graycode-cli/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("HAWK_STATE_DIR", filepath.Join(t.TempDir(), "state")) + t.Setenv("GRAYCODE_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 daf52911..17647c7d 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" "github.com/mattn/go-runewidth" ) diff --git a/cmd/autonomy_picker_test.go b/cmd/autonomy_picker_test.go index fc06a75c..fbe6ed07 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" "github.com/mattn/go-runewidth" ) diff --git a/cmd/autonomy_tiers.go b/cmd/autonomy_tiers.go index ed2a9cce..de0d8782 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 045f63a5..135c9bf2 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) func TestAutonomyTierDescriptions_PlainLanguage(t *testing.T) { diff --git a/cmd/autonomy_tiers_test.go b/cmd/autonomy_tiers_test.go index f129b9e3..7ce1eb03 100644 --- a/cmd/autonomy_tiers_test.go +++ b/cmd/autonomy_tiers_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) func TestAutonomyTierNames(t *testing.T) { diff --git a/cmd/bg_sessions.go b/cmd/bg_sessions.go index 48f9596c..af1e0481 100644 --- a/cmd/bg_sessions.go +++ b/cmd/bg_sessions.go @@ -10,12 +10,12 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/storage" "github.com/spf13/cobra" ) // ───────────────────────────────────────────────────────────────────────────── -// Background Sessions — run hawk sessions in the background and manage them. +// Background Sessions — run graycode sessions in the background and manage them. // ───────────────────────────────────────────────────────────────────────────── func bgSessionsDir() string { @@ -118,15 +118,15 @@ func KillBGSession(id string) error { return SaveBGSession(info) } -// StartBGSession launches hawk in background mode. +// StartBGSession launches graycode in background mode. func StartBGSession(prompt string, args []string) (*BGSessionInfo, error) { id := genID() cwd, _ := os.Getwd() logFile := filepath.Join(bgSessionsDir(), id+".log") - // Build command: hawk --print with all inherited flags + // Build command: graycode --print with all inherited flags cmdArgs := append([]string{"--print", "--session-id", id, prompt}, args...) - cmd := exec.CommandContext(context.Background(), "hawk", cmdArgs...) // #nosec G204 -- fixed command 'hawk' relaunching self with internal flags + cmd := exec.CommandContext(context.Background(), "graycode", cmdArgs...) // #nosec G204 -- fixed command 'graycode' relaunching self with internal flags cmd.Dir = cwd // 0600: the log captures full session output (private user state, matching @@ -190,11 +190,11 @@ func FormatBGSessions(sessions []*BGSessionInfo) string { var bgCmd = &cobra.Command{ Use: "bg [prompt]", Short: "Run a session in the background", - Long: `Start hawk in the background and continue working in your terminal. + Long: `Start graycode in the background and continue working in your terminal. Examples: - hawk bg "Refactor the auth module" - hawk bg "Run tests and fix failures"`, + graycode bg "Refactor the auth module" + graycode bg "Run tests and fix failures"`, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { return fmt.Errorf("prompt required") @@ -212,7 +212,7 @@ Examples: if len(attachID) > 8 { attachID = attachID[:8] } - cmd.Printf("Attach: hawk attach %s\n", attachID) + cmd.Printf("Attach: graycode attach %s\n", attachID) return nil }, } diff --git a/cmd/braille_spinner.go b/cmd/braille_spinner.go index 4429358b..3080f480 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" - 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" + 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" ) -// hawkSpinnerGlyphs is the default TUI spinner — partial-circle compass (smooth, readable). -var hawkSpinnerGlyphs = []string{"◐", "◓", "◑", "◒"} +// graycodeSpinnerGlyphs is the default TUI spinner — partial-circle compass (smooth, readable). +var graycodeSpinnerGlyphs = []string{"◐", "◓", "◑", "◒"} -// hawkQuadBlockGlyphs is the legacy QUADBLOCK animation (kept for tests / bubbles compat). -var hawkQuadBlockGlyphs = []string{"▛", "▜", "▟", "▙"} +// graycodeQuadBlockGlyphs is the legacy QUADBLOCK animation (kept for tests / bubbles compat). +var graycodeQuadBlockGlyphs = []string{"▛", "▜", "▟", "▙"} // spinnerFrames maps style names to their animation frames. var spinnerFrames = map[SpinnerStyle][]string{ - SpinnerBraille: {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}, - SpinnerBrailleWave: {"⠁⠂⠄⡀", "⠂⠄⡀⢀", "⠄⡀⢀⠠", "⡀⢀⠠⠐", "⢀⠠⠐⠈", "⠠⠐⠈⠁", "⠐⠈⠁⠂", "⠈⠁⠂⠄"}, - SpinnerHawk: hawkSpinnerGlyphs, - SpinnerHawkQuad: hawkQuadBlockGlyphs, - SpinnerDNA: {"⠋⠉⠙⠚", "⠉⠙⠚⠒", "⠙⠚⠒⠂", "⠚⠒⠂⠂", "⠒⠂⠂⠒", "⠂⠂⠒⠲", "⠂⠒⠲⠴", "⠒⠲⠴⠤", "⠲⠴⠤⠄", "⠴⠤⠄⠋", "⠤⠄⠋⠉", "⠄⠋⠉⠙"}, - SpinnerScan: {"⡇⠀⠀⠀", "⣿⠀⠀⠀", "⢸⡇⠀⠀", "⠀⣿⠀⠀", "⠀⢸⡇⠀", "⠀⠀⣿⠀", "⠀⠀⢸⡇", "⠀⠀⠀⣿", "⠀⠀⠀⢸", "⠀⠀⠀⠀"}, - SpinnerPulse: {"⠀", "⠄", "⠆", "⠇", "⡇", "⣇", "⣧", "⣷", "⣿", "⣷", "⣧", "⣇", "⡇", "⠇", "⠆", "⠄"}, - SpinnerSnake: {"⠈⠁", "⠈⠑", "⠈⠱", "⠈⡱", "⢁⡱", "⢁⡰", "⢁⡠", "⢁⡀", "⢁⠀", "⠁⠀"}, - SpinnerOrbit: {"⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠", "⣀", "⢠", "⢐", "⢈", "⢁"}, - SpinnerWing: {"⫷", "⫸"}, - SpinnerTalons: {"⩤", "⩥", "⩦", "⩧"}, + SpinnerBraille: {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}, + SpinnerBrailleWave: {"⠁⠂⠄⡀", "⠂⠄⡀⢀", "⠄⡀⢀⠠", "⡀⢀⠠⠐", "⢀⠠⠐⠈", "⠠⠐⠈⠁", "⠐⠈⠁⠂", "⠈⠁⠂⠄"}, + SpinnerGraycode: graycodeSpinnerGlyphs, + SpinnerGraycodeQuad: graycodeQuadBlockGlyphs, + SpinnerDNA: {"⠋⠉⠙⠚", "⠉⠙⠚⠒", "⠙⠚⠒⠂", "⠚⠒⠂⠂", "⠒⠂⠂⠒", "⠂⠂⠒⠲", "⠂⠒⠲⠴", "⠒⠲⠴⠤", "⠲⠴⠤⠄", "⠴⠤⠄⠋", "⠤⠄⠋⠉", "⠄⠋⠉⠙"}, + SpinnerScan: {"⡇⠀⠀⠀", "⣿⠀⠀⠀", "⢸⡇⠀⠀", "⠀⣿⠀⠀", "⠀⢸⡇⠀", "⠀⠀⣿⠀", "⠀⠀⢸⡇", "⠀⠀⠀⣿", "⠀⠀⠀⢸", "⠀⠀⠀⠀"}, + SpinnerPulse: {"⠀", "⠄", "⠆", "⠇", "⡇", "⣇", "⣧", "⣷", "⣿", "⣷", "⣧", "⣇", "⡇", "⠇", "⠆", "⠄"}, + SpinnerSnake: {"⠈⠁", "⠈⠑", "⠈⠱", "⠈⡱", "⢁⡱", "⢁⡰", "⢁⡠", "⢁⡀", "⢁⠀", "⠁⠀"}, + SpinnerOrbit: {"⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠", "⣀", "⢠", "⢐", "⢈", "⢁"}, + SpinnerWing: {"⫷", "⫸"}, + SpinnerTalons: {"⩤", "⩥", "⩦", "⩧"}, } -// hawkTypingDots is the number of trailing typing-indicator dots. -const hawkTypingDots = 3 +// graycodeTypingDots is the number of trailing typing-indicator dots. +const graycodeTypingDots = 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..hawkTypingDots-1 — position of the highlighted dot + dots int // 0..graycodeTypingDots-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{SpinnerHawk, SpinnerBraille, SpinnerBrailleWave, SpinnerDNA, SpinnerScan, SpinnerPulse, SpinnerSnake, SpinnerOrbit} + styles := []SpinnerStyle{SpinnerGraycode, 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) % hawkTypingDots + s.dots = (s.dots + 1) % graycodeTypingDots s.mu.Unlock() return s.Frame() } diff --git a/cmd/braille_spinner_test.go b/cmd/braille_spinner_test.go index 96c1c6f0..e326eb02 100644 --- a/cmd/braille_spinner_test.go +++ b/cmd/braille_spinner_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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, SpinnerHawk, SpinnerDNA, + SpinnerBraille, SpinnerBrailleWave, SpinnerGraycode, SpinnerDNA, SpinnerScan, SpinnerPulse, SpinnerSnake, SpinnerOrbit, SpinnerWing, SpinnerTalons, } @@ -50,14 +50,14 @@ func TestBrailleSpinner_Random(t *testing.T) { } } -func TestHawkSpinner_Frames(t *testing.T) { - if len(hawkSpinnerGlyphs) != 4 { - t.Fatalf("expected 4 compass glyphs, got %d", len(hawkSpinnerGlyphs)) +func TestGraycodeSpinner_Frames(t *testing.T) { + if len(graycodeSpinnerGlyphs) != 4 { + t.Fatalf("expected 4 compass glyphs, got %d", len(graycodeSpinnerGlyphs)) } - if hawkSpinnerGlyphs[0] != "◐" { - t.Fatalf("expected first compass frame ◐, got %q", hawkSpinnerGlyphs[0]) + if graycodeSpinnerGlyphs[0] != "◐" { + t.Fatalf("expected first compass frame ◐, got %q", graycodeSpinnerGlyphs[0]) } - s := NewBrailleSpinner(SpinnerHawk, "Working") + s := NewBrailleSpinner(SpinnerGraycode, "Working") f0 := s.Frame() if !strings.Contains(f0, "◐") { t.Fatalf("expected compass glyph, got %q", f0) @@ -70,20 +70,20 @@ func TestHawkSpinner_Frames(t *testing.T) { } } -func TestHawkQuadBlock_LegacyFrames(t *testing.T) { - s := NewBrailleSpinner(SpinnerHawkQuad, "Working") +func TestGraycodeQuadBlock_LegacyFrames(t *testing.T) { + s := NewBrailleSpinner(SpinnerGraycodeQuad, "Working") if !strings.Contains(s.Frame(), "▛") { t.Fatalf("expected QuadBlock glyph, got %q", s.Frame()) } } -func TestHawkAnimatedDots_PresentInFrame(t *testing.T) { - s := NewBrailleSpinner(SpinnerHawk, "Crafting") +func TestGraycodeAnimatedDots_PresentInFrame(t *testing.T) { + s := NewBrailleSpinner(SpinnerGraycode, "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 != hawkTypingDots { - t.Errorf("expected %d trailing circle-dots, got %d in %q", hawkTypingDots, total, f) + if total != graycodeTypingDots { + t.Errorf("expected %d trailing circle-dots, got %d in %q", graycodeTypingDots, 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 55aaee42..bfd211b4 100644 --- a/cmd/cascade_diag_test.go +++ b/cmd/cascade_diag_test.go @@ -3,14 +3,14 @@ package cmd import ( "testing" - hawkbranch "github.com/GrayCodeAI/hawk/internal/engine/branching" - "github.com/GrayCodeAI/hawk/internal/provider/routing" + graycodebranch "github.com/GrayCodeAI/graycode-cli/internal/engine/branching" + "github.com/GrayCodeAI/graycode-cli/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 := hawkbranch.NewCascadeRouter("opencodego/minimax-m2.5", roles) + cr := graycodebranch.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 b69aeac8..e4be71b3 100644 --- a/cmd/catalog_startup.go +++ b/cmd/catalog_startup.go @@ -4,7 +4,7 @@ import ( "context" "os" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" ) var ( @@ -13,15 +13,15 @@ var ( ) func ensureCatalogBeforeAgent(ctx context.Context, strict bool) error { - opts := hawkconfig.CatalogStartupOptions{ + opts := graycodeconfig.CatalogStartupOptions{ ForceRefresh: refreshCatalogFlag, SkipAutoRefresh: skipCatalogRefreshFlag, VerboseOutput: refreshCatalogFlag, } if strict { - return hawkconfig.PrepareCatalogForSession(ctx, os.Stderr, opts) + return graycodeconfig.PrepareCatalogForSession(ctx, os.Stderr, opts) } - hawkconfig.StartupCatalogPrefetch(ctx) + graycodeconfig.StartupCatalogPrefetch(ctx) return nil } @@ -29,5 +29,5 @@ func startBackgroundCatalogRefresh(ctx context.Context) { if skipCatalogRefreshFlag { return } - hawkconfig.ScheduleBackgroundCatalogRefresh(ctx) + graycodeconfig.ScheduleBackgroundCatalogRefresh(ctx) } diff --git a/cmd/chat.go b/cmd/chat.go index 50726cde..ac968889 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/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" + "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" ) // 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 Hawk to inspect, edit, or run something... (Shift+Enter for newline, ? for help)` +const workInputPlaceholder = `Ask Graycode 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, "hawk: --session-id ignored during resume/continue\n") + fmt.Fprintf(os.Stderr, "graycode: --session-id ignored during resume/continue\n") } 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 hawkconfig.Settings, registry *tool.Registry) (chatModel, error) { +func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings graycodeconfig.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 hawkco CursorLine: lipgloss.NewStyle(), Base: lipgloss.NewStyle().Foreground(textPrimary), Placeholder: lipgloss.NewStyle().Foreground(textPlaceholder), - Prompt: lipgloss.NewStyle().Foreground(hawkColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), }, Blurred: textarea.StyleState{ Base: lipgloss.NewStyle().Foreground(textPlaceholder), Placeholder: lipgloss.NewStyle().Foreground(textPlaceholder), - Prompt: lipgloss.NewStyle().Foreground(hawkColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), }, Cursor: textarea.CursorStyle{ - Color: hawkColor, + Color: graycodeColor, }, }) ta.Prompt = icons.ChevronRight() + " " @@ -146,8 +146,8 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings hawkco ci.EchoMode = textinput.EchoNormal sp := spinner.New() - sp.Spinner = spinner.Spinner{Frames: hawkSpinnerFrames, FPS: hawkSpinnerFrameInterval} - sp.Style = lipgloss.NewStyle().Foreground(hawkColor).Bold(true) + sp.Spinner = spinner.Spinner{Frames: graycodeSpinnerFrames, FPS: graycodeSpinnerFrameInterval} + sp.Style = lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) startup.EndPhase("newChatModel:ui-init") startup.MarkPhase("newChatModel:effectiveModelAndProvider") @@ -158,9 +158,9 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings hawkco startup.MarkPhase("newChatModel:defaultRegistry") startup.EndPhase("newChatModel:defaultRegistry") - startup.MarkPhase("newChatModel:newHawkSession") - sess := newStartupHawkSession(selection, systemPrompt, registry) - startup.EndPhase("newChatModel:newHawkSession") + startup.MarkPhase("newChatModel:newGraycodeSession") + sess := newStartupGraycodeSession(selection, systemPrompt, registry) + startup.EndPhase("newChatModel:newGraycodeSession") startup.MarkPhase("newChatModel:configureSession") if cfgErr := prepareInteractiveSessionStartup(sess, settings); cfgErr != nil { @@ -184,7 +184,7 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings hawkco // Initialize conversation DAG for branching support startup.MarkPhase("newChatModel:dag") - graphPath := filepath.Join(hawkstorage.SessionsDir(), "conversations", sid+".json") + graphPath := filepath.Join(graycodestorage.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 hawkco m.contextualHelp = NewContextualHelp() m.modeManager = shellmode.NewModeManager() m.modeManager.LoadPersistedMode() - m.brailleSpinner = NewBrailleSpinner(SpinnerHawk, "") + m.brailleSpinner = NewBrailleSpinner(SpinnerGraycode, "") m.brailleSpinner.SetLabel(m.spinnerVerb) startup.EndPhase("newChatModel:lacy-features") @@ -297,7 +297,7 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings hawkco // Prefetch live models for the active provider so footer ctx/pricing stay current. go func() { providerName := effectiveProvider - entries, _ := hawkconfig.ListEngineModels(context.Background(), providerName, false) + entries, _ := graycodeconfig.ListEngineModels(context.Background(), providerName, false) opts := configModelOptionsFromEyrie(entries) if len(opts) > 0 { modelCacheMu.Lock() @@ -324,7 +324,7 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings hawkco 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("HAWK_QUIET_START") == "" && os.Getenv("HAWK_SUPPRESS_HINTS") == "" && os.Getenv("HAWK_QUIET") == "" { + if saved == nil && os.Getenv("GRAYCODE_QUIET_START") == "" && os.Getenv("GRAYCODE_SUPPRESS_HINTS") == "" && os.Getenv("GRAYCODE_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 hawkco // the initial UI. go func(currentSessionID string) { if recovered := session.CheckForRecovery(); len(recovered) > 0 { - walDir := hawkstorage.SessionsDir() + walDir := graycodestorage.SessionsDir() for _, rid := range recovered { if rid == currentSessionID { continue // current session WAL @@ -417,7 +417,7 @@ func newChatModelWithRegistry(ref *progRef, systemPrompt string, settings hawkco // Warm footer data and the model catalog after the first frame. go func(model chatModel) { startup.MarkPhase("newChatModel:ui-cache-warm") - hawkconfig.RefreshConfigCredSnapshot(context.Background()) + graycodeconfig.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 hawkco } // refreshInputPlaceholder updates the input placeholder based on the current -// container lifecycle. Hawk never executes agent tools directly on the host. +// container lifecycle. Graycode 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 - // HAWK_DISABLE_AUTO_INIT. No-op for projects that already have context. + // GRAYCODE_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 hawkconfig.Settings + settings graycodeconfig.Settings err error } type startupRegistryResult struct { @@ -772,7 +772,7 @@ func runChat() error { fmt.Print(formatQuitResumeMessage(fm.sessionID)) return nil } - hawkC := ansiOrange + graycodeC := 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(hawkC + "█" + rst + " " + msg.content) + fmt.Println(graycodeC + "█" + rst + " " + msg.content) fmt.Println() case "assistant": - fmt.Println(hawkC + icons.Robot() + " " + rst + msg.content) + fmt.Println(graycodeC + 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(hawkColor).Bold(true).Render(">") + " ") + fmt.Println(lipgloss.NewStyle().Foreground(graycodeColor).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: hawk --resume %s", fm.sessionID))) + fmt.Println(dimStyle.Render(fmt.Sprintf("To resume this session, run: graycode --resume %s", fm.sessionID))) } return nil } diff --git a/cmd/chat_commands.go b/cmd/chat_commands.go index 33ccf5ff..ab7aa9a4 100644 --- a/cmd/chat_commands.go +++ b/cmd/chat_commands.go @@ -11,8 +11,8 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/hawk/internal/multiagent/parallel" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/multiagent/parallel" + "github.com/GrayCodeAI/graycode-cli/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 hawk", + "/feedback": "Submit feedback about graycode", "/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 hawk/agent-* branch if on main/master", + "/branch-agent": "Create graycode/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,7 +254,7 @@ var slashDescriptions = map[string]string{ "/tools": "List enabled tools", "/undo": "Undo the most recent file change", "/usage": "Show cost summary", - "/version": "Show hawk version", + "/version": "Show graycode version", "/vim": "Toggle vim mode", "/welcome": "Re-print the welcome header", "/ecosystem": "Show eyrie, harrier, and shrike integration status", @@ -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 hawk's personality/soul", + "/soul": "Show or update graycode'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 6d625184..2f8fe626 100644 --- a/cmd/chat_commands_config.go +++ b/cmd/chat_commands_config.go @@ -6,14 +6,14 @@ import ( tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/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 := hawkconfig.SetGlobalSetting("provider", value); err != nil { + if err := graycodeconfig.SetGlobalSetting("provider", value); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) return m, nil } @@ -24,7 +24,7 @@ func (m *chatModel) handleConfigCommand(parts []string, text string) (tea.Model, modelCacheMu.RUnlock() if cacheHit && len(cached) > 0 { m.session.SetModel(cached[0].ID) - _ = hawkconfig.SetGlobalSetting("model", cached[0].ID) + _ = graycodeconfig.SetGlobalSetting("model", cached[0].ID) } 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 @@ -47,7 +47,7 @@ func (m *chatModel) handleConfigCommand(parts []string, text string) (tea.Model, return m, nil } } - if err := hawkconfig.SetGlobalSetting("model", value); err != nil { + if err := graycodeconfig.SetGlobalSetting("model", value); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) return m, nil } @@ -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 := hawkconfig.SettingValue(settings, parts[2]) + value, ok := graycodeconfig.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 := hawkconfig.SetGlobalSetting(key, value); err != nil { + if err := graycodeconfig.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 3cd5041d..48bb2a4a 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) // handleImageCommand implements the /image slash command: diff --git a/cmd/chat_commands_session.go b/cmd/chat_commands_session.go index 446ac1b5..944f6325 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/hawk/internal/session" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/graycode-cli/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 Hawk!\n" + return "Thank you for using Graycode!\n" } - return fmt.Sprintf("Thank you for using Hawk!\n\nTo resume this session, run: hawk --resume %s\n", sessionID) + return fmt.Sprintf("Thank you for using Graycode!\n\nTo resume this session, run: graycode --resume %s\n", sessionID) } // handleSessionCommand dispatches session-management slash commands. diff --git a/cmd/chat_commands_skills.go b/cmd/chat_commands_skills.go index 4fcafdb1..a65f478b 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/hawk/internal/plugin" - "github.com/GrayCodeAI/hawk/internal/tool" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/plugin" + "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/graycode-cli/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 791f3ed2..4f94da28 100644 --- a/cmd/chat_commands_test.go +++ b/cmd/chat_commands_test.go @@ -6,9 +6,9 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/tool" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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: hawkconfig.Settings{MCPServers: []hawkconfig.MCPServerConfig{{Name: "demo", Command: "demo-mcp"}}}, + settings: graycodeconfig.Settings{MCPServers: []graycodeconfig.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 := hawkconfig.Settings{ + settings := graycodeconfig.Settings{ Provider: "openai", Model: "gpt-4o", - MCPServers: []hawkconfig.MCPServerConfig{ + MCPServers: []graycodeconfig.MCPServerConfig{ {Name: "demo", Command: "demo-mcp", Args: []string{"--stdio"}}, }, } report := doctorReport(settings) - if !strings.Contains(report, "Hawk doctor") || !strings.Contains(report, "Built-in tools") { + if !strings.Contains(report, "Graycode 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 e53bb2cd..b3d50e35 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/hawk/internal/feature/shellmode" - "github.com/GrayCodeAI/hawk/internal/plugin" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/feature/shellmode" + "github.com/GrayCodeAI/graycode-cli/internal/plugin" + "github.com/GrayCodeAI/graycode-cli/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. "/hawk:go-review" + invoke := cmd // e.g. "/graycode: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 || "/hawk:"+name == invoke { + if skill.Invoke == invoke || "/graycode:"+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 || "/hawk:"+skills[i].Name == invoke { + if skills[i].Invoke == invoke || "/graycode:"+skills[i].Name == invoke { matched = &skills[i] break } diff --git a/cmd/chat_commands_util.go b/cmd/chat_commands_util.go index d0352176..6c9b8765 100644 --- a/cmd/chat_commands_util.go +++ b/cmd/chat_commands_util.go @@ -8,11 +8,11 @@ import ( "path/filepath" "strings" - 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" + 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" ) 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 := hawkconfig.LoadAgentsMDFrom(abs); md != "" { + if md := graycodeconfig.LoadAgentsMDFrom(abs); md != "" { b.WriteString("\nAdditional directory instructions (" + abs + "):\n" + md) } return abs, b.String(), nil @@ -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 Hawk user settings" + return "Hooks: pre_query, post_query, pre_tool, post_tool, session_start, session_end, permission_ask, error\nConfigure in Graycode user settings" } func pluginsSummary(rt *plugin.Runtime) string { diff --git a/cmd/chat_config_deployment.go b/cmd/chat_config_deployment.go index 7c4718ba..1310c1c6 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" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 != "" && hawkconfig.HasStoredCredentialForProvider(ctx, p) { + if p := strings.TrimSpace(m.configModelProvider); p != "" && graycodeconfig.HasStoredCredentialForProvider(ctx, p) { return p } if m.session != nil { - if p := strings.TrimSpace(m.session.Provider()); p != "" && hawkconfig.HasStoredCredentialForProvider(ctx, p) { + if p := strings.TrimSpace(m.session.Provider()); p != "" && graycodeconfig.HasStoredCredentialForProvider(ctx, p) { return p } } - if p := strings.TrimSpace(hawkconfig.ActiveGateway(ctx)); p != "" && hawkconfig.HasStoredCredentialForProvider(ctx, p) { + if p := strings.TrimSpace(graycodeconfig.ActiveGateway(ctx)); p != "" && graycodeconfig.HasStoredCredentialForProvider(ctx, p) { return p } - if p := hawkconfig.DefaultModelProviderFilter(ctx); p != "" && hawkconfig.HasStoredCredentialForProvider(ctx, p) { + if p := graycodeconfig.DefaultModelProviderFilter(ctx); p != "" && graycodeconfig.HasStoredCredentialForProvider(ctx, p) { return p } - for _, p := range hawkconfig.AllSetupGateways() { - if hawkconfig.HasStoredCredentialForProvider(ctx, p) { + for _, p := range graycodeconfig.AllSetupGateways() { + if graycodeconfig.HasStoredCredentialForProvider(ctx, p) { return p } } return "" } -func saveProviderKeyAsync(inference hawkconfig.CredentialInference, secret string) tea.Cmd { +func saveProviderKeyAsync(inference graycodeconfig.CredentialInference, secret string) tea.Cmd { return saveCredentialAsync(inference, secret) } func saveOllamaAsync(baseURL string) tea.Cmd { return func() tea.Msg { - inference, err := hawkconfig.LocalCredentialInference(configProviderOllama) + inference, err := graycodeconfig.LocalCredentialInference(configProviderOllama) if err != nil { return configApplyCredentialsMsg{err: err} } - inf := hawkconfig.CredentialInference{ + inf := graycodeconfig.CredentialInference{ ProviderID: inference.ProviderID, DeploymentID: inference.DeploymentID, EnvVar: inference.EnvVar, @@ -66,22 +66,22 @@ func saveOllamaAsync(baseURL string) tea.Cmd { } } -func saveCredentialAsync(inference hawkconfig.CredentialInference, secret string) tea.Cmd { +func saveCredentialAsync(inference graycodeconfig.CredentialInference, secret string) tea.Cmd { return func() tea.Msg { ctx := context.Background() - if err := hawkconfig.SaveCredential(ctx, inference, secret); err != nil { + if err := graycodeconfig.SaveCredential(ctx, inference, secret); err != nil { return configApplyCredentialsMsg{ err: err, providerID: inference.ProviderID, deploymentID: inference.DeploymentID, } } - 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) + graycodeconfig.InvalidateConfigUICache() + graycodeconfig.RefreshConfigCredSnapshot(ctx) + result, err := graycodeconfig.ApplyEyrieCredentialsForProvider(ctx, inference.ProviderID) + if err != nil && graycodeconfig.IsCatalogCacheRequired(err) { + if refreshErr := graycodeconfig.RefreshCatalogAfterCredentials(ctx, nil); refreshErr == nil { + result, err = graycodeconfig.ApplyEyrieCredentialsForProvider(ctx, inference.ProviderID) } else { err = fmt.Errorf("%w; automatic catalog refresh failed: %w", err, refreshErr) } @@ -94,7 +94,7 @@ func saveCredentialAsync(inference hawkconfig.CredentialInference, secret string } } - entries, listErr := hawkconfig.ListEngineModels(ctx, inference.ProviderID, false) + entries, listErr := graycodeconfig.ListEngineModels(ctx, inference.ProviderID, false) if listErr != nil { return configApplyCredentialsMsg{ err: listErr, @@ -104,7 +104,7 @@ func saveCredentialAsync(inference hawkconfig.CredentialInference, secret string } opts := configModelOptionsFromEyrie(entries) return configApplyCredentialsMsg{ - summary: hawkconfig.FormatApplyCredentialsSummary(result), + summary: graycodeconfig.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(hawkColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), Text: lipgloss.NewStyle().Foreground(textPrimary), }, Blurred: textinput.StyleState{ - Prompt: lipgloss.NewStyle().Foreground(hawkColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), Text: lipgloss.NewStyle().Foreground(textPrimary), }, Cursor: textinput.CursorStyle{ - Color: hawkColor, + Color: graycodeColor, }, }) 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() - hawkconfig.RefreshConfigCredSnapshot(ctx) + graycodeconfig.RefreshConfigCredSnapshot(ctx) m = m.refreshConfigGatewayRows() if msg.err != nil { m.invalidateConnStatus() if msg.providerID == configProviderOllama { return m.returnToOllamaURLAfterError(msg.err) } - notice := sanitizeConfigNotice(hawkconfig.FormatConfigProviderError(msg.providerID, msg.err)) - saved := hawkconfig.HasStoredCredentialForProvider(ctx, msg.providerID) || + notice := sanitizeConfigNotice(graycodeconfig.FormatConfigProviderError(msg.providerID, msg.err)) + saved := graycodeconfig.HasStoredCredentialForProvider(ctx, msg.providerID) || strings.Contains(strings.ToLower(msg.err.Error()), "key saved in keychain") if saved { - if hawkconfig.IsCatalogCacheRequired(msg.err) { + if graycodeconfig.IsCatalogCacheRequired(msg.err) { notice = "Key saved in " + credentialsStoreLabel() + " — model catalog unavailable: " + notice - notice += " · run hawk models refresh" + notice += " · run graycode 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 !hawkconfig.IsCatalogCacheRequired(msg.err) && !strings.Contains(strings.ToLower(notice), "refresh") { - notice += " · press r on " + hawkconfig.GatewayDisplayName(msg.providerID) + " to retry" + if !graycodeconfig.IsCatalogCacheRequired(msg.err) && !strings.Contains(strings.ToLower(notice), "refresh") { + notice += " · press r on " + graycodeconfig.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 " + hawkconfig.GatewayDisplayName(post) + next.configNotice = "Key updated for " + graycodeconfig.GatewayDisplayName(post) return next, cmd } if msg.providerID == configProviderOllama { - _ = hawkconfig.SetGlobalSetting("provider", configProviderOllama) + _ = graycodeconfig.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 = hawkconfig.LoadSettings() + m.settings = graycodeconfig.LoadSettings() m.syncSessionSelection() - selection := hawkconfig.EffectiveSelectionWithSettings(context.Background(), m.settings, hawkconfig.SelectionOptions{ + selection := graycodeconfig.EffectiveSelectionWithSettings(context.Background(), m.settings, graycodeconfig.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 23c08661..335b1b6d 100644 --- a/cmd/chat_config_gateways.go +++ b/cmd/chat_config_gateways.go @@ -7,8 +7,8 @@ import ( tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/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 := hawkconfig.GatewayStatuses(ctx, active, activeModel) + statuses := graycodeconfig.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 = hawkconfig.CredentialEnvironmentConflict(ctx, status.ID) + credentialEnv, keyConflict = graycodeconfig.CredentialEnvironmentConflict(ctx, status.ID) } display := status.DisplayName - if status.RegionRequired || status.RegionLabel != "" || hawkconfig.HasRegionOptions(status.ID) { + if status.RegionRequired || status.RegionLabel != "" || graycodeconfig.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 || hawkconfig.ActiveProviderID(status.ID) == hawkconfig.ActiveProviderID(active), + Active: status.Active || graycodeconfig.ActiveProviderID(status.ID) == graycodeconfig.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 := hawkconfig.GatewayDisplayName(m.configKeysPendingRemove) + name := graycodeconfig.GatewayDisplayName(m.configKeysPendingRemove) b.WriteString("\n" + mutedStyle.Render(indent+configGatewayRemovePrompt(m.configKeysRemoveStep, name))) - } else if !hawkconfig.HasConfiguredDeploymentCached(ctx) { + } else if !graycodeconfig.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 != "" || hawkconfig.HasRegionOptions(rows[targetIdx].ID)) { + if targetIdx >= 0 && targetIdx < len(rows) && (rows[targetIdx].RegionRequired || rows[targetIdx].RegionLabel != "" || graycodeconfig.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 != "" || hawkconfig.HasRegionOptions(rows[targetIdx].ID)) { + if targetIdx >= 0 && targetIdx < len(rows) && (rows[targetIdx].RegionRequired || rows[targetIdx].RegionLabel != "" || graycodeconfig.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 || hawkconfig.HasRegionOptions(row.ID)) && (!row.HasKey || row.RegionRequired) { + if (row.RegionRequired || graycodeconfig.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 - _ = hawkconfig.SetGlobalSetting("provider", gw) - if active := hawkconfig.ActiveProvider(context.Background()); active != "" { + _ = graycodeconfig.SetGlobalSetting("provider", gw) + if active := graycodeconfig.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 := hawkconfig.RefreshGatewayCatalog(context.Background(), providerID) + summary, err := graycodeconfig.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(hawkconfig.FormatConfigProviderError(msg.providerID, msg.err)) + m.configNotice = sanitizeConfigNotice(graycodeconfig.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 9de3552e..57e585cb 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" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) func chatModelForConfigPasteTest() chatModel { @@ -37,12 +37,12 @@ func TestConfigGatewayRows_UsesPanelCacheUntilInvalidated(t *testing.T) { func TestConfigGatewayRowsKeepRegistryOrderAfterKeySaved(t *testing.T) { isolateCredentialHome(t) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) m := chatModel{configTab: configTabGateways} @@ -87,12 +87,12 @@ func TestConfigGatewaysView_RequiresKeyForModelCounts(t *testing.T) { } func TestConfigGatewaysView_ShowsSaveOrProbeNotice(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) m := chatModel{ @@ -109,17 +109,17 @@ func TestConfigGatewaysView_ShowsSaveOrProbeNotice(t *testing.T) { } func TestConfigGatewayRefreshTargetIndex_UsesSelectedRow(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() sess := engine.NewSession("", "", "", nil) sess.SetProvider("openrouter") @@ -138,17 +138,17 @@ func TestConfigGatewayRefreshTargetIndex_UsesSelectedRow(t *testing.T) { } func TestConfigGatewayRefreshTargetIndex_UsesFocusOnRefreshRow(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() sess := engine.NewSession("", "", "", nil) sess.SetProvider("openrouter") @@ -196,19 +196,19 @@ func TestFocusConfigActiveGateway_SelectsActiveRow(t *testing.T) { } func TestHandleConfigGatewaysSelect_TokenPlanNoKeyShowsRegion(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) m := chatModel{configTab: configTabGateways} rows := m.configGatewayRows() idx := -1 for i, row := range rows { - if row.ID == hawkconfig.ProviderXiaomiTokenPlan { + if row.ID == graycodeconfig.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 != hawkconfig.ProviderXiaomiTokenPlan { + if next.configProvider != graycodeconfig.ProviderXiaomiTokenPlan { t.Fatalf("provider = %q", next.configProvider) } } func TestHandleConfigGatewaysSelect_NoKeyStartsPaste(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) m := chatModelForConfigPasteTest() @@ -243,7 +243,7 @@ func TestHandleConfigGatewaysSelect_NoKeyStartsPaste(t *testing.T) { } sel := 0 for i, row := range gwRows { - if row.ID == hawkconfig.ProviderXiaomiTokenPlan { + if row.ID == graycodeconfig.ProviderXiaomiTokenPlan { continue } sel = i diff --git a/cmd/chat_config_hub.go b/cmd/chat_config_hub.go index 58ca7431..6741f151 100644 --- a/cmd/chat_config_hub.go +++ b/cmd/chat_config_hub.go @@ -5,7 +5,7 @@ import ( tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" ) func (m chatModel) openConfigPanel() (chatModel, tea.Cmd) { @@ -69,7 +69,7 @@ func catalogPricesAreStale(opts []configModelOption) bool { } func providerHasLiveFetcher(providerID string) bool { - return hawkconfig.GatewaySupportsLiveDiscovery(providerID) + return graycodeconfig.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 = hawkconfig.FormatConfigProviderError(configProviderOllama, err) + m.configNotice = graycodeconfig.FormatConfigProviderError(configProviderOllama, err) } return m.startConfigOllamaURLWithValue(url) } diff --git a/cmd/chat_config_keys.go b/cmd/chat_config_keys.go index d766204d..6033a0e4 100644 --- a/cmd/chat_config_keys.go +++ b/cmd/chat_config_keys.go @@ -7,11 +7,11 @@ import ( tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" ) func credentialsStoreLabel() string { - return hawkconfig.CredentialStoreName() + return graycodeconfig.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 := hawkconfig.GatewayDisplayName(providerName) - masked := hawkconfig.MaskCredentialForProvider(context.Background(), providerName) + displayName := graycodeconfig.GatewayDisplayName(providerName) + masked := graycodeconfig.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 := hawkconfig.GatewayRegionLabel(providerName); reg != "" || hawkconfig.NeedsGatewayRegion(providerName) || hawkconfig.HasRegionOptions(providerName) { + if reg := graycodeconfig.GatewayRegionLabel(providerName); reg != "" || graycodeconfig.NeedsGatewayRegion(providerName) || graycodeconfig.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 hawkconfig.NeedsGatewayRegion(provider) { + if graycodeconfig.NeedsGatewayRegion(provider) { m.configPostSaveKeysProvider = provider return m.startConfigGatewayRegion(provider), nil } - name := hawkconfig.GatewayDisplayName(provider) + name := graycodeconfig.GatewayDisplayName(provider) m.configNotice = "Paste API key for " + name return m.startConfigEntry(configEntryAPIKeyPaste, provider) } func (m chatModel) startConfigKeyReplace(provider string) (chatModel, tea.Cmd) { - if hawkconfig.NeedsGatewayRegion(provider) { + if graycodeconfig.NeedsGatewayRegion(provider) { m.configPostSaveKeysProvider = provider return m.startConfigGatewayRegion(provider), nil } m.configReplaceProvider = provider m.configEntry = configEntryNone - m.configNotice = "Paste replacement API key for " + hawkconfig.GatewayDisplayName(provider) + m.configNotice = "Paste replacement API key for " + graycodeconfig.GatewayDisplayName(provider) return m.startConfigEntry(configEntryAPIKeyPaste, provider) } func (m chatModel) beginConfigGatewayKeyRemove(provider string) chatModel { m.configKeysPendingRemove = provider m.configKeysRemoveStep = 1 - name := hawkconfig.GatewayDisplayName(provider) + name := graycodeconfig.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 := hawkconfig.GatewayDisplayName(trimmedProvider) + name := graycodeconfig.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…", hawkconfig.GatewayDisplayName(trimmedProvider)) + m.configNotice = fmt.Sprintf("Removing key for %s…", graycodeconfig.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 (hawkconfig.HasRegionOptions(trimmedProvider) || hawkconfig.GatewayRegionLabel(trimmedProvider) != "") && strings.EqualFold(key.Text, "g") { + if (graycodeconfig.HasRegionOptions(trimmedProvider) || graycodeconfig.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 15aee677..3f3931a7 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" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) func TestConfigGatewaysView_KeyHintsWithCredentials(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() m := chatModel{configTab: configTabGateways} view := m.configGatewaysView() @@ -30,17 +30,17 @@ func TestConfigGatewaysView_KeyHintsWithCredentials(t *testing.T) { } func TestConfigGatewaysKeyView_OpenWithK(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() rows := chatModel{}.configGatewayRows() sel := 0 @@ -61,17 +61,17 @@ func TestConfigGatewaysKeyView_OpenWithK(t *testing.T) { } func TestConfigGatewaysDelete_PendingRemove(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() rows := chatModel{}.configGatewayRows() sel := 0 @@ -92,17 +92,17 @@ func TestConfigGatewaysDelete_PendingRemove(t *testing.T) { } func TestConfigGatewaysDelete_DoubleConfirm(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() rows := chatModel{}.configGatewayRows() sel := 0 @@ -131,12 +131,12 @@ func TestConfigGatewaysDelete_DoubleConfirm(t *testing.T) { } func TestOpenConfigRemoveKeyPanel_OpensGatewaysTab(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) m := chatModel{} diff --git a/cmd/chat_config_models.go b/cmd/chat_config_models.go index 44522f1f..26818871 100644 --- a/cmd/chat_config_models.go +++ b/cmd/chat_config_models.go @@ -7,8 +7,8 @@ import ( tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) // configModelOption is one row in the /config model picker (display from eyrie, id for settings). @@ -42,7 +42,7 @@ func InvalidateModelCache() { modelSyncAttempted = make(map[string]bool) modelSyncMu.Unlock() invalidatePlatformContextCache() - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() } // InvalidateModelCacheProvider drops one gateway's cached picker rows. @@ -54,7 +54,7 @@ func InvalidateModelCacheProvider(provider string) { modelSyncMu.Lock() delete(modelSyncAttempted, provider) modelSyncMu.Unlock() - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() } func fetchModelsAsync(provider string) tea.Cmd { @@ -62,13 +62,13 @@ func fetchModelsAsync(provider string) tea.Cmd { ctx := context.Background() provider = strings.TrimSpace(provider) if provider == "" { - provider = hawkconfig.DefaultModelProviderFilter(ctx) + provider = graycodeconfig.DefaultModelProviderFilter(ctx) } - entries, err := hawkconfig.ListEngineModels(ctx, provider, false) + entries, err := graycodeconfig.ListEngineModels(ctx, provider, false) if err != nil { - if _, derr := hawkconfig.ListEngineModels(ctx, provider, true); derr == nil { + if _, derr := graycodeconfig.ListEngineModels(ctx, provider, true); derr == nil { InvalidateModelCacheProvider(provider) - entries, err = hawkconfig.ListEngineModels(ctx, provider, false) + entries, err = graycodeconfig.ListEngineModels(ctx, provider, false) } } if err != nil { @@ -84,7 +84,7 @@ func fetchModelsAsync(provider string) tea.Cmd { } } -func configModelOptionsFromEyrie(entries []hawkconfig.EngineModel) []configModelOption { +func configModelOptionsFromEyrie(entries []graycodeconfig.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 := hawkconfig.ListEngineModels(ctx, provider, false) + entries, err := graycodeconfig.ListEngineModels(ctx, provider, false) if err != nil { - if _, derr := hawkconfig.ListEngineModels(ctx, provider, true); derr == nil { - entries, err = hawkconfig.ListEngineModels(ctx, provider, false) + if _, derr := graycodeconfig.ListEngineModels(ctx, provider, true); derr == nil { + entries, err = graycodeconfig.ListEngineModels(ctx, provider, false) } } if err != nil || len(entries) == 0 { @@ -250,7 +250,7 @@ func loadConfigModelOptions(provider string) []configModelOption { return cached } modelCacheMu.RUnlock() - entries, err := hawkconfig.ListEngineModels(context.Background(), provider, false) + entries, err := graycodeconfig.ListEngineModels(context.Background(), provider, false) if err == nil && len(entries) > 0 { opts := configModelOptionsFromEyrie(entries) modelCacheMu.Lock() diff --git a/cmd/chat_config_models_test.go b/cmd/chat_config_models_test.go index c23732e1..9f034ed5 100644 --- a/cmd/chat_config_models_test.go +++ b/cmd/chat_config_models_test.go @@ -3,7 +3,7 @@ package cmd import ( "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" ) func TestFilterConfigModelOptions(t *testing.T) { @@ -45,7 +45,7 @@ func TestModelOptionIsActive(t *testing.T) { } func TestConfigModelOptionsCarryResolvedEngineIdentity(t *testing.T) { - opts := configModelOptionsFromEyrie([]hawkconfig.EngineModel{{ + opts := configModelOptionsFromEyrie([]graycodeconfig.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 64d608e2..af340265 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" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/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() + " " + hawkconfig.GatewayDisplayName(providerName) + title = icons.Key() + " " + graycodeconfig.GatewayDisplayName(providerName) hint = "paste key for this gateway only · " + hint } - if providerName == hawkconfig.ProviderXiaomiTokenPlan { - reg := hawkconfig.XiaomiTokenPlanRegionLabel() + if providerName == graycodeconfig.ProviderXiaomiTokenPlan { + reg := graycodeconfig.GatewayRegionLabel(graycodeconfig.ProviderXiaomiTokenPlan) if reg == "" { reg = "not set — esc and pick region with g or enter on gateway row" } @@ -198,7 +198,7 @@ func (m chatModel) configModelsTabView() string { gw = strings.TrimSpace(m.session.Provider()) } if gw != "" { - body.WriteString(renderConfigGatewayLine(hawkconfig.GatewayDisplayName(gw)) + "\n\n") + body.WriteString(renderConfigGatewayLine(graycodeconfig.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(hawkColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), Text: lipgloss.NewStyle().Foreground(textPrimary), }, Blurred: textinput.StyleState{ - Prompt: lipgloss.NewStyle().Foreground(hawkColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), Text: lipgloss.NewStyle().Foreground(textPrimary), }, Cursor: textinput.CursorStyle{ - Color: hawkColor, + Color: graycodeColor, }, }) m.configInput.Focus() @@ -333,11 +333,11 @@ func (m chatModel) configActiveModelID() string { return modelName } } - return strings.TrimSpace(hawkconfig.ActiveModel(context.Background())) + return strings.TrimSpace(graycodeconfig.ActiveModel(context.Background())) } func modelOptionIsActive(opt configModelOption, activeModelID string) bool { - return modelOptionIsActiveResolved(opt, activeModelID, hawkconfig.CanonicalModelID(context.Background(), activeModelID)) + return modelOptionIsActiveResolved(opt, activeModelID, graycodeconfig.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 := hawkconfig.CanonicalModelID(context.Background(), activeID) + activeCanonicalID := graycodeconfig.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 := hawkconfig.CanonicalModelID(context.Background(), activeModelID) + activeCanonicalID := graycodeconfig.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 := hawkconfig.CatalogEmptyHint(context.Background()); hint != "" { + if hint := graycodeconfig.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(hawkColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), Text: lipgloss.NewStyle().Foreground(textPrimary), }, Blurred: textinput.StyleState{ - Prompt: lipgloss.NewStyle().Foreground(hawkColor).Bold(true), + Prompt: lipgloss.NewStyle().Foreground(graycodeColor).Bold(true), Text: lipgloss.NewStyle().Foreground(textPrimary), }, Cursor: textinput.CursorStyle{ - Color: hawkColor, + Color: graycodeColor, }, }) m.configInput.Focus() @@ -580,7 +580,7 @@ func (m chatModel) finishConfigEntry() (chatModel, tea.Cmd) { m.restoreChatInput() return m, nil } - if providerName == hawkconfig.ProviderXiaomiTokenPlan && hawkconfig.NeedsXiaomiTokenPlanRegion(providerName) { + if providerName == graycodeconfig.ProviderXiaomiTokenPlan && graycodeconfig.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 := hawkconfig.CredentialInferenceForProvider(providerName) + inference, err := graycodeconfig.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 := hawkconfig.CredentialGuidance(providerName, value); hint != "" { + if hint := graycodeconfig.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 == hawkconfig.ProviderXiaomiTokenPlan { + if row, ok := m.selectedConfigGateway(); ok && row.ID == graycodeconfig.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 := hawkconfig.SetGlobalSetting("model", modelID); err != nil { + if err := graycodeconfig.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 := hawkconfig.SetGlobalSetting("provider", provider); err != nil { + if err := graycodeconfig.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 !hawkconfig.EvaluateSetupCached(context.Background()).NeedsSetup { + if !graycodeconfig.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 !hawkconfig.ModelCapabilitySupportsThinking(selected.Capabilities) { + if !graycodeconfig.ModelCapabilitySupportsThinking(selected.Capabilities) { m.configNotice = "Thinking not supported for this model" return m, nil } - settings := hawkconfig.LoadSettings() - pref := hawkconfig.ThinkingPrefForModel(settings, selected.ID) + settings := graycodeconfig.LoadSettings() + pref := graycodeconfig.ThinkingPrefForModel(settings, selected.ID) // Current effective display: unset → off. Toggle flips that. currentlyOn := false if pref != nil { currentlyOn = *pref } next := !currentlyOn - if err := hawkconfig.SetModelThinking(selected.ID, next); err != nil { + if err := graycodeconfig.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(), hawkconfig.CanonicalModelID(context.Background(), m.configActiveModelID())) { + if m.session != nil && modelOptionIsActiveResolved(selected, m.configActiveModelID(), graycodeconfig.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 := hawkconfig.LoadSettings() + settings := graycodeconfig.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(hawkconfig.ResolveThinkingForModel(settings, selected.ID, provider)) + m.session.SetThinkingEnabled(graycodeconfig.ResolveThinkingForModel(settings, selected.ID, provider)) } diff --git a/cmd/chat_config_region.go b/cmd/chat_config_region.go index 41884256..c8c22898 100644 --- a/cmd/chat_config_region.go +++ b/cmd/chat_config_region.go @@ -7,7 +7,7 @@ import ( tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" ) func gatewayRegionOptionIndex(providerID, region string) int { @@ -15,7 +15,7 @@ func gatewayRegionOptionIndex(providerID, region string) int { if region == "" { return 0 } - opts := hawkconfig.GatewayRegionOptions(providerID) + opts := graycodeconfig.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 hawkconfig.ProviderXiaomiTokenPlan: + case graycodeconfig.ProviderXiaomiTokenPlan: m.configEntry = configEntryXiaomiRegion - case hawkconfig.ProviderZAICoding, hawkconfig.ProviderZAIPayg: + case graycodeconfig.ProviderZAICoding, graycodeconfig.ProviderZAIPayg: m.configEntry = configEntryZAIRegion default: m.configEntry = configEntryGatewayRegion } m.configProvider = providerID idx := 0 - if !hawkconfig.NeedsGatewayRegion(providerID) { - idx = gatewayRegionOptionIndex(providerID, hawkconfig.GatewayRegionLabel(providerID)) + if !graycodeconfig.NeedsGatewayRegion(providerID) { + idx = gatewayRegionOptionIndex(providerID, graycodeconfig.GatewayRegionLabel(providerID)) } m.configGatewayRegionSel = idx m.configZAIRegionSel = idx - name := hawkconfig.GatewayDisplayName(providerID) + name := graycodeconfig.GatewayDisplayName(providerID) notice := fmt.Sprintf("Select %s region (↑↓ · enter · esc cancel)", name) - if saved := hawkconfig.GatewayRegionLabel(providerID); saved != "" { + if saved := graycodeconfig.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 := hawkconfig.GatewayDisplayName(prov) + name := graycodeconfig.GatewayDisplayName(prov) b.WriteString(renderConfigBreadcrumb(name+" region") + "\n\n") - opts := hawkconfig.GatewayRegionOptions(prov) + opts := graycodeconfig.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 := hawkconfig.GatewayRegionOptions(m.configProvider) + opts := graycodeconfig.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 := hawkconfig.SetGatewayRegion(m.configProvider, chosen.Value); err != nil { + if err := graycodeconfig.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 hawkconfig.HasStoredCredentialForProvider(ctx, m.configProvider) { - m.configNotice = "Saved region for " + hawkconfig.GatewayDisplayName(m.configProvider) + if graycodeconfig.HasStoredCredentialForProvider(ctx, m.configProvider) { + m.configNotice = "Saved region for " + graycodeconfig.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 41b53ead..8c3c6cbc 100644 --- a/cmd/chat_config_remove.go +++ b/cmd/chat_config_remove.go @@ -6,7 +6,7 @@ import ( tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" ) type configRemoveCredentialMsg struct { @@ -17,7 +17,7 @@ type configRemoveCredentialMsg struct { func removeCredentialAsync(provider string) tea.Cmd { return func() tea.Msg { - removed, err := hawkconfig.RemoveStoredCredential(context.Background(), provider) + removed, err := graycodeconfig.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() - hawkconfig.RefreshConfigCredSnapshot(ctx) + graycodeconfig.RefreshConfigCredSnapshot(ctx) m = m.refreshConfigGatewayRows() - if hawkconfig.ShouldClearSelectionAfterCredentialRemove(ctx, msg.provider) { - _ = hawkconfig.ClearActiveSelection(ctx) + if graycodeconfig.ShouldClearSelectionAfterCredentialRemove(ctx, msg.provider) { + _ = graycodeconfig.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", hawkconfig.GatewayDisplayName(msg.provider)) - if !hawkconfig.HasConfiguredDeploymentCached(ctx) { + m.configNotice = fmt.Sprintf("Removed API key for %s", graycodeconfig.GatewayDisplayName(msg.provider)) + if !graycodeconfig.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(hawkconfig.ConfiguredCredentialProviders()) == 0 { + if len(graycodeconfig.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 ae83e0fd..45cefedb 100644 --- a/cmd/chat_config_remove_test.go +++ b/cmd/chat_config_remove_test.go @@ -4,22 +4,22 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) func TestConfigGatewayRows_ShowsSavedKey(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() rows := chatModel{}.configGatewayRows() var found bool @@ -35,36 +35,36 @@ func TestConfigGatewayRows_ShowsSavedKey(t *testing.T) { } func TestConfiguredCredentialProviders_UsedByGatewaysTab(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() - got := hawkconfig.ConfiguredCredentialProviders() + got := graycodeconfig.ConfiguredCredentialProviders() if len(got) != 1 || got[0] != "openrouter" { t.Fatalf("configured = %v", got) } } func TestRemoveCredentialAsync(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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 3a444261..bf496252 100644 --- a/cmd/chat_config_save_flow_test.go +++ b/cmd/chat_config_save_flow_test.go @@ -10,23 +10,23 @@ import ( tea "charm.land/bubbletea/v2" "github.com/GrayCodeAI/eyrie/catalog" "github.com/GrayCodeAI/eyrie/credentials" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" ) func TestFinishConfigEntry_APIKeyPaste_SavesBeforeProbe(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &credentials.MapStore{} credentials.SetDefaultStore(store) t.Cleanup(func() { credentials.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) oldTransport := http.DefaultTransport http.DefaultTransport = configSaveTestTransport(http.StatusUnauthorized) t.Cleanup(func() { http.DefaultTransport = oldTransport }) - if _, err := hawkconfig.CredentialInferenceForProvider("xiaomi_mimo_payg"); err != nil { + if _, err := graycodeconfig.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/hawk/issues/28 + t.Skip("all gateways already have keys in this environment") // TODO: https://github.com/GrayCodeAI/graycode-cli/issues/28 } m.configSel = sel next, _ := m.handleConfigGatewaysSelect() @@ -134,7 +134,7 @@ func configSaveTestTransport(status int) http.RoundTripper { }) } -// roundTripFunc is defined in eyrie/runtime tests; duplicate here for hawk cmd tests. +// roundTripFunc is defined in eyrie/runtime tests; duplicate here for graycode 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) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &credentials.MapStore{} credentials.SetDefaultStore(store) t.Cleanup(func() { credentials.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &credentials.MapStore{} credentials.SetDefaultStore(store) t.Cleanup(func() { credentials.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), credentials.AccountForEnv("POOLSIDE_API_KEY"), "poolside-test-key"); err != nil { t.Fatalf("store key: %v", err) @@ -218,7 +218,7 @@ 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, "hawk models refresh") { + !strings.Contains(next.configNotice, "graycode models refresh") { t.Fatalf("catalog recovery guidance missing: %q", next.configNotice) } } @@ -227,12 +227,12 @@ func TestHandleConfigApplyCredentialsMsg_CatalogFailureDoesNotBlameProvider(t *t func TestHandleConfigApplyCredentialsMsg_ValidationFailureDoesNotBlameProvider(t *testing.T) { // 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() + graycodeconfig.InvalidateConfigUICache() store := &credentials.MapStore{} credentials.SetDefaultStore(store) t.Cleanup(func() { credentials.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) if err := store.Set(t.Context(), credentials.AccountForEnv("CONCENTRATE_API_KEY"), "concentrate-test-key"); err != nil { t.Fatalf("store key: %v", err) @@ -256,12 +256,12 @@ func TestHandleConfigApplyCredentialsMsg_ValidationFailureDoesNotBlameProvider(t func TestHandleConfigApplyCredentialsMsg_AuthenticationFailureBlamesKey(t *testing.T) { // TODO: enable once eyrie catalog fixtures pin the auth-failure model state. t.Skip("requires specific eyrie model catalog state") - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &credentials.MapStore{} credentials.SetDefaultStore(store) t.Cleanup(func() { credentials.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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 aa8d10e4..68598403 100644 --- a/cmd/chat_config_security.go +++ b/cmd/chat_config_security.go @@ -4,7 +4,7 @@ import ( "strings" "sync" - "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) var ( diff --git a/cmd/chat_config_tabs.go b/cmd/chat_config_tabs.go index 7ac1a93e..01c58d2e 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" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) func (m chatModel) configStatus() (gateway, model string, configured bool) { ctx := context.Background() - if !hawkconfig.HasConfiguredDeploymentCached(ctx) { + if !graycodeconfig.HasConfiguredDeploymentCached(ctx) { return "none", "", false } gw := strings.TrimSpace(m.configModelProvider) - if gw != "" && hawkconfig.IsSetupGateway(gw) { - gw = hawkconfig.GatewayDisplayName(gw) - } else if active := hawkconfig.ActiveGateway(ctx); active != "" { - gw = hawkconfig.GatewayDisplayName(active) + if gw != "" && graycodeconfig.IsSetupGateway(gw) { + gw = graycodeconfig.GatewayDisplayName(gw) + } else if active := graycodeconfig.ActiveGateway(ctx); active != "" { + gw = graycodeconfig.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(hawkconfig.ActiveModel(ctx)) + model = strings.TrimSpace(graycodeconfig.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(hawkColor).Bold(true) + return lipgloss.NewStyle().Foreground(graycodeColor).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 && !hawkconfig.HasConfiguredDeploymentCached(ctx) { + if tab == configTabModels && !graycodeconfig.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 - hawkconfig.RefreshConfigCredSnapshot(ctx) + graycodeconfig.RefreshConfigCredSnapshot(ctx) m = m.refreshConfigGatewayRows() - setup := hawkconfig.EvaluateSetupCached(ctx) + setup := graycodeconfig.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 743e7787..fd2974e1 100644 --- a/cmd/chat_config_tabs_test.go +++ b/cmd/chat_config_tabs_test.go @@ -4,9 +4,9 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/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) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) m := chatModel{} @@ -47,12 +47,12 @@ func TestOpenConfigPanel_FirstRunOpensGateways(t *testing.T) { } func TestOpenConfigAtTab_ModelsWithoutCredentialsFallsBackToGateways(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) m := chatModel{} diff --git a/cmd/chat_config_ui.go b/cmd/chat_config_ui.go index 30cdbaa8..f4d6f974 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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(hawkColor).Bold(true) + return lipgloss.NewStyle().Foreground(graycodeColor).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(hawkColor).Bold(true) + return lipgloss.NewStyle().Foreground(graycodeColor).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(hawkColor) + return lipgloss.NewStyle().Foreground(graycodeColor) } func renderConfigBreadcrumb(title string) string { diff --git a/cmd/chat_copy_e2e_test.go b/cmd/chat_copy_e2e_test.go index 94a776ed..27c2ad78 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" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/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("HAWK_MOUSE", "") + t.Setenv("GRAYCODE_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 hawk"}) + m.messages = append(m.messages, displayMsg{role: "assistant", content: "Hello from graycode"}) m.input.SetValue("") - if content, _, got := m.copyContent(copyModeAssistant); !got || content != "Hello from hawk" { + if content, _, got := m.copyContent(copyModeAssistant); !got || content != "Hello from graycode" { t.Fatalf("pass %d: /copy assistant content = %q got=%v", pass, content, got) } - if line, got := m.lastMessageContent(); !got || !strings.Contains(line, "Hello from hawk") { + if line, got := m.lastMessageContent(); !got || !strings.Contains(line, "Hello from graycode") { 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: hawkconfig.Settings{TuiMouse: &disabled}} + m2 := chatModel{settings: graycodeconfig.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 f143ee37..36b72c15 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" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/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("HAWK_MOUSE", "") + t.Setenv("GRAYCODE_MOUSE", "") if !(chatModel{}).mouseEnabled() { t.Fatal("expected mouse capture to default on") } disabled := false - m := chatModel{settings: hawkconfig.Settings{TuiMouse: &disabled}} + m := chatModel{settings: graycodeconfig.Settings{TuiMouse: &disabled}} if m.mouseEnabled() { t.Fatal("expected settings tui_mouse=false to disable capture") } - t.Setenv("HAWK_MOUSE", "0") + t.Setenv("GRAYCODE_MOUSE", "0") if m.mouseEnabled() { - t.Fatal("expected HAWK_MOUSE=0 to disable capture") + t.Fatal("expected GRAYCODE_MOUSE=0 to disable capture") } - t.Setenv("HAWK_MOUSE", "1") + t.Setenv("GRAYCODE_MOUSE", "1") if !m.mouseEnabled() { - t.Fatal("expected HAWK_MOUSE=1 to enable capture") + t.Fatal("expected GRAYCODE_MOUSE=1 to enable capture") } } diff --git a/cmd/chat_export.go b/cmd/chat_export.go index 54b51a3f..dafafdd8 100644 --- a/cmd/chat_export.go +++ b/cmd/chat_export.go @@ -8,8 +8,8 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/session" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/graycode-cli/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("Hawk Session: %s\n", m.sessionID)) + b.WriteString(fmt.Sprintf("Graycode 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 09a0ec28..dd5c7898 100644 --- a/cmd/chat_focus.go +++ b/cmd/chat_focus.go @@ -9,9 +9,9 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/storage" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/graycode-cli/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 d9dafe06..d4dfb4da 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/hawk/internal/textutil" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/textutil" + "github.com/GrayCodeAI/graycode-cli/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 019b5df6..e67b1366 100644 --- a/cmd/chat_journey_e2e_test.go +++ b/cmd/chat_journey_e2e_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/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) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) ctx := context.Background() if err := store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890"); err != nil { t.Fatal(err) } - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() m := newTestChatModel() diff --git a/cmd/chat_layout_mouse_test.go b/cmd/chat_layout_mouse_test.go index 35201db8..287022a1 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: "HAWK LOGO\nv0.1.0", + welcomeCache: "GRAYCODE 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 9a84817a..0b30e124 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: "HAWK LOGO\nv0.1.0", + welcomeCache: "GRAYCODE 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, "HAWK LOGO") { + if !strings.Contains(got, "GRAYCODE 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: "HAWK LOGO\nv0.1.0", + welcomeCache: "GRAYCODE 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(), "HAWK LOGO") { + if strings.Contains(m.viewport.View(), "GRAYCODE LOGO") { t.Fatal("expected empty initial viewport before priming") } m.primeInitialViewportContent() - if !strings.Contains(m.viewport.View(), "HAWK LOGO") { + if !strings.Contains(m.viewport.View(), "GRAYCODE 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 a63bf90f..d765d7ab 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/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" + "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" ) // 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 hawk crashes. Check disk space and permissions." + m.durabilityWarning = "Warning: session persistence is failing — recent messages may be lost if graycode crashes. Check disk space and permissions." } -// All hawk color/icon/glyph constants live in theme.go. This file holds +// All graycode 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(hawkColor).Bold(true) - slashSelDescStyle = lipgloss.NewStyle().Foreground(hawkColor) + slashSelCmdStyle = lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) + slashSelDescStyle = lipgloss.NewStyle().Foreground(graycodeColor) 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 ) -// hawkSpinnerFrames feeds the bubbles spinner (matches BrailleSpinner default). -var hawkSpinnerFrames = hawkSpinnerGlyphs +// graycodeSpinnerFrames feeds the bubbles spinner (matches BrailleSpinner default). +var graycodeSpinnerFrames = graycodeSpinnerGlyphs -// hawkSpinnerFrameInterval — compass frame cadence. -const hawkSpinnerFrameInterval = 80 * time.Millisecond +// graycodeSpinnerFrameInterval — compass frame cadence. +const graycodeSpinnerFrameInterval = 80 * time.Millisecond -// Spinner verbs (from hawk-archive) — picked randomly per session +// Spinner verbs (from graycode-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 hawkconfig.SetupState + welcomeSetup graycodeconfig.SetupState welcomeAgentsOK bool } processArrowTickMsg struct { @@ -209,7 +209,7 @@ type chatModel struct { viewport viewport.Model session *engine.Session registry *tool.Registry - settings hawkconfig.Settings + settings graycodeconfig.Settings ref *progRef cancel context.CancelFunc // cancel current stream sessionID string @@ -307,7 +307,7 @@ type chatModel struct { sessionBootstrapDone bool toolStartTime time.Time welcomeCache string - welcomeSetupState hawkconfig.SetupState + welcomeSetupState graycodeconfig.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 8bc4b7e5..16ed411d 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/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" + "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" ) func newTestChatModel() *chatModel { @@ -43,7 +43,7 @@ func newTestChatModel() *chatModel { hintsLoader: engine.NewHintsLoader(), selfImprover: engine.NewSelfImprover(), codingSoul: engine.LoadCodingSoul(), - brailleSpinner: NewBrailleSpinner(SpinnerHawk, "Thinking"), + brailleSpinner: NewBrailleSpinner(SpinnerGraycode, "Thinking"), testStreamStarter: func() {}, } return m @@ -62,12 +62,12 @@ func isolateChatCommandSweepEnv(t *testing.T) { root := t.TempDir() storage.SetTestDirs(t, root) isolateCredentialHome(t) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() gateway.SetDefaultStore(&gateway.MapStore{}) restoreThemeGlobals(t) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) } @@ -77,7 +77,7 @@ func isolateChatCommandSweepEnv(t *testing.T) { // (e.g. TestAdaptiveNeutralsPreserveDarkAppearance). func restoreThemeGlobals(t *testing.T) { t.Helper() - savedHawk, savedSuccess, savedWarn, savedErr, savedInfo := hawkColor, successTeal, warnAmber, errorCoral, infoSky + savedGraycode, savedSuccess, savedWarn, savedErr, savedInfo := graycodeColor, 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() { - hawkColor, successTeal, warnAmber, errorCoral, infoSky = savedHawk, savedSuccess, savedWarn, savedErr, savedInfo + graycodeColor, successTeal, warnAmber, errorCoral, infoSky = savedGraycode, 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 Hawk!\n\nTo resume this session, run: hawk --resume 44418bdd52745678\n" + want := "Thank you for using Graycode!\n\nTo resume this session, run: graycode --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 Hawk!\n" + want := "Thank you for using Graycode!\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 0be52a4b..32e9a9c5 100644 --- a/cmd/chat_mouse.go +++ b/cmd/chat_mouse.go @@ -5,11 +5,11 @@ import ( "os" "strings" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" ) func mouseEnabledFromEnv() bool { - v := strings.TrimSpace(os.Getenv("HAWK_MOUSE")) + v := strings.TrimSpace(os.Getenv("GRAYCODE_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("HAWK_MOUSE")) + v := strings.TrimSpace(os.Getenv("GRAYCODE_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: HAWK_MOUSE env → runtime override → settings → default on. +// "mouse": false). Priority: GRAYCODE_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 = "HAWK_MOUSE env" + source = "GRAYCODE_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 HAWK_MOUSE env in this session. " + + content: "Mouse is controlled by GRAYCODE_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) - _ = hawkconfig.SetGlobalSetting("tui_mouse", "true") + _ = graycodeconfig.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) - _ = hawkconfig.SetGlobalSetting("tui_mouse", "false") + _ = graycodeconfig.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." } - _ = hawkconfig.SetGlobalSetting("tui_mouse", val) + _ = graycodeconfig.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 c1eb67a0..913c1548 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" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) func configureReadyChatState(t *testing.T) { t.Helper() isolateChatCommandSweepEnv(t) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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 := hawkconfig.SetActiveProvider(ctx, "openrouter"); err != nil { + if err := graycodeconfig.SetActiveProvider(ctx, "openrouter"); err != nil { t.Fatal(err) } - if err := hawkconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { + if err := graycodeconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { t.Fatal(err) } - hawkconfig.InvalidateConfigUICache() - hawkconfig.RefreshConfigCredSnapshot(ctx) + graycodeconfig.InvalidateConfigUICache() + graycodeconfig.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 93765c47..ed239c36 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/eagle/policy" - "github.com/GrayCodeAI/hawk/internal/engine" + contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/policy" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) func TestPermissionAlwaysAllowDoesNotNilDeref(t *testing.T) { diff --git a/cmd/chat_platform_ctx.go b/cmd/chat_platform_ctx.go index 0ced9846..2a8fdbcf 100644 --- a/cmd/chat_platform_ctx.go +++ b/cmd/chat_platform_ctx.go @@ -8,7 +8,7 @@ import ( "time" tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" ) var platformCtxCache struct { @@ -51,7 +51,7 @@ type platformContextIndexMsg struct { func fetchPlatformContextIndexCmd() tea.Cmd { return func() tea.Msg { - models, err := hawkconfig.ListPublicEngineModels(context.Background(), "xiaomi_mimo_payg") + models, err := graycodeconfig.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 996c4697..ce2999d5 100644 --- a/cmd/chat_print.go +++ b/cmd/chat_print.go @@ -14,12 +14,12 @@ import ( "unicode/utf8" lipgloss "charm.land/lipgloss/v2" - 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" + 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" ) // Print mode and session persistence functions extracted from chat.go @@ -40,7 +40,7 @@ func runPrint(text string) error { return err } - sess, cfgErr := newConfiguredHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error)) + sess, cfgErr := newConfiguredGraycodeSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error)) if cfgErr != nil { return cfgErr } @@ -262,7 +262,7 @@ func saveEyrieSession(id string, sess *engine.Session) { // runRepl starts an interactive REPL mode for multi-turn conversation without TUI. func runRepl() error { - fmt.Fprintln(os.Stderr, "Hawk REPL — type 'exit' or 'quit' to leave, 'help' for commands") + fmt.Fprintln(os.Stderr, "Graycode REPL — type 'exit' or 'quit' to leave, 'help' for commands") fmt.Fprintln(os.Stderr) systemPrompt, err := buildSystemPrompt() @@ -281,7 +281,7 @@ func runRepl() error { return err } - sess, cfgErr := newConfiguredHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error)) + sess, cfgErr := newConfiguredGraycodeSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error)) if cfgErr != nil { return cfgErr } @@ -433,7 +433,7 @@ func runRepl() error { } } -func replBuiltinResponse(input string, sess *engine.Session, settings hawkconfig.Settings, sessionID string) (string, bool, error) { +func replBuiltinResponse(input string, sess *engine.Session, settings graycodeconfig.Settings, sessionID string) (string, bool, error) { switch strings.TrimSpace(input) { case "/tools": return builtInToolsSummary(), true, nil @@ -452,12 +452,12 @@ func replBuiltinResponse(input string, sess *engine.Session, settings hawkconfig } } -func replModelsSummary(settings hawkconfig.Settings, sessionProvider string) (string, bool, error) { +func replModelsSummary(settings graycodeconfig.Settings, sessionProvider string) (string, bool, error) { providerName := effectiveProviderForREPL(settings, sessionProvider) if providerName == "" { - return "No active provider selected. Set one with `hawk config provider ` or start REPL with `--provider`.", true, nil + return "No active provider selected. Set one with `graycode config provider ` or start REPL with `--provider`.", true, nil } - models, err := hawkconfig.FetchModelsForProvider(providerName) + models, err := graycodeconfig.FetchModelsForProvider(providerName) if err != nil { return "", true, err } @@ -483,7 +483,7 @@ func replModelsSummary(settings hawkconfig.Settings, sessionProvider string) (st return b.String(), true, nil } -func effectiveProviderForREPL(settings hawkconfig.Settings, sessionProvider string) string { +func effectiveProviderForREPL(settings graycodeconfig.Settings, sessionProvider string) string { if provider != "" { return strings.TrimSpace(provider) } @@ -508,7 +508,7 @@ func formatModelTablePlain(rows []modelTableRow) string { } // watchIgnoreDirs are directory names skipped when scanning for AI directives. -var watchIgnoreDirs = []string{".git", "node_modules", "vendor", "__pycache__", ".hawk"} +var watchIgnoreDirs = []string{".git", "node_modules", "vendor", "__pycache__", ".graycode"} // 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_prompt_timeout_test.go b/cmd/chat_prompt_timeout_test.go index c8f3198e..83aac204 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/eagle/policy" - "github.com/GrayCodeAI/hawk/internal/engine" + contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/policy" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) func TestPermissionPromptTimeoutClearsStaleState(t *testing.T) { diff --git a/cmd/chat_scrollbar.go b/cmd/chat_scrollbar.go index 8ca748eb..538c75f6 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(hawkColor) + scrollbarThumbStyle = lipgloss.NewStyle().Foreground(graycodeColor) scrollbarTrackStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("238")) ) diff --git a/cmd/chat_select.go b/cmd/chat_select.go index 499da7b2..1376d084 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 hawk.") + fmt.Fprintln(os.Stderr, " Press any key to return to graycode.") 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("hawk: ") + b.WriteString("graycode: ") 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 "hawk: " + content, true + return "graycode: " + 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 b5037eb3..1552d477 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" "github.com/mattn/go-runewidth" - "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/graycode-cli/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 7014878d..739e71ff 100644 --- a/cmd/chat_status.go +++ b/cmd/chat_status.go @@ -8,8 +8,8 @@ import ( lipgloss "charm.land/lipgloss/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/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(hawkconfig.ConfiguredCredentialProviders(), ",") + creds := strings.Join(graycodeconfig.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(hawkconfig.ProviderOfModel(model)) + gateway = strings.TrimSpace(graycodeconfig.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 !hawkconfig.CredentialSnapshotReady() { + if !graycodeconfig.CredentialSnapshotReady() { return "" } - if !hawkconfig.HasConfiguredDeploymentCached(ctx) { + if !graycodeconfig.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 = hawkconfig.GatewayDisplayName(gw) + gateway = graycodeconfig.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 !hawkconfig.CredentialSnapshotReady() { + if !graycodeconfig.CredentialSnapshotReady() { return "", 0, "", 0 } - if !hawkconfig.HasConfiguredDeploymentCached(ctx) { + if !graycodeconfig.HasConfiguredDeploymentCached(ctx) { return "", 0, "", 0 } diff --git a/cmd/chat_status_metadata_test.go b/cmd/chat_status_metadata_test.go index 4a0660f7..5f50fa7b 100644 --- a/cmd/chat_status_metadata_test.go +++ b/cmd/chat_status_metadata_test.go @@ -4,8 +4,8 @@ import ( "testing" "time" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/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 := hawkconfig.GatewayDisplayName("xiaomi-mimo-token-plan"); got != "Xiaomi MiMo — Token Plan" { + if got := graycodeconfig.GatewayDisplayName("xiaomi-mimo-token-plan"); got != "Xiaomi MiMo — Token Plan" { t.Fatalf("GatewayDisplayName(hyphen) = %q, want nice name", got) } - if got := hawkconfig.GatewayDisplayName("xiaomi_mimo_token_plan"); got != "Xiaomi MiMo — Token Plan" { + if got := graycodeconfig.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 d456149c..cbf8e79f 100644 --- a/cmd/chat_status_test.go +++ b/cmd/chat_status_test.go @@ -7,9 +7,9 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" "github.com/charmbracelet/x/ansi" ) @@ -92,23 +92,23 @@ func TestFormatContextUsedLabel(t *testing.T) { } func TestChatConnectionStatus_WithModel(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() - _ = hawkconfig.SetActiveProvider(ctx, "openrouter") - _ = hawkconfig.SetActiveModel(ctx, "moonshotai/kimi-k2.6") - hawkconfig.RefreshConfigCredSnapshot(ctx) + graycodeconfig.InvalidateConfigUICache() + _ = graycodeconfig.SetActiveProvider(ctx, "openrouter") + _ = graycodeconfig.SetActiveModel(ctx, "moonshotai/kimi-k2.6") + graycodeconfig.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) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() - _ = hawkconfig.ClearActiveSelection(ctx) - _ = hawkconfig.SetActiveProvider(ctx, "openrouter") - hawkconfig.RefreshConfigCredSnapshot(ctx) + graycodeconfig.InvalidateConfigUICache() + _ = graycodeconfig.ClearActiveSelection(ctx) + _ = graycodeconfig.SetActiveProvider(ctx, "openrouter") + graycodeconfig.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) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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) } - hawkconfig.InvalidateConfigUICache() - _ = hawkconfig.ClearActiveSelection(ctx) - hawkconfig.RefreshConfigCredSnapshot(ctx) + graycodeconfig.InvalidateConfigUICache() + _ = graycodeconfig.ClearActiveSelection(ctx) + graycodeconfig.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: hawkconfig.SetupState{NeedsSetup: true}, + welcomeSetup: graycodeconfig.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, hawkconfig.Settings{}, 0, false, 80, 24, &running) + msg := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.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, hawkconfig.Settings{}, 0, false, 80, 24, nil) + msg := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.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, hawkconfig.Settings{}, 0, false, 80, 24, nil) + msg := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.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 d46f209f..942418ba 100644 --- a/cmd/chat_stream.go +++ b/cmd/chat_stream.go @@ -8,7 +8,7 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 c9062d4c..9610f7fa 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 6ece33d5..196cca5c 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/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) -// branchAgentSubcommand creates a hawk/agent-* branch from main/master. +// branchAgentSubcommand creates a graycode/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 hawk/agent-* branch if on main/master" + return "create graycode/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 bf5ae740..3f346fec 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) // checkpointSubcommand implements the /checkpoint slash command. diff --git a/cmd/chat_subcommand_context.go b/cmd/chat_subcommand_context.go index 76b04e86..4144b18d 100644 --- a/cmd/chat_subcommand_context.go +++ b/cmd/chat_subcommand_context.go @@ -6,8 +6,8 @@ import ( tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine/project" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/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: hawkconfig.BuildContextWithDirs(addDirs)}) + m.messages = append(m.messages, displayMsg{role: "system", content: graycodeconfig.BuildContextWithDirs(addDirs)}) return m, nil } diff --git a/cmd/chat_subcommand_council.go b/cmd/chat_subcommand_council.go index 57db644f..9bac3eaf 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/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 4a8902d4..064cc5eb 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/hawk/internal/intelligence/memory" + "github.com/GrayCodeAI/graycode-cli/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 ec4288b9..6f3c99be 100644 --- a/cmd/chat_subcommand_ecosystem.go +++ b/cmd/chat_subcommand_ecosystem.go @@ -5,7 +5,7 @@ import ( tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/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: hawkconfig.FormatEcosystemPanel(context.Background(), providerName, modelName)}) + m.messages = append(m.messages, displayMsg{role: "system", content: graycodeconfig.FormatEcosystemPanel(context.Background(), providerName, modelName)}) return m, nil } diff --git a/cmd/chat_subcommand_harness.go b/cmd/chat_subcommand_harness.go index 3861e464..5d390abd 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/hawk/internal/harness" + "github.com/GrayCodeAI/graycode-cli/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 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.", + 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.", 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 d1482ba2..b675d667 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/hawk/internal/intelligence/memory" + "github.com/GrayCodeAI/graycode-cli/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 afd05bda..8738633f 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) // investigateSubcommand implements the /investigate slash command. diff --git a/cmd/chat_subcommand_isolation.go b/cmd/chat_subcommand_isolation.go index fbcac867..457df94f 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 92bacee4..59c5561e 100644 --- a/cmd/chat_subcommand_memory.go +++ b/cmd/chat_subcommand_memory.go @@ -5,7 +5,7 @@ import ( tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/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(hawkconfig.LoadAgentsMD()) + md := strings.TrimSpace(graycodeconfig.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 a47a5c47..be716abf 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/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/feature/shellmode" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/feature/shellmode" ) // modeSubcommand implements the /mode slash command. diff --git a/cmd/chat_subcommand_model.go b/cmd/chat_subcommand_model.go index b37f6bd1..aa6b2196 100644 --- a/cmd/chat_subcommand_model.go +++ b/cmd/chat_subcommand_model.go @@ -6,7 +6,7 @@ import ( tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/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 hawkconfig.DeploymentRoutingEnabled(m.settings) { - arg = hawkconfig.ResolveCanonicalModel(arg) + if graycodeconfig.DeploymentRoutingEnabled(m.settings) { + arg = graycodeconfig.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 := hawkconfig.SetGlobalSetting("model", arg); err != nil { + if err := graycodeconfig.SetGlobalSetting("model", arg); err != nil { m.messages = append(m.messages, displayMsg{role: "error", content: err.Error()}) return m, nil } @@ -81,11 +81,11 @@ func (mo *modelSubcommand) Handle(m *chatModel, args []string, text string) (tea if m.session != nil { provider = m.session.Provider() } - m.session.SetThinkingEnabled(hawkconfig.ResolveThinkingForModel(hawkconfig.LoadSettings(), arg, provider)) + m.session.SetThinkingEnabled(graycodeconfig.ResolveThinkingForModel(graycodeconfig.LoadSettings(), arg, provider)) } - thinkLabel := hawkconfig.FormatModelThinkingLabel( - selected != nil && hawkconfig.ModelCapabilitySupportsThinking(selected.Capabilities), - hawkconfig.ThinkingPrefForModel(hawkconfig.LoadSettings(), arg), + thinkLabel := graycodeconfig.FormatModelThinkingLabel( + selected != nil && graycodeconfig.ModelCapabilitySupportsThinking(selected.Capabilities), + graycodeconfig.ThinkingPrefForModel(graycodeconfig.LoadSettings(), arg), m.session.Provider(), ) m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf( diff --git a/cmd/chat_subcommand_party.go b/cmd/chat_subcommand_party.go index 898d0cd7..192f869c 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 1fd773f5..606a9c33 100644 --- a/cmd/chat_subcommand_path.go +++ b/cmd/chat_subcommand_path.go @@ -5,7 +5,7 @@ import ( tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/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: hawkconfig.FormatDeveloperPathReport(context.Background())}) + m.messages = append(m.messages, displayMsg{role: "system", content: graycodeconfig.FormatDeveloperPathReport(context.Background())}) return m, nil } diff --git a/cmd/chat_subcommand_recipe.go b/cmd/chat_subcommand_recipe.go index 7bed6af8..bb5ad474 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/hawk/internal/recipe" + "github.com/GrayCodeAI/graycode-cli/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 Hawk user state or .agents/recipes/"}) + m.messages = append(m.messages, displayMsg{role: "system", content: "No recipes found in Graycode 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 9b69ab5f..b5f0198a 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) // reflectSubcommand implements the /reflect slash command. It diff --git a/cmd/chat_subcommand_render.go b/cmd/chat_subcommand_render.go index 90fbf48a..e175c635 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 84fc9cde..5f66d5a0 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 7bd5564e..c8e6dbab 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) func TestRunSubcommand_UsesBashToolForSafeCommand(t *testing.T) { diff --git a/cmd/chat_subcommand_simple.go b/cmd/chat_subcommand_simple.go index c666ec80..8f3f6af8 100644 --- a/cmd/chat_subcommand_simple.go +++ b/cmd/chat_subcommand_simple.go @@ -11,13 +11,13 @@ import ( tea "charm.land/bubbletea/v2" - 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" + 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" ) // 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 := hawkconfig.LoadGlobalSettings().Theme + current := graycodeconfig.LoadGlobalSettings().Theme m.themePicker.OpenWithCurrent(current) m.viewDirty = true m.updateViewportContent() @@ -96,7 +96,7 @@ func init() { } // Inline: /theme themeName := args[0] - if err := hawkconfig.SetGlobalSetting("theme", themeName); err != nil { + if err := graycodeconfig.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 := hawkconfig.SetGlobalSetting("agentColor", args[0]); err != nil { + if err := graycodeconfig.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 := hawkconfig.ActiveModel(context.Background()) + savedModel := graycodeconfig.ActiveModel(context.Background()) if m.session.Model() == savedModel { providerName := strings.TrimSpace(m.session.Provider()) - fastModel := hawkconfig.CheapestModelForProvider(providerName, m.session.Model()) + fastModel := graycodeconfig.CheapestModelForProvider(providerName, m.session.Model()) if strings.TrimSpace(fastModel) == "" { - fastModel = hawkconfig.DefaultModelForProvider(providerName) + fastModel = graycodeconfig.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": - _ = hawkconfig.SetGlobalSetting("reasoningEffort", level) + _ = graycodeconfig.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 := hawkconfig.LoadGlobalSettings() + settings := graycodeconfig.LoadGlobalSettings() current := settings.CompactMode newVal := !current valStr := "false" if newVal { valStr = "true" } - if err := hawkconfig.SetGlobalSetting("compact_mode", valStr); err != nil { + if err := graycodeconfig.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", hawkconfig.LoadGlobalSettings().ScrollSpeed)}) + m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Usage: /scroll-speed <1-100>\nCurrent: %d", graycodeconfig.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 := hawkconfig.SetGlobalSetting("scroll_speed", args[0]); err != nil { + if err := graycodeconfig.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 := hawkconfig.LoadGlobalSettings() + settings := graycodeconfig.LoadGlobalSettings() current := settings.InvertScroll newVal := !current valStr := "false" @@ -383,7 +383,7 @@ func init() { if newVal { enabled = "enabled" } - if err := hawkconfig.SetGlobalSetting("invert_scroll", valStr); err != nil { + if err := graycodeconfig.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", hawkconfig.LoadGlobalSettings().ScrollMode)}) + m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("Usage: /scroll-mode \nCurrent: %s", graycodeconfig.LoadGlobalSettings().ScrollMode)}) return m, nil } mode := strings.ToLower(args[0]) switch mode { case "auto", "wheel", "trackpad": - if err := hawkconfig.SetGlobalSetting("scrollmode", mode); err != nil { + if err := graycodeconfig.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, hawkconfig.LoadGlobalSettings().ScrollMode, hawkconfig.LoadGlobalSettings().ScrollSpeed, hawkconfig.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, graycodeconfig.LoadGlobalSettings().ScrollMode, graycodeconfig.LoadGlobalSettings().ScrollSpeed, graycodeconfig.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 := hawkconfig.LoadGlobalSettings() + s := graycodeconfig.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 := hawkconfig.SetGlobalSetting("paginatorlines", value); err != nil { + if err := graycodeconfig.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 := hawkconfig.SetGlobalSetting("paginatorshowlinenumbers", "true"); err != nil { + if err := graycodeconfig.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 := hawkconfig.SetGlobalSetting("paginatorshowlinenumbers", "false"); err != nil { + if err := graycodeconfig.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 hawk updates", + description: "check for graycode updates", usage: "", handler: func(m *chatModel, args []string, text string) (tea.Model, tea.Cmd) { - return m.startPromptCommand("/upgrade", "Check for hawk updates and show the latest available version.") + return m.startPromptCommand("/upgrade", "Check for graycode updates and show the latest available version.") }, }) @@ -783,7 +783,7 @@ func init() { style := strings.ToLower(args[0]) switch style { case "concise", "normal", "detailed": - _ = hawkconfig.SetGlobalSetting("outputStyle", style) + _ = graycodeconfig.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 Hawk user state. + // /feedback — submit feedback saved to Graycode user state. subcommandRegistry.Register(&delegatingCommand{ name: "feedback", - description: "submit feedback (saved to Hawk user state)", + description: "submit feedback (saved to Graycode 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 Hawk user state."}) + m.messages = append(m.messages, displayMsg{role: "system", content: "Usage: /feedback \nCaptures session context and saves feedback to Graycode 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 := hawkconfig.DeploymentStatusReport(context.Background(), m.session.Model()) + report, err := graycodeconfig.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 := hawkconfig.RefreshModelCatalogV1(context.Background()) + summary, err := graycodeconfig.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 0dd3f3bc..c4710ce4 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 7f2f2efe..c6266fb0 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/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/spec" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 0e2375bf..858f91da 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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("## Hawk quick start\n\n") + b.WriteString("## Graycode 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 dd4cc383..4c2daa96 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/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) // statusSubcommand implements the /status slash command. It prints diff --git a/cmd/chat_subcommand_trust.go b/cmd/chat_subcommand_trust.go index f205b8cc..ebcd3acb 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/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 98ff1352..4b3c6b1e 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 hawk version. Follows the SubcommandRegistry pattern +// the running graycode 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 hawk version" } +func (v *versionSubcommand) Description() string { return "print the running graycode 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("hawk v%s", DisplayVersion())}) + m.messages = append(m.messages, displayMsg{role: "system", content: fmt.Sprintf("graycode v%s", DisplayVersion())}) return m, nil } diff --git a/cmd/chat_subcommand_voice.go b/cmd/chat_subcommand_voice.go index 0c16b47e..acc95f7d 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(), "hawk_voice_input.wav") + tmpFile := filepath.Join(os.TempDir(), "graycode_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 6b04039f..19e91d39 100644 --- a/cmd/chat_submit.go +++ b/cmd/chat_submit.go @@ -11,11 +11,11 @@ import ( tea "charm.land/bubbletea/v2" - 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" + 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" ) // 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 := hawkconfig.EvaluateSetupCached(context.Background()); setup.NeedsSetup { + if setup := graycodeconfig.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 7221aace..47b0e0a0 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 Hawk process, then + // Explicitly disable any-motion mode left by an older Graycode 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 bb8a64bd..829aa528 100644 --- a/cmd/chat_tools.go +++ b/cmd/chat_tools.go @@ -6,9 +6,9 @@ import ( "sync" "time" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/lsp" - "github.com/GrayCodeAI/hawk/internal/tool" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/lsp" + "github.com/GrayCodeAI/graycode-cli/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 hawkconfig.Settings) []startupMCPServerSpec { +func configuredStartupMCPServers(settings graycodeconfig.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 hawkconfig.Settings) []startupMCPServe // 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 hawkconfig.MCPServerConfig) map[string]string { +func mergedMCPHeaders(cfg graycodeconfig.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 hawkconfig.Settings) (*tool.Registry, error) { +func defaultRegistry(settings graycodeconfig.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 373efeb5..ab29d5b5 100644 --- a/cmd/chat_tools_lsp_test.go +++ b/cmd/chat_tools_lsp_test.go @@ -3,12 +3,12 @@ package cmd import ( "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/tool" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/tool" ) func TestDefaultRegistryWiresLanguageServerManager(t *testing.T) { - registry, err := defaultRegistry(hawkconfig.Settings{}) + registry, err := defaultRegistry(graycodeconfig.Settings{}) if err != nil { t.Fatal(err) } diff --git a/cmd/chat_tools_test.go b/cmd/chat_tools_test.go index 5e226751..e98dfca4 100644 --- a/cmd/chat_tools_test.go +++ b/cmd/chat_tools_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/tool" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/tool" ) type registryTestTool struct { @@ -72,8 +72,8 @@ func TestLoadStartupMCPToolSets_UsesTimeoutAndPreservesOrder(t *testing.T) { } func TestConfiguredStartupMCPServers_DispatchesByType(t *testing.T) { - settings := hawkconfig.Settings{ - MCPServers: []hawkconfig.MCPServerConfig{ + settings := graycodeconfig.Settings{ + MCPServers: []graycodeconfig.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 := hawkconfig.MCPServerConfig{ + cfg := graycodeconfig.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(hawkconfig.Settings{ - MCPServers: []hawkconfig.MCPServerConfig{{Name: "demo", Command: "demo-mcp"}}, + registry, err := defaultRegistry(graycodeconfig.Settings{ + MCPServers: []graycodeconfig.MCPServerConfig{{Name: "demo", Command: "demo-mcp"}}, }) if err != nil { t.Fatalf("defaultRegistry returned error: %v", err) diff --git a/cmd/chat_update.go b/cmd/chat_update.go index 10ea470f..37807d4c 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" - 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" + 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" ) // 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 := hawkconfig.SetGlobalSetting("theme", chosen.Name); err != nil { + if err := graycodeconfig.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. Hawk is fail-closed: the only + // Container failed and is retryable. Graycode 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(hawkconfig.FormatConfigProviderError(msg.provider, msg.err)) + m.configNotice = sanitizeConfigNotice(graycodeconfig.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 = hawkconfig.CatalogEmptyHint(context.Background()) + m.configNotice = graycodeconfig.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, - formatHawkTokenCount(msg.tokensBefore), - formatHawkTokenCount(msg.tokensAfter), + formatGraycodeTokenCount(msg.tokensBefore), + formatGraycodeTokenCount(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("hawk", "Agent turn complete") + sendTerminalNotification("graycode", "Agent turn complete") } m.backgrounded = false m.notifiedComplete = false diff --git a/cmd/chat_update_test.go b/cmd/chat_update_test.go index 28645a38..a0a4ce2e 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/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/sandbox" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/sandbox" ) // TestContainerStatusErrFallsBackToHostAutonomy preserves the historical test diff --git a/cmd/chat_view.go b/cmd/chat_view.go index b6a3bcee..af4704fc 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 "hawk" / "GrayCode AI". +// sanitizeIdentity replaces model self-identifications with "graycode" / "GrayCode AI". func sanitizeIdentity(s string) string { s = reModelName.ReplaceAllStringFunc(s, func(m string) string { parts := reModelName.FindStringSubmatch(m) - return parts[1] + " hawk" + return parts[1] + " graycode" }) 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(hawkColor).Render("[y] allow once [n] deny [a] always allow tool [d] always deny tool") + options := lipgloss.NewStyle().Foreground(graycodeColor).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(hawkColor).Bold(true) + titleStyle := lipgloss.NewStyle().Foreground(graycodeColor).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(hawkColor). + BorderForeground(graycodeColor). 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(formatHawkTokenCount(outTok)) + b.WriteString(formatGraycodeTokenCount(outTok)) b.WriteString(ansiReset) b.WriteString(ansiDim + " " + ansiReset) b.WriteString(ansiCyan + ansiBold + "↑" + ansiReset) b.WriteString(ansiCyan) - b.WriteString(formatHawkTokenCount(inTok)) + b.WriteString(formatGraycodeTokenCount(inTok)) b.WriteString(ansiReset) return b.String() } @@ -753,9 +753,9 @@ func (m *chatModel) tokenOutputTarget() int { return m.turnEstimatedOutputRunes / 4 } -// formatHawkTokenCount renders a token count in hawk's compact form: +// formatGraycodeTokenCount renders a token count in graycode's compact form: // ≥1m → "1.5m", ≥10k → "150k", else raw digits. -func formatHawkTokenCount(tokens int) string { +func formatGraycodeTokenCount(tokens int) string { if tokens <= 0 { return "0" } diff --git a/cmd/chat_view_test.go b/cmd/chat_view_test.go index 11927fe2..2d59fce6 100644 --- a/cmd/chat_view_test.go +++ b/cmd/chat_view_test.go @@ -4,7 +4,7 @@ import ( "testing" ) -func TestFormatHawkTokenCount(t *testing.T) { +func TestFormatGraycodeTokenCount(t *testing.T) { t.Parallel() cases := []struct { in int @@ -25,9 +25,9 @@ func TestFormatHawkTokenCount(t *testing.T) { tc := tc t.Run("", func(t *testing.T) { t.Parallel() - got := formatHawkTokenCount(tc.in) + got := formatGraycodeTokenCount(tc.in) if got != tc.want { - t.Fatalf("formatHawkTokenCount(%d) = %q, want %q", tc.in, got, tc.want) + t.Fatalf("formatGraycodeTokenCount(%d) = %q, want %q", tc.in, got, tc.want) } }) } diff --git a/cmd/chat_viewport.go b/cmd/chat_viewport.go index 5df6beef..7a350dd1 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; hawk routes wheel +// syncViewportMouseWheel disables bubbletea viewport auto-wheel; graycode 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 b47d721c..b25038a0 100644 --- a/cmd/chat_viewport_render.go +++ b/cmd/chat_viewport_render.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 { - hawkC := ansiOrange + graycodeC := 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 + hawkC + "█" + rst + bgDark + " " + wl) + b.WriteString(bgDark + graycodeC + "█" + 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(hawkC + icons.Robot() + " " + rst + renderMarkdown(content, viewWidth-3)) + b.WriteString(graycodeC + 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 af9988b9..fb67e688 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" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/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("HAWK_MOUSE", "") + t.Setenv("GRAYCODE_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; hawk routes wheel by pane") + t.Fatal("viewport auto-wheel must stay off; graycode routes wheel by pane") } } func TestSyncViewportMouseWheel_DisabledWithOptOut(t *testing.T) { - t.Setenv("HAWK_MOUSE", "0") + t.Setenv("GRAYCODE_MOUSE", "0") vp := viewport.New(viewport.WithWidth(80), viewport.WithHeight(10)) disabled := false - m := chatModel{viewport: vp, uiFocus: focusPrompt, settings: hawkconfig.Settings{TuiMouse: &disabled}} + m := chatModel{viewport: vp, uiFocus: focusPrompt, settings: graycodeconfig.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 8fc9c421..5b74ae97 100644 --- a/cmd/chat_welcome.go +++ b/cmd/chat_welcome.go @@ -8,15 +8,15 @@ import ( "github.com/mattn/go-runewidth" - 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" + 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" ) type welcomeStatusSnapshot struct { - setup hawkconfig.SetupState + setup graycodeconfig.SetupState agentsOK bool } @@ -52,8 +52,8 @@ func (m chatModel) welcomeDockerRunning() *bool { func loadWelcomeStatusSnapshot() welcomeStatusSnapshot { ctx := context.Background() return welcomeStatusSnapshot{ - setup: hawkconfig.EvaluateSetupCached(ctx), - agentsOK: hawkconfig.LoadAgentsMD() != "", + setup: graycodeconfig.EvaluateSetupCached(ctx), + agentsOK: graycodeconfig.LoadAgentsMD() != "", } } @@ -99,8 +99,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 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 { +// 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 { frame := 0 if blinkClosed { frame = 2 @@ -108,8 +108,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 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 +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 // theme palette (theme.go) so a rebrand stays a one-file change. logoC := ansiOrange dimC := ansiDim @@ -147,7 +147,7 @@ func buildWelcomeMessageWithSnapshot(sess *engine.Session, sessionID string, reg return strings.Repeat(" ", pad) + styled } - art := hawkLogoArtLines + art := graycodeLogoArtLines var eyeGlyph string switch eyeFrame { case 1, 3: @@ -156,13 +156,13 @@ func buildWelcomeMessageWithSnapshot(sess *engine.Session, sessionID string, reg eyeGlyph = "|-\\/-|" } if eyeGlyph != "" { - art = append([]string(nil), hawkLogoArtLines...) + art = append([]string(nil), graycodeLogoArtLines...) for i, line := range art { art[i] = strings.Replace(line, "|0\\/0|", eyeGlyph, 1) } } - // Inject the version into the hawk's body — centered in the lower gap. + // Inject the version into the graycode's body — centered in the lower gap. verStr := DisplayVersion() if verStr != "" && !strings.HasPrefix(verStr, "v") && !strings.HasPrefix(verStr, "V") { verStr = "v" + verStr @@ -185,13 +185,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 hawk is hidden. + // inline so it's always visible even when the full graycode is hidden. verDisplay := DisplayVersion() if verDisplay != "" && !strings.HasPrefix(verDisplay, "v") && !strings.HasPrefix(verDisplay, "V") { verDisplay = "v" + verDisplay } - compactArt := logoC + "HAWK" + rst + " " + verDisplay - b.WriteString(center(runewidth.StringWidth("HAWK "+verDisplay), compactArt) + "\n") + compactArt := logoC + "GRAYCODE" + rst + " " + verDisplay + b.WriteString(center(runewidth.StringWidth("GRAYCODE "+verDisplay), compactArt) + "\n") } else { artW := blockLinesWidth(art) for _, line := range art { @@ -402,7 +402,7 @@ func envSummary(provider, model string) string { func envSummaryWithSelection(provider, model string, includeSelection bool) string { var providers []string - for _, gateway := range hawkconfig.GatewayStatuses(context.Background(), provider, model) { + for _, gateway := range graycodeconfig.GatewayStatuses(context.Background(), provider, model) { providers = append(providers, gateway.ID) } sort.Strings(providers) @@ -410,33 +410,33 @@ 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", hawkconfig.CredentialStoreName())) + b.WriteString(fmt.Sprintf("Credentials (%s):\n", graycodeconfig.CredentialStoreName())) for _, providerID := range providers { - b.WriteString(fmt.Sprintf(" %s: %s\n", providerID, hawkconfig.EnvKeyStatus(providerID))) + b.WriteString(fmt.Sprintf(" %s: %s\n", providerID, graycodeconfig.EnvKeyStatus(providerID))) } return strings.TrimRight(b.String(), "\n") } -func configCommandSummary(settings hawkconfig.Settings) string { +func configCommandSummary(settings graycodeconfig.Settings) string { _ = settings - providerName := displayConfigValue(hawkconfig.ActiveProvider(context.Background())) - modelName := displayConfigValue(hawkconfig.ActiveModel(context.Background())) + providerName := displayConfigValue(graycodeconfig.ActiveProvider(context.Background())) + modelName := displayConfigValue(graycodeconfig.ActiveModel(context.Background())) return fmt.Sprintf(`Setup (eyrie) /config → paste API key (OS keychain) + pick model /path → verify readiness in TUI - hawk path (CLI) + graycode path (CLI) Current: provider: %s model: %s keys: %s -Model catalog and routing live in eyrie — hawk is the UI only.`, providerName, modelName, configuredKeyList()) +Model catalog and routing live in eyrie — graycode is the UI only.`, providerName, modelName, configuredKeyList()) } func apiKeyConfigSummary() string { - return "API keys (" + hawkconfig.CredentialStoreName() + ")\n" + indentedAPIKeyLines() + return "API keys (" + graycodeconfig.CredentialStoreName() + ")\n" + indentedAPIKeyLines() } func configuredKeyList() string { @@ -462,11 +462,11 @@ func indentedAPIKeyLines() string { } func apiKeyStatusLines() []string { - providers := hawkconfig.AllSetupGateways() + providers := graycodeconfig.AllSetupGateways() sort.Strings(providers) var lines []string for _, provider := range providers { - lines = append(lines, fmt.Sprintf("%s: %s", provider, hawkconfig.EnvKeyStatus(provider))) + lines = append(lines, fmt.Sprintf("%s: %s", provider, graycodeconfig.EnvKeyStatus(provider))) } return lines } diff --git a/cmd/chat_yolo_confirm_test.go b/cmd/chat_yolo_confirm_test.go index b6987c80..ae4ec423 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) func TestYOLOConfirm_PendingConsumesNextInput(t *testing.T) { diff --git a/cmd/checkpoint.go b/cmd/checkpoint.go index daa8b549..2a506637 100644 --- a/cmd/checkpoint.go +++ b/cmd/checkpoint.go @@ -5,19 +5,19 @@ import ( "fmt" "time" - "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/graycode-cli/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 `hawk resume `. +// labeled point you can come back to with `graycode 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 "hawk resume ". +return to it later with "graycode resume ". Subcommands: save Snapshot the latest session in this directory under @@ -50,7 +50,7 @@ var checkpointSaveCmd = &cobra.Command{ } cmd.Printf("Saved checkpoint %q (session %s, %d messages, %s/%s)\n", cp.Name, cp.Session.ID, len(cp.Session.Messages), cp.Session.Provider, cp.Session.Model) - cmd.Printf("Resume with: hawk resume %s\n", name) + cmd.Printf("Resume with: graycode resume %s\n", name) return nil }, } @@ -114,12 +114,12 @@ var checkpointDeleteCmd = &cobra.Command{ }, } -// resumeCmd is a top-level convenience for `hawk resume `: it restores a +// resumeCmd is a top-level convenience for `graycode 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 "hawk checkpoint save" + Long: `resume restores a session previously saved with "graycode 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 { @@ -144,7 +144,7 @@ func restoreNamedCheckpoint(cmd *cobra.Command, name string) error { } cmd.Printf("Restored checkpoint %q into session %s (%d messages, %s/%s)\n", cp.Name, cp.Session.ID, len(cp.Session.Messages), cp.Session.Provider, cp.Session.Model) - cmd.Printf("Continue with: hawk --resume %s\n", cp.Session.ID) + cmd.Printf("Continue with: graycode --resume %s\n", cp.Session.ID) return nil } diff --git a/cmd/cli_contracts_test.go b/cmd/cli_contracts_test.go index 6fb96ef5..17512400 100644 --- a/cmd/cli_contracts_test.go +++ b/cmd/cli_contracts_test.go @@ -10,9 +10,9 @@ import ( "testing" "time" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/session" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR"), "sessions", saved.ID+".wal") + walPath := filepath.Join(os.Getenv("GRAYCODE_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, hawkconfig.Settings{}, "session-1") + toolsOut, handled, err := replBuiltinResponse("/tools", sess, graycodeconfig.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, hawkconfig.Settings{}, "session-1") + sessionOut, handled, err := replBuiltinResponse("/session", sess, graycodeconfig.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, hawkconfig.Settings{}, "session-1") + out, handled, err := replBuiltinResponse("/models", sess, graycodeconfig.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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) oldResumeID := resumeID oldContinueFlag := continueFlag diff --git a/cmd/clipboard.go b/cmd/clipboard.go index 3cf3f1d3..7634fc11 100644 --- a/cmd/clipboard.go +++ b/cmd/clipboard.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/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 -// hawk state directory when the system clipboard is unavailable. This +// graycode 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 cd762fdd..7c00dc5f 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 := "hawk clipboard test" + text := "graycode 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 458df537..b3cee47c 100644 --- a/cmd/cloud.go +++ b/cmd/cloud.go @@ -7,15 +7,15 @@ import ( "runtime" "time" - cloud "github.com/GrayCodeAI/hawk/internal/platform/cloud" + cloud "github.com/GrayCodeAI/graycode-cli/internal/platform/cloud" "github.com/spf13/cobra" ) -var cloudCmd = &cobra.Command{Use: "cloud", Short: "Manage optional Hawk Cloud synchronization"} +var cloudCmd = &cobra.Command{Use: "cloud", Short: "Manage optional Graycode Cloud synchronization"} var cloudConnectCmd = &cobra.Command{ Use: "connect", - Short: "Connect this Hawk device to Hawk Cloud", + Short: "Connect this Graycode device to Graycode 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("Hawk Cloud connected. Usage synchronization is opt-in and fail-open.") + cmd.Println("Graycode Cloud connected. Usage synchronization is opt-in and fail-open.") return nil }, } var cloudLoginCmd = &cobra.Command{ Use: "login", - Short: "Sign in to Hawk Cloud in a browser", + Short: "Sign in to Graycode 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("HAWK_CLOUD_URL") + endpoint = os.Getenv("GRAYCODE_CLOUD_URL") } if endpoint == "" { - return fmt.Errorf("hawk cloud endpoint is required (use --endpoint or HAWK_CLOUD_URL)") + return fmt.Errorf("graycode cloud endpoint is required (use --endpoint or GRAYCODE_CLOUD_URL)") } if label == "" { label, _ = os.Hostname() @@ -76,42 +76,42 @@ var cloudLoginCmd = &cobra.Command{ } case "approved": if poll.Token == "" || poll.DeviceID == "" || poll.ProjectID == "" { - return fmt.Errorf("hawk cloud returned an incomplete device authorization") + return fmt.Errorf("graycode cloud returned an incomplete device authorization") } if err := cloud.SaveDeviceConfig(cloud.DeviceConfig{Endpoint: endpoint, DeviceID: poll.DeviceID, ProjectID: poll.ProjectID}, poll.Token); err != nil { return err } - cmd.Printf("Hawk Cloud connected for project %s.\n", poll.ProjectID) + cmd.Printf("Graycode Cloud connected for project %s.\n", poll.ProjectID) return nil case "expired": - return fmt.Errorf("hawk cloud device authorization expired") + return fmt.Errorf("graycode cloud device authorization expired") default: - return fmt.Errorf("hawk cloud returned unknown device authorization status %q", poll.Status) + return fmt.Errorf("graycode cloud returned unknown device authorization status %q", poll.Status) } } }, } var cloudStatusCmd = &cobra.Command{ - Use: "status", Short: "Show Hawk Cloud connection status", + Use: "status", Short: "Show Graycode Cloud connection status", RunE: func(cmd *cobra.Command, _ []string) error { client, cfg, err := cloud.LoadClient() if err != nil || !client.Enabled() { - cmd.Println("Hawk Cloud is not connected.") + cmd.Println("Graycode Cloud is not connected.") return nil } - cmd.Printf("Hawk Cloud connected: %s (device %s, project %s)\n", cfg.Endpoint, cfg.DeviceID, cfg.ProjectID) + cmd.Printf("Graycode Cloud connected: %s (device %s, project %s)\n", cfg.Endpoint, cfg.DeviceID, cfg.ProjectID) return nil }, } var cloudContextCmd = &cobra.Command{ Use: "context", - Short: "Sync repository context to Hawk Cloud", + Short: "Sync repository context to Graycode Cloud", RunE: func(cmd *cobra.Command, _ []string) error { client, cfg, err := cloud.LoadClient() if err != nil || !client.Enabled() { - return fmt.Errorf("hawk cloud is not connected") + return fmt.Errorf("graycode cloud is not connected") } detected, detectErr := detectGitContext(cmd.Context()) repository, _ := cmd.Flags().GetString("repository") @@ -171,7 +171,7 @@ var cloudContextCmd = &cobra.Command{ event.Deployment = &cloud.DeploymentContext{Provider: contextProvider, ExternalID: deploymentID, Environment: deploymentEnvironment, Status: deploymentStatus} } client.RecordDeliveryContext(cmd.Context(), event) - cmd.Println("Repository context queued for Hawk Cloud.") + cmd.Println("Repository context queued for Graycode Cloud.") return nil }, } @@ -186,12 +186,12 @@ func firstValue(values ...string) string { } func init() { - 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") + 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") 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 c4f05c30..651d1d55 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/hawk.git", nil + return "git@github.com:GrayCodeAI/graycode-cli.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/hawk" || got.Provider != "github" || got.Branch != "main" || got.Commit != "abc123" { + if got.Repository != "GrayCodeAI/graycode-cli" || 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 92e734ac..12d514ee 100644 --- a/cmd/cloud_graph.go +++ b/cmd/cloud_graph.go @@ -5,8 +5,8 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/executiongraph" - cloud "github.com/GrayCodeAI/hawk/internal/platform/cloud" + "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" + cloud "github.com/GrayCodeAI/graycode-cli/internal/platform/cloud" "github.com/spf13/cobra" ) @@ -22,9 +22,9 @@ func newCloudGraphCmd() *cobra.Command { var missionDir string syncCmd := &cobra.Command{ Use: "sync [session-id]", - 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 + 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 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.`, @@ -50,11 +50,11 @@ execution never depends on cloud synchronization.`, } client, cfg, err := cloud.LoadClient() if err != nil || !client.Enabled() { - return fmt.Errorf("hawk cloud is not connected") + return fmt.Errorf("graycode cloud is not connected") } prepared, err := cloud.PrepareGraph(export) if err != nil { - return fmt.Errorf("prepare graph for Hawk Cloud: %w", err) + return fmt.Errorf("prepare graph for Graycode Cloud: %w", err) } result, err := client.SyncGraph(cmd.Context(), cloud.GraphSyncRequest{ SyncID: prepared.SyncID, diff --git a/cmd/cmdhistory_cmd.go b/cmd/cmdhistory_cmd.go index 1c247bdf..5bdc7a68 100644 --- a/cmd/cmdhistory_cmd.go +++ b/cmd/cmdhistory_cmd.go @@ -6,8 +6,8 @@ import ( "path/filepath" "strconv" - "github.com/GrayCodeAI/hawk/internal/cmdhistory" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/cmdhistory" + "github.com/GrayCodeAI/graycode-cli/internal/storage" "github.com/spf13/cobra" ) diff --git a/cmd/command_history.go b/cmd/command_history.go index 04a1a50a..f546c59b 100644 --- a/cmd/command_history.go +++ b/cmd/command_history.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/storage" ) // commandHistory persists recently-used slash commands so the command diff --git a/cmd/compact_ui.go b/cmd/compact_ui.go index a8b9268d..1b043f14 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/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) const compactProgressBarWidth = 40 diff --git a/cmd/compact_ui_test.go b/cmd/compact_ui_test.go index 8effc732..a965c51d 100644 --- a/cmd/compact_ui_test.go +++ b/cmd/compact_ui_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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(SpinnerHawk, "Compacting conversation"), + brailleSpinner: NewBrailleSpinner(SpinnerGraycode, "Compacting conversation"), manualCompacting: true, } out := m.renderCompactProgressPanel(80) diff --git a/cmd/compat-test/drift.go b/cmd/compat-test/drift.go index 8b63cd87..2456bdfb 100644 --- a/cmd/compat-test/drift.go +++ b/cmd/compat-test/drift.go @@ -9,23 +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 hawk's +// a consumer (merlin, kestrel, ...) can pin an older version than what graycode's // own go.mod requires, and Go's minimal version selection will silently pull -// in hawk's newer version at build time without the consumer's own CI ever +// in graycode'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/eagle", "github.com/GrayCodeAI/falcon", } -// 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 +// 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 // 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 { - hawkRequires, err := readRequires(filepath.Join(repoRoot, "go.mod")) + graycodeRequires, err := readRequires(filepath.Join(repoRoot, "go.mod")) if err != nil { - return fmt.Errorf("read hawk go.mod: %w", err) + return fmt.Errorf("read graycode go.mod: %w", err) } workspaceDir := filepath.Join(repoRoot, "..") @@ -46,18 +45,18 @@ func checkDrift(repoRoot string) error { continue // sibling not a Go module / no go.mod — skip silently } for _, pin := range trackedPins { - hawkVer, hawkHas := hawkRequires[pin] + graycodeVer, graycodeHas := graycodeRequires[pin] consumerVer, consumerHas := consumerRequires[pin] - if !hawkHas || !consumerHas || hawkVer == consumerVer { + if !graycodeHas || !consumerHas || graycodeVer == consumerVer { continue } drifted++ - fmt.Printf(" %-22s requires %s@%s, hawk requires %s@%s\n", - e.Name(), pin, consumerVer, pin, hawkVer) + fmt.Printf(" %-22s requires %s@%s, graycode requires %s@%s\n", + e.Name(), pin, consumerVer, pin, graycodeVer) } } if drifted == 0 { - fmt.Println(" OK — no drift between hawk's pins and sibling consumers") + fmt.Println(" OK — no drift between graycode's pins and sibling consumers") } return nil } diff --git a/cmd/compat-test/drift_test.go b/cmd/compat-test/drift_test.go index 14e75b85..27dac8dc 100644 --- a/cmd/compat-test/drift_test.go +++ b/cmd/compat-test/drift_test.go @@ -68,28 +68,28 @@ func TestReadRequires_InvalidMod(t *testing.T) { } } -// TestCheckDrift reports drift when a consumer pins an older version than hawk. +// TestCheckDrift reports drift when a consumer pins an older version than graycode. func TestCheckDrift_DetectsDrift(t *testing.T) { ws := t.TempDir() - writeMod(t, filepath.Join(ws, "hawk", "go.mod"), `module github.com/GrayCodeAI/hawk + writeMod(t, filepath.Join(ws, "graycode", "go.mod"), `module github.com/GrayCodeAI/graycode-cli go 1.26 -require github.com/GrayCodeAI/eagle v1.5.0 +require github.com/GrayCodeAI/falcon v1.5.0 `) // Consumer sibling pins an older version of the shared contract. writeMod(t, filepath.Join(ws, "merlin", "go.mod"), `module github.com/GrayCodeAI/merlin go 1.26 -require github.com/GrayCodeAI/eagle v1.2.0 +require github.com/GrayCodeAI/falcon v1.2.0 `) var buf bytes.Buffer old := os.Stdout r, w, _ := os.Pipe() os.Stdout = w - err := checkDrift(filepath.Join(ws, "hawk")) + err := checkDrift(filepath.Join(ws, "graycode")) _ = w.Close() os.Stdout = old _, _ = buf.ReadFrom(r) @@ -106,24 +106,24 @@ require github.com/GrayCodeAI/eagle 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, "hawk", "go.mod"), `module github.com/GrayCodeAI/hawk + writeMod(t, filepath.Join(ws, "graycode", "go.mod"), `module github.com/GrayCodeAI/graycode-cli go 1.26 -require github.com/GrayCodeAI/eagle v1.5.0 +require github.com/GrayCodeAI/falcon v1.5.0 `) writeMod(t, filepath.Join(ws, "kestrel", "go.mod"), `module github.com/GrayCodeAI/kestrel go 1.26 -require github.com/GrayCodeAI/eagle v1.5.0 +require github.com/GrayCodeAI/falcon v1.5.0 `) var buf bytes.Buffer r, w, _ := os.Pipe() old := os.Stdout os.Stdout = w - err := checkDrift(filepath.Join(ws, "hawk")) + err := checkDrift(filepath.Join(ws, "graycode")) _ = w.Close() os.Stdout = old _, _ = buf.ReadFrom(r) @@ -140,11 +140,11 @@ require github.com/GrayCodeAI/eagle 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, "hawk", "go.mod"), `module github.com/GrayCodeAI/hawk + writeMod(t, filepath.Join(ws, "graycode", "go.mod"), `module github.com/GrayCodeAI/graycode-cli go 1.26 -require github.com/GrayCodeAI/eagle v1.5.0 +require github.com/GrayCodeAI/falcon v1.5.0 `) // Directory present but no go.mod — must be skipped silently. if err := os.MkdirAll(filepath.Join(ws, "not-checked-out"), 0o755); err != nil { @@ -154,7 +154,7 @@ require github.com/GrayCodeAI/eagle v1.5.0 r, w, _ := os.Pipe() old := os.Stdout os.Stdout = w - err := checkDrift(filepath.Join(ws, "hawk")) + err := checkDrift(filepath.Join(ws, "graycode")) _ = 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 8f7ae7d8..b9d6d638 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 hawk's own +// go run ./cmd/compat-test -check-external # advisory: compare graycode'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 hawk/platform-capabilities.json, which is a different document. +// It must not pick graycode/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", - "hawk/testdata/compatibility-matrix.json", + "graycode/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 4e22ea37..6d09852c 100644 --- a/cmd/completions.go +++ b/cmd/completions.go @@ -9,8 +9,8 @@ import ( "sort" "strings" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/provider/routing" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/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 hawk. +// CompletionGenerator generates shell completion scripts for graycode. 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 hawk's +// NewCompletionGenerator creates a CompletionGenerator pre-populated with graycode'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 = hawkconfig.AllSetupGateways() + g.Providers = graycodeconfig.AllSetupGateways() } func (g *CompletionGenerator) populateModels() { @@ -91,12 +91,12 @@ func (g *CompletionGenerator) populateSlashCommands() { } } -// GenerateBash returns a complete bash completion script for hawk. +// GenerateBash returns a complete bash completion script for graycode. func (g *CompletionGenerator) GenerateBash() string { var b strings.Builder - b.WriteString("# bash completion for hawk\n") - b.WriteString("# Auto-generated by hawk completions generator\n\n") + b.WriteString("# bash completion for graycode\n") + b.WriteString("# Auto-generated by graycode 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("_hawk_completions() {\n") + b.WriteString("_graycode_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 hawk\n") + b.WriteString(" # Complete subcommands after graycode\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 _hawk_completions hawk\n") + b.WriteString("complete -F _graycode_completions graycode\n") return b.String() } -// GenerateZsh returns a complete zsh completion script for hawk. +// GenerateZsh returns a complete zsh completion script for graycode. func (g *CompletionGenerator) GenerateZsh() string { var b strings.Builder - b.WriteString("#compdef hawk\n") - b.WriteString("# zsh completion for hawk\n") - b.WriteString("# Auto-generated by hawk completions generator\n\n") + b.WriteString("#compdef graycode\n") + b.WriteString("# zsh completion for graycode\n") + b.WriteString("# Auto-generated by graycode completions generator\n\n") - b.WriteString("_hawk() {\n") + b.WriteString("_graycode() {\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 'hawk commands' commands\n") + b.WriteString(" _describe -t commands 'graycode 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("_hawk_slash_commands() {\n") + b.WriteString("_graycode_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("_hawk_providers() {\n") + b.WriteString("_graycode_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("_hawk \"$@\"\n") + b.WriteString("_graycode \"$@\"\n") return b.String() } -// GenerateFish returns a complete fish shell completion script for hawk. +// GenerateFish returns a complete fish shell completion script for graycode. func (g *CompletionGenerator) GenerateFish() string { var b strings.Builder - b.WriteString("# fish completion for hawk\n") - b.WriteString("# Auto-generated by hawk completions generator\n\n") + b.WriteString("# fish completion for graycode\n") + b.WriteString("# Auto-generated by graycode completions generator\n\n") // Disable file completions by default - b.WriteString("complete -c hawk -f\n\n") + b.WriteString("complete -c graycode -f\n\n") // Subcommands b.WriteString("# Subcommands\n") for _, cmd := range g.Commands { - b.WriteString(fmt.Sprintf("complete -c hawk -n '__fish_use_subcommand' -a '%s' -d '%s'\n", + b.WriteString(fmt.Sprintf("complete -c graycode -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 hawk -n '__fish_seen_subcommand_from %s' -a '%s' -d '%s'\n", + b.WriteString(fmt.Sprintf("complete -c graycode -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 hawk -n '__fish_seen_subcommand_from %s' -l '%s' -s '%s' -d '%s'", + b.WriteString(fmt.Sprintf("complete -c graycode -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 hawk -n '__fish_seen_subcommand_from %s' -l '%s' -d '%s'", + b.WriteString(fmt.Sprintf("complete -c graycode -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 hawk -l '%s' -s '%s' -d '%s'", + b.WriteString(fmt.Sprintf("complete -c graycode -l '%s' -s '%s' -d '%s'", f.Name, f.Short, escapeFish(f.Description))) } else { - b.WriteString(fmt.Sprintf("complete -c hawk -l '%s' -d '%s'", + b.WriteString(fmt.Sprintf("complete -c graycode -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 hawk -l 'provider' -r -a '%s' -d 'LLM provider'\n", + b.WriteString(fmt.Sprintf("complete -c graycode -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 hawk -a '%s' -d 'Slash command'\n", sc)) + b.WriteString(fmt.Sprintf("complete -c graycode -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: "hawk", + Name: "graycode", 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/hawk" + return "/etc/bash_completion.d/graycode" } localDir := filepath.Join(home, ".local", "share", "bash-completion", "completions") if info, err := os.Stat(localDir); err == nil && info.IsDir() { - return filepath.Join(localDir, "hawk") + return filepath.Join(localDir, "graycode") } // 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, "hawk") + return filepath.Join(brewDir, "graycode") } } } // Fallback: try system-wide sysDir := "/etc/bash_completion.d" if info, err := os.Stat(sysDir); err == nil && info.IsDir() { - return filepath.Join(sysDir, "hawk") + return filepath.Join(sysDir, "graycode") } // Final fallback: user-local - return filepath.Join(home, ".local", "share", "bash-completion", "completions", "hawk") + return filepath.Join(home, ".local", "share", "bash-completion", "completions", "graycode") } 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, "_hawk") + return filepath.Join(p, "_graycode") } } } // Use first entry even if it doesn't exist yet if parts[0] != "" { - return filepath.Join(parts[0], "_hawk") + return filepath.Join(parts[0], "_graycode") } } home, err := os.UserHomeDir() if err != nil { - return "/usr/local/share/zsh/site-functions/_hawk" + return "/usr/local/share/zsh/site-functions/_graycode" } - return filepath.Join(home, ".zsh", "completions", "_hawk") + return filepath.Join(home, ".zsh", "completions", "_graycode") } func fishInstallPath() string { home, err := os.UserHomeDir() if err != nil { - return filepath.Join(os.TempDir(), "hawk.fish") + return filepath.Join(os.TempDir(), "graycode.fish") } - return filepath.Join(home, ".config", "fish", "completions", "hawk.fish") + return filepath.Join(home, ".config", "fish", "completions", "graycode.fish") } // escapeZsh escapes single quotes for zsh completion descriptions. diff --git a/cmd/completions_test.go b/cmd/completions_test.go index d47625bb..2dfb805f 100644 --- a/cmd/completions_test.go +++ b/cmd/completions_test.go @@ -35,10 +35,10 @@ func TestGenerateBashContainsFunctionDefinition(t *testing.T) { g := NewCompletionGenerator() bash := g.GenerateBash() - if !strings.Contains(bash, "_hawk_completions()") { - t.Error("Bash completion should contain _hawk_completions() function definition") + if !strings.Contains(bash, "_graycode_completions()") { + t.Error("Bash completion should contain _graycode_completions() function definition") } - if !strings.Contains(bash, "complete -F _hawk_completions hawk") { + if !strings.Contains(bash, "complete -F _graycode_completions graycode") { 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 hawk") { - t.Error("Zsh completion should start with #compdef hawk header") + if !strings.HasPrefix(zsh, "#compdef graycode") { + t.Error("Zsh completion should start with #compdef graycode header") } } @@ -121,8 +121,8 @@ func TestGenerateZshContainsFunction(t *testing.T) { g := NewCompletionGenerator() zsh := g.GenerateZsh() - if !strings.Contains(zsh, "_hawk()") { - t.Error("Zsh completion should contain _hawk() function") + if !strings.Contains(zsh, "_graycode()") { + t.Error("Zsh completion should contain _graycode() 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 hawk") { - t.Error("Fish completion should contain 'complete -c hawk' directives") + if !strings.Contains(fish, "complete -c graycode") { + t.Error("Fish completion should contain 'complete -c graycode' directives") } } @@ -334,8 +334,8 @@ func TestInstallCompletionBash(t *testing.T) { if path == "" { t.Error("InstallCompletion(bash) returned empty path") } - if !strings.Contains(path, "hawk") { - t.Errorf("Bash install path should contain 'hawk', got %q", path) + if !strings.Contains(path, "graycode") { + t.Errorf("Bash install path should contain 'graycode', 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, "_hawk") { - t.Errorf("Zsh install path should contain '_hawk', got %q", path) + if !strings.Contains(path, "_graycode") { + t.Errorf("Zsh install path should contain '_graycode', 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, "hawk.fish") { - t.Errorf("Fish install path should contain 'hawk.fish', got %q", path) + if !strings.Contains(path, "graycode.fish") { + t.Errorf("Fish install path should contain 'graycode.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, "_hawk_providers()") { - t.Error("Zsh completion should contain _hawk_providers() function") + if !strings.Contains(zsh, "_graycode_providers()") { + t.Error("Zsh completion should contain _graycode_providers() function") } } diff --git a/cmd/config_table.go b/cmd/config_table.go index 3bfa9157..0a249dfc 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" "github.com/mattn/go-runewidth" ) diff --git a/cmd/container_boot.go b/cmd/container_boot.go index ec491895..4b6b19ed 100644 --- a/cmd/container_boot.go +++ b/cmd/container_boot.go @@ -8,8 +8,8 @@ import ( tea "charm.land/bubbletea/v2" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/sandbox" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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: Hawk agent command +// shouldUseContainer is intentionally unconditional: Graycode agent command // execution is Docker-only and never falls back to the host. func shouldUseContainer() bool { return true } -// startRequiredContainer starts Hawk's mandatory Docker sandbox. It fails +// startRequiredContainer starts Graycode'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 -// HAWK_DISABLE_EGRESS_PROXY=1 to opt out (unrestricted bridge egress). +// GRAYCODE_DISABLE_EGRESS_PROXY=1 to opt out (unrestricted bridge egress). func startRequiredContainer(projectDir string) (*sandbox.ContainerSandbox, error) { var cs *sandbox.ContainerSandbox - if os.Getenv("HAWK_DISABLE_EGRESS_PROXY") == "1" { + if os.Getenv("GRAYCODE_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 2ccdc642..ff095efe 100644 --- a/cmd/container_boot_test.go +++ b/cmd/container_boot_test.go @@ -4,13 +4,13 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) func TestShouldUseContainerAlwaysTrue(t *testing.T) { - t.Setenv("HAWK_NO_CONTAINER", "1") + t.Setenv("GRAYCODE_NO_CONTAINER", "1") if !shouldUseContainer() { - t.Fatal("Hawk must require Docker even when the legacy opt-out variable is set") + t.Fatal("Graycode 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 81ab48fe..c53f29a1 100644 --- a/cmd/context_export.go +++ b/cmd/context_export.go @@ -9,9 +9,9 @@ import ( "path/filepath" "strings" - "github.com/GrayCodeAI/hawk/internal/fsutil" - "github.com/GrayCodeAI/hawk/internal/intelligence/repomap" - "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/graycode-cli/internal/fsutil" + "github.com/GrayCodeAI/graycode-cli/internal/intelligence/repomap" + "github.com/GrayCodeAI/graycode-cli/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", ".hawk.md"} { + for _, instrFile := range []string{"AGENTS.md", "AGENTS.md", "CLAUDE.md", ".graycode.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 b4612001..d3e09177 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 hawk action (commit, file edit, etc.) using safe snapshot-based rollback.", + Detail: "Reverts the last graycode 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 hawk settings, model selection, and preferences.", + Detail: "Opens the interactive configuration panel for graycode 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 hawk sessions", - Detail: "Start, stop, resume, or list hawk working sessions with full context preservation.", + Summary: "Manage graycode sessions", + Detail: "Start, stop, resume, or list graycode 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 hawk", + Summary: "Fixing failing tests with graycode", 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 hawk", + Summary: "Making commits with graycode", 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 hawk AI assistance", + Summary: "Debugging with graycode 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 hawk can apply changes. Use /snapshot before large refactors for safety.", + Detail: "Use /chat to discuss refactoring strategies, then graycode 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 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"}, + 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"}, 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", "hawk credentials status — verify stored keys"}, + Examples: []string{"/config — paste API key in the config panel", "graycode 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. Hawk will automatically retry with exponential backoff. Consider upgrading your plan for higher limits.", + Detail: "You have exceeded the API rate limit. Graycode 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. Hawk can assist with conflict resolution using AI to understand both sides.", + Detail: "A merge conflict was encountered. Graycode 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: "Hawk requires a git repository. Initialize one with git init or navigate to an existing repo.", + Detail: "Graycode 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", "hawk credentials status — list configured providers"}, + Examples: []string{"/config — paste API key in the config panel", "/config key remove — remove a stored key", "graycode 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 hawk uses when opening files for editing.", + Detail: "Configure which editor graycode 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 hawk terminal output. Supports light and dark terminal backgrounds.", + Detail: "Choose the color theme for graycode 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 hawk uses when running commands.", + Detail: "Configure which shell graycode 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 Hawk never falls back to the host.", + 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.", 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{"hawk daemon start — start the daemon", "hawk daemon status — check daemon status", "hawk daemon stop — stop the daemon"}, + Examples: []string{"graycode daemon start — start the daemon", "graycode daemon status — check daemon status", "graycode 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 c379a1c9..ca00ee6a 100644 --- a/cmd/control_plane_hints.go +++ b/cmd/control_plane_hints.go @@ -3,7 +3,7 @@ package cmd import ( "fmt" - "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) // controlPlaneOnboardingHint is a short first-session tip (not a wall of text). diff --git a/cmd/cost.go b/cmd/cost.go index 985a6214..0b479fe6 100644 --- a/cmd/cost.go +++ b/cmd/cost.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - analytics "github.com/GrayCodeAI/hawk/internal/observability" + analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" "github.com/spf13/cobra" ) @@ -64,7 +64,7 @@ var costAnalyzeCmd = &cobra.Command{ cmd.Println(" - Model routing recommendations") cmd.Println(" - Prompt caching suggestions") cmd.Println() - cmd.Println("To track progress: https://github.com/GrayCodeAI/hawk/issues") + cmd.Println("To track progress: https://github.com/GrayCodeAI/graycode-cli/issues") return nil } diff --git a/cmd/cost_test.go b/cmd/cost_test.go index bad0476d..2b73fa57 100644 --- a/cmd/cost_test.go +++ b/cmd/cost_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - analytics "github.com/GrayCodeAI/hawk/internal/observability" + analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" ) func TestCostAnalyze_JSON_Empty(t *testing.T) { diff --git a/cmd/credential_gate.go b/cmd/credential_gate.go index 0554975b..1f07a34c 100644 --- a/cmd/credential_gate.go +++ b/cmd/credential_gate.go @@ -3,7 +3,7 @@ package cmd import ( "sync/atomic" - "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/graycode-cli/internal/tool" ) // credentialGate holds the current host-side credential gate callback. It is diff --git a/cmd/credentials.go b/cmd/credentials.go index 9d06b8ac..5ea3f048 100644 --- a/cmd/credentials.go +++ b/cmd/credentials.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/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(hawkconfig.FormatCredentialCLIStatus(ctx)) + cmd.Println(graycodeconfig.FormatCredentialCLIStatus(ctx)) return nil }, } @@ -30,11 +30,11 @@ var credentialsRemoveCmd = &cobra.Command{ Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { ctx := context.Background() - removed, err := hawkconfig.RemoveStoredCredential(ctx, args[0]) + removed, err := graycodeconfig.RemoveStoredCredential(ctx, args[0]) if err != nil { return err } - cmd.Printf("Removed %d key(s) from %s: %s\n", len(removed), hawkconfig.CredentialStoreName(), strings.Join(removed, ", ")) + cmd.Printf("Removed %d key(s) from %s: %s\n", len(removed), graycodeconfig.CredentialStoreName(), strings.Join(removed, ", ")) return nil }, } @@ -44,18 +44,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 := hawkconfig.CredentialStorageStatus(ctx) + storage := graycodeconfig.CredentialStorageStatus(ctx) if !storage.Writable { return fmt.Errorf("cannot migrate: %s", storage.Detail) } - n, err := hawkconfig.MigrateEnvFileCredentials(ctx) + n, err := graycodeconfig.MigrateEnvFileCredentials(ctx) if err != nil { return err } if n == 0 { cmd.Println("No plaintext credential files found (already using secure storage).") } else { - cmd.Printf("Migrated %d key(s) to %s and removed plaintext credential files.\n", n, hawkconfig.CredentialStoreName()) + cmd.Printf("Migrated %d key(s) to %s and removed plaintext credential files.\n", n, graycodeconfig.CredentialStoreName()) } return nil }, diff --git a/cmd/daemon.go b/cmd/daemon.go index 19b98455..cfe65d9f 100644 --- a/cmd/daemon.go +++ b/cmd/daemon.go @@ -14,18 +14,18 @@ import ( "syscall" "time" - 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" + 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" "github.com/spf13/cobra" ) @@ -43,8 +43,8 @@ var ( var daemonCmd = &cobra.Command{ Use: "daemon", - Short: "Manage the hawk background server", - Long: "Run hawk as a background HTTP server for programmatic/CI access.", + Short: "Manage the graycode background server", + Long: "Run graycode 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 HAWK_DAEMON_API_KEY or a generated key)") + 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(&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 := hawkconfig.LoadSettings() + settings := graycodeconfig.LoadSettings() - // Initialize OpenTelemetry telemetry (opt-in via HAWK_CODE_ENABLE_TELEMETRY=1). + // Initialize OpenTelemetry telemetry (opt-in via GRAYCODE_ENABLE_TELEMETRY=1). telemetryProviders, telemetryErr := oteltrace.InitTelemetry(oteltrace.DefaultTelemetryConfig()) if telemetryErr != nil { fmt.Fprintln(os.Stderr, "warning: telemetry initialization failed:", telemetryErr) @@ -111,7 +111,7 @@ func runDaemonStart(_ *cobra.Command, _ []string) error { } // Set up file-backed logging for the daemon. Logs go to - // ~/.hawk/state/daemon.log with slog structured output. + // ~/.graycode/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 := newConfiguredHawkSessionFactory(settings, daemonLogger) + newSession := newConfiguredGraycodeSessionFactory(settings, daemonLogger) // Log startup banner. - daemonLogger.Info("hawk daemon starting", map[string]interface{}{ + daemonLogger.Info("graycode 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("HAWK_DAEMON_API_KEY") + apiKey = os.Getenv("GRAYCODE_DAEMON_API_KEY") } if apiKey == "" { var err error @@ -210,7 +210,7 @@ func runDaemonStart(_ *cobra.Command, _ []string) error { }) // Wire Eyrie's authoritative local preflight into GET /v1/ready. A session - // factory only proves Hawk can attempt construction; readiness additionally + // factory only proves Graycode can attempt construction; readiness additionally // requires Eyrie's provider state, catalog, credentials, and model selection. srv.SetReadyFn(daemonReadyProbe(factory)) addr, err := srv.Start() @@ -227,19 +227,19 @@ func runDaemonStart(_ *cobra.Command, _ []string) error { }) defer preheater.Stop() - fmt.Printf("hawk daemon running on http://%s\n", addr) + fmt.Printf("graycode 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 HAWK_DAEMON_API_KEY)") + fmt.Println("API key: (set via --api-key or GRAYCODE_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 HAWK_CODE_ENABLE_TELEMETRY=1 to enable)") + fmt.Println("Telemetry: disabled (set GRAYCODE_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 -// ~/.hawk/state/daemon.log and returns it. The directory is created if needed. +// ~/.graycode/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 @@ -337,10 +337,10 @@ func slogLevelFromString(s string) slog.Level { // 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, hawkconfig.EnginePreflightReport) + return daemonReadyProbeWithPreflight(factory, graycodeconfig.EnginePreflightReport) } -func daemonReadyProbeWithPreflight(factory daemon.SessionFactory, preflight func(context.Context) hawkconfig.EnginePreflight) func() (bool, string) { +func daemonReadyProbeWithPreflight(factory daemon.SessionFactory, preflight func(context.Context) graycodeconfig.EnginePreflight) func() (bool, string) { return func() (bool, string) { if factory == nil { return false, "engine not configured" @@ -397,14 +397,14 @@ func generateDaemonAPIKey() (string, error) { return base64.RawURLEncoding.EncodeToString(b[:]), nil } -// daemonAutonomyFromFlag resolves the --autonomy flag / HAWK_DAEMON_AUTONOMY +// daemonAutonomyFromFlag resolves the --autonomy flag / GRAYCODE_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("HAWK_DAEMON_AUTONOMY") + s = os.Getenv("GRAYCODE_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 d67bc3ee..b3ac3cd9 100644 --- a/cmd/daemon_ready_test.go +++ b/cmd/daemon_ready_test.go @@ -12,10 +12,10 @@ import ( "testing" "time" - 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" + 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" ) // TestDaemonReadyProbe_NilFactory verifies the probe reports not-ready when no @@ -34,8 +34,8 @@ func TestDaemonReadyProbe_NilFactory(t *testing.T) { // 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) hawkconfig.EnginePreflight { - return hawkconfig.EnginePreflight{Ready: false} + probe := daemonReadyProbeWithPreflight(factory, func(context.Context) graycodeconfig.EnginePreflight { + return graycodeconfig.EnginePreflight{Ready: false} }) ok, reason := probe() if ok { @@ -48,8 +48,8 @@ func TestDaemonReadyProbe_FailedEyriePreflight(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) hawkconfig.EnginePreflight { - return hawkconfig.EnginePreflight{Ready: true} + probe := daemonReadyProbeWithPreflight(factory, func(context.Context) graycodeconfig.EnginePreflight { + return graycodeconfig.EnginePreflight{Ready: true} }) ok, reason := probe() if !ok || reason != "" { @@ -62,8 +62,8 @@ func TestDaemonReadyProbe_ReadyEyriePreflight(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) hawkconfig.EnginePreflight { - return hawkconfig.EnginePreflight{Ready: false} + srv.SetReadyFn(daemonReadyProbeWithPreflight(factory, func(context.Context) graycodeconfig.EnginePreflight { + return graycodeconfig.EnginePreflight{Ready: false} })) addr, err := srv.Start() diff --git a/cmd/diagnostics.go b/cmd/diagnostics.go index cd6c0569..746abb01 100644 --- a/cmd/diagnostics.go +++ b/cmd/diagnostics.go @@ -11,16 +11,16 @@ import ( "strings" "time" - 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/tool" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + 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/tool" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) -func doctorReport(settings hawkconfig.Settings) string { +func doctorReport(settings graycodeconfig.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 @@ -36,7 +36,7 @@ func doctorReport(settings hawkconfig.Settings) string { cwd, _ := os.Getwd() var b strings.Builder - b.WriteString("Hawk doctor\n") + b.WriteString("Graycode 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)) @@ -68,12 +68,12 @@ func doctorReport(settings hawkconfig.Settings) string { b.WriteString(fmt.Sprintf(" %s (%s): not checked out\n", component.product, component.directory)) } } - 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.CredentialStorageStatus(context.Background()).Formatted + "\n") - if deployReport, err := hawkconfig.DeploymentStatusReportWithSettings(context.Background(), settings, modelName); err == nil { + 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.CredentialStorageStatus(context.Background()).Formatted + "\n") + if deployReport, err := graycodeconfig.DeploymentStatusReportWithSettings(context.Background(), settings, modelName); err == nil { b.WriteString("\n" + deployReport + "\n") } b.WriteString("\n" + envSummaryWithSelection(providerName, modelName, false) + "\n") @@ -85,18 +85,18 @@ func doctorReport(settings hawkconfig.Settings) string { } // Project instructions - if md := hawkconfig.LoadAgentsMD(); md != "" { + if md := graycodeconfig.LoadAgentsMD(); md != "" { b.WriteString("\nProject instructions: found\n") } else { b.WriteString("\nProject instructions: not found (consider creating AGENTS.md)\n") } - // Installed skills (hawk ships none; skills come from user/marketplace installs) + // Installed skills (graycode 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 `hawk skills install`)\n") + b.WriteString("Installed skills: none (install with `graycode skills install`)\n") } b.WriteString(fmt.Sprintf("Configured MCP servers: %d\n", len(settings.MCPServers)+len(mcpServers))) @@ -105,7 +105,7 @@ func doctorReport(settings hawkconfig.Settings) string { // Session recovery status recoveryCandidates := session.ScanForRecovery() if len(recoveryCandidates) > 0 { - b.WriteString(fmt.Sprintf("\nInterrupted sessions: %d (run hawk recover)\n", len(recoveryCandidates))) + b.WriteString(fmt.Sprintf("\nInterrupted sessions: %d (run graycode recover)\n", len(recoveryCandidates))) } else { b.WriteString("\nInterrupted sessions: none\n") } @@ -114,14 +114,14 @@ func doctorReport(settings hawkconfig.Settings) string { return strings.TrimRight(b.String(), "\n") } -func healthCheckReport(settings hawkconfig.Settings, provider string) string { +func healthCheckReport(settings graycodeconfig.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 := hawkconfig.ValidateSettings(settings) + result := graycodeconfig.ValidateSettings(settings) if result.Valid { return health.Check{Name: "config", Status: health.Healthy, Message: "Configuration valid"} } @@ -196,7 +196,7 @@ func providerCredentialHealthChecker(provider string) health.Checker { status := health.Unhealthy message := label + " credential not configured" - if providerID != "" && hawkconfig.HasStoredCredentialForProvider(ctx, providerID) { + if providerID != "" && graycodeconfig.HasStoredCredentialForProvider(ctx, providerID) { status = health.Healthy message = label + " credential configured" } @@ -214,19 +214,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(hawkconfig.ActiveGateway(ctx)) + provider = strings.TrimSpace(graycodeconfig.ActiveGateway(ctx)) if provider == "" || strings.EqualFold(provider, "auto") { - provider = strings.TrimSpace(hawkconfig.EffectiveSelection(ctx, hawkconfig.SelectionOptions{}).Provider) + provider = strings.TrimSpace(graycodeconfig.EffectiveSelection(ctx, graycodeconfig.SelectionOptions{}).Provider) } } - return hawkconfig.ActiveProviderID(provider) + return graycodeconfig.ActiveProviderID(provider) } -func settingsSummary(settings hawkconfig.Settings) string { +func settingsSummary(settings graycodeconfig.Settings) string { return configCommandSummary(settings) } -func mcpConfigSummary(settings hawkconfig.Settings) string { +func mcpConfigSummary(settings graycodeconfig.Settings) string { if len(settings.MCPServers) == 0 && len(mcpServers) == 0 { return "No MCP servers configured." } diff --git a/cmd/diagnostics_test.go b/cmd/diagnostics_test.go index b4dfdb73..48d2ff40 100644 --- a/cmd/diagnostics_test.go +++ b/cmd/diagnostics_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" - "github.com/GrayCodeAI/hawk/internal/resilience/health" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/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 := hawkconfig.Settings{} + settings := graycodeconfig.Settings{} report := doctorReport(settings) if report == "" { t.Error("doctorReport should produce non-empty output") @@ -40,7 +40,7 @@ func TestDoctorReport(t *testing.T) { func TestDoctorReportProviderModelOrder(t *testing.T) { t.Parallel() - settings := hawkconfig.Settings{ + settings := graycodeconfig.Settings{ Model: "claude-sonnet-4-20250514", Provider: "anthropic", } @@ -56,14 +56,14 @@ func TestDoctorReportProviderModelOrder(t *testing.T) { func TestDoctorReportUsesResolvedProviderForChecks(t *testing.T) { isolateCredentialHome(t) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() gateway.SetDefaultStore(&gateway.MapStore{}) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) - report := doctorReport(hawkconfig.Settings{ + report := doctorReport(graycodeconfig.Settings{ Model: "claude-sonnet-4-20250514", Provider: "anthropic", }) @@ -75,22 +75,22 @@ func TestDoctorReportUsesResolvedProviderForChecks(t *testing.T) { func TestProviderCredentialHealthCheckerResolvesAuto(t *testing.T) { isolateCredentialHome(t) t.Setenv("EYRIE_CONFIG_DIR", t.TempDir()) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &contextRecordingCredentialStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.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 := hawkconfig.SetActiveProvider(ctx, "openrouter"); err != nil { + if err := graycodeconfig.SetActiveProvider(ctx, "openrouter"); err != nil { t.Fatal(err) } - if err := hawkconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { + if err := graycodeconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { t.Fatal(err) } store.contextValue = nil @@ -118,11 +118,11 @@ func TestProviderCredentialHealthCheckerResolvesAuto(t *testing.T) { func TestProviderCredentialHealthCheckerMissingIsUnhealthy(t *testing.T) { isolateCredentialHome(t) t.Setenv("EYRIE_CONFIG_DIR", t.TempDir()) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() gateway.SetDefaultStore(&gateway.MapStore{}) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) result := providerCredentialHealthChecker("openai")(context.Background()) @@ -142,7 +142,7 @@ func TestProviderCredentialHealthCheckerMissingIsUnhealthy(t *testing.T) { func TestSettingsSummary(t *testing.T) { t.Parallel() - settings := hawkconfig.Settings{ + settings := graycodeconfig.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 := hawkconfig.Settings{} + settings := graycodeconfig.Settings{} summary := mcpConfigSummary(settings) if summary == "" { t.Error("mcpConfigSummary should produce output") diff --git a/cmd/dx.go b/cmd/dx.go index b46950e7..02300ac2 100644 --- a/cmd/dx.go +++ b/cmd/dx.go @@ -10,19 +10,19 @@ import ( "strings" "time" - 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" + 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" ) // 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 hawkconfig.Settings) string { +func doctorOutput(settings graycodeconfig.Settings) string { var b strings.Builder - b.WriteString("=== Hawk Doctor ===\n\n") + b.WriteString("=== Graycode Doctor ===\n\n") // Go version, OS, arch b.WriteString("System:\n") @@ -49,12 +49,12 @@ func doctorOutput(settings hawkconfig.Settings) string { b.WriteString(fmt.Sprintf(" TERM: %s\n", termVal)) b.WriteString(fmt.Sprintf(" COLORTERM: %s\n", colorTerm)) - // Hawk version + // Graycode version v := version if v == "" { v = "(dev)" } - b.WriteString("\nHawk:\n") + b.WriteString("\nGraycode:\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 hawkconfig.Settings) string { } b.WriteString("\nProvider:\n") b.WriteString(fmt.Sprintf(" Provider: %s\n", effectiveProvider)) - b.WriteString(fmt.Sprintf(" API key: %s\n", maskedKeyStatus(hawkconfig.ActiveProvider(context.Background())))) + b.WriteString(fmt.Sprintf(" API key: %s\n", maskedKeyStatus(graycodeconfig.ActiveProvider(context.Background())))) // Model configured (eyrie provider.json) - effectiveModel := strings.TrimSpace(hawkconfig.ActiveModel(context.Background())) + effectiveModel := strings.TrimSpace(graycodeconfig.ActiveModel(context.Background())) if effectiveModel == "" { effectiveModel = "(not configured)" } @@ -111,7 +111,7 @@ func doctorOutput(settings hawkconfig.Settings) string { } // AGENTS.md found - agentsMD := hawkconfig.LoadAgentsMD() + agentsMD := graycodeconfig.LoadAgentsMD() if agentsMD != "" { b.WriteString("AGENTS.md: found\n") } else { @@ -150,7 +150,7 @@ func maskedKeyStatus(provider string) string { if provider == "" { return "(no provider set)" } - status := hawkconfig.EnvKeyStatus(provider) + status := graycodeconfig.EnvKeyStatus(provider) if status == "set" { return "configured (masked)" } @@ -271,7 +271,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("# Hawk Session: %s\n\n", sessionID)) + b.WriteString(fmt.Sprintf("# Graycode Session: %s\n\n", sessionID)) b.WriteString(fmt.Sprintf("Exported: %s\n\n", time.Now().Format(time.RFC3339))) b.WriteString("---\n\n") @@ -300,7 +300,7 @@ func exportMarkdown(messages []displayMsg, sessionID string) (string, error) { } } - filename := fmt.Sprintf("hawk-session-%s.md", sessionID) + filename := fmt.Sprintf("graycode-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) } @@ -347,7 +347,7 @@ func exportJSON(messages []displayMsg, sessionID string) (string, error) { return "", fmt.Errorf("failed to marshal session: %w", err) } - filename := fmt.Sprintf("hawk-session-%s.json", sessionID) + filename := fmt.Sprintf("graycode-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 1db3dfe0..28ef4f14 100644 --- a/cmd/dx_test.go +++ b/cmd/dx_test.go @@ -7,15 +7,15 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/tool" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/tool" ) func TestDoctorOutputContainsSections(t *testing.T) { preserveCLICompilerVersionState(t) version = "test-dx-version" - settings := hawkconfig.Settings{ + settings := graycodeconfig.Settings{ Provider: "openai", Model: "gpt-4o", } @@ -23,7 +23,7 @@ func TestDoctorOutputContainsSections(t *testing.T) { out := doctorOutput(settings) sections := []string{ - "Hawk Doctor", + "Graycode 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 := hawkconfig.Settings{ + settings := graycodeconfig.Settings{ Provider: "anthropic", Model: "claude-sonnet-4-20250514", - MCPServers: []hawkconfig.MCPServerConfig{ + MCPServers: []graycodeconfig.MCPServerConfig{ {Name: "test-mcp", Command: "test-cmd"}, }, } @@ -134,7 +134,7 @@ func TestExportMarkdownCreatesFile(t *testing.T) { defer os.Chdir(origDir) messages := []displayMsg{ - {role: "user", content: "Hello hawk"}, + {role: "user", content: "Hello graycode"}, {role: "assistant", content: "Hello! How can I help?"}, {role: "system", content: "System message here"}, {role: "welcome", content: "Should be skipped"}, @@ -155,10 +155,10 @@ func TestExportMarkdownCreatesFile(t *testing.T) { } content := string(data) - if !strings.Contains(content, "# Hawk Session: test-export-id") { + if !strings.Contains(content, "# Graycode Session: test-export-id") { t.Errorf("export missing session header") } - if !strings.Contains(content, "Hello hawk") { + if !strings.Contains(content, "Hello graycode") { 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 6049c5a9..d3977373 100644 --- a/cmd/ecosystem.go +++ b/cmd/ecosystem.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" "github.com/spf13/cobra" ) @@ -13,7 +13,7 @@ var ecosystemJSON bool var ecosystemCmd = &cobra.Command{ Use: "ecosystem", 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.", + Long: "Print the ecosystem panel summarizing LLM provider (eyrie), memory graph (harrier), and token pipeline (shrike). Same block as the top of graycode 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 := hawkconfig.BuildEcosystemReport(context.Background(), providerName, modelName) + report := graycodeconfig.BuildEcosystemReport(context.Background(), providerName, modelName) enc := json.NewEncoder(cmd.OutOrStdout()) enc.SetIndent("", " ") return enc.Encode(report) } - cmd.Println(hawkconfig.FormatEcosystemPanel(context.Background(), providerName, modelName)) + cmd.Println(graycodeconfig.FormatEcosystemPanel(context.Background(), providerName, modelName)) return nil }, } diff --git a/cmd/ecosystem_test.go b/cmd/ecosystem_test.go index 82b9f127..6603d664 100644 --- a/cmd/ecosystem_test.go +++ b/cmd/ecosystem_test.go @@ -4,16 +4,16 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" ) func TestEcosystemCmdRuns(t *testing.T) { - settings := hawkconfig.Settings{} + settings := graycodeconfig.Settings{} model, provider := effectiveModelAndProvider(settings) if provider == "" { provider = "auto" } - out := hawkconfig.FormatEcosystemPanel(t.Context(), provider, model) + out := graycodeconfig.FormatEcosystemPanel(t.Context(), provider, model) if !strings.Contains(out, "Ecosystem (eyrie · harrier · shrike)") { t.Fatalf("unexpected panel: %q", out) } diff --git a/cmd/error_classify.go b/cmd/error_classify.go index e5216247..3b59cb4f 100644 --- a/cmd/error_classify.go +++ b/cmd/error_classify.go @@ -4,41 +4,41 @@ import ( "fmt" "strings" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/errhint" - "github.com/GrayCodeAI/hawk/internal/hawkerr" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/errhint" + "github.com/GrayCodeAI/graycode-cli/internal/graycodeerr" ) // friendlyErrorMessage returns the user-friendly message for an error. -// Delegates to the shared hawkerr.ClassifyErrorMessage for the base message, +// Delegates to the shared graycodeerr.ClassifyErrorMessage for the base message, // then enriches specific cases with dynamic hints that require the config -// package (hawkerr can't import internal/config without a cycle). +// package (graycodeerr can't import internal/config without a cycle). func friendlyErrorMessage(err error) string { - msg := hawkerr.ClassifyErrorMessage(err) + msg := graycodeerr.ClassifyErrorMessage(err) if err == nil { return msg } - ec := hawkerr.ClassifyError(err) + ec := graycodeerr.ClassifyError(err) low := strings.ToLower(err.Error()) switch ec.ExitCode { - case hawkerr.ExitNotFound: + case graycodeerr.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 := hawkconfig.ExampleModelHints() + ex1, ex2 := graycodeconfig.ExampleModelHints() msg = fmt.Sprintf( "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 hawkerr.ExitAuth: + case graycodeerr.ExitAuth: msg += "\n Check your API key with /config. Keys can expire or be revoked." - case hawkerr.ExitNetwork: + case graycodeerr.ExitNetwork: msg += "\n Check your internet connection. If you're behind a proxy, configure it with /config." - case hawkerr.ExitTimeout: + case graycodeerr.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 82eca742..0a94997d 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 (hawk's own + // A local error must not draw an errhint provider hint (graycode'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 80ad5a0f..baabee7f 100644 --- a/cmd/errors.go +++ b/cmd/errors.go @@ -11,8 +11,8 @@ import ( "sync" "time" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/storage" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/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 -// Hawk's user state crash log, and exits with a user-friendly message. +// Graycode'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: hawk-YYMMDD-<6 hex chars from stack hash> + // Format: graycode-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, "\nhawk encountered an unexpected error and needs to exit.\n") + _, _ = fmt.Fprintf(os.Stderr, "\ngraycode 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/hawk/issues\n") + _, _ = fmt.Fprintf(os.Stderr, "Please report this at: https://github.com/GrayCodeAI/graycode-cli/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: hawk-YYMMDD-<6 hex chars> — enough to correlate with crash.log +// Format: graycode-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("hawk-%s-%06x", time.Now().Format("060102"), hash&0xFFFFFF) + return fmt.Sprintf("graycode-%s-%06x", time.Now().Format("060102"), hash&0xFFFFFF) } // ─── errorLogger ────────────────────────────────────────────────────────────── -// Writes errors to Hawk's user state error log with timestamps. Thread-safe. +// Writes errors to Graycode'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 Hawk error log. +// LogError writes a timestamped error entry to the Graycode 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 Hawk error log. +// LogErrorf writes a formatted, timestamped error entry to the Graycode 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 hawkconfig.Settings) []StartupWarning { +func validateStartup(settings graycodeconfig.Settings) []StartupWarning { var warnings []StartupWarning // 1. Check API key for configured provider. - // Hawk reads credentials from the OS secret store (macOS Keychain / + // Graycode 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(hawkconfig.ActiveProvider(context.Background())) + providerName = strings.TrimSpace(graycodeconfig.ActiveProvider(context.Background())) } if providerName != "" && providerName != "ollama" { hasEnv := false - if envKey := hawkconfig.ProviderAPIKeyEnv(providerName); envKey != "" { + if envKey := graycodeconfig.ProviderAPIKeyEnv(providerName); envKey != "" { hasEnv = os.Getenv(envKey) != "" } - hasStored := hawkconfig.HasStoredCredentialForProvider(context.Background(), providerName) + hasStored := graycodeconfig.HasStoredCredentialForProvider(context.Background(), providerName) if !hasEnv && !hasStored { - envKey := hawkconfig.ProviderAPIKeyEnv(providerName) + envKey := graycodeconfig.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 hawkconfig.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 hawkconfig.Settings) string { +func checkNetworkReachability(settings graycodeconfig.Settings) string { providerName := strings.TrimSpace(settings.Provider) if providerName == "" { - providerName = strings.TrimSpace(hawkconfig.ActiveProvider(context.Background())) + providerName = strings.TrimSpace(graycodeconfig.ActiveProvider(context.Background())) } if providerName == "" || providerName == "ollama" { return "" @@ -254,5 +254,5 @@ func checkNetworkReachability(settings hawkconfig.Settings) string { // providerDNSHost returns a hostname to check DNS resolution for a provider. func providerDNSHost(provider string) string { - return hawkconfig.GatewayDNSHost(provider) + return graycodeconfig.GatewayDNSHost(provider) } diff --git a/cmd/errors_test.go b/cmd/errors_test.go index 26880194..ee759769 100644 --- a/cmd/errors_test.go +++ b/cmd/errors_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/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 := hawkconfig.ExampleModelHints() + example1, example2 := graycodeconfig.ExampleModelHints() if !strings.Contains(got, example1) || !strings.Contains(got, example2) { t.Errorf("friendlyError(%q) = %q, should suggest valid model names", tt.errMsg, got) } @@ -604,8 +604,8 @@ func TestFriendlyErrorBackwardCompat(t *testing.T) { } // helper for tests -func emptySettings() hawkconfig.Settings { - return hawkconfig.Settings{} +func emptySettings() graycodeconfig.Settings { + return graycodeconfig.Settings{} } // ── signalHandler test ──────────────────────────────────────────────────────── diff --git a/cmd/eval.go b/cmd/eval.go index 2506d8af..5d2d0721 100644 --- a/cmd/eval.go +++ b/cmd/eval.go @@ -9,10 +9,10 @@ import ( "strings" "time" - 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" + 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" "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 := hawkconfig.LoadGlobalSettings() + settings := graycodeconfig.LoadGlobalSettings() ctx := context.Background() - gw, err := hawkconfig.NewEyrieEngineForSettings(settings) + gw, err := graycodeconfig.NewEyrieEngineForSettings(settings) if err != nil { return fmt.Errorf("eval loop: build engine client: %w", err) } model := strings.TrimSpace(evalLoopModel) if model == "" { - model = strings.TrimSpace(hawkconfig.ActiveModel(ctx)) + model = strings.TrimSpace(graycodeconfig.ActiveModel(ctx)) } if model == "" { model = strings.TrimSpace(settings.Model) } - workDir, err := os.MkdirTemp("", "hawk-eval-loop-*") + workDir, err := os.MkdirTemp("", "graycode-eval-loop-*") if err != nil { return fmt.Errorf("eval loop: create temp dir: %w", err) } @@ -216,7 +216,7 @@ func runEval(_ *cobra.Command, _ []string) error { fmt.Printf("Running %d tasks with model %s...\n", len(tasks), modelName) - suite := &eval.BenchmarkSuite{Name: "hawk-eval", Tasks: tasks} + suite := &eval.BenchmarkSuite{Name: "graycode-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 1614856c..460b2fb5 100644 --- a/cmd/eval_tools.go +++ b/cmd/eval_tools.go @@ -6,9 +6,9 @@ import ( "fmt" "time" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/feature/eval" - "github.com/GrayCodeAI/hawk/internal/types" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/feature/eval" + "github.com/GrayCodeAI/graycode-cli/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 hawk's standard tools. +// negative tool-trigger situations against graycode'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 := hawkconfig.LoadSettings() + settings := graycodeconfig.LoadSettings() registry, err := defaultRegistry(settings) if err != nil { @@ -79,7 +79,7 @@ func runEvalTools(cmd *cobra.Command, _ []string) error { return err } modelName, providerName := effectiveModelAndProvider(settings) - sess, err := newConfiguredHawkSession(settings, providerName, modelName, systemPrompt, registry, nil) + sess, err := newConfiguredGraycodeSession(settings, providerName, modelName, systemPrompt, registry, nil) if err != nil { return err } diff --git a/cmd/exec.go b/cmd/exec.go index 195cf13a..665a55b5 100644 --- a/cmd/exec.go +++ b/cmd/exec.go @@ -13,16 +13,16 @@ import ( "strings" "time" - 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" + 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" "github.com/spf13/cobra" ) @@ -92,13 +92,13 @@ Autonomy Levels: yolo Never ask for permission Examples: - 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"`, + 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"`, Args: cobra.MaximumNArgs(1), RunE: runExec, } @@ -109,7 +109,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 Hawk user state)") + execCmd.Flags().StringVar(&execAgent, "agent", "", "Agent persona to use (from Graycode 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") @@ -175,7 +175,7 @@ func runExec(_ *cobra.Command, args []string) error { base := getCurrentBranch(cwd) branch := execWorktreeName if branch == "" { - branch = fmt.Sprintf("hawk-exec/%d-%s", start.UnixMilli(), randomHex(4)) + branch = fmt.Sprintf("graycode-exec/%d-%s", start.UnixMilli(), randomHex(4)) } var wtErr error wtPath, wtErr = createExecWorktree(cwd, base, branch) @@ -190,7 +190,7 @@ func runExec(_ *cobra.Command, args []string) error { } // Load settings - settings := hawkconfig.LoadSettings() + settings := graycodeconfig.LoadSettings() // Build system prompt systemPrompt, err := buildSystemPrompt() @@ -226,7 +226,7 @@ func runExec(_ *cobra.Command, args []string) error { } // Create engine session - sess, cfgErr := newConfiguredHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error), execMaxTurns) + sess, cfgErr := newConfiguredGraycodeSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error), execMaxTurns) if cfgErr != nil { return cfgErr } @@ -249,12 +249,12 @@ 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 - // HAWK_GHA_TRUST_EVENT=1. + // GRAYCODE_GHA_TRUST_EVENT=1. if ghaCtx.Active && !ghaCtx.Trusted { const ceiling = engine.AutonomyBasic if sess.PermSvc().Autonomy() > ceiling { fmt.Fprintf(os.Stderr, - "hawk: untrusted GitHub event (author_association=%q); capping autonomy at %s\n", + "graycode: untrusted GitHub event (author_association=%q); capping autonomy at %s\n", ghaCtx.AuthorAssociation, ceiling) sess.PermSvc().SetAutonomy(ceiling) } @@ -370,7 +370,7 @@ func runExec(_ *cobra.Command, args []string) error { DeviceID: cfg.DeviceID, ProjectID: cfg.ProjectID, SessionID: sessionID, - Capability: "hawk", + Capability: "graycode", Model: effectiveModel, InputTokens: totalIn, OutputTokens: totalOut, @@ -450,16 +450,16 @@ type GHAMode string const ( // GHAModeNone means we are not running inside GitHub Actions. GHAModeNone GHAMode = "" - // GHAModeInteractive is used when a human mentioned @hawk in a comment and + // GHAModeInteractive is used when a human mentioned @graycode in a comment and // expects a conversational reply. GHAModeInteractive GHAMode = "interactive" - // GHAModeAutomation is used for label/issue triggers where hawk should act + // GHAModeAutomation is used for label/issue triggers where graycode 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 = "@hawk" +const ghMention = "@graycode" // ghTrustedAssociations are the GitHub author_association values that identify // a repository insider. Everyone else (CONTRIBUTOR, FIRST_TIME_CONTRIBUTOR, @@ -477,7 +477,7 @@ type GHAContext struct { EventName string // GITHUB_EVENT_NAME Mode GHAMode // resolved operating mode Prompt string // event-derived prompt body - Mention bool // whether an @hawk mention was found in a comment + Mention bool // whether an @graycode 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) } @@ -510,7 +510,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). - // HAWK_GHA_TRUST_EVENT=1 lets a maintainer opt into trusting all events. + // GRAYCODE_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) @@ -538,7 +538,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("HAWK_GHA_TRUST_EVENT"))) { + switch strings.ToLower(strings.TrimSpace(getenv("GRAYCODE_GHA_TRUST_EVENT"))) { case "1", "true", "yes", "on": return true default: @@ -595,7 +595,7 @@ func ghIssueBody(payload map[string]interface{}) string { return strings.TrimSpace(ghCommentBody(payload)) } -// ghStripMention removes the leading @hawk mention from a comment so the +// ghStripMention removes the leading @graycode mention from a comment so the // remaining text becomes the prompt. func ghStripMention(body string) string { out := body @@ -617,7 +617,7 @@ type skillRunner interface { Run(name string) (string, error) } -// pluginSkillRunner is the production skillRunner backed by Hawk skill storage. +// pluginSkillRunner is the production skillRunner backed by Graycode skill storage. type pluginSkillRunner struct{} func (pluginSkillRunner) Run(name string) (string, error) { @@ -627,7 +627,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 `hawk skills` to list available skills)", name) + return "", fmt.Errorf("skill %q not found (run `graycode skills` to list available skills)", name) } // defaultSkillRunner is overridable in tests. @@ -756,7 +756,7 @@ func runExecFanout(prompt string, n int) error { fmt.Fprintf(os.Stderr, "\n=== fanout attempt %d/%d ===\n", i, n) att := fanoutAttempt{Attempt: i} - branch := fmt.Sprintf("hawk-exec/%d-fanout%d-%s", start.UnixMilli(), i, randomHex(4)) + branch := fmt.Sprintf("graycode-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) @@ -825,7 +825,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: "hawk exec --fanout", OK: anyOK, Body: fanoutSummaryLines(attempts), + Title: title, Source: "graycode exec --fanout", OK: anyOK, Body: fanoutSummaryLines(attempts), }) if !anyOK { @@ -889,7 +889,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 := hawkconfig.LoadSettings() + settings := graycodeconfig.LoadSettings() systemPrompt, err := buildSystemPrompt() if err != nil { @@ -912,7 +912,7 @@ func execOnceInWorktree(prompt string, attemptIdx int) (*ExecResult, error) { if err != nil { return nil, err } - sess, cfgErr := newConfiguredHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, logger.New(io.Discard, logger.Error), execMaxTurns) + sess, cfgErr := newConfiguredGraycodeSession(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 de312db9..6de569f8 100644 --- a/cmd/exec_test.go +++ b/cmd/exec_test.go @@ -7,7 +7,7 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/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":"@hawk please fix the failing test"}}` + payload := `{"comment":{"body":"@graycode 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":"@Hawk explain this change"}}` + payload := `{"comment":{"body":"@Graycode 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", - "HAWK_GHA_TRUST_EVENT": "1", + "GITHUB_ACTIONS": "true", + "GITHUB_EVENT_NAME": "issues", + "GITHUB_EVENT_PATH": "/tmp/event.json", + "GRAYCODE_GHA_TRUST_EVENT": "1", } outsider := `{"issue":{"title":"t","body":"b","author_association":"NONE"}}` if gha := detectGitHubActions(envFunc(env), fileFunc(outsider)); !gha.Trusted { - t.Error("HAWK_GHA_TRUST_EVENT=1 should trust even NONE association") + t.Error("GRAYCODE_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("HAWK_STATE_DIR", filepath.Join(dir, "state")) + t.Setenv("GRAYCODE_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: "hawk-exec/123", + Branch: "graycode-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 != "hawk-exec/123" { + if decoded.Branch != "graycode-exec/123" { t.Errorf("expected branch, got %s", decoded.Branch) } } diff --git a/cmd/execution_graph.go b/cmd/execution_graph.go index 3ade6769..14a266d7 100644 --- a/cmd/execution_graph.go +++ b/cmd/execution_graph.go @@ -10,22 +10,22 @@ import ( "strings" "time" - graphcontracts "github.com/GrayCodeAI/eagle/graph" - policycontracts "github.com/GrayCodeAI/eagle/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" + 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" "github.com/spf13/cobra" ) func newExecutionGraphCmd() *cobra.Command { graphCmd := &cobra.Command{ Use: "graph", - Short: "Merlin Hawk's portable execution graph", - Long: `Project Hawk-owned sessions, task requests, structured tasks, runtime tasks, + Short: "Merlin Graycode's portable execution graph", + Long: `Project Graycode-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 Hawk session or mission as graph JSON", + Short: "Export a Graycode 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: "hawk/session/" + saved.ID, + ID: "graycode/session/" + saved.ID, } if _, ok := toolCallIDs[entry.ToolCallID]; ok && entry.ToolCallID != "" { subject = graphcontracts.Ref{ Kind: graphcontracts.NodeExecution, - ID: "hawk/tool-call/" + saved.ID + "/" + entry.ToolCallID, + ID: "graycode/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 64ba2cc6..7d0931cd 100644 --- a/cmd/execution_graph_test.go +++ b/cmd/execution_graph_test.go @@ -11,11 +11,11 @@ import ( "testing" "time" - graphcontracts "github.com/GrayCodeAI/eagle/graph" - policycontracts "github.com/GrayCodeAI/eagle/policy" - "github.com/GrayCodeAI/hawk/internal/executiongraph" - "github.com/GrayCodeAI/hawk/internal/graphjournal" - "github.com/GrayCodeAI/hawk/internal/session" + 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" ) 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: "hawk/mission/m1"} + mission := graphcontracts.Ref{Kind: graphcontracts.NodeExecution, ID: "graycode/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: "hawk"}, + Provenance: graphcontracts.Provenance{Producer: "graycode"}, }}, Events: []graphcontracts.Event{{ - ID: "hawk/event/mission/m1/created", Type: graphcontracts.EventCreated, + ID: "graycode/event/mission/m1/created", Type: graphcontracts.EventCreated, Subject: mission, OccurredAt: now, - Provenance: graphcontracts.Provenance{Producer: "hawk"}, + Provenance: graphcontracts.Provenance{Producer: "graycode"}, }}, } dir := t.TempDir() @@ -76,7 +76,7 @@ func TestLoadMissionGraphExportValidatesTopology(t *testing.T) { t.Fatalf("loaded graph = %#v", loaded) } - export.Events[0].Subject.ID = "hawk/mission/missing" + export.Events[0].Subject.ID = "graycode/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/hawk"); got != "custom" { + if got := executionGraphRepositoryID("custom", "/work/graycode"); got != "custom" { t.Fatalf("override repository ID = %q, want custom", got) } - if got := executionGraphRepositoryID("", "/work/hawk"); got != "hawk" { - t.Fatalf("derived repository ID = %q, want hawk", got) + if got := executionGraphRepositoryID("", "/work/graycode"); got != "graycode" { + t.Fatalf("derived repository ID = %q, want graycode", got) } } @@ -111,11 +111,11 @@ func TestValidateSwiftCheckpointID(t *testing.T) { } func TestExecutionGraphExportCommand(t *testing.T) { - t.Setenv("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) saved := &session.Session{ ID: "graph-command-session", - CWD: "/workspace/hawk", + CWD: "/workspace/graycode", 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 != "hawk" { - t.Fatalf("Scope.RepositoryID = %q, want hawk", export.Scope.RepositoryID) + if export.Scope.RepositoryID != "graycode" { + t.Fatalf("Scope.RepositoryID = %q, want graycode", 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, "hawk/policy/") { + if !hasExportNodePrefix(export, "graycode/policy/") { t.Fatal("graph export omitted automatic policy observation") } - if !hasExportNodePrefix(export, "hawk/verification/") { + if !hasExportNodePrefix(export, "graycode/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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) now := time.Date(2026, time.July, 25, 6, 0, 0, 0, time.UTC) saved := &session.Session{ - ID: "hawk-correlated-session", - CWD: "/workspace/hawk", + ID: "graycode-correlated-session", + CWD: "/workspace/graycode", CreatedAt: now.Add(-time.Hour), UpdatedAt: now, } @@ -272,7 +272,7 @@ func TestBuildExecutionGraphExportComposesAuthoritativeSwiftCorrelation(t *testi } resolver := stubSwiftCorrelationResolver{correlation: swiftCorrelation{ SchemaVersion: swiftCorrelationSchemaVersion, - HawkSessionID: saved.ID, + GraycodeSessionID: saved.ID, CheckpointLookupComplete: true, Matches: []swiftCorrelationMatch{ { @@ -312,7 +312,7 @@ func TestBuildExecutionGraphExportComposesAuthoritativeSwiftCorrelation(t *testi assertExportEdge( t, export, - "hawk/session/"+saved.ID, + "graycode/session/"+saved.ID, "swift/session/swift-alpha", graphcontracts.EdgeReferences, ) @@ -326,12 +326,12 @@ func TestBuildExecutionGraphExportComposesAuthoritativeSwiftCorrelation(t *testi } func TestBuildExecutionGraphExportSwiftLookupFailureIsFailOpen(t *testing.T) { - t.Setenv("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) now := time.Date(2026, time.July, 25, 6, 30, 0, 0, time.UTC) saved := &session.Session{ - ID: "hawk-swift-fail-open", - CWD: "/workspace/hawk", + ID: "graycode-swift-fail-open", + CWD: "/workspace/graycode", CreatedAt: now.Add(-time.Hour), UpdatedAt: now, } diff --git a/cmd/features.go b/cmd/features.go index 895f448c..aaf94f8b 100644 --- a/cmd/features.go +++ b/cmd/features.go @@ -5,7 +5,7 @@ import ( "sort" "strings" - "github.com/GrayCodeAI/hawk/internal/feature" + "github.com/GrayCodeAI/graycode-cli/internal/feature" "github.com/spf13/cobra" ) @@ -20,14 +20,14 @@ capabilities without code changes or restarts (some changes may require a daemon restart). Override a flag via environment variable: - HAWK_FEATURE_=1 hawk daemon start + GRAYCODE_FEATURE_=1 graycode daemon start Show a specific flag: - hawk features get `, + graycode 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: hawk features get ") + return fmt.Errorf("usage: graycode features get ") } f, ok := feature.Info(args[1]) if !ok { @@ -37,7 +37,7 @@ Show a specific flag: fmt.Printf("Default: %v\n", f.DefaultValue()) fmt.Printf("Current: %v\n", feature.EnabledByName(args[1])) fmt.Printf("Description: %s\n", f.Description()) - envVar := "HAWK_FEATURE_" + strings.ReplaceAll(strings.ToUpper(args[1]), "-", "_") + envVar := "GRAYCODE_FEATURE_" + strings.ReplaceAll(strings.ToUpper(args[1]), "-", "_") fmt.Printf("Env var: %s\n", envVar) return nil } @@ -62,7 +62,7 @@ Show a specific flag: if f != nil { fmt.Printf(" default: %v\n", f.DefaultValue()) fmt.Printf(" description: %s\n", f.Description()) - envVar := "HAWK_FEATURE_" + strings.ReplaceAll(strings.ToUpper(name), "-", "_") + envVar := "GRAYCODE_FEATURE_" + strings.ReplaceAll(strings.ToUpper(name), "-", "_") fmt.Printf(" env: %s\n", envVar) } fmt.Println() diff --git a/cmd/feedback.go b/cmd/feedback.go index 06cc56fe..93769876 100644 --- a/cmd/feedback.go +++ b/cmd/feedback.go @@ -12,7 +12,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/storage" "github.com/spf13/cobra" ) @@ -37,17 +37,17 @@ type FeedbackReport struct { var feedbackCmd = &cobra.Command{ Use: "feedback [message]", - Short: "Submit feedback about hawk", - Long: `Capture feedback about your hawk experience. By default, opens a + Short: "Submit feedback about graycode", + Long: `Capture feedback about your graycode experience. By default, opens a GitHub issue template URL in your browser. Use --local to save feedback -to Hawk's user state directory for later submission. +to Graycode's user state directory for later submission. Categories: bug, feature, ux, performance, other Examples: - hawk feedback "The completion is slow" - hawk feedback --category bug "Crash when using /compact" - hawk feedback --local "Wish it could do X"`, + graycode feedback "The completion is slow" + graycode feedback --category bug "Crash when using /compact" + graycode 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/hawk/issues/new?title=%s&body=%s&labels=%s", + "https://github.com/GrayCodeAI/graycode-cli/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 bfd4fb4c..664ecf43 100644 --- a/cmd/fingerprint.go +++ b/cmd/fingerprint.go @@ -5,7 +5,7 @@ import ( "fmt" "os" - "github.com/GrayCodeAI/hawk/internal/feature/fingerprint" + "github.com/GrayCodeAI/graycode-cli/internal/feature/fingerprint" "github.com/spf13/cobra" ) @@ -19,10 +19,10 @@ including detected languages, dependency counts, CI presence, license, and git metadata. Examples: - hawk fingerprint - hawk fingerprint ./myproject - hawk fingerprint --format json . - hawk fingerprint --format markdown /path/to/repo`, + graycode fingerprint + graycode fingerprint ./myproject + graycode fingerprint --format json . + graycode 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 eb03f451..78136875 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/hawk") + left := lipgloss.NewStyle().Foreground(statusCWDColor).Inline(true).Render("~/OSS2026/RealWork/graycode-eco/graycode") 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/hawk") + left := lipgloss.NewStyle().Foreground(statusCWDColor).Inline(true).Render("~/Desktop/OSS2026/RealWork/graycode-eco/graycode") 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 d8568700..ac4444ef 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("~/hawk") + " " + cwdStyle.Render("⎇ main") + left := cwdStyle.Render("~/graycode") + " " + 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 d6d01263..e63587fb 100644 --- a/cmd/formatter.go +++ b/cmd/formatter.go @@ -9,12 +9,12 @@ import ( "golang.org/x/term" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 hawk's output — this is false, and color/Unicode +// shell script captures graycode'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 8c8aa134..0fb7e587 100644 --- a/cmd/formatter_test.go +++ b/cmd/formatter_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) func TestDetectColorSupport_NonTTYStdout(t *testing.T) { diff --git a/cmd/golden_test.go b/cmd/golden_test.go index fec827c4..f15b7201 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, "hawk") { - t.Error("help output should contain 'hawk'") + if !strings.Contains(got, "graycode") { + t.Error("help output should contain 'graycode'") } if len(got) < 100 { t.Error("help output seems too short") diff --git a/cmd/governance_cmd.go b/cmd/governance_cmd.go index deac39a3..810fd7b8 100644 --- a/cmd/governance_cmd.go +++ b/cmd/governance_cmd.go @@ -6,7 +6,7 @@ import ( "sort" "strings" - "github.com/GrayCodeAI/hawk/internal/governance" + "github.com/GrayCodeAI/graycode-cli/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. - 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`, + 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`, RunE: func(cmd *cobra.Command, args []string) error { return runGovernanceStatus(cmd) }, diff --git a/cmd/hawk/integration_test.go b/cmd/graycode/integration_test.go similarity index 98% rename from cmd/hawk/integration_test.go rename to cmd/graycode/integration_test.go index 1c6889c6..605dedac 100644 --- a/cmd/hawk/integration_test.go +++ b/cmd/graycode/integration_test.go @@ -8,11 +8,11 @@ import ( "path/filepath" "testing" + "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" + "github.com/GrayCodeAI/graycode-cli/internal/testutil" "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/kestrel" "github.com/GrayCodeAI/merlin" "github.com/GrayCodeAI/shrike" diff --git a/cmd/hawk/main.go b/cmd/graycode/main.go similarity index 81% rename from cmd/hawk/main.go rename to cmd/graycode/main.go index 43aa093c..45e6e2cf 100644 --- a/cmd/hawk/main.go +++ b/cmd/graycode/main.go @@ -7,12 +7,12 @@ import ( "os" "time" - "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" + "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" ) // Version, Commit, and BuildDate are set at build time via ldflags. @@ -41,12 +41,12 @@ func main() { // Handle --version flag immediately if len(os.Args) > 1 && os.Args[1] == "--version" { - fmt.Println("hawk " + Version) + fmt.Println("graycode " + Version) return } - // Initialize OpenTelemetry telemetry (opt-in via HAWK_CODE_ENABLE_TELEMETRY=1). - // Telemetry failures are non-fatal: hawk continues with in-memory tracing only. + // Initialize OpenTelemetry telemetry (opt-in via GRAYCODE_ENABLE_TELEMETRY=1). + // Telemetry failures are non-fatal: graycode continues with in-memory tracing only. telemetryProviders, telemetryErr := oteltrace.InitTelemetry(oteltrace.DefaultTelemetryConfig()) if telemetryErr != nil { fmt.Fprintln(os.Stderr, "warning: telemetry initialization failed:", telemetryErr) @@ -60,7 +60,7 @@ func main() { } // OTLP log-record export (DSH session-telemetry-otel port). Opt-in like - // tracing: HAWK_CODE_ENABLE_TELEMETRY=1 plus an OTLP logs endpoint. The + // tracing: GRAYCODE_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 { @@ -91,6 +91,6 @@ func main() { if errors.As(err, &exitErr) { os.Exit(exitErr.Code) } - os.Exit(hawkerr.ClassifyExitCode(err)) + os.Exit(graycodeerr.ClassifyExitCode(err)) } } diff --git a/cmd/harness.go b/cmd/harness.go index cdae05fc..22dd43c0 100644 --- a/cmd/harness.go +++ b/cmd/harness.go @@ -6,7 +6,7 @@ import ( "os" "path/filepath" - "github.com/GrayCodeAI/hawk/internal/harness" + "github.com/GrayCodeAI/graycode-cli/internal/harness" "github.com/spf13/cobra" ) @@ -50,7 +50,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("[FIX] Hawk Harness Auto-Repair Results:\n") + fmt.Printf("[FIX] Graycode Harness Auto-Repair Results:\n") for _, repair := range fixResult.RepairsPerformed { fmt.Printf(" + %s\n", repair) } @@ -60,7 +60,7 @@ Use --fix to automatically repair missing AGENTS.md, skills, or spec directories outDir := harnessOutDir if outDir == "" { - outDir = filepath.Join(targetDir, ".hawk", "harness") + outDir = filepath.Join(targetDir, ".graycode", "harness") } if mkdirErr := os.MkdirAll(outDir, 0o750); mkdirErr != nil { @@ -91,10 +91,10 @@ 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 Hawk execution graph + // Journal quality observation to Graycode execution graph _ = harness.JournalHarnessReport(report, "") - fmt.Printf("[HAWK] Hawk Harness Evaluation Complete\n") + fmt.Printf("[GRAYCODE] Graycode Harness Evaluation Complete\n") fmt.Printf(" Overall Score : %d/100 (%s)\n", report.OverallScore, report.OverallStatus) fmt.Printf(" Findings : %d prioritized issues\n", len(report.Findings)) fmt.Printf(" HTML Report : %s\n", htmlPath) @@ -106,7 +106,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: .hawk/harness)") + harnessCmd.Flags().StringVar(&harnessOutDir, "out-dir", "", "Directory to save harness reports (default: .graycode/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/history.go b/cmd/history.go index 506bdba3..bfc70d5b 100644 --- a/cmd/history.go +++ b/cmd/history.go @@ -5,7 +5,7 @@ import ( "path/filepath" "strings" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/storage" ) const maxHistoryEntries = 1000 @@ -15,7 +15,7 @@ func historyFilePath() string { return filepath.Join(storage.StateDir(), "history") } -// loadInputHistory loads input history from Hawk's user state directory. +// loadInputHistory loads input history from Graycode'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 Hawk's user state directory. +// saveInputHistory writes the history list to Graycode'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 cda9aab7..412e71c2 100644 --- a/cmd/history_test.go +++ b/cmd/history_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/testutil" + "github.com/GrayCodeAI/graycode-cli/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("HAWK_STATE_DIR", stateDir) + t.Setenv("GRAYCODE_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 7c830ca2..12e98f7a 100644 --- a/cmd/hud_panel.go +++ b/cmd/hud_panel.go @@ -6,7 +6,7 @@ import ( lipgloss "charm.land/lipgloss/v2" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 0051520b..267f8321 100644 --- a/cmd/image.go +++ b/cmd/image.go @@ -12,7 +12,7 @@ import ( "regexp" "strings" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) // ImageAttachment represents an image ready to be attached to a message. diff --git a/cmd/input_indicator.go b/cmd/input_indicator.go index cb3c0ac8..0dee4c8e 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/hawk/internal/feature/shellmode" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/feature/shellmode" + "github.com/GrayCodeAI/graycode-cli/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 e39c519e..7b1d9230 100644 --- a/cmd/input_indicator_test.go +++ b/cmd/input_indicator_test.go @@ -3,7 +3,7 @@ package cmd import ( "testing" - "github.com/GrayCodeAI/hawk/internal/feature/shellmode" + "github.com/GrayCodeAI/graycode-cli/internal/feature/shellmode" ) func TestInputIndicator_Classify(t *testing.T) { diff --git a/cmd/issue.go b/cmd/issue.go index cb3af952..c226377e 100644 --- a/cmd/issue.go +++ b/cmd/issue.go @@ -128,5 +128,5 @@ func generateIssueBody(ctx string) string { if ctx == "" { return "No additional context was provided." } - return "**Reported via hawk**\n\n```\n" + ctx + "\n```\n" + return "**Reported via graycode**\n\n```\n" + ctx + "\n```\n" } diff --git a/cmd/learn_cmd.go b/cmd/learn_cmd.go index 74b0d20d..cc263323 100644 --- a/cmd/learn_cmd.go +++ b/cmd/learn_cmd.go @@ -5,7 +5,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" "github.com/spf13/cobra" ) @@ -22,13 +22,13 @@ var ( var learnCmd = &cobra.Command{ Use: "learn", Short: "Manage lessons learned across sessions", - Long: `Hawk persists lessons from failures (and manual entries) so future + Long: `Graycode persists lessons from failures (and manual entries) so future sessions avoid repeating them. Lessons are injected into the system prompt. - 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`, + 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`, RunE: func(cmd *cobra.Command, args []string) error { return runLearnList(cmd) }, @@ -94,7 +94,7 @@ func runLearnList(cmd *cobra.Command) error { si := engine.NewSelfImprover() lessons := si.Lessons("") if len(lessons) == 0 { - cmd.Println("No lessons yet. Add one with: hawk learn add --what ... --lesson ...") + cmd.Println("No lessons yet. Add one with: graycode learn add --what ... --lesson ...") return nil } diff --git a/cmd/main_test.go b/cmd/main_test.go index 1f6d9ee7..e27d9d96 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -4,8 +4,8 @@ import ( "os" "testing" - "github.com/GrayCodeAI/hawk/internal/catalogtest" - "github.com/GrayCodeAI/hawk/internal/testutil" + "github.com/GrayCodeAI/graycode-cli/internal/catalogtest" + "github.com/GrayCodeAI/graycode-cli/internal/testutil" ) func TestMain(m *testing.M) { diff --git a/cmd/manpage.go b/cmd/manpage.go index 2c65961d..7c58496c 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 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", + 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", 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 hawk. +// GenerateManPage produces a man page in roff format for graycode. // The OPTIONS section is generated from the live Cobra flag set so it // never drifts from the actual CLI surface. func GenerateManPage() string { @@ -35,19 +35,19 @@ func GenerateManPage() string { var b strings.Builder // Header - b.WriteString(fmt.Sprintf(`.TH HAWK 1 "%s" "%s" "User Commands"`, date, ver)) + b.WriteString(fmt.Sprintf(`.TH GRAYCODE 1 "%s" "%s" "User Commands"`, date, ver)) b.WriteString("\n") // Name - b.WriteString(".SH NAME\nhawk \\- AI coding agent powered by eyrie\n") + b.WriteString(".SH NAME\ngraycode \\- AI coding agent powered by eyrie\n") // Synopsis b.WriteString(".SH SYNOPSIS\n") - b.WriteString(".B hawk\n[\\fIOPTIONS\\fR] [\\fIPROMPT\\fR]\n") + b.WriteString(".B graycode\n[\\fIOPTIONS\\fR] [\\fIPROMPT\\fR]\n") // Description b.WriteString(".SH DESCRIPTION\n") - b.WriteString("hawk is an AI coding agent that reads, writes, and runs code in your terminal.\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 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\\fBhawk %s\\fR\n%s\n", sub.Use, sub.Short)) + b.WriteString(fmt.Sprintf(".TP\n\\fBgraycode %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 hawk"}, + {"/quit", "Exit graycode"}, } 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\\fBHawk user config directory\\fR\nGlobal configuration files\n") + b.WriteString(".TP\n\\fBGraycode user config directory\\fR\nGlobal configuration files\n") b.WriteString(".TP\n\\fBAGENTS.md\\fR\nProject instructions file\n") - b.WriteString(".TP\n\\fBHawk user state directory\\fR\nSaved session data, plans, skills, and runtime state\n") + b.WriteString(".TP\n\\fBGraycode 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 \\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") + 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") // 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)"}, - {"HAWK_CONFIG_DIR", "Override hawk config directory"}, + {"GRAYCODE_CONFIG_DIR", "Override graycode 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 6bfa5532..e8971a5d 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 HAWK 1") { + if !strings.Contains(page, ".TH GRAYCODE 1") { t.Fatal("missing .TH header") } if !strings.Contains(page, "1.0.0") { diff --git a/cmd/markdown.go b/cmd/markdown.go index 7707c54c..fcbaab47 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(hawkColor).Bold(true).Underline(true) + mdH1Style = lipgloss.NewStyle().Foreground(graycodeColor).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(hawkColor).Bold(true) + mdBoldStyle = lipgloss.NewStyle().Foreground(graycodeColor).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("\x00HAWK_INLINE_CODE_%d\x00", len(replacements)) + placeholder := fmt.Sprintf("\x00GRAYCODE_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("\x00HAWK_INLINE_CODE_%d\x00", i), repl) + s = strings.ReplaceAll(s, fmt.Sprintf("\x00GRAYCODE_INLINE_CODE_%d\x00", i), repl) } return s } diff --git a/cmd/markdown_renderer.go b/cmd/markdown_renderer.go index c206c5db..8fce937f 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("\x00HAWK_R_INLINE_CODE_%d\x00", len(replacements)) + placeholder := fmt.Sprintf("\x00GRAYCODE_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("\x00HAWK_R_INLINE_CODE_%d\x00", i), repl) + s = strings.ReplaceAll(s, fmt.Sprintf("\x00GRAYCODE_R_INLINE_CODE_%d\x00", i), repl) } return s } diff --git a/cmd/markdown_test.go b/cmd/markdown_test.go index 5e25fe56..53ebee74 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 [Hawk](https://example.com) for info" + input := "Visit [Graycode](https://example.com) for info" out := renderMarkdown(input, 80) plain := stripAnsi(out) - if !strings.Contains(plain, "Hawk") { + if !strings.Contains(plain, "Graycode") { 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"}, - {"hawk", "Go", "1200"}, + {"graycode", "Go", "1200"}, {"glow", "Go", "15000"}, {"bat", "Rust", "47000"}, } diff --git a/cmd/mcp_serve.go b/cmd/mcp_serve.go index 8997af38..13379b85 100644 --- a/cmd/mcp_serve.go +++ b/cmd/mcp_serve.go @@ -6,8 +6,8 @@ import ( "os/signal" "syscall" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/mcp" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/mcp" "github.com/spf13/cobra" ) @@ -20,28 +20,28 @@ func init() { mcpCmd.AddCommand(mcpConfigCmd) } -// mcpServeCmd runs hawk itself as an MCP server over stdio, exposing hawk's +// mcpServeCmd runs graycode itself as an MCP server over stdio, exposing graycode'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 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.", + 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.", RunE: runMCPServe, } func runMCPServe(cmd *cobra.Command, _ []string) error { - settings := hawkconfig.LoadSettings() + settings := graycodeconfig.LoadSettings() serverVersion := version if serverVersion == "" { serverVersion = "dev" } - server := mcp.NewMCPServer(mcp.ServerInfo{Name: "hawk", Version: serverVersion}) + server := mcp.NewMCPServer(mcp.ServerInfo{Name: "graycode", Version: serverVersion}) - // Wire hawk's tool registry in as the executor so delegating tools run for + // Wire graycode'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 hawk as an MCP server in a +// mcpConfigCmd emits the JSON block that registers graycode 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 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" + + 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" + "Pipe it to the client's config file, e.g.:\n" + - " hawk mcp config >> ~/Library/Application Support/Claude/claude_desktop_config.json", + " graycode mcp config >> ~/Library/Application Support/Claude/claude_desktop_config.json", RunE: runMCPConfig, } func runMCPConfig(cmd *cobra.Command, _ []string) error { - exe := hawkExecutablePath() + exe := graycodeExecutablePath() block := map[string]any{ "mcpServers": map[string]any{ - "hawk": map[string]any{ + "graycode": 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 \"hawk\" entry below into the \"mcpServers\" object of your client config:") + cmd.Println("# Add the \"graycode\" 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 } -// 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 +// 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 // emitted config is still copy-pasteable. -func hawkExecutablePath() string { +func graycodeExecutablePath() string { if exe, err := os.Executable(); err == nil && exe != "" { return exe } - return "hawk" + return "graycode" } diff --git a/cmd/mcp_serve_test.go b/cmd/mcp_serve_test.go index 8b37d46b..ab256eaa 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()) } - hawk, ok := cfg.MCPServers["hawk"] + graycode, ok := cfg.MCPServers["graycode"] if !ok { - t.Fatal(`config missing "hawk" server entry`) + t.Fatal(`config missing "graycode" server entry`) } - if hawk.Command == "" { - t.Error("hawk server entry missing command") + if graycode.Command == "" { + t.Error("graycode server entry missing command") } - if len(hawk.Args) != 2 || hawk.Args[0] != "mcp" || hawk.Args[1] != "serve" { - t.Errorf(`args = %v, want ["mcp" "serve"]`, hawk.Args) + if len(graycode.Args) != 2 || graycode.Args[0] != "mcp" || graycode.Args[1] != "serve" { + t.Errorf(`args = %v, want ["mcp" "serve"]`, graycode.Args) } } diff --git a/cmd/mentions.go b/cmd/mentions.go index a78aa9d3..329315ac 100644 --- a/cmd/mentions.go +++ b/cmd/mentions.go @@ -6,8 +6,8 @@ import ( "strings" "unicode/utf8" - "github.com/GrayCodeAI/hawk/internal/mention" - "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/graycode-cli/internal/mention" + "github.com/GrayCodeAI/graycode-cli/internal/tool" ) // handleMentions processes @-prefixed file mentions in user input. diff --git a/cmd/mentions_test.go b/cmd/mentions_test.go index 63802f00..8c320435 100644 --- a/cmd/mentions_test.go +++ b/cmd/mentions_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) func TestHandleMentions_BlocksSensitivePaths(t *testing.T) { diff --git a/cmd/merlin_pipeline.go b/cmd/merlin_pipeline.go index dd9d9b66..691cd012 100644 --- a/cmd/merlin_pipeline.go +++ b/cmd/merlin_pipeline.go @@ -5,10 +5,10 @@ import ( "fmt" "strings" - graphcontracts "github.com/GrayCodeAI/eagle/graph" - contracts "github.com/GrayCodeAI/eagle/types" - verifycontracts "github.com/GrayCodeAI/eagle/verify" - hawkMerlin "github.com/GrayCodeAI/hawk/internal/bridge/merlin" + 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" 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 := hawkMerlin.NewBridge(opts...) + bridge := graycodeMerlin.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, hawkMerlin.GraphObservation{ + report, err = bridge.RunContractsObserved(ctx, cfg.Target, graycodeMerlin.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 89a3aab8..52e14e81 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/eagle/types" - verifycontracts "github.com/GrayCodeAI/eagle/verify" + contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" + verifycontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/verify" ) func TestMerlinToReviewFindings_NilReport(t *testing.T) { diff --git a/cmd/migrate_secrets_test.go b/cmd/migrate_secrets_test.go index 4e1a50cc..4f4c099f 100644 --- a/cmd/migrate_secrets_test.go +++ b/cmd/migrate_secrets_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/observability/logger" + "github.com/GrayCodeAI/graycode-cli/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, "hawk /config") { - t.Errorf("expected remediation hint mentioning `hawk /config`, got: %q", out) + if !strings.Contains(out, "graycode /config") { + t.Errorf("expected remediation hint mentioning `graycode /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 7422b9f8..d3e9ec5d 100644 --- a/cmd/mission.go +++ b/cmd/mission.go @@ -9,12 +9,12 @@ import ( "strings" "time" - 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" + 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" "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: - 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`, + 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`, Args: cobra.ArbitraryArgs, RunE: runMission, } @@ -60,7 +60,7 @@ func runMission(_ *cobra.Command, args []string) error { cwd, _ := os.Getwd() baseBranch := getCurrentBranch(cwd) - settings := hawkconfig.LoadSettings() + settings := graycodeconfig.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/hawk-missions/ + // whether it succeeded or failed. Without this, /tmp/graycode-missions/ // accumulates one directory per run indefinitely (C6 fix). defer func() { _ = m.Cleanup() }() @@ -160,7 +160,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 `hawk mission` exited 0. + // when every feature failed (H9), so `graycode mission` exited 0. failed := 0 for _, f := range m.Features { if f.Status == mission.FeatureFailed { @@ -174,7 +174,7 @@ func runMission(_ *cobra.Command, args []string) error { return nil } -func planWithLLM(ctx context.Context, prompt, provider, model string, settings hawkconfig.Settings) ([]mission.Feature, error) { +func planWithLLM(ctx context.Context, prompt, provider, model string, settings graycodeconfig.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"+ @@ -187,7 +187,7 @@ func planWithLLM(ctx context.Context, prompt, provider, model string, settings h ) registry, _ := defaultRegistry(settings) - sess, err := newConfiguredHawkSession(settings, provider, model, planPrompt, registry, logger.New(io.Discard, logger.Error)) + sess, err := newConfiguredGraycodeSession(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 3f221b8c..0946f8b8 100644 --- a/cmd/mission_graph.go +++ b/cmd/mission_graph.go @@ -6,8 +6,8 @@ import ( "strings" "time" - mission "github.com/GrayCodeAI/hawk/internal/multiagent" - "github.com/GrayCodeAI/hawk/internal/tool" + mission "github.com/GrayCodeAI/graycode-cli/internal/multiagent" + "github.com/GrayCodeAI/graycode-cli/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("hawk-mission/%s/%s", missionID, task.ID), + Branch: fmt.Sprintf("graycode-mission/%s/%s", missionID, task.ID), Status: status, }) } diff --git a/cmd/mission_graph_test.go b/cmd/mission_graph_test.go index d9e204c9..dec1d03c 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/hawk/internal/multiagent" - "github.com/GrayCodeAI/hawk/internal/tool" + mission "github.com/GrayCodeAI/graycode-cli/internal/multiagent" + "github.com/GrayCodeAI/graycode-cli/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 != "hawk-mission/mission123/"+root.ID { + if features[0].Branch != "graycode-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 14f6884f..e07ecf2f 100644 --- a/cmd/model_table.go +++ b/cmd/model_table.go @@ -6,8 +6,8 @@ import ( "strings" lipgloss "charm.land/lipgloss/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" "github.com/mattn/go-runewidth" ) @@ -119,17 +119,17 @@ func modelTableRowFromOption(o configModelOption) modelTableRow { } func formatModelThinkingCell(o configModelOption) string { - supports := hawkconfig.ModelCapabilitySupportsThinking(o.Capabilities) - settings := hawkconfig.LoadSettings() - pref := hawkconfig.ThinkingPrefForModel(settings, o.ID) + supports := graycodeconfig.ModelCapabilitySupportsThinking(o.Capabilities) + settings := graycodeconfig.LoadSettings() + pref := graycodeconfig.ThinkingPrefForModel(settings, o.ID) if pref == nil && o.CanonicalID != "" && o.CanonicalID != o.ID { - pref = hawkconfig.ThinkingPrefForModel(settings, o.CanonicalID) + pref = graycodeconfig.ThinkingPrefForModel(settings, o.CanonicalID) } provider := strings.TrimSpace(o.GatewayID) if provider == "" { provider = strings.TrimSpace(o.ProviderID) } - return hawkconfig.FormatModelThinkingLabel(supports, pref, provider) + return graycodeconfig.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 hawkconfig.EngineModel) modelTableRow { +func modelTableRowFromCatalogEntry(m graycodeconfig.EngineModel) modelTableRow { name := strings.TrimSpace(m.DisplayName) if name == "" { name = m.ID @@ -401,7 +401,7 @@ func modelTableRowFromCatalogEntry(m hawkconfig.EngineModel) modelTableRow { Model: name, Provider: owner, Caps: formatModelCapabilities(m.Capabilities), - Think: hawkconfig.FormatModelThinkingLabel(hawkconfig.ModelCapabilitySupportsThinking(m.Capabilities), hawkconfig.ThinkingPrefForModel(hawkconfig.LoadSettings(), m.ID), provider), + Think: graycodeconfig.FormatModelThinkingLabel(graycodeconfig.ModelCapabilitySupportsThinking(m.Capabilities), graycodeconfig.ThinkingPrefForModel(graycodeconfig.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 6e482be4..a7571588 100644 --- a/cmd/model_table_test.go +++ b/cmd/model_table_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/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 hawk settings (per-model thinking + // Isolate from the developer's real graycode 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(hawkconfig.EngineModel{GatewayID: "gateway"}) + entry := modelTableRowFromCatalogEntry(graycodeconfig.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 25d7b036..500ad44b 100644 --- a/cmd/models.go +++ b/cmd/models.go @@ -7,7 +7,7 @@ import ( "strings" "time" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" "github.com/spf13/cobra" ) @@ -20,11 +20,11 @@ var ( var modelsCmd = &cobra.Command{ Use: "models", Short: "Deployment-aware model catalog (via eyrie)", - Long: `Manage the eyrie model catalog used by hawk for models, pricing, and deployment routing. + Long: `Manage the eyrie model catalog used by graycode for models, pricing, and deployment routing. 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.`, +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.`, } var modelsRefreshCmd = &cobra.Command{ @@ -38,7 +38,7 @@ var modelsRefreshCmd = &cobra.Command{ } ctx, cancel := context.WithTimeout(cmd.Context(), 60*time.Second) defer cancel() - summary, err := hawkconfig.RefreshModelCatalogV1WithSettings(ctx, settings) + summary, err := graycodeconfig.RefreshModelCatalogV1WithSettings(ctx, settings) if err != nil { return err } @@ -56,13 +56,13 @@ var modelsStatusCmd = &cobra.Command{ if err != nil { return err } - cmd.Println(hawkconfig.FormatCatalogHealth(hawkconfig.CatalogHealthReport(ctx))) + cmd.Println(graycodeconfig.FormatCatalogHealth(graycodeconfig.CatalogHealthReport(ctx))) cmd.Println() modelName, _ := effectiveModelAndProvider(settings) if len(args) > 0 { modelName = args[0] } - report, err := hawkconfig.DeploymentStatusReportWithSettings(ctx, settings, modelName) + report, err := graycodeconfig.DeploymentStatusReportWithSettings(ctx, settings, modelName) if err != nil { return err } @@ -81,7 +81,7 @@ var modelsRoutingPreviewCmd = &cobra.Command{ return err } modelName := args[0] - out, err := hawkconfig.RoutingPreviewJSONWithSettings(cmd.Context(), settings, modelName) + out, err := graycodeconfig.RoutingPreviewJSONWithSettings(cmd.Context(), settings, modelName) if err != nil { return err } @@ -103,14 +103,14 @@ var modelsListCmd = &cobra.Command{ providerName = args[0] } ctx := cmd.Context() - var models []hawkconfig.EngineModel + var models []graycodeconfig.EngineModel if modelsListLive { if providerName == "" { - return fmt.Errorf("provider required with --live (e.g. hawk models list canopywave --live --json)") + return fmt.Errorf("provider required with --live (e.g. graycode models list canopywave --live --json)") } - models, err = hawkconfig.ListLiveEngineModelsWithSettings(ctx, settings, hawkconfig.ActiveProviderID(providerName)) + models, err = graycodeconfig.ListLiveEngineModelsWithSettings(ctx, settings, graycodeconfig.ActiveProviderID(providerName)) } else { - models, err = hawkconfig.FetchModelsForProviderWithSettings(ctx, settings, providerName) + models, err = graycodeconfig.FetchModelsForProviderWithSettings(ctx, settings, providerName) } if err != nil { return err @@ -137,9 +137,9 @@ var modelsListCmd = &cobra.Command{ }, } -// modelListJSONEntry is Hawk's versioned command-output contract. Keep this +// modelListJSONEntry is Graycode'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`. +// without breaking users that consume `graycode models list --json`. type modelListJSONEntry struct { ID string `json:"id"` InputPricePer1M float64 `json:"input_price_per_1m"` @@ -153,7 +153,7 @@ type modelListJSONEntry struct { LiveMetadata json.RawMessage `json:"live_metadata,omitempty"` } -func modelListJSONEntryFromEngine(m hawkconfig.EngineModel) modelListJSONEntry { +func modelListJSONEntryFromEngine(m graycodeconfig.EngineModel) modelListJSONEntry { return modelListJSONEntry{ ID: m.ID, InputPricePer1M: m.InputPricePer1M, @@ -176,7 +176,7 @@ func validModelLiveMetadata(raw json.RawMessage) json.RawMessage { return append(json.RawMessage(nil), metadata...) } -func marshalModelListJSON(models []hawkconfig.EngineModel, rawOnly, live bool) ([]byte, error) { +func marshalModelListJSON(models []graycodeconfig.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 3d896756..6aeea06c 100644 --- a/cmd/models_test.go +++ b/cmd/models_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" ) func TestMarshalModelListJSONCompatibilityGolden(t *testing.T) { @@ -72,7 +72,7 @@ func TestMarshalModelListJSONCompatibilityGolden(t *testing.T) { } func TestModelListJSONEntryFromEnginePreservesLegacyFieldMapping(t *testing.T) { - model := hawkconfig.EngineModel{ + model := graycodeconfig.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([]hawkconfig.EngineModel{model}, false, false) + out, err := marshalModelListJSON([]graycodeconfig.EngineModel{model}, false, false) if err != nil { t.Fatalf("marshalModelListJSON() error = %v", err) } diff --git a/cmd/notifications.go b/cmd/notifications.go index 95ebd81a..e6a677ae 100644 --- a/cmd/notifications.go +++ b/cmd/notifications.go @@ -15,7 +15,7 @@ func notifyCompletion(duration time.Duration) { return } - msg := "Hawk query completed" + msg := "Graycode 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 "Hawk"`, + `display notification "`+msg+`" with title "Graycode"`, ).Start() case "linux": // Linux: use notify-send if available - _ = exec.CommandContext(context.Background(), "notify-send", "Hawk", msg).Start() + _ = exec.CommandContext(context.Background(), "notify-send", "Graycode", msg).Start() } } diff --git a/cmd/notify.go b/cmd/notify.go index 781140b8..05fbec0c 100644 --- a/cmd/notify.go +++ b/cmd/notify.go @@ -10,7 +10,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) // Notification represents a single notification event. @@ -23,7 +23,7 @@ type Notification struct { Read bool } -// Notifier manages terminal notifications for hawk events. +// Notifier manages terminal notifications for graycode 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('hawk').Show($toast)`, +[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier('graycode').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 9a5f0a07..cb1098a5 100644 --- a/cmd/notify_test.go +++ b/cmd/notify_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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("hawk: running") + n.SetTerminalTitle("graycode: running") n.ClearTitle() } diff --git a/cmd/ocg_live_test.go b/cmd/ocg_live_test.go index 2ec04ee9..90cdf8a1 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 TestLiveOpenCodeGoMiniMaxM3FullHawkPath ./cmd +// OPENCODEGO_API_KEY=... go test -tags=live_test -run TestLiveOpenCodeGoMiniMaxM3FullGraycodePath ./cmd // // Or via `make test-live` in this repo. package cmd @@ -18,12 +18,12 @@ import ( eyriecfg "github.com/GrayCodeAI/eyrie/config" "github.com/GrayCodeAI/eyrie/credentials" "github.com/GrayCodeAI/eyrie/setup" - "github.com/GrayCodeAI/hawk/internal/observability/logger" + "github.com/GrayCodeAI/graycode-cli/internal/observability/logger" ) -func TestLiveOpenCodeGoMiniMaxM3FullHawkPath(t *testing.T) { +func TestLiveOpenCodeGoMiniMaxM3FullGraycodePath(t *testing.T) { if credentials.LookupSecret(context.Background(), "OPENCODEGO_API_KEY") == "" { - t.Skip("OPENCODEGO_API_KEY not configured") // TODO: https://github.com/GrayCodeAI/hawk/issues/29 + t.Skip("OPENCODEGO_API_KEY not configured") // TODO: https://github.com/GrayCodeAI/graycode-cli/issues/29 } settings, err := loadEffectiveSettings() if err != nil { @@ -43,7 +43,7 @@ func TestLiveOpenCodeGoMiniMaxM3FullHawkPath(t *testing.T) { adapter := setup.ConfiguredDeploymentAdapters(eyriecfg.LoadProviderConfig(""))["opencodego"] t.Logf("adapter_type=%T", adapter.Provider) - sess := newHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry) + sess := newGraycodeSession(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 7d0163b0..aafc2bf8 100644 --- a/cmd/options.go +++ b/cmd/options.go @@ -9,20 +9,20 @@ import ( "os" "strings" - 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" + 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" ) func buildSystemPrompt() (string, error) { @@ -70,7 +70,7 @@ func buildSystemPromptWithOptions(includeWorkspaceContext, includeRepoMap bool) modularPrompt = "" } - base := prompt.System() + "\n\n" + hawkconfig.BuildStartupContextWithDirs(addDirs) + base := prompt.System() + "\n\n" + graycodeconfig.BuildStartupContextWithDirs(addDirs) if modularPrompt != "" { base += "\n\n" + modularPrompt } @@ -120,7 +120,7 @@ func buildDeferredWorkspacePromptContext() string { return "" } var sections []string - if deferred := strings.TrimSpace(hawkconfig.BuildDeferredContextWithDirs(addDirs)); deferred != "" { + if deferred := strings.TrimSpace(graycodeconfig.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 := hawkconfig.LoadSettings() + settings := graycodeconfig.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() (hawkconfig.Settings, error) { - return hawkconfig.LoadSettingsWithOverride(settingsFlag) +func loadEffectiveSettings() (graycodeconfig.Settings, error) { + return graycodeconfig.LoadSettingsWithOverride(settingsFlag) } -func resolveSelection(settings hawkconfig.Settings) hawkconfig.Selection { - return hawkconfig.EffectiveSelectionWithSettings(context.Background(), settings, hawkconfig.SelectionOptions{ +func resolveSelection(settings graycodeconfig.Settings) graycodeconfig.Selection { + return graycodeconfig.EffectiveSelectionWithSettings(context.Background(), settings, graycodeconfig.SelectionOptions{ ProviderOverride: firstNonEmptyTrimmed(provider, settings.Provider), ModelOverride: firstNonEmptyTrimmed(model, settings.Model), }) } -func startupSelection(settings hawkconfig.Settings) hawkconfig.Selection { +func startupSelection(settings graycodeconfig.Settings) graycodeconfig.Selection { providerOverride := firstNonEmptyTrimmed(provider, settings.Provider) modelOverride := firstNonEmptyTrimmed(model, settings.Model) explicitProvider, explicitModel := explicitSelection(context.Background()) - providerID := hawkconfig.ActiveProviderID(firstNonEmptyTrimmed(providerOverride, explicitProvider)) + providerID := graycodeconfig.ActiveProviderID(firstNonEmptyTrimmed(providerOverride, explicitProvider)) modelID := strings.TrimSpace(firstNonEmptyTrimmed(modelOverride, explicitModel)) if providerID == "" && modelID != "" { - providerID = hawkconfig.ActiveProviderID(hawkconfig.ProviderOfModelWithSettings(settings, modelID)) + providerID = graycodeconfig.ActiveProviderID(graycodeconfig.ProviderOfModelWithSettings(settings, modelID)) } if modelID == "" && providerID != "" { - modelID = strings.TrimSpace(hawkconfig.DefaultModelForProviderWithSettings(settings, providerID)) + modelID = strings.TrimSpace(graycodeconfig.DefaultModelForProviderWithSettings(settings, providerID)) } if providerID == "" { providerID = startupPlaceholderProvider } - return hawkconfig.Selection{ + return graycodeconfig.Selection{ Provider: providerID, Model: modelID, } } -func effectiveModelAndProvider(settings hawkconfig.Settings) (string, string) { +func effectiveModelAndProvider(settings graycodeconfig.Settings) (string, string) { selection := resolveSelection(settings) if !selection.HasConfiguredDeployment { return "", "" @@ -227,7 +227,7 @@ func effectiveModelAndProvider(settings hawkconfig.Settings) (string, string) { return selection.Model, selection.Provider } -func newStartupHawkSession(selection hawkconfig.Selection, systemPrompt string, registry *tool.Registry) *engine.Session { +func newStartupGraycodeSession(selection graycodeconfig.Selection, systemPrompt string, registry *tool.Registry) *engine.Session { providerID := strings.TrimSpace(selection.Provider) if providerID == "" { providerID = startupPlaceholderProvider @@ -235,7 +235,7 @@ func newStartupHawkSession(selection hawkconfig.Selection, systemPrompt string, return engine.NewSession(providerID, strings.TrimSpace(selection.Model), systemPrompt, registry) } -func newHawkSession(settings hawkconfig.Settings, effectiveProvider, effectiveModel, systemPrompt string, registry *tool.Registry) *engine.Session { +func newGraycodeSession(settings graycodeconfig.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 newHawkSession(settings hawkconfig.Settings, effectiveProvider, effectiveMo if strings.TrimSpace(selection.Model) == "" { selection.Model = effectiveModel } - 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 + 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 // container remains fail-closed at the engine tool boundary. sess.SetContainerRequired(true) return sess } -// newConfiguredHawkSession is the non-interactive command composition root. +// newConfiguredGraycodeSession 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 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) +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) if sessionLogger != nil { sess.SetLogger(sessionLogger) } @@ -264,11 +264,11 @@ func newConfiguredHawkSession(settings hawkconfig.Settings, effectiveProvider, e return sess, nil } -// newConfiguredHawkSessionFactory is the shared composition seam for +// newConfiguredGraycodeSessionFactory 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 newConfiguredHawkSessionFactory(settings hawkconfig.Settings, sessionLogger *logger.Logger) func(string, string, ...int) (*engine.Session, error) { +func newConfiguredGraycodeSessionFactory(settings graycodeconfig.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 newConfiguredHawkSessionFactory(settings hawkconfig.Settings, sessionLogger if strings.TrimSpace(modelOverride) != "" { effectiveModel = modelOverride } - return newConfiguredHawkSession(settings, effectiveProvider, effectiveModel, systemPrompt, registry, sessionLogger, maxTurnsOverride...) + return newConfiguredGraycodeSession(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 hawkconfig.Settings) error { +func prepareInteractiveSessionStartup(sess *engine.Session, settings graycodeconfig.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 hawkconfig.Settings, maxTurnsOverride ...int) error { +func configureSession(sess *engine.Session, settings graycodeconfig.Settings, maxTurnsOverride ...int) error { if err := configureSessionStartup(sess, settings, maxTurnsOverride...); err != nil { return err } @@ -308,7 +308,7 @@ func configureSession(sess *engine.Session, settings hawkconfig.Settings, maxTur return nil } -func configureSessionStartup(sess *engine.Session, settings hawkconfig.Settings, maxTurnsOverride ...int) error { +func configureSessionStartup(sess *engine.Session, settings graycodeconfig.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 hawkconfig.Settings, } // Model cascade router: automatically routes tasks to optimal model tier - roles := hawkmodel.DefaultRoles(sess.Model()) + roles := graycodemodel.DefaultRoles(sess.Model()) if settings.ModelRoles != nil { roles = *settings.ModelRoles } @@ -420,7 +420,7 @@ func configureSessionStartup(sess *engine.Session, settings hawkconfig.Settings, // provider-specific defaults (e.g. LongCat off). modelID := strings.TrimSpace(sess.Model()) providerID := strings.TrimSpace(sess.Provider()) - sess.SetThinkingEnabled(hawkconfig.ResolveThinkingForModel(settings, modelID, providerID)) + sess.SetThinkingEnabled(graycodeconfig.ResolveThinkingForModel(settings, modelID, providerID)) return nil } diff --git a/cmd/options_test.go b/cmd/options_test.go index 1ff3f35c..33149f03 100644 --- a/cmd/options_test.go +++ b/cmd/options_test.go @@ -3,7 +3,7 @@ package cmd import ( "testing" - "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/graycode-cli/internal/tool" ) func TestValidateRootFlagsRejectsInvalidSandbox(t *testing.T) { diff --git a/cmd/options_welcome_test.go b/cmd/options_welcome_test.go index b003a1d8..ed959c14 100644 --- a/cmd/options_welcome_test.go +++ b/cmd/options_welcome_test.go @@ -7,65 +7,65 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) func isolateCredentialHome(t *testing.T) { t.Helper() home := t.TempDir() - hawkDir := filepath.Join(home, ".hawk") - _ = os.MkdirAll(hawkDir, 0o700) + graycodeDir := filepath.Join(home, ".graycode") + _ = os.MkdirAll(graycodeDir, 0o700) t.Setenv("HOME", home) - t.Setenv("HAWK_CONFIG_DIR", hawkDir) + t.Setenv("GRAYCODE_CONFIG_DIR", graycodeDir) t.Setenv("EYRIE_CONFIG_DIR", filepath.Join(home, "eyrie")) } func TestEffectiveModelAndProvider_ClearsWithoutCredentials(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) ctx := context.Background() - if err := hawkconfig.SetActiveProvider(ctx, "openrouter"); err != nil { + if err := graycodeconfig.SetActiveProvider(ctx, "openrouter"); err != nil { t.Fatal(err) } - if err := hawkconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { + if err := graycodeconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { t.Fatal(err) } - model, provider := effectiveModelAndProvider(hawkconfig.Settings{}) + model, provider := effectiveModelAndProvider(graycodeconfig.Settings{}) if model != "" || provider != "" { t.Fatalf("expected empty selection without credentials, got model=%q provider=%q", model, provider) } } func TestEffectiveModelAndProvider_KeepsWithCredentials(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) ctx := context.Background() _ = store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890") - hawkconfig.InvalidateConfigUICache() - if err := hawkconfig.SetActiveProvider(ctx, "openrouter"); err != nil { + graycodeconfig.InvalidateConfigUICache() + if err := graycodeconfig.SetActiveProvider(ctx, "openrouter"); err != nil { t.Fatal(err) } - if err := hawkconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { + if err := graycodeconfig.SetActiveModel(ctx, "gpt-4o"); err != nil { t.Fatal(err) } - model, provider := effectiveModelAndProvider(hawkconfig.Settings{}) + model, provider := effectiveModelAndProvider(graycodeconfig.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 a923979a..0df35572 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("HAWK_PAGER"); v != "" { + if v := os.Getenv("GRAYCODE_PAGER"); v != "" { if v == "cat" || v == "none" { return "" } diff --git a/cmd/path.go b/cmd/path.go index cbc78831..5b8bb79b 100644 --- a/cmd/path.go +++ b/cmd/path.go @@ -5,7 +5,7 @@ import ( "encoding/json" "fmt" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" "github.com/spf13/cobra" ) @@ -17,7 +17,7 @@ var ( var pathCmd = &cobra.Command{ Use: "path", Short: "Developer path readiness (setup, security, sandbox, ecosystem)", - Long: `Check whether hawk is configured on the developer path: + Long: `Check whether graycode is configured on the developer path: API keys in OS secret store, model selected, no secrets on disk, mandatory Docker isolation, and eyrie/harrier/shrike integration. @@ -26,7 +26,7 @@ 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 := hawkconfig.EvaluateDeveloperPath(ctx) + report := graycodeconfig.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(hawkconfig.FormatDeveloperPathReport(ctx)) + cmd.Println(graycodeconfig.FormatDeveloperPathReport(ctx)) if pathStrict { for _, c := range report.Checks { - if c.Section == "Sandbox" && c.Name == "docker" && c.Status == hawkconfig.PathWarn { + if c.Section == "Sandbox" && c.Name == "docker" && c.Status == graycodeconfig.PathWarn { return fmt.Errorf("strict mode: start Docker for isolated Bash") } } diff --git a/cmd/path_test.go b/cmd/path_test.go index ae0d3dc8..f56299c7 100644 --- a/cmd/path_test.go +++ b/cmd/path_test.go @@ -5,20 +5,20 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/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/hawk/issues/30 + t.Skip("machine has full developer path setup") // TODO: https://github.com/GrayCodeAI/graycode-cli/issues/30 } } func TestPathCmdPrintsReport(t *testing.T) { useInMemoryCredentials(t) - out := hawkconfig.FormatDeveloperPathReport(context.Background()) + out := graycodeconfig.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 f3a54157..58f3f65f 100644 --- a/cmd/permissions.go +++ b/cmd/permissions.go @@ -7,8 +7,8 @@ import ( "strconv" "strings" - "github.com/GrayCodeAI/hawk/internal/permissions" - "github.com/GrayCodeAI/hawk/internal/permissions/stableid" + "github.com/GrayCodeAI/graycode-cli/internal/permissions" + "github.com/GrayCodeAI/graycode-cli/internal/permissions/stableid" "github.com/spf13/cobra" ) diff --git a/cmd/permissions_center.go b/cmd/permissions_center.go index fab0e5d3..46c9fa01 100644 --- a/cmd/permissions_center.go +++ b/cmd/permissions_center.go @@ -7,9 +7,9 @@ import ( "time" tea "charm.land/bubbletea/v2" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/sandbox" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/sandbox" ) const defaultPermissionSandbox = "workspace" @@ -87,7 +87,7 @@ func normalizePermissionSandbox(raw string) (string, string, bool) { } } -func effectivePermissionSandbox(settings hawkconfig.Settings) string { +func effectivePermissionSandbox(settings graycodeconfig.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 hawkconfig.Settings) []string { +func effectiveAllowRules(settings graycodeconfig.Settings) []string { var rules []string rules = append(rules, settings.AutoAllow...) rules = append(rules, settings.AllowedTools...) @@ -305,7 +305,7 @@ func effectiveAllowRules(settings hawkconfig.Settings) []string { return dedupeStrings(rules) } -func effectiveDenyRules(settings hawkconfig.Settings) []string { +func effectiveDenyRules(settings graycodeconfig.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 hawkconfig.Settings) { +func rebuildSessionPermissionRules(sess *engine.Session, settings graycodeconfig.Settings) { if sess == nil { return } @@ -356,7 +356,7 @@ func rebuildSessionPermissionRules(sess *engine.Session, settings hawkconfig.Set } } -func savePermissionSettings(scope string, settings hawkconfig.Settings, level engine.AutonomyLevel) (string, error) { +func savePermissionSettings(scope string, settings graycodeconfig.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 hawkconfig.Settings, level en case "project": return "", fmt.Errorf("project-local settings writes are disabled; use scope \"global\" or an explicit --settings file") case "global": - target := hawkconfig.LoadGlobalSettings() + target := graycodeconfig.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 := hawkconfig.SaveGlobal(target); err != nil { + if err := graycodeconfig.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 07d1f40c..df68abaf 100644 --- a/cmd/permissions_center_test.go +++ b/cmd/permissions_center_test.go @@ -4,8 +4,8 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) func TestNormalizePermissionTier(t *testing.T) { @@ -34,7 +34,7 @@ func TestNormalizePermissionSandbox(t *testing.T) { } func TestEffectivePermissionRules(t *testing.T) { - settings := hawkconfig.Settings{ + settings := graycodeconfig.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: hawkconfig.Settings{ + settings: graycodeconfig.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: hawkconfig.Settings{ + settings: graycodeconfig.Settings{ Autonomy: permissionTierSettingValue(engine.AutonomyYOLO), Sandbox: "strict", AutoAllow: []string{"Read"}, diff --git a/cmd/plan.go b/cmd/plan.go index 724f23a3..ec7496d7 100644 --- a/cmd/plan.go +++ b/cmd/plan.go @@ -8,8 +8,8 @@ import ( "strconv" "strings" - "github.com/GrayCodeAI/hawk/internal/intelligence/planner" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/intelligence/planner" + "github.com/GrayCodeAI/graycode-cli/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 Hawk's user state directory, partitioned by project. +Plans are stored in Graycode's user state directory, partitioned by project. Subcommands: create Create a new plan (generates a plan prompt) @@ -44,7 +44,7 @@ var planCreateCmd = &cobra.Command{ cmd.Println("--- User ---") cmd.Println(prompt.User) cmd.Println() - cmd.Println("Once you have the LLM response, save it with an explicit output path or import it into Hawk plans.") + cmd.Println("Once you have the LLM response, save it with an explicit output path or import it into Graycode plans.") return nil }, } @@ -60,7 +60,7 @@ var planListCmd = &cobra.Command{ if planJSON { fmt.Println("[]") } else { - cmd.Println("No plans found. Create one with: hawk plan create ") + cmd.Println("No plans found. Create one with: graycode plan create ") } return nil } @@ -90,7 +90,7 @@ var planListCmd = &cobra.Command{ } if len(plans) == 0 { - cmd.Println("No plans found. Create one with: hawk plan create ") + cmd.Println("No plans found. Create one with: graycode plan create ") return nil } @@ -141,10 +141,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 Hawk's user state directory for the current project. +recently modified plan in Graycode'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: hawk plan done `, +followed by the task ID: graycode plan done `, Args: cobra.RangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { var planPath string @@ -198,7 +198,7 @@ func init() { planShowCmd.Flags().BoolVar(&planJSON, "json", false, "output plan as JSON") } -// resolvePlanPath converts a plan name to a file path in Hawk's user state dir. +// resolvePlanPath converts a plan name to a file path in Graycode'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 b2312973..29d2c49c 100644 --- a/cmd/plugin_dynamic.go +++ b/cmd/plugin_dynamic.go @@ -8,7 +8,7 @@ import ( "text/tabwriter" "time" - "github.com/GrayCodeAI/hawk/internal/plugin" + "github.com/GrayCodeAI/graycode-cli/internal/plugin" "github.com/spf13/cobra" ) @@ -75,7 +75,7 @@ var pluginStatusCmd = &cobra.Command{ statuses := dm.Status() if len(statuses) == 0 { - cmd.Println("No plugins discovered. Run 'hawk plugin install' to add plugins.") + cmd.Println("No plugins discovered. Run 'graycode plugin install' to add plugins.") return nil } @@ -195,7 +195,7 @@ var pluginCreateCmd = &cobra.Command{ manifest := &plugin.ManifestV2{ Name: name, Version: "0.1.0", - Description: fmt.Sprintf("A hawk plugin: %s", name), + Description: fmt.Sprintf("A graycode plugin: %s", name), Author: "", Mode: "subprocess", Tools: []plugin.ManifestTool{ @@ -267,12 +267,12 @@ func main() { // Write README.md readme := fmt.Sprintf(`# %s -A hawk plugin. +A graycode plugin. ## Installation `+"```bash"+` -hawk plugin install ./%s +graycode plugin install ./%s `+"```"+` ## Usage @@ -325,8 +325,8 @@ See `+"`plugin.json`"+` for the full manifest configuration. cmd.Println() cmd.Printf("Next steps:\n") cmd.Printf(" cd %s && go mod init %s\n", name, name) - cmd.Printf(" hawk plugin install ./%s\n", name) - cmd.Printf(" hawk plugin activate %s\n", name) + cmd.Printf(" graycode plugin install ./%s\n", name) + cmd.Printf(" graycode plugin activate %s\n", name) return nil }, } @@ -408,11 +408,11 @@ var pluginMarketplaceListCmd = &cobra.Command{ mc := plugin.NewMarketplaceClient() entries, err := mc.FetchAll() if err != nil { - return fmt.Errorf("fetch marketplace: %w (indexes may be unpublished; add a source with hawk plugin marketplace add)", err) + return fmt.Errorf("fetch marketplace: %w (indexes may be unpublished; add a source with graycode plugin marketplace add)", err) } if len(entries) == 0 { cmd.Println("No marketplace plugins found.") - cmd.Println("Add a source: hawk plugin marketplace add ") + cmd.Println("Add a source: graycode plugin marketplace add ") return nil } w := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 0, 2, ' ', 0) diff --git a/cmd/power.go b/cmd/power.go index 178d5b04..1b29e2fb 100644 --- a/cmd/power.go +++ b/cmd/power.go @@ -3,8 +3,8 @@ package cmd import ( "fmt" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/provider/routing" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 54e40d82..106e9534 100644 --- a/cmd/pr.go +++ b/cmd/pr.go @@ -30,10 +30,10 @@ Subcommands: describe Generate or update a PR description Examples: - hawk pr review - hawk pr review 42 --post-comments - hawk pr create --base develop --draft - hawk pr describe 42 --update`, + graycode pr review + graycode pr review 42 --post-comments + graycode pr create --base develop --draft + graycode pr describe 42 --update`, } var prReviewCmd = &cobra.Command{ diff --git a/cmd/preflight_test.go b/cmd/preflight_test.go index c1479d61..37cee90d 100644 --- a/cmd/preflight_test.go +++ b/cmd/preflight_test.go @@ -23,7 +23,7 @@ type preflightCheckShape struct { func TestPreflightJSON_Structure(t *testing.T) { dir := t.TempDir() - t.Setenv("HAWK_STATE_DIR", filepath.Join(dir, "state")) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", filepath.Join(dir, "state")) + t.Setenv("GRAYCODE_STATE_DIR", filepath.Join(dir, "state")) old := preflightJSON oldLive := preflightLiveFlag diff --git a/cmd/progress_debug_test.go b/cmd/progress_debug_test.go index 0ac60e57..c26d869d 100644 --- a/cmd/progress_debug_test.go +++ b/cmd/progress_debug_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) func TestZDebugProgress(t *testing.T) { diff --git a/cmd/progress_tracker.go b/cmd/progress_tracker.go index 3cbb70e3..b68e43c2 100644 --- a/cmd/progress_tracker.go +++ b/cmd/progress_tracker.go @@ -6,7 +6,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 a3825cd1..f5c500a4 100644 --- a/cmd/progress_tracker_test.go +++ b/cmd/progress_tracker_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) func TestNewProgressTracker(t *testing.T) { diff --git a/cmd/progressive_disclosure.go b/cmd/progressive_disclosure.go index ee735b8b..fd82b67f 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 hawk + return `Getting Started with graycode - Type your question or task and hawk will help. - hawk reads your project and understands your code. + Type your question or task and graycode will help. + graycode 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 hawk review your code + /review Have graycode review your code /clear Start a fresh conversation Tips: - Just describe what you want in plain English - - hawk will read files, run commands, and make changes + - graycode 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 `hawk Commands + return `graycode 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 `hawk Full Command Reference + return `graycode Full Command Reference Workflow: /test [cmd] Run tests (default: go test ./...) diff --git a/cmd/prompt_input.go b/cmd/prompt_input.go index 12b7a569..674b531b 100644 --- a/cmd/prompt_input.go +++ b/cmd/prompt_input.go @@ -7,7 +7,7 @@ import ( "os" "strings" - "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) var errNoInteractivePromptInput = errors.New("no interactive terminal available for permission prompt") diff --git a/cmd/record.go b/cmd/record.go index 88e80d6b..ebc49429 100644 --- a/cmd/record.go +++ b/cmd/record.go @@ -4,7 +4,7 @@ import ( "io" "os" - "github.com/GrayCodeAI/hawk/internal/terminal/tape" + "github.com/GrayCodeAI/graycode-cli/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 7ee56f42..2014620c 100644 --- a/cmd/record_test.go +++ b/cmd/record_test.go @@ -6,7 +6,7 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/hawk/internal/terminal/tape" + "github.com/GrayCodeAI/graycode-cli/internal/terminal/tape" ) func TestStartRecordingCapturesOutput(t *testing.T) { diff --git a/cmd/replay.go b/cmd/replay.go index 3fde35d8..5aeb92d9 100644 --- a/cmd/replay.go +++ b/cmd/replay.go @@ -8,7 +8,7 @@ import ( "github.com/spf13/cobra" - "github.com/GrayCodeAI/hawk/internal/terminal/tape" + "github.com/GrayCodeAI/graycode-cli/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 hawk's tape package reads +binary fxtape format from vercel-labs/fx, which graycode'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 33482420..cc776dce 100644 --- a/cmd/resize_unix.go +++ b/cmd/resize_unix.go @@ -7,7 +7,7 @@ import ( "os/signal" "syscall" - "github.com/GrayCodeAI/hawk/internal/terminal/tape" + "github.com/GrayCodeAI/graycode-cli/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 11038a9c..b50bb0f7 100644 --- a/cmd/resize_windows.go +++ b/cmd/resize_windows.go @@ -2,7 +2,7 @@ package cmd -import "github.com/GrayCodeAI/hawk/internal/terminal/tape" +import "github.com/GrayCodeAI/graycode-cli/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 e7b12d4c..2a9e031e 100644 --- a/cmd/review.go +++ b/cmd/review.go @@ -10,17 +10,17 @@ import ( "github.com/spf13/cobra" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) var reviewCmd = &cobra.Command{ Use: "review", Short: "Continuous AI code review on commits", - Long: `hawk review provides continuous background code review. + Long: `graycode review provides continuous background code review. -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'.`, +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'.`, } var reviewInitCmd = &cobra.Command{ @@ -38,10 +38,10 @@ func init() { } const hookScript = `#!/bin/sh -# hawk review — continuous code review hook -# Installed by 'hawk review init' +# graycode review — continuous code review hook +# Installed by 'graycode review init' SHA=$(git rev-parse HEAD) -hawk review run "$SHA" --background & +graycode 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), "hawk review") { - fmt.Println(icons.CheckBold() + " hawk review hook already installed") + if strings.Contains(string(existing), "graycode review") { + fmt.Println(icons.CheckBold() + " graycode review hook already installed") 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.Printf("%s Installed post-commit hook at %s\n", icons.CheckBold(), hookPath) fmt.Println(" Every commit will now be reviewed automatically.") - fmt.Println(" View reviews: hawk review status") - fmt.Println(" Interactive: hawk review tui") + fmt.Println(" View reviews: graycode review status") + fmt.Println(" Interactive: graycode review tui") return nil } diff --git a/cmd/review_analyze.go b/cmd/review_analyze.go index 2662dc0a..0d0a5706 100644 --- a/cmd/review_analyze.go +++ b/cmd/review_analyze.go @@ -8,11 +8,11 @@ import ( "strings" "time" - reviewcontracts "github.com/GrayCodeAI/eagle/review" - hawkKestrel "github.com/GrayCodeAI/hawk/internal/bridge/kestrel" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + 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" kestrelLib "github.com/GrayCodeAI/kestrel" "github.com/spf13/cobra" ) @@ -38,9 +38,9 @@ Types: test-fixtures Find test helper opportunities Examples: - hawk review analyze security ./... - hawk review analyze complexity --fix main.go - hawk review analyze duplication ./internal/...`, + graycode review analyze security ./... + graycode review analyze complexity --fix main.go + graycode 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 Hawk's Eyrie engine boundary. + // Build the Kestrel bridge through Graycode's Eyrie engine boundary. ctx := context.Background() - selection := hawkconfig.EffectiveSelection(ctx, hawkconfig.SelectionOptions{ + selection := graycodeconfig.EffectiveSelection(ctx, graycodeconfig.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 := hawkKestrel.NewBridge(chatProvider, providerID, opts...) + bridge := graycodeKestrel.NewBridge(chatProvider, providerID, opts...) if !bridge.Ready() { return fmt.Errorf("kestrel bridge not ready (check API key)") } @@ -245,12 +245,12 @@ func autoFixAnalysis(result *reviewcontracts.Result) error { } b.WriteString("\nApply minimal, focused fixes. Commit with 'fix: address analysis findings'.") - hawkBin, err := os.Executable() + graycodeBin, err := os.Executable() if err != nil { - hawkBin = "hawk" + graycodeBin = "graycode" } - 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 := 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.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() diff --git a/cmd/review_fix.go b/cmd/review_fix.go index de8b40cd..67a7a2cc 100644 --- a/cmd/review_fix.go +++ b/cmd/review_fix.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/cobra" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) var ( @@ -21,8 +21,8 @@ var ( var reviewFixCmd = &cobra.Command{ Use: "fix [id...]", - Short: "Auto-fix review findings using hawk exec", - Long: `Feeds review findings to hawk's engine which applies fixes and commits. + Short: "Auto-fix review findings using graycode exec", + Long: `Feeds review findings to graycode's engine which applies fixes and commits. Without arguments, fixes all open reviews. Specify IDs to fix specific ones.`, RunE: runReviewFix, } @@ -81,7 +81,7 @@ func fixReview(store *ReviewStore, r *ReviewRecord) error { prompt := buildFixPrompt(r) - // Invoke hawk exec with the fix prompt. + // Invoke graycode exec with the fix prompt. execArgs := []string{"exec", "--auto", "full"} if reviewFixWorktree { execArgs = append(execArgs, "--worktree") @@ -91,18 +91,18 @@ func fixReview(store *ReviewStore, r *ReviewRecord) error { } execArgs = append(execArgs, prompt) - hawkBin, err := os.Executable() + graycodeBin, err := os.Executable() if err != nil { - hawkBin = "hawk" + graycodeBin = "graycode" } - cmd := exec.CommandContext(context.Background(), hawkBin, execArgs...) // #nosec G204 -- hawkBin resolved via os.Executable() or literal 'hawk'; args are internal flags + cmd := exec.CommandContext(context.Background(), graycodeBin, execArgs...) // #nosec G204 -- graycodeBin resolved via os.Executable() or literal 'graycode'; 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("hawk exec: %w", err) + return fmt.Errorf("graycode exec: %w", err) } // Mark as fixed. diff --git a/cmd/review_read.go b/cmd/review_read.go index 17957c5a..f9e996db 100644 --- a/cmd/review_read.go +++ b/cmd/review_read.go @@ -10,7 +10,7 @@ import ( lipgloss "charm.land/lipgloss/v2" "github.com/spf13/cobra" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) var reviewStatusCmd = &cobra.Command{ @@ -71,7 +71,7 @@ func runReviewStatus(_ *cobra.Command, _ []string) error { total += v } if total == 0 { - fmt.Println("No reviews yet. Run 'hawk review init' to start.") + fmt.Println("No reviews yet. Run 'graycode review init' to start.") return nil } diff --git a/cmd/review_refine.go b/cmd/review_refine.go index 7894deff..9bd6431a 100644 --- a/cmd/review_refine.go +++ b/cmd/review_refine.go @@ -10,7 +10,7 @@ import ( "github.com/spf13/cobra" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) var ( @@ -129,7 +129,7 @@ func runReviewRefine(_ *cobra.Command, args []string) error { } if len(remaining) > 0 { fmt.Printf("\n%s %d review(s) still open after %d iterations.\n", icons.Alert(), len(remaining), refineMaxIter) - fmt.Println(" Run 'hawk review show' to inspect, or increase --max-iterations.") + fmt.Println(" Run 'graycode review show' to inspect, or increase --max-iterations.") } return nil } @@ -137,9 +137,9 @@ func runReviewRefine(_ *cobra.Command, args []string) error { func fixReviewRefine(store *ReviewStore, r *ReviewRecord) error { prompt := buildFixPrompt(r) - hawkBin, err := os.Executable() + graycodeBin, err := os.Executable() if err != nil { - hawkBin = "hawk" + graycodeBin = "graycode" } 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(), hawkBin, execArgs...) // #nosec G204 -- hawkBin resolved via os.Executable() or literal 'hawk'; args are internal flags + cmd := exec.CommandContext(context.Background(), graycodeBin, execArgs...) // #nosec G204 -- graycodeBin resolved via os.Executable() or literal 'graycode'; 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 { - hawkBin, err := os.Executable() + graycodeBin, err := os.Executable() if err != nil { - hawkBin = "hawk" + graycodeBin = "graycode" } 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(), hawkBin, args...) // #nosec G204 -- hawkBin resolved via os.Executable() or literal 'hawk'; args are internal flags + cmd := exec.CommandContext(context.Background(), graycodeBin, args...) // #nosec G204 -- graycodeBin resolved via os.Executable() or literal 'graycode'; 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 2c31ce3b..2b7e0700 100644 --- a/cmd/review_run.go +++ b/cmd/review_run.go @@ -8,11 +8,11 @@ import ( "strings" "time" - reviewcontracts "github.com/GrayCodeAI/eagle/review" - hawkKestrel "github.com/GrayCodeAI/hawk/internal/bridge/kestrel" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + 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" kestrelLib "github.com/GrayCodeAI/kestrel" "github.com/spf13/cobra" ) @@ -96,9 +96,9 @@ func runReviewRun(_ *cobra.Command, args []string) error { return nil } - // Build the Kestrel bridge through Hawk's Eyrie engine boundary. + // Build the Kestrel bridge through Graycode's Eyrie engine boundary. ctx := context.Background() - selection := hawkconfig.EffectiveSelection(ctx, hawkconfig.SelectionOptions{ + selection := graycodeconfig.EffectiveSelection(ctx, graycodeconfig.SelectionOptions{ ProviderOverride: strings.TrimSpace(provider), ModelOverride: strings.TrimSpace(reviewRunModel), }) @@ -122,7 +122,7 @@ func runReviewRun(_ *cobra.Command, args []string) error { opts = append(opts, kestrelLib.WithConcerns(concerns...)) } - bridge := hawkKestrel.NewBridge(chatProvider, providerID, opts...) + bridge := graycodeKestrel.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 ffa3bfa6..7e1f4a35 100644 --- a/cmd/review_run_test.go +++ b/cmd/review_run_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - reviewcontracts "github.com/GrayCodeAI/eagle/review" - contracts "github.com/GrayCodeAI/eagle/types" + reviewcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/review" + contracts "github.com/GrayCodeAI/graycode-cli/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 55c3cc16..71ac9402 100644 --- a/cmd/review_store.go +++ b/cmd/review_store.go @@ -10,8 +10,8 @@ import ( "sync" "time" - reviewcontracts "github.com/GrayCodeAI/eagle/review" - "github.com/GrayCodeAI/hawk/internal/storage" + reviewcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/review" + "github.com/GrayCodeAI/graycode-cli/internal/storage" ) // ReviewStatus represents the state of a review. diff --git a/cmd/review_test.go b/cmd/review_test.go index 5de945c3..cfd4c4ce 100644 --- a/cmd/review_test.go +++ b/cmd/review_test.go @@ -6,11 +6,11 @@ import ( "strings" "testing" - reviewcontracts "github.com/GrayCodeAI/eagle/review" - contracts "github.com/GrayCodeAI/eagle/types" + reviewcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/review" + contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" - "github.com/GrayCodeAI/hawk/internal/storage" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/graycode-cli/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, ".hawk"), 0o755) + os.MkdirAll(filepath.Join(dir, ".graycode"), 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, ".hawk"), 0o755) + os.MkdirAll(filepath.Join(dir, ".graycode"), 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, ".hawk"), 0o755) + os.MkdirAll(filepath.Join(dir, ".graycode"), 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, ".hawk"), 0o755) + os.MkdirAll(filepath.Join(dir, ".graycode"), 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, ".hawk"), 0o755) + os.MkdirAll(filepath.Join(dir, ".graycode"), 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, ".hawk"), 0o755) + os.MkdirAll(filepath.Join(dir, ".graycode"), 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, ".hawk"), 0o755) + os.MkdirAll(filepath.Join(dir, ".graycode"), 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, ".hawk"), 0o755) + os.MkdirAll(filepath.Join(dir, ".graycode"), 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, ".hawk"), 0o755) + os.MkdirAll(filepath.Join(dir, ".graycode"), 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, ".hawk"), 0o755) + os.MkdirAll(filepath.Join(dir, ".graycode"), 0o755) store, err := OpenReviewStore(dir) if err != nil { @@ -365,9 +365,9 @@ func TestAnalysisPrompts_AllTypesExist(t *testing.T) { } } -func TestHookScript_ContainsHawkReview(t *testing.T) { - if !strings.Contains(hookScript, "hawk review") { - t.Error("hook script should contain 'hawk review'") +func TestHookScript_ContainsGraycodeReview(t *testing.T) { + if !strings.Contains(hookScript, "graycode review") { + t.Error("hook script should contain 'graycode 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 e25e8492..e395dee0 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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(" hawk review") + dim.Render(" j/k:nav enter:expand c:close f:fix r:refresh q:quit") + "\n") + b.WriteString(header.Render(" graycode 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 'hawk review init' to get started.\n") + b.WriteString("\n No reviews yet. Run 'graycode 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 2b08c511..a7ad6b1e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -10,14 +10,14 @@ import ( "strings" "time" - 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" + 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" "github.com/spf13/cobra" ) @@ -87,33 +87,33 @@ func SetBuildDate(d string) { } func registeredProviderCount() int { - return hawkconfig.RegisteredProviderCount() + return graycodeconfig.RegisteredProviderCount() } var rootCmd = &cobra.Command{ - Use: "hawk [prompt]", + Use: "graycode [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. + Long: fmt.Sprintf(`graycode is an AI coding agent that reads, writes, and runs code in your terminal. 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: - 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 + 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 API keys are stored in the OS keychain (macOS Keychain / Linux keyring). -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`, +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`, Args: cobra.ArbitraryArgs, SilenceUsage: true, SilenceErrors: true, @@ -147,10 +147,10 @@ Run hawk and use /config to set up your first provider.`, registeredProviderCoun if printMode || promptFlag != "" || inputFormat == "stream-json" || replFlag || watchFlag { // Credential migration is deferred until a path that actually - // uses credentials: `hawk path`, `hawk version`, auto-skill and + // uses credentials: `graycode path`, `graycode version`, auto-skill and // other cold commands no longer construct the eyrie engine // (M17 — was ~1.8s on every root command). - logMigrateProviderSecretsError(logger.Default(), hawkconfig.MigrateProviderSecrets()) + logMigrateProviderSecretsError(logger.Default(), graycodeconfig.MigrateProviderSecrets()) if promptFlag == "" && !replFlag && !watchFlag { stdinPrompt, err := readPromptFromStdin(inputFormat) if err != nil { @@ -169,7 +169,7 @@ Run hawk and use /config to set up your first provider.`, registeredProviderCoun // 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 'hawk trust add' to trust this folder before running hawk", tr.Path) + 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) } if replFlag { return runRepl() @@ -205,11 +205,11 @@ Run hawk and use /config to set up your first provider.`, registeredProviderCoun } // TUI path uses credentials — run the one-time hygiene pass here. - logMigrateProviderSecretsError(logger.Default(), hawkconfig.MigrateProviderSecrets()) + logMigrateProviderSecretsError(logger.Default(), graycodeconfig.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 'hawk trust add' to trust this folder before starting hawk", tr.Path) + 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) } // Launch TUI — use /config to set API keys; eyrie supplies providers and models @@ -309,7 +309,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 -// HAWK_DANGEROUSLY_SKIP_PERMISSIONS=1 environment variable. +// GRAYCODE_DANGEROUSLY_SKIP_PERMISSIONS=1 environment variable. func confirmDangerousSkipPermissions() error { if isStdinTerminal() { fmt.Fprintf(os.Stderr, "Type %s to confirm skipping permission prompts: ", dangerSkipConfirmToken) @@ -324,8 +324,8 @@ func confirmDangerousSkipPermissions() error { return nil } // Non-interactive: require explicit env var override. - if os.Getenv("HAWK_DANGEROUSLY_SKIP_PERMISSIONS") != "1" { - return fmt.Errorf("--dangerously-skip-permissions requires HAWK_DANGEROUSLY_SKIP_PERMISSIONS=1 in non-interactive mode") + if os.Getenv("GRAYCODE_DANGEROUSLY_SKIP_PERMISSIONS") != "1" { + return fmt.Errorf("--dangerously-skip-permissions requires GRAYCODE_DANGEROUSLY_SKIP_PERMISSIONS=1 in non-interactive mode") } return nil } @@ -349,31 +349,31 @@ var completionCmd = &cobra.Command{ Long: `To load completions: Bash: - source <(hawk completion bash) + source <(graycode completion bash) # To load completions for each session, execute once: # Linux: - hawk completion bash > /etc/bash_completion.d/hawk + graycode completion bash > /etc/bash_completion.d/graycode # macOS: - hawk completion bash > /usr/local/etc/bash_completion.d/hawk + graycode completion bash > /usr/local/etc/bash_completion.d/graycode Zsh: - source <(hawk completion zsh) + source <(graycode completion zsh) # To load completions for each session, execute once: - hawk completion zsh > "${fpath[1]}/_hawk" + graycode completion zsh > "${fpath[1]}/_graycode" Fish: - hawk completion fish | source + graycode completion fish | source # To load completions for each session, execute once: - hawk completion fish > ~/.config/fish/completions/hawk.fish + graycode completion fish > ~/.config/fish/completions/graycode.fish PowerShell: - hawk completion powershell | Out-String | Invoke-Expression + graycode completion powershell | Out-String | Invoke-Expression # To load completions for every new session, run: - hawk completion powershell > hawk.ps1 + graycode completion powershell > graycode.ps1 # and source this file from your PowerShell profile. JSON: - hawk completion json + graycode completion json # Print a machine-readable command/flag spec for IDE integration. `, DisableFlagsInUseLine: true, @@ -407,17 +407,17 @@ var completionInstallCmd = &cobra.Command{ Long: `Install the shell completion script to the standard location for your OS. Bash: - hawk completion install bash - # Installs to ~/.local/share/bash-completion/completions/hawk (Linux) - # or /opt/homebrew/etc/bash_completion.d/hawk (macOS Homebrew) + graycode completion install bash + # Installs to ~/.local/share/bash-completion/completions/graycode (Linux) + # or /opt/homebrew/etc/bash_completion.d/graycode (macOS Homebrew) Zsh: - hawk completion install zsh - # Installs to the first directory in $fpath (e.g. /usr/local/share/zsh/site-functions/_hawk) + graycode completion install zsh + # Installs to the first directory in $fpath (e.g. /usr/local/share/zsh/site-functions/_graycode) Fish: - hawk completion install fish - # Installs to ~/.config/fish/completions/hawk.fish`, + graycode completion install fish + # Installs to ~/.config/fish/completions/graycode.fish`, DisableFlagsInUseLine: true, ValidArgs: []string{"bash", "zsh", "fish"}, Args: cobra.ExactArgs(1), @@ -458,8 +458,8 @@ Fish: var updateCmd = &cobra.Command{ Use: "update", - Short: "Check for hawk updates", - Long: "Check GitHub for a newer hawk release and print upgrade instructions.", + Short: "Check for graycode updates", + Long: "Check GitHub for a newer graycode release and print upgrade instructions.", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { ver := version @@ -481,13 +481,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("## hawk bug report\n\n") + b.WriteString("## graycode 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 := hawkconfig.LoadSettings() + settings := graycodeconfig.LoadSettings() b.WriteString(doctorReport(settings)) b.WriteString("\n```\n") cmd.Print(b.String()) @@ -505,7 +505,7 @@ var versionJSON bool var versionCmd = &cobra.Command{ Use: "version", - Short: "Print hawk version", + Short: "Print graycode version", Run: func(cmd *cobra.Command, args []string) { if versionJSON { info := versionInfo{Version: DisplayVersion()} @@ -542,7 +542,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 hawk.", + Long: "Launch the interactive setup wizard to configure credentials, select providers/models, and initialize graycode.", RunE: func(cmd *cobra.Command, args []string) error { onboarding.Welcome(version) return onboarding.RunSetup() @@ -591,7 +591,7 @@ var preflightCmd = &cobra.Command{ ctx, cancel = context.WithTimeout(ctx, limit) defer cancel() } - r := hawkconfig.EnginePreflightReportWithSettings(ctx, settings, hawkconfig.EnginePreflightOptions{VerifyLive: preflightLiveFlag}) + r := graycodeconfig.EnginePreflightReportWithSettings(ctx, settings, graycodeconfig.EnginePreflightOptions{VerifyLive: preflightLiveFlag}) if preflightJSON { out, err := json.MarshalIndent(r, "", " ") if err != nil { @@ -599,13 +599,13 @@ var preflightCmd = &cobra.Command{ } cmd.Println(string(out)) } else { - cmd.Println(hawkconfig.FormatEnginePreflight(r)) + cmd.Println(graycodeconfig.FormatEnginePreflight(r)) } 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 hawk and complete /config") + return fmt.Errorf("preflight failed — run graycode and complete /config") } return nil }, @@ -619,13 +619,13 @@ var configCmd = &cobra.Command{ switch args[0] { case "get": if len(args) != 2 { - return fmt.Errorf("usage: hawk config get ") + return fmt.Errorf("usage: graycode config get ") } settings, err := loadEffectiveSettings() if err != nil { return err } - value, ok := hawkconfig.SettingValue(settings, args[1]) + value, ok := graycodeconfig.SettingValue(settings, args[1]) if !ok { return fmt.Errorf("unsupported setting key %q", args[1]) } @@ -633,27 +633,27 @@ var configCmd = &cobra.Command{ return nil case "set": if len(args) < 3 { - return fmt.Errorf("usage: hawk config set ") + return fmt.Errorf("usage: graycode config set ") } - if err := hawkconfig.SetGlobalSetting(args[1], strings.Join(args[2:], " ")); err != nil { + if err := graycodeconfig.SetGlobalSetting(args[1], strings.Join(args[2:], " ")); err != nil { return err } cmd.Println("updated", args[1]) return nil case "provider": if len(args) < 2 { - return fmt.Errorf("usage: hawk config provider ") + return fmt.Errorf("usage: graycode config provider ") } - if err := hawkconfig.SetGlobalSetting("provider", strings.Join(args[1:], " ")); err != nil { + if err := graycodeconfig.SetGlobalSetting("provider", strings.Join(args[1:], " ")); err != nil { return err } cmd.Println("updated provider") return nil case "model": if len(args) < 2 { - return fmt.Errorf("usage: hawk config model ") + return fmt.Errorf("usage: graycode config model ") } - if err := hawkconfig.SetGlobalSetting("model", strings.Join(args[1:], " ")); err != nil { + if err := graycodeconfig.SetGlobalSetting("model", strings.Join(args[1:], " ")); err != nil { return err } cmd.Println("updated model") @@ -663,13 +663,13 @@ var configCmd = &cobra.Command{ return nil case "routing-preview": if len(args) < 2 { - return fmt.Errorf("usage: hawk config routing-preview ") + return fmt.Errorf("usage: graycode config routing-preview ") } settings, err := loadEffectiveSettings() if err != nil { return err } - out, err := hawkconfig.RoutingPreviewJSONWithSettings(cmd.Context(), settings, strings.Join(args[1:], " ")) + out, err := graycodeconfig.RoutingPreviewJSONWithSettings(cmd.Context(), settings, strings.Join(args[1:], " ")) if err != nil { return err } @@ -690,10 +690,10 @@ var configCmd = &cobra.Command{ var mcpCmd = &cobra.Command{ Use: "mcp", - 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", + 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", RunE: func(cmd *cobra.Command, args []string) error { settings, err := loadEffectiveSettings() if err != nil { @@ -775,7 +775,7 @@ var ( var researchCmd = &cobra.Command{ Use: "research [flags] ", Short: "Autonomous research loop (Karpathy autoresearch pattern)", - Long: "hawk research --grep '^val_bpb:' --direction lower 'uv run train.py'", + Long: "graycode 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") @@ -855,9 +855,9 @@ var recoverCmd = &cobra.Command{ and offer to resume them. If a session-id is provided, resume that specific session. Examples: - hawk recover # List interrupted sessions - hawk recover abc123 # Resume specific session - hawk --recover # Auto-resume most recent interrupted session`, + graycode recover # List interrupted sessions + graycode recover abc123 # Resume specific session + graycode --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]) @@ -875,8 +875,8 @@ Examples: cmd.Println(session.FormatRecoveryCandidates(candidates)) if len(candidates) > 0 { - cmd.Println("Resume with: hawk recover ") - cmd.Println("Or launch TUI with: hawk --recover") + cmd.Println("Resume with: graycode recover ") + cmd.Println("Or launch TUI with: graycode --recover") } return nil }, @@ -892,12 +892,12 @@ func resumeRecoveredSession(ctx context.Context, sessionID string) error { } // logMigrateProviderSecretsError surfaces a non-nil error from -// hawkconfig.MigrateProviderSecrets via the structured logger. +// graycodeconfig.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 hawk /config to move them to the OS keychain. Previously +// they can run graycode /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. @@ -910,7 +910,7 @@ func logMigrateProviderSecretsError(l *logger.Logger, err error) { return } l.Warn( - "provider secret migration failed; API keys may remain in provider.json. Run `hawk /config` to move them to the OS keychain.", + "provider secret migration failed; API keys may remain in provider.json. Run `graycode /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 447978e5..f63b60ce 100644 --- a/cmd/rules.go +++ b/cmd/rules.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/GrayCodeAI/hawk/internal/rules" + "github.com/GrayCodeAI/graycode-cli/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: hawk, cursor, claudecode, copilot, gemini. +Supported formats: graycode, 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 hawk", + Short: "Import rules from another tool's format into graycode", 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 hawk format. - if err := rules.Export(".", rules.FormatHawk, imported); err != nil { - return fmt.Errorf("export to hawk format failed: %w", err) + // Export to graycode format. + if err := rules.Export(".", rules.FormatGraycode, imported); err != nil { + return fmt.Errorf("export to graycode format failed: %w", err) } cmd.Println(fmt.Sprintf("Imported %d rule(s) from %s to .agents/rules/.", len(imported), rulesImportFrom)) @@ -88,30 +88,30 @@ var rulesImportCmd = &cobra.Command{ var rulesExportCmd = &cobra.Command{ Use: "export", - Short: "Export hawk rules to another tool's format", + Short: "Export graycode 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 hawk rules. - hawkRules, err := rules.Import(".", rules.FormatHawk) + // Read graycode rules. + graycodeRules, err := rules.Import(".", rules.FormatGraycode) if err != nil { - return fmt.Errorf("read hawk rules failed: %w", err) + return fmt.Errorf("read graycode rules failed: %w", err) } - if len(hawkRules) == 0 { - cmd.Println("No hawk rules found in .agents/rules/. Nothing to export.") + if len(graycodeRules) == 0 { + cmd.Println("No graycode rules found in .agents/rules/. Nothing to export.") return nil } to := rules.Format(rulesExportTo) - if err := rules.Export(".", to, hawkRules); err != nil { + if err := rules.Export(".", to, graycodeRules); err != nil { return fmt.Errorf("export to %s format failed: %w", rulesExportTo, err) } - cmd.Println(fmt.Sprintf("Exported %d rule(s) to %s format.", len(hawkRules), rulesExportTo)) - for _, r := range hawkRules { + cmd.Println(fmt.Sprintf("Exported %d rule(s) to %s format.", len(graycodeRules), rulesExportTo)) + for _, r := range graycodeRules { cmd.Println(fmt.Sprintf(" - %s", r.Name)) } return nil diff --git a/cmd/sandbox.go b/cmd/sandbox.go index 7e8d7f79..75943497 100644 --- a/cmd/sandbox.go +++ b/cmd/sandbox.go @@ -4,12 +4,12 @@ import ( "fmt" "sync" - "github.com/GrayCodeAI/hawk/internal/diffsandbox" + "github.com/GrayCodeAI/graycode-cli/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 hawk engine; +// In a real integration this would be loaded/shared from the graycode 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 1080cb54..8f281251 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 hawk settings.json", - Long: "Prints the JSON schema for hawk's settings.json configuration file. Use with $schema for IDE autocompletion.", + 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.", RunE: func(cmd *cobra.Command, args []string) error { schema := map[string]interface{}{ "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Hawk Settings", - "description": "Configuration for the Hawk AI coding agent", + "title": "Graycode Settings", + "description": "Configuration for the Graycode 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 Hawk' to commits"}, + "generated_with": map[string]interface{}{"type": "boolean", "description": "Append 'Generated with Graycode' to commits"}, }, }, "repo_map": map[string]interface{}{"type": "boolean"}, diff --git a/cmd/search.go b/cmd/search.go index b60f18b2..489a28d0 100644 --- a/cmd/search.go +++ b/cmd/search.go @@ -6,7 +6,7 @@ import ( "strings" "text/tabwriter" - "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/graycode-cli/internal/session" "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 hawk sessions. + Long: `Full-text search across all saved graycode sessions. Searches message content, tool results, and assistant responses. Examples: - hawk search "authentication" - hawk search --limit 5 "database migration" - hawk search "func main"`, + graycode search "authentication" + graycode search --limit 5 "database migration" + graycode 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 0b539846..116e1fb4 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/hawk/internal/securitylog" + "github.com/GrayCodeAI/graycode-cli/internal/securitylog" ) -// withTempState runs a cli test body with HAWK_STATE_DIR pointed at a temp dir +// withTempState runs a cli test body with GRAYCODE_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("HAWK_STATE_DIR", dir) + t.Setenv("GRAYCODE_STATE_DIR", dir) body(dir) } diff --git a/cmd/securitylog_cmd.go b/cmd/securitylog_cmd.go index 4614d2b4..3bd32ea9 100644 --- a/cmd/securitylog_cmd.go +++ b/cmd/securitylog_cmd.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "github.com/GrayCodeAI/hawk/internal/securitylog" + "github.com/GrayCodeAI/graycode-cli/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: `Hawk records security-relevant events (permission denials, approval + Long: `Graycode 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. - 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`, + 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`, 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 5315f0f3..b822dc28 100644 --- a/cmd/session_export.go +++ b/cmd/session_export.go @@ -4,7 +4,7 @@ import ( "fmt" "os" - "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/graycode-cli/internal/session" "github.com/spf13/cobra" ) diff --git a/cmd/session_migrate.go b/cmd/session_migrate.go index c8b4c32b..7beebf09 100644 --- a/cmd/session_migrate.go +++ b/cmd/session_migrate.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/graycode-cli/internal/session" "github.com/spf13/cobra" ) diff --git a/cmd/session_migrate_test.go b/cmd/session_migrate_test.go index 1596166c..3b1e9b20 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("HAWK_STATE_DIR", state) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", state) + t.Setenv("GRAYCODE_STATE_DIR", state) id := "migrate-cmd-json" sessDir := filepath.Join(state, "sessions") _ = os.MkdirAll(sessDir, 0o700) diff --git a/cmd/session_sync.go b/cmd/session_sync.go index 2dff22d1..68cba775 100644 --- a/cmd/session_sync.go +++ b/cmd/session_sync.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/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(hawkconfig.ActiveGateway(ctx)), strings.TrimSpace(hawkconfig.ActiveModel(ctx)) + return strings.TrimSpace(graycodeconfig.ActiveGateway(ctx)), strings.TrimSpace(graycodeconfig.ActiveModel(ctx)) } // syncSessionFromPersistedSelection copies explicit eyrie provider.json // selection into the live session when the session fields are empty. -// It intentionally avoids runtime defaults so Hawk can preserve the +// It intentionally avoids runtime defaults so Graycode 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 c78046d4..2c519199 100644 --- a/cmd/session_sync_test.go +++ b/cmd/session_sync_test.go @@ -5,26 +5,26 @@ import ( "strings" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) func TestSyncSessionFromPersistedSelection_FillsEmptySessionModel(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) ctx := context.Background() _ = store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890") - hawkconfig.InvalidateConfigUICache() - _ = hawkconfig.SetActiveProvider(ctx, "openrouter") - _ = hawkconfig.SetActiveModel(ctx, "gpt-4o") + graycodeconfig.InvalidateConfigUICache() + _ = graycodeconfig.SetActiveProvider(ctx, "openrouter") + _ = graycodeconfig.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) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) ctx := context.Background() _ = store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890") - hawkconfig.InvalidateConfigUICache() - _ = hawkconfig.SetActiveProvider(ctx, "openrouter") - _ = hawkconfig.SetActiveModel(ctx, "gpt-4o") + graycodeconfig.InvalidateConfigUICache() + _ = graycodeconfig.SetActiveProvider(ctx, "openrouter") + _ = graycodeconfig.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) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) ctx := context.Background() - hawkconfig.InvalidateConfigUICache() - _ = hawkconfig.ClearActiveSelection(ctx) + graycodeconfig.InvalidateConfigUICache() + _ = graycodeconfig.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) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() isolateCredentialHome(t) store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) ctx := context.Background() _ = store.Set(ctx, gateway.AccountForEnv("OPENROUTER_API_KEY"), "sk-or-test-key-1234567890") - hawkconfig.InvalidateConfigUICache() - _ = hawkconfig.SetActiveProvider(ctx, "openrouter") - _ = hawkconfig.SetActiveModel(ctx, "gpt-4o") + graycodeconfig.InvalidateConfigUICache() + _ = graycodeconfig.SetActiveProvider(ctx, "openrouter") + _ = graycodeconfig.SetActiveModel(ctx, "gpt-4o") m := &chatModel{ session: engine.NewSession("", "", "base", nil), diff --git a/cmd/skills_cmd.go b/cmd/skills_cmd.go index 973b4377..49646cea 100644 --- a/cmd/skills_cmd.go +++ b/cmd/skills_cmd.go @@ -8,8 +8,8 @@ import ( "strconv" "strings" - "github.com/GrayCodeAI/hawk/internal/plugin" - "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/graycode-cli/internal/plugin" + "github.com/GrayCodeAI/graycode-cli/internal/tool" "github.com/spf13/cobra" ) diff --git a/cmd/skills_curator_cmd.go b/cmd/skills_curator_cmd.go index ea5dc617..858f2c36 100644 --- a/cmd/skills_curator_cmd.go +++ b/cmd/skills_curator_cmd.go @@ -4,14 +4,14 @@ import ( "fmt" "path/filepath" - "github.com/GrayCodeAI/hawk/internal/intelligence/skillcurator" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/intelligence/skillcurator" + "github.com/GrayCodeAI/graycode-cli/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 ~/.hawk/skills. +// in ~/.graycode/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 41957077..b9576178 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=hawk", + "--who=graycode", "--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 ac8a1739..904197bd 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/hawk/internal/snapshot" + "github.com/GrayCodeAI/graycode-cli/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 hawk modifies files."}) + m.messages = append(m.messages, displayMsg{role: "system", content: "No snapshots yet. Snapshots are created automatically when graycode 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. Hawk automatically snapshots every file modification.", + Long: "View, restore, and diff file snapshots. Graycode automatically snapshots every file modification.", } var snapshotListCmd = &cobra.Command{ diff --git a/cmd/spec_picker.go b/cmd/spec_picker.go index 35e233c6..d320784a 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" "github.com/mattn/go-runewidth" ) diff --git a/cmd/spec_picker_test.go b/cmd/spec_picker_test.go index 3fe5a8ff..e8aa31ab 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) func TestSpecPicker_HasSevenActions(t *testing.T) { diff --git a/cmd/spinner_wave.go b/cmd/spinner_wave.go index 98d82a3e..aa737809 100644 --- a/cmd/spinner_wave.go +++ b/cmd/spinner_wave.go @@ -5,12 +5,12 @@ import ( "strings" "unicode/utf8" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 Hawk's brand color. + {255, 215, 0}, // Talon Gold — anchor the wave in Graycode'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 + hawkTypingDots + total := 1 + verbRunes + 1 + graycodeTypingDots 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 < hawkTypingDots; i++ { + for i := 0; i < graycodeTypingDots; i++ { g := icons.CircleOutline() bold := false - if i == dotPhase%hawkTypingDots { + if i == dotPhase%graycodeTypingDots { g = icons.CircleFilled() bold = true } diff --git a/cmd/spinner_wave_test.go b/cmd/spinner_wave_test.go index dde12f26..48ac5aed 100644 --- a/cmd/spinner_wave_test.go +++ b/cmd/spinner_wave_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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(SpinnerHawk, "Hi") + s := NewBrailleSpinner(SpinnerGraycode, "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(SpinnerHawk, "Go") + s := NewBrailleSpinner(SpinnerGraycode, "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 f26701da..a1df3310 100644 --- a/cmd/stats.go +++ b/cmd/stats.go @@ -8,7 +8,7 @@ import ( "strings" "time" - analytics "github.com/GrayCodeAI/hawk/internal/observability" + analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" "github.com/spf13/cobra" ) @@ -85,7 +85,7 @@ func runStats(cmd *cobra.Command, args []string) error { if len(filtered) == 0 { cmd.Println("No session data found for the specified time period.") - cmd.Println("Sessions are recorded automatically when you use hawk.") + cmd.Println("Sessions are recorded automatically when you use graycode.") return nil } @@ -180,7 +180,7 @@ func printStatsText(cmd *cobra.Command, out *statsOutput) { _, _ = fmt.Fprintf(w, "\n") _, _ = fmt.Fprintf(w, "══════════════════════════════════════════════════\n") - _, _ = fmt.Fprintf(w, " Hawk Usage Statistics (%s)\n", out.Period) + _, _ = fmt.Fprintf(w, " Graycode Usage Statistics (%s)\n", out.Period) _, _ = fmt.Fprintf(w, "══════════════════════════════════════════════════\n") // Overview section diff --git a/cmd/status_snapshot.go b/cmd/status_snapshot.go index 0b133052..882d9f78 100644 --- a/cmd/status_snapshot.go +++ b/cmd/status_snapshot.go @@ -5,11 +5,11 @@ import ( "fmt" "strings" - 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" + 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" "github.com/spf13/cobra" ) @@ -35,11 +35,11 @@ var statusCmd = &cobra.Command{ func buildStatusSnapshot() status.Snapshot { snapshot := status.New() - snapshot.HawkVersion = version + snapshot.GraycodeVersion = version snapshot.Workspace = status.Workspace() snapshot.GitBranch = engine.InspectGitBranch("").Branch - settings := hawkconfig.LoadGlobalSettings() - selection := hawkconfig.EffectiveSelection(context.Background(), hawkconfig.SelectionOptions{}) + settings := graycodeconfig.LoadGlobalSettings() + selection := graycodeconfig.EffectiveSelection(context.Background(), graycodeconfig.SelectionOptions{}) snapshot.Model = strings.TrimSpace(selection.Model) snapshot.Provider = strings.TrimSpace(selection.Provider) if snapshot.Model == "" { @@ -85,7 +85,7 @@ func formatStatusSnapshot(s status.Snapshot) string { if s.Permission.SandboxBackend != "" { backend = " (" + s.Permission.SandboxBackend + ")" } - return fmt.Sprintf("Hawk status\nSchema: %s\nWorkspace: %s\nGit branch: %s\nProvider: %s\nModel: %s\nAutonomy tier: %s\nSandbox: %s%s\nPermission rules: %d\nMCP: %d configured (%s)\nSkills: %d (%s)\nSecrets redacted: %t\n", + return fmt.Sprintf("Graycode status\nSchema: %s\nWorkspace: %s\nGit branch: %s\nProvider: %s\nModel: %s\nAutonomy tier: %s\nSandbox: %s%s\nPermission rules: %d\nMCP: %d configured (%s)\nSkills: %d (%s)\nSecrets redacted: %t\n", s.SchemaVersion, s.Workspace, s.GitBranch, s.Provider, s.Model, s.Permission.AutonomyTier, s.Permission.SandboxMode, backend, s.Permission.EffectiveRules, s.MCP.Configured, s.MCP.State, diff --git a/cmd/status_snapshot_test.go b/cmd/status_snapshot_test.go index ed8ad8d0..f0fbdd35 100644 --- a/cmd/status_snapshot_test.go +++ b/cmd/status_snapshot_test.go @@ -4,13 +4,13 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/sandbox" + "github.com/GrayCodeAI/graycode-cli/internal/sandbox" ) func TestFormatStatusSnapshot(t *testing.T) { snapshot := buildStatusSnapshot() formatted := formatStatusSnapshot(snapshot) - for _, expected := range []string{"Hawk status", "Schema: 1", "Secrets redacted: true"} { + for _, expected := range []string{"Graycode 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 03d3c408..3c47d00f 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/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/engine/git" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine/git" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) var ( diff --git a/cmd/statusbar_test.go b/cmd/statusbar_test.go index 7c87259d..bc92e3a6 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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/hawk") - if got != "~/project/hawk" { + got := shortenHomePath(home + "/project/graycode") + if got != "~/project/graycode" { t.Fatalf("got %q", got) } } diff --git a/cmd/storage_policy_test.go b/cmd/storage_policy_test.go index 20025a6d..22f14e6b 100644 --- a/cmd/storage_policy_test.go +++ b/cmd/storage_policy_test.go @@ -7,10 +7,10 @@ import ( "testing" ) -func TestStoragePolicyHelpersDoNotCreateProjectHawk(t *testing.T) { +func TestStoragePolicyHelpersDoNotCreateProjectGraycode(t *testing.T) { project := t.TempDir() - t.Setenv("HAWK_STATE_DIR", filepath.Join(t.TempDir(), "state")) - t.Setenv("HAWK_CACHE_DIR", filepath.Join(t.TempDir(), "cache")) + t.Setenv("GRAYCODE_STATE_DIR", filepath.Join(t.TempDir(), "state")) + t.Setenv("GRAYCODE_CACHE_DIR", filepath.Join(t.TempDir(), "cache")) oldwd, err := os.Getwd() if err != nil { t.Fatal(err) @@ -21,13 +21,13 @@ func TestStoragePolicyHelpersDoNotCreateProjectHawk(t *testing.T) { } planPath := resolvePlanPath("demo") - if strings.Contains(planPath, filepath.Join(project, ".hawk")) { - t.Fatalf("resolvePlanPath leaked project .hawk: %q", planPath) + if strings.Contains(planPath, filepath.Join(project, ".graycode")) { + t.Fatalf("resolvePlanPath leaked project .graycode: %q", planPath) } saveInputHistory([]string{"hello"}) recordTipShown("slash-help") - if _, err := os.Stat(filepath.Join(project, ".hawk")); !os.IsNotExist(err) { - t.Fatalf("normal storage helpers created project .hawk, stat err=%v", err) + if _, err := os.Stat(filepath.Join(project, ".graycode")); !os.IsNotExist(err) { + t.Fatalf("normal storage helpers created project .graycode, stat err=%v", err) } } diff --git a/cmd/swift.go b/cmd/swift.go index ad4394e8..194d8a28 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 hawk, not a standalone product. Its full +// swift is a sibling library consumed by graycode, not a standalone product. Its full // command tree is built by swiftcli.NewRootCmd() (Use: "swift"), so mounting it here -// surfaces every swift feature under `hawk swift ...` without porting any code. +// surfaces every swift feature under `graycode swift ...` without porting any code. func init() { rootCmd.AddCommand(swiftcli.NewRootCmd()) } diff --git a/cmd/swift_correlation.go b/cmd/swift_correlation.go index 1742fd07..6ce05c1b 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"` - HawkSessionID string `json:"hawk_session_id"` + GraycodeSessionID string `json:"graycode_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, - hawkSessionID string, + graycodeSessionID string, ) (swiftCorrelation, error) { - hawkSessionID = strings.TrimSpace(hawkSessionID) - if hawkSessionID == "" { - return swiftCorrelation{}, fmt.Errorf("resolve Swift correlation: Hawk session ID is required") + graycodeSessionID = strings.TrimSpace(graycodeSessionID) + if graycodeSessionID == "" { + return swiftCorrelation{}, fmt.Errorf("resolve Swift correlation: Graycode 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", "--hawk-session", hawkSessionID}) + root.SetArgs([]string{"graph", "correlation", "--graycode-session", graycodeSessionID}) // 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(), hawkSessionID) + return decodeSwiftCorrelation(stdout.Bytes(), graycodeSessionID) } -func decodeSwiftCorrelation(payload []byte, hawkSessionID string) (swiftCorrelation, error) { +func decodeSwiftCorrelation(payload []byte, graycodeSessionID string) (swiftCorrelation, error) { decoder := json.NewDecoder(bytes.NewReader(payload)) decoder.DisallowUnknownFields() var correlation swiftCorrelation @@ -81,13 +81,13 @@ func decodeSwiftCorrelation(payload []byte, hawkSessionID string) (swiftCorrelat if err := requireJSONEOF(decoder); err != nil { return swiftCorrelation{}, err } - if err := normalizeSwiftCorrelation(&correlation, hawkSessionID); err != nil { + if err := normalizeSwiftCorrelation(&correlation, graycodeSessionID); err != nil { return swiftCorrelation{}, err } return correlation, nil } -func normalizeSwiftCorrelation(correlation *swiftCorrelation, hawkSessionID string) error { +func normalizeSwiftCorrelation(correlation *swiftCorrelation, graycodeSessionID string) error { if correlation == nil { return fmt.Errorf("validate Swift correlation: response is nil") } @@ -97,8 +97,8 @@ func normalizeSwiftCorrelation(correlation *swiftCorrelation, hawkSessionID stri correlation.SchemaVersion, ) } - if correlation.HawkSessionID != hawkSessionID { - return fmt.Errorf("validate Swift correlation: Hawk session identity mismatch") + if correlation.GraycodeSessionID != graycodeSessionID { + return fmt.Errorf("validate Swift correlation: Graycode 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 b16cc513..d4652f06 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, - HawkSessionID: "hawk-session", + GraycodeSessionID: "graycode-session", CheckpointLookupComplete: true, Matches: []swiftCorrelationMatch{ { @@ -26,7 +26,7 @@ func TestNormalizeSwiftCorrelationDeterministicAndDeduplicated(t *testing.T) { }, }, } - if err := normalizeSwiftCorrelation(&correlation, "hawk-session"); err != nil { + if err := normalizeSwiftCorrelation(&correlation, "graycode-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, - HawkSessionID: "hawk-session", + GraycodeSessionID: "graycode-session", CheckpointLookupComplete: true, Matches: []swiftCorrelationMatch{{ SwiftSessionID: "swift-session", @@ -63,9 +63,9 @@ func TestNormalizeSwiftCorrelationRejectsUntrustedIdentityData(t *testing.T) { }, }, { - name: "hawk identity", + name: "graycode identity", mutate: func(value *swiftCorrelation) { - value.HawkSessionID = "other-session" + value.GraycodeSessionID = "other-session" }, }, { @@ -93,7 +93,7 @@ func TestNormalizeSwiftCorrelationRejectsUntrustedIdentityData(t *testing.T) { t.Parallel() value := valid() test.mutate(&value) - if err := normalizeSwiftCorrelation(&value, "hawk-session"); err == nil { + if err := normalizeSwiftCorrelation(&value, "graycode-session"); err == nil { t.Fatal("normalizeSwiftCorrelation() error = nil") } }) @@ -105,14 +105,14 @@ func TestNormalizeSwiftCorrelationDropsUnverifiedCheckpointList(t *testing.T) { correlation := swiftCorrelation{ SchemaVersion: swiftCorrelationSchemaVersion, - HawkSessionID: "hawk-session", + GraycodeSessionID: "graycode-session", CheckpointLookupComplete: false, Matches: []swiftCorrelationMatch{{ SwiftSessionID: "swift-session", CheckpointIDs: []string{"abc123def456"}, }}, } - if err := normalizeSwiftCorrelation(&correlation, "hawk-session"); err != nil { + if err := normalizeSwiftCorrelation(&correlation, "graycode-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", - "hawk_session_id": "hawk-session", + "graycode_session_id": "graycode-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, "hawk-session") + correlation, err := decodeSwiftCorrelation(payload, "graycode-session") if err != nil { t.Fatalf("decodeSwiftCorrelation() error = %v", err) } diff --git a/cmd/swift_report.go b/cmd/swift_report.go index 3a2d564f..bd24e687 100644 --- a/cmd/swift_report.go +++ b/cmd/swift_report.go @@ -10,9 +10,9 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/permissions" - "github.com/GrayCodeAI/hawk/internal/swift" + "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/permissions" + "github.com/GrayCodeAI/graycode-cli/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("HAWK_SESSION_ID"), + SessionID: os.Getenv("GRAYCODE_SESSION_ID"), PID: os.Getpid(), Terminal: terminalSize(), Env: selectedEnv(), diff --git a/cmd/tabcomplete.go b/cmd/tabcomplete.go index dcd95fc0..cdd6f9b2 100644 --- a/cmd/tabcomplete.go +++ b/cmd/tabcomplete.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/graycode-cli/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 3f1ba84b..1f88eb69 100644 --- a/cmd/tabcomplete_test.go +++ b/cmd/tabcomplete_test.go @@ -5,7 +5,7 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/graycode-cli/internal/tool" ) func TestFilePathCompletions_CurrentDir(t *testing.T) { diff --git a/cmd/tape.go b/cmd/tape.go index 479060db..f42a74d9 100644 --- a/cmd/tape.go +++ b/cmd/tape.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/terminal/tape" + "github.com/GrayCodeAI/graycode-cli/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: HAWK_TAPES_DIR or user config dir)") + tapeCommitCmd.Flags().StringVar(&tapeCommitDir, "dir", "", "commit directory (default: GRAYCODE_TAPES_DIR or user config dir)") tapeCmd.AddCommand(tapeStatusCmd, tapeCommitCmd) rootCmd.AddCommand(tapeCmd) } diff --git a/cmd/taste.go b/cmd/taste.go index 8301894f..775c4d0e 100644 --- a/cmd/taste.go +++ b/cmd/taste.go @@ -6,7 +6,7 @@ import ( "path/filepath" "strings" - "github.com/GrayCodeAI/hawk/internal/feature/taste" + "github.com/GrayCodeAI/graycode-cli/internal/feature/taste" "github.com/spf13/cobra" ) @@ -36,8 +36,8 @@ var tastePushCmd = &cobra.Command{ with teammates or used across machines. Examples: - hawk taste push # Export to stdout - hawk taste push --file team.json # Export to file`, + graycode taste push # Export to stdout + graycode 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: - hawk taste pull team.json - cat team.json | hawk taste pull -`, + graycode taste pull team.json + cat team.json | graycode taste pull -`, Args: cobra.ExactArgs(1), RunE: runTastePull, } diff --git a/cmd/terminal_notify.go b/cmd/terminal_notify.go index 074f97a3..a29b9c63 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=hawk:d=0;%s\033\\", body) + _, _ = fmt.Fprintf(os.Stderr, "\033]99;i=graycode: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 51fcb735..f3d5eb79 100644 --- a/cmd/textutil_shim.go +++ b/cmd/textutil_shim.go @@ -1,6 +1,6 @@ package cmd -import "github.com/GrayCodeAI/hawk/internal/textutil" +import "github.com/GrayCodeAI/graycode-cli/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 1f5976d0..abfb4ad8 100644 --- a/cmd/theme.go +++ b/cmd/theme.go @@ -1,11 +1,11 @@ package cmd -// theme.go — the single source of truth for hawk's visual identity. +// theme.go — the single source of truth for graycode'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 hawk, edit this file; to audit +// hex codes or magic strings. To rebrand graycode, 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/hawk/internal/theme" + internaltheme "github.com/GrayCodeAI/graycode-cli/internal/theme" ) // --------------------------------------------------------------------------- // 1. Brand & identity // --------------------------------------------------------------------------- -// hawkColor is Talon Gold (#FFD700). Used for the HAWK wordmark, hawk, +// graycodeColor is Talon Gold (#FFD700). Used for the GRAYCODE wordmark, graycode, // ⛬ assistant prefix, prompt arrow, cursor, exit prompt, and // any place that should "speak" as the brand. -var hawkColor = lipgloss.Color(internaltheme.BrandPrimary) +var graycodeColor = lipgloss.Color(internaltheme.BrandPrimary) // --------------------------------------------------------------------------- // 2. UI state @@ -197,7 +197,7 @@ const ( // --------------------------------------------------------------------------- // 10. Icons & glyphs // -// Hawk's icon registry lives in internal/ui/icons. Every call site +// Graycode'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. - hawkColor = lipgloss.Color(internaltheme.BrandPrimary) + graycodeColor = 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(hawkColor).Bold(true).Underline(true) + mdH1Style = lipgloss.NewStyle().Foreground(graycodeColor).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(hawkColor).Bold(true) + mdBoldStyle = lipgloss.NewStyle().Foreground(graycodeColor).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(hawkColor).Bold(true) - slashSelDescStyle = lipgloss.NewStyle().Foreground(hawkColor) + slashSelCmdStyle = lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) + slashSelDescStyle = lipgloss.NewStyle().Foreground(graycodeColor) 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(hawkColor).Padding(0, 1) + agentActiveStyle = lipgloss.NewStyle().Border(lipgloss.NormalBorder()).BorderForeground(graycodeColor).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(hawkColor) + scrollbarThumbStyle = lipgloss.NewStyle().Foreground(graycodeColor) } diff --git a/cmd/theme_picker.go b/cmd/theme_picker.go index 3e6026a3..e101a987 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" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - internaltheme "github.com/GrayCodeAI/hawk/internal/theme" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + internaltheme "github.com/GrayCodeAI/graycode-cli/internal/theme" ) // detectAutoIsDark returns whether "auto" theme currently resolves to dark. func detectAutoIsDark() bool { - settings := hawkconfig.LoadGlobalSettings() + settings := graycodeconfig.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(hawkColor). + Background(graycodeColor). 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(hawkColor).Bold(true) + rowStyle := lipgloss.NewStyle().Foreground(graycodeColor).Bold(true) b.WriteString(fmt.Sprintf(" ▶ %s\n", rowStyle.Render(e.Name))) - b.WriteString(fmt.Sprintf(" %s\n", lipgloss.NewStyle().Foreground(hawkColor).Render(e.Desc))) + b.WriteString(fmt.Sprintf(" %s\n", lipgloss.NewStyle().Foreground(graycodeColor).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 3557b8f4..e5454c8b 100644 --- a/cmd/tips.go +++ b/cmd/tips.go @@ -8,10 +8,10 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/storage" ) -// Tip represents a single hawk usage tip. +// Tip represents a single graycode 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 hawk.", Category: "shortcuts"}, + {ID: "ctrl-c-quit", Text: "Press Ctrl+C twice to quit graycode.", 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"}, @@ -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 hawk/agent-* branch.", Category: "git"}, + {ID: "slash-branch-agent", Text: "Use /branch-agent before big edits on main — creates graycode/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 efc3212c..6ba98bd5 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, ".hawk"), 0o755) + os.MkdirAll(filepath.Join(tmp, ".graycode"), 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, ".hawk"), 0o755) + os.MkdirAll(filepath.Join(tmp, ".graycode"), 0o755) defer os.Setenv("HOME", orig) recordTipShown("slash-help") diff --git a/cmd/toolset_cmd.go b/cmd/toolset_cmd.go index 38f5faca..2b263b10 100644 --- a/cmd/toolset_cmd.go +++ b/cmd/toolset_cmd.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/hawk/internal/toolset" + "github.com/GrayCodeAI/graycode-cli/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. - hawk toolset List available toolsets - hawk toolset research Resolve 'research' to its concrete tool list + graycode toolset List available toolsets + graycode 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 9eab1fb4..ac29c935 100644 --- a/cmd/trust.go +++ b/cmd/trust.go @@ -6,8 +6,8 @@ import ( "os" "text/tabwriter" - "github.com/GrayCodeAI/hawk/internal/flags" - "github.com/GrayCodeAI/hawk/internal/trust" + "github.com/GrayCodeAI/graycode-cli/internal/flags" + "github.com/GrayCodeAI/graycode-cli/internal/trust" "github.com/spf13/cobra" ) @@ -17,10 +17,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 HAWK_Y0_FOLDER_TRUST is enabled (default after Year 0 PACK-03), +When GRAYCODE_Y0_FOLDER_TRUST is enabled (default after Year 0 PACK-03), untrusted projects cannot run project automation (RCE mitigation). -User-global plugins under the Hawk state directory always load.`, +User-global plugins under the Graycode state directory always load.`, } var trustAddCmd = &cobra.Command{ @@ -94,7 +94,7 @@ var trustListCmd = &cobra.Command{ fmt.Println("[]") } else { cmd.Println("No trusted directories.") - cmd.Printf("Folder trust enforcement: %v (HAWK_Y0_FOLDER_TRUST)\n", flags.FolderTrust()) + cmd.Printf("Folder trust enforcement: %v (GRAYCODE_Y0_FOLDER_TRUST)\n", flags.FolderTrust()) } return nil } diff --git a/cmd/usage.go b/cmd/usage.go index e0f884a3..ff0fbbd0 100644 --- a/cmd/usage.go +++ b/cmd/usage.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/GrayCodeAI/hawk/internal/usage" + "github.com/GrayCodeAI/graycode-cli/internal/usage" "github.com/spf13/cobra" ) diff --git a/cmd/verify_cmd.go b/cmd/verify_cmd.go index d1f4db28..1d4b7a89 100644 --- a/cmd/verify_cmd.go +++ b/cmd/verify_cmd.go @@ -6,9 +6,9 @@ import ( "os/exec" "strings" - "github.com/GrayCodeAI/hawk/internal/governance" - "github.com/GrayCodeAI/hawk/internal/securitylog" - "github.com/GrayCodeAI/hawk/internal/testrunner" + "github.com/GrayCodeAI/graycode-cli/internal/governance" + "github.com/GrayCodeAI/graycode-cli/internal/securitylog" + "github.com/GrayCodeAI/graycode-cli/internal/testrunner" "github.com/spf13/cobra" ) @@ -17,7 +17,7 @@ import ( var verifyCmd = &cobra.Command{ Use: "verify", Short: "Run local self-verification (security log, governance policy)", - Long: `Run hawk's self-verification checks without a model: + Long: `Run graycode'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 0633ebd8..af49145e 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 -// `hawk --version` and `hawk version`. +// `graycode --version` and `graycode version`. func versionLine() string { ver := DisplayVersion() if ver != "" && !strings.HasPrefix(ver, "v") && !strings.HasPrefix(ver, "V") { ver = "v" + ver } - line := "hawk " + ver + line := "graycode " + ver if d := strings.TrimSpace(buildDate); d != "" && d != "unknown" { line += " (built " + d + ")" } diff --git a/cmd/version_display_test.go b/cmd/version_display_test.go index 7047f4b3..59fdc2ad 100644 --- a/cmd/version_display_test.go +++ b/cmd/version_display_test.go @@ -5,8 +5,8 @@ import ( "path/filepath" "testing" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/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) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) m := chatModel{session: nil} @@ -45,12 +45,12 @@ func TestChatConnectionStatus_NoCredentials(t *testing.T) { } func TestChatBottomRightStatus_NoCredentials(t *testing.T) { - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() store := &gateway.MapStore{} gateway.SetDefaultStore(store) t.Cleanup(func() { gateway.SetDefaultStore(nil) - hawkconfig.InvalidateConfigUICache() + graycodeconfig.InvalidateConfigUICache() }) m := chatModel{inputIndicator: &InputIndicator{}} diff --git a/cmd/vibe.go b/cmd/vibe.go index 6ac26035..48c20f5b 100644 --- a/cmd/vibe.go +++ b/cmd/vibe.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) // VibeConfig controls vibe coding behavior. diff --git a/cmd/visual_diff.go b/cmd/visual_diff.go index 0f77fee3..1cb053ae 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 hawk's semantic palette so diff +// DefaultDiffTheme returns a DiffTheme using graycode'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 94cf0642..2e76ee35 100644 --- a/cmd/welcome_banner.go +++ b/cmd/welcome_banner.go @@ -6,8 +6,8 @@ import ( "github.com/mattn/go-runewidth" ) -// hawkBlockGlyphs — fixed 8-column ██ font used by the welcome gate banners. -var hawkBlockGlyphs = map[rune][5]string{ +// graycodeBlockGlyphs — fixed 8-column ██ font used by the welcome gate banners. +var graycodeBlockGlyphs = map[rune][5]string{ 'H': {"██ ██ ", "██ ██ ", "███████ ", "██ ██ ", "██ ██ "}, 'A': {" ███ ", " █████ ", "███████ ", "██ ██ ", "██ ██ "}, 'W': {"██ ██ ", "██ ██ ", "██ █ ██ ", "███ ███ ", "██ ██ "}, @@ -20,8 +20,8 @@ var hawkBlockGlyphs = map[rune][5]string{ 'T': {"████████", " ██ ", " ██ ", " ██ ", " ██ "}, } -// hawkLogoArtLines is the canonical HAWK wordmark, with the hawk forming the W. -var hawkLogoArtLines = []string{ +// graycodeLogoArtLines is the canonical GRAYCODE wordmark, with the graycode forming the W. +var graycodeLogoArtLines = []string{ " . .", " . . . .", " . | | .", @@ -40,19 +40,19 @@ var hawkLogoArtLines = []string{ } const ( - hawkBlockCellW = 8 - hawkBlockLetterGap = 1 - hawkBlockWordGap = 4 + graycodeBlockCellW = 8 + graycodeBlockLetterGap = 1 + graycodeBlockWordGap = 4 ) // welcomeWordLines — "WELCOME" block (row-aligned, fixed grid). -var welcomeWordLines = composeHawkBlockLines("WELCOME") +var welcomeWordLines = composeGraycodeBlockLines("WELCOME") // welcomeToWordLines — "TO" block, centered under WELCOME on the gate. -var welcomeToWordLines = composeHawkBlockLines("TO") +var welcomeToWordLines = composeGraycodeBlockLines("TO") // welcomeToPhraseLines — "WELCOME TO" block for wide welcome gates. -var welcomeToPhraseLines = composeHawkBlockLines("WELCOME TO") +var welcomeToPhraseLines = composeGraycodeBlockLines("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 composeHawkBlockLines(text string) []string { +func composeGraycodeBlockLines(text string) []string { rows := make([]string, 5) words := strings.Fields(text) for wi, word := range words { for ci, ch := range word { - glyph, ok := hawkBlockGlyphs[ch] + glyph, ok := graycodeBlockGlyphs[ch] if !ok { continue } for i := range rows { if rows[i] != "" { if ci == 0 && wi > 0 { - rows[i] += strings.Repeat(" ", hawkBlockWordGap) + rows[i] += strings.Repeat(" ", graycodeBlockWordGap) } else { - rows[i] += strings.Repeat(" ", hawkBlockLetterGap) + rows[i] += strings.Repeat(" ", graycodeBlockLetterGap) } } - cell := padBlockCell(glyph[i], hawkBlockCellW) + cell := padBlockCell(glyph[i], graycodeBlockCellW) rows[i] += cell } } diff --git a/cmd/welcome_banner_test.go b/cmd/welcome_banner_test.go index d79ef1e8..bfe5a529 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 := composeHawkBlockLines("TO") + to := composeGraycodeBlockLines("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_SameWGlyphAsHAWK(t *testing.T) { - w := strings.TrimRight(hawkBlockGlyphs['W'][0], " ") +func TestWelcomeWordLines_SameWGlyphAsGRAYCODE(t *testing.T) { + w := strings.TrimRight(graycodeBlockGlyphs['W'][0], " ") if !strings.HasPrefix(welcomeWordLines[0], w) { - t.Fatalf("W row should start with hawk logo glyph:\nwelcome: %q\nhawk W: %q", + t.Fatalf("W row should start with graycode logo glyph:\nwelcome: %q\ngraycode W: %q", welcomeWordLines[0], w) } } diff --git a/cmd/welcome_inline_test.go b/cmd/welcome_inline_test.go index 3820818b..d44038e4 100644 --- a/cmd/welcome_inline_test.go +++ b/cmd/welcome_inline_test.go @@ -8,9 +8,9 @@ import ( "charm.land/bubbles/v2/textarea" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/tool" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/graycode-cli/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, hawkconfig.Settings{}, 0, false, 100, 24, docker) + out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.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, hawkconfig.Settings{}, 0, false, 100, 24, nil) + out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.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, hawkconfig.Settings{}, 0, false, 100, 24, nil) + out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.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, hawkconfig.Settings{}, 0, false, 72, 20, nil) + out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.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_WideTerminalUsesHawkWordmark(t *testing.T) { - out := buildWelcomeMessage(nil, "", nil, nil, hawkconfig.Settings{}, 0, false, 120, 40, nil) +func TestBuildWelcomeMessage_WideTerminalUsesGraycodeWordmark(t *testing.T) { + out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.Settings{}, 0, false, 120, 40, nil) for _, want := range []string{ "___ ___ _________", "(\\.|\\/|./)", "|0\\/0|", } { if !strings.Contains(out, want) { - t.Fatalf("wide welcome missing hawk wordmark line %q in:\n%s", want, out) + t.Fatalf("wide welcome missing graycode wordmark line %q in:\n%s", want, out) } } } -func TestBuildWelcomeMessage_HawkWordmarkBlinks(t *testing.T) { - out := buildWelcomeMessage(nil, "", nil, nil, hawkconfig.Settings{}, 0, true, 120, 40, nil) +func TestBuildWelcomeMessage_GraycodeWordmarkBlinks(t *testing.T) { + out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.Settings{}, 0, true, 120, 40, nil) if !strings.Contains(out, "|-\\/-|") { - t.Fatalf("blinking welcome should close the hawk's eyes, got:\n%s", out) + t.Fatalf("blinking welcome should close the graycode'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, hawkconfig.Settings{}, 0, false, 120, 40, nil) + out := buildWelcomeMessage(nil, "", nil, nil, graycodeconfig.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 8c9ffb74..645e72a1 100644 --- a/deploy/docker/docker-compose.yml +++ b/deploy/docker/docker-compose.yml @@ -1,7 +1,7 @@ -name: hawk +name: graycode services: - hawk: + graycode: build: context: ../../ dockerfile: Dockerfile @@ -11,7 +11,7 @@ services: VERSION: ${VERSION:-dev} COMMIT: ${COMMIT:-none} BUILD_DATE: ${BUILD_DATE:-unknown} - image: ghcr.io/graycodeai/hawk:dev + image: ghcr.io/graycodeai/graycode: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: - - HAWK_DAEMON_API_KEY=${HAWK_DAEMON_API_KEY:-} - - HAWK_DAEMON_PORT=4590 - - HAWK_DAEMON_HOST=0.0.0.0 + - GRAYCODE_DAEMON_API_KEY=${GRAYCODE_DAEMON_API_KEY:-} + - GRAYCODE_DAEMON_PORT=4590 + - GRAYCODE_DAEMON_HOST=0.0.0.0 env_file: - path: ../../.env.example required: false diff --git a/docs/DAEMON-PORT-THREAT-MODEL.md b/docs/DAEMON-PORT-THREAT-MODEL.md index 01b84e66..3de2e7ae 100644 --- a/docs/DAEMON-PORT-THREAT-MODEL.md +++ b/docs/DAEMON-PORT-THREAT-MODEL.md @@ -1,6 +1,6 @@ -# Hawk Daemon — Port 4590 Threat Model +# Graycode Daemon — Port 4590 Threat Model -The Hawk daemon (`hawk daemon`) binds an HTTP server on port **4590** (default) +The Graycode daemon (`graycode 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 `HAWK_DAEMON_API_KEY` and configure the daemon with `--api-key` to +- Set `GRAYCODE_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 hawk. +Any local process can detect that port 4590 is open and identify graycode. **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 -`~/.hawk/sessions/hawk.db`. A local attacker with filesystem read access can +`~/.graycode/sessions/graycode.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 `HAWK_DAEMON_MAX_CONCURRENT`). When the cap is hit, new `/v1/chat` + via `GRAYCODE_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**: hawk does not run as root and does not use +- **Process privilege escalation**: graycode 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 HAWK_DAEMON_API_KEY="your-random-secret-here" -hawk daemon +export GRAYCODE_DAEMON_API_KEY="your-random-secret-here" +graycode daemon # Option 2: CLI flag -hawk daemon --api-key "your-random-secret-here" +graycode 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 `HAWK_DAEMON_API_KEY` automatically if the env var is +The SDK reads this from `GRAYCODE_DAEMON_API_KEY` automatically if the env var is set. --- @@ -104,10 +104,10 @@ set. ## Changing the port ```bash -hawk daemon --port 9000 +graycode daemon --port 9000 ``` -Or in `~/.hawk/settings.json`: +Or in `~/.graycode/settings.json`: ```json { "daemon": { @@ -120,13 +120,13 @@ Or in `~/.hawk/settings.json`: ## Shared machine considerations -If hawk is running on a machine shared by multiple OS users (e.g., a dev +If graycode 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 hawk-owner UID +# Linux — iptables: allow only the graycode-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 f282371f..d34571b2 100644 --- a/docs/DEVELOPER-PATH.md +++ b/docs/DEVELOPER-PATH.md @@ -1,13 +1,13 @@ -# Hawk Developer Path +# Graycode Developer Path -This guide explains what `hawk path` checks and how to get a fresh developer machine ready to use Hawk safely. +This guide explains what `graycode path` checks and how to get a fresh developer machine ready to use Graycode safely. ## What "developer path" means -For Hawk, the developer path is the minimum local setup required to chat, edit code, and keep credentials off disk: +For Graycode, 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 Hawk settings +- A model selected in Graycode 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 @@ -16,101 +16,94 @@ For Hawk, the developer path is the minimum local setup required to chat, edit c Run the report at any time: ```bash -hawk path -hawk path --strict -hawk doctor -hawk preflight +graycode path +graycode path --strict +graycode doctor +graycode preflight ``` ## Setup checklist ### 1. Build and workspace setup -If you are contributing from source, clone Hawk as the main CLI. The support +If you are contributing from source, clone Graycode as the main CLI. The support repositories are independent sibling checkouts when you need the full local -workspace; they are not nested under Hawk: +workspace; they are not nested under Graycode: ```bash mkdir graycode-eco && cd graycode-eco -git clone https://github.com/GrayCodeAI/hawk -git clone https://github.com/GrayCodeAI/eagle -git clone https://github.com/GrayCodeAI/eyrie -git clone https://github.com/GrayCodeAI/falcon -git clone https://github.com/GrayCodeAI/harrier -git clone https://github.com/GrayCodeAI/shrike -git clone https://github.com/GrayCodeAI/swift -git clone https://github.com/GrayCodeAI/kestrel -git clone https://github.com/GrayCodeAI/merlin -cd hawk +git clone https://github.com/GrayCodeAI/graycode-cli +git clone https://github.com/GrayCodeAI/graycode-router +cd graycode-cli make setup -go build -o hawk ./cmd/hawk +go build -o graycode ./cmd/graycode ``` `make setup` validates the canonical 15-repository manifest and regenerates the -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 +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 workspace is only required for cross-repository development and boundary checks. ### 2. Configure credentials -Start Hawk and use `/config` to paste an API key or configure a local provider like Ollama. +Start Graycode and use `/config` to paste an API key or configure a local provider like Ollama. -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. +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. Useful checks: ```bash -hawk credentials status -hawk preflight +graycode credentials status +graycode preflight ``` -`hawk preflight` is a local-ready check; it does not contact a provider. To +`graycode 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 `hawk models list --live`. +validation or `graycode models list --live`. ### 3. Select a model -Pick a model in `/config`. Hawk stores the selected model in settings and uses eyrie for provider routing and catalog resolution. +Pick a model in `/config`. Graycode stores the selected model in settings and uses eyrie for provider routing and catalog resolution. If the catalog is missing or empty: ```bash -hawk models refresh +graycode models refresh ``` ## Security checks -`hawk path` treats these as important security conditions: +`graycode path` treats these as important security conditions: - Eyrie's resolved `provider.json` must not contain secret fields -- legacy `~/.hawk/env` or `~/.hawk/.env` files should be migrated away +- legacy `~/.graycode/env` or `~/.graycode/.env` files should be migrated away - sensitive files like provider config and SSH paths should be blocked from agent reads 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. +`GRAYCODE_CONFIG_DIR` for compatibility, then the platform user-config directory. +Graycode protects that resolved path even when it is customized or symlinked. -If Hawk detects old plaintext secrets, run Hawk once and complete `/config`, or remove the secret fields manually after backing up the file. +If Graycode detects old plaintext secrets, run Graycode 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, -`hawk path` reports a blocking failure and agent tools remain locked. Hawk does +`graycode path` reports a blocking failure and agent tools remain locked. Graycode does not offer a host-execution fallback. -The versioned `graycodeai/hawk-sandbox` image is pulled automatically when it -is not already local. If the public registry is unavailable, Hawk builds its +The versioned `graycodeai/graycode-sandbox` image is pulled automatically when it +is not already local. If the public registry is unavailable, Graycode builds its bundled sandbox Dockerfile locally through Docker. ```bash -hawk path --strict +graycode path --strict ``` ## Ecosystem checks -`hawk path` also verifies the core support layer behind Hawk: +`graycode path` also verifies the core support layer behind Graycode: - `eyrie` for provider routing and local preflight readiness - `shrike` for token estimation and compression @@ -119,19 +112,19 @@ hawk path --strict If you want the broader status summary: ```bash -hawk ecosystem -hawk doctor +graycode ecosystem +graycode doctor ``` ## Typical recovery path -If `hawk path` says you are not ready, this is the intended order: +If `graycode path` says you are not ready, this is the intended order: -1. Run `hawk` +1. Run `graycode` 2. Open `/config` 3. Paste an API key or configure Ollama 4. Pick a model -5. Re-run `hawk preflight` -6. Re-run `hawk path` +5. Re-run `graycode preflight` +6. Re-run `graycode 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 574c8c51..4a131d15 100644 --- a/docs/DYNAMIC-MODELS.md +++ b/docs/DYNAMIC-MODELS.md @@ -1,8 +1,8 @@ -# Dynamic Model Discovery — Hawk Developer Guide +# Dynamic Model Discovery — Graycode Developer Guide ## Ownership and flow -Hawk is the product face; Eyrie is the provider engine. Hawk owns the CLI/TUI, +Graycode is the product face; Eyrie is the provider engine. Graycode owns the CLI/TUI, model-picker presentation, user intent, and output compatibility. The `eyrie/engine` facade alone owns provider registry details, credentials, discovery, catalog/cache policy, model aliases, deployment routing, and chat @@ -17,18 +17,18 @@ provider APIs / remote catalog / local cache | stable host DTOs and methods v - Hawk composition and presentation + Graycode composition and presentation /config | models | conversation ``` -Production Hawk packages must not import Eyrie packages below +Production Graycode packages must not import Eyrie packages below `github.com/GrayCodeAI/eyrie/engine`. Shell and AST guards enforce a zero-exception boundary. -## Hawk composition boundary +## Graycode composition boundary -`internal/config` is Hawk's control-plane composition root. It creates an -Eyrie engine and projects engine models into Hawk UI and command contracts: +`internal/config` is Graycode's control-plane composition root. It creates an +Eyrie engine and projects engine models into Graycode UI and command contracts: ```go models, err := config.ListEngineModels(ctx, "anthropic", false) @@ -36,10 +36,10 @@ live, err := config.ListEngineModels(ctx, "anthropic", true) public, err := config.ListPublicEngineModels(ctx, "xiaomi_mimo_payg") ``` -Conversation construction goes through Hawk's `internal/engine` adapter. The -adapter translates Hawk-owned message, tool, usage, and stream DTOs to the +Conversation construction goes through Graycode's `internal/engine` adapter. The +adapter translates Graycode-owned message, tool, usage, and stream DTOs to the Eyrie engine facade; conversation history, WAL, resume, approvals, and tool -execution remain Hawk-owned. +execution remain Graycode-owned. ## Catalog and live discovery @@ -52,20 +52,20 @@ process-global credentials. Use the normal cache-backed path for repeatable UI and automation: ```bash -hawk models list anthropic -hawk models list anthropic --json +graycode models list anthropic +graycode models list anthropic --json ``` Use a provider-scoped live request when current connectivity and credentials must be checked: ```bash -hawk models list anthropic --live -hawk models list anthropic --live --json -hawk models list anthropic --live --raw +graycode models list anthropic --live +graycode models list anthropic --live --json +graycode models list anthropic --live --raw ``` -`hawk preflight` reports **local readiness**: usable local state, a selected +`graycode 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,7 +73,7 @@ validation) as **live verified**. ## Stable command output -`hawk models list --json` is a Hawk-owned compatibility contract, not a direct +`graycode models list --json` is a Graycode-owned compatibility contract, not a direct serialization of Eyrie's evolving `engine.Model` DTO. Its stable fields are: ```text @@ -83,11 +83,11 @@ 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 Hawk compatibility row instead of `null`. +metadata, it returns the stable Graycode compatibility row instead of `null`. ## Custom gateways -Hawk converts effective `custom_providers` settings into +Graycode 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 Eyrie process-global state: tests, parallel sessions, and future multi-tenant @@ -100,20 +100,20 @@ hosts must be isolated from one another. 2. Add Eyrie tests for cache and live discovery, credential status, selection, and generation/streaming. 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. +4. Advance Graycode's `../graycode-router` sibling checkout to that exact commit, then update + Graycode's module version when the Eyrie revision is published. 5. Verify both the workspace (`go.work`) and published-module (`GOWORK=off`) build modes. -Hawk changes are needed only for a new product behavior or an additive -Hawk-owned presentation field—not for provider-specific mechanics. +Graycode changes are needed only for a new product behavior or an additive +Graycode-owned presentation field—not for provider-specific mechanics. ## Checks ```bash -hawk models refresh -hawk models status -hawk preflight +graycode models refresh +graycode models status +graycode 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 77ea2af1..722d6232 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: hawk, eyrie, harrier, shrike, swift +Applies to: graycode, 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 (hawk: `config.json` + `HAWK_*`/`GRAYCODE_*` env; eyrie: +flags, and config files (graycode: `config.json` + `GRAYCODE_*`/`GRAYCODE_*` 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 hawk + eyrie). A repo only reads its sections. + `model`/`providers` shared by graycode + 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. `$HAWK_ECO_CONFIG` +2. `$GRAYCODE_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 (hawk + eyrie) ─────────────────────────────── +# ─── Shared: model + providers (graycode + eyrie) ─────────────────────────────── model: default: anthropic/claude-sonnet-4-5 # provider/model the agent uses small_fast: anthropic/claude-haiku # cheap model for trivial steps @@ -68,7 +68,7 @@ providers: # ─── eyrie: gateway / runtime ─────────────────────────────────────────────── gateway: - base_url: http://localhost:8080 # eyrie endpoint hawk talks to + base_url: http://localhost:8080 # eyrie endpoint graycode talks to api_key_env: EYRIE_API_KEY allow_insecure_public_api: false deployment_routing: "" # EYRIE_DEPLOYMENT_ROUTING @@ -119,9 +119,9 @@ swift: telemetry: # OTel exporter settings shared by all repos. Span attribute keys follow # docs/OTEL-CONVENTIONS.md. - enabled: false # hawk: HAWK_CODE_ENABLE_TELEMETRY + enabled: false # graycode: GRAYCODE_ENABLE_TELEMETRY otlp_endpoint: "" # OTEL_EXPORTER_OTLP_ENDPOINT - shutdown_timeout_ms: 0 # HAWK_CODE_OTEL_SHUTDOWN_TIMEOUT_MS + shutdown_timeout_ms: 0 # GRAYCODE_OTEL_SHUTDOWN_TIMEOUT_MS ``` ## Setting → repo → existing mechanism @@ -129,13 +129,13 @@ telemetry: The authoritative mapping. "Mechanism today" is what already implements the setting; the unified key is rendered down to it. -### Shared: model / providers (hawk + eyrie) +### Shared: model / providers (graycode + eyrie) | Unified key | Repo | Mechanism today | |---------------------------------|-------------|--------------------------------------------------| -| `model.default` | hawk | `HAWK_MODEL` env / `config.json` | -| `model.small_fast` | hawk | `GRAYCODE_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` | +| `model.default` | graycode | `GRAYCODE_MODEL` env / `config.json` | +| `model.small_fast` | graycode | `GRAYCODE_SMALL_FAST_MODEL` env | +| `providers[].api_key_env` | eyrie/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` | 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 | @@ -145,14 +145,14 @@ setting; the unified key is rendered down to it. | Unified key | Mechanism today (eyrie) | |--------------------------------------|--------------------------------------| -| `gateway.base_url` | `EYRIE_BASE_URL` (hawk→eyrie link) | +| `gateway.base_url` | `EYRIE_BASE_URL` (graycode→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.deployment_routing` | `EYRIE_DEPLOYMENT_ROUTING` (also `GRAYCODE_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`) | +| `gateway.model_catalog.refresh` | `EYRIE_MODEL_CATALOG_REFRESH` / `GRAYCODE_AUTO_REFRESH_CATALOG` / `GRAYCODE_CATALOG_REFRESH_ALWAYS` | +| `gateway` config dir | `GRAYCODE_CONFIG_DIR` (default `~/.eyrie`) | ### harrier: memory @@ -193,22 +193,22 @@ few env vars. ### swift + telemetry -| Unified key | Mechanism today (swift / hawk) | +| Unified key | Mechanism today (swift / graycode) | |-----------------------------------|-------------------------------------------------| | `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` | `HAWK_CODE_ENABLE_TELEMETRY` env (hawk) | -| `telemetry.shutdown_timeout_ms` | `HAWK_CODE_OTEL_SHUTDOWN_TIMEOUT_MS` env (hawk) | +| `telemetry.enabled` | `GRAYCODE_ENABLE_TELEMETRY` env (graycode) | +| `telemetry.shutdown_timeout_ms` | `GRAYCODE_OTEL_SHUTDOWN_TIMEOUT_MS` env (graycode) | | `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** (hawk, eyrie, shrike, swift): export the mapped env var for +- **env-based repos** (graycode, 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 diff --git a/docs/ECOSYSTEM-WIRING.md b/docs/ECOSYSTEM-WIRING.md index 03e71c79..c055b666 100644 --- a/docs/ECOSYSTEM-WIRING.md +++ b/docs/ECOSYSTEM-WIRING.md @@ -1,6 +1,10 @@ # GrayCode ecosystem wiring -This document is the implementation contract for the 15 repositories in the +> NOTE (2026-09-04): the `eagle` repository has been removed; its contracts +> were vendored into graycode-cli'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 GrayCodeAI ecosystem. `ecosystem.yaml` is the canonical machine-readable inventory; generated workspaces, boundary checks, release parity, and Owl's repository catalog derive from it. @@ -13,23 +17,23 @@ flowchart LR Lists --> Scripts[Workspace and release scripts] Lists --> Owl[Owl catalog] - Hawk[Hawk product] --> Engines[Six engine repositories] + Graycode[Graycode product] --> Engines[Six engine repositories] Engines --> Eagle[Eagle contracts] - Hawk --> Eagle + Graycode --> Eagle - HawkSpec[Hawk OpenAPI] -. manual snapshots .-> Sparrow - HawkSpec -. manual snapshots .-> Robin - HawkSpec -. no snapshot .-> Wren + GraycodeSpec[Graycode OpenAPI] -. manual snapshots .-> Sparrow + GraycodeSpec -. manual snapshots .-> Robin + GraycodeSpec -. no snapshot .-> Wren Browser[GrayCode browser] --> Mixed[One mixed Worker] Mixed --> Identity[(Identity tables)] - Mixed --> Cloud[(Hawk Cloud tables)] + Mixed --> Cloud[(Graycode 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 Hawk control plane +SDK contracts were inconsistent, browser identity and the Graycode control plane shared a Worker and D1 binding, and a successful usage write did not guarantee a durable Queue-delivery intent. @@ -37,20 +41,20 @@ durable Queue-delivery intent. ```mermaid flowchart TB - Manifest[hawk/ecosystem.yaml\ncanonical repo and contract inventory] + Manifest[graycode/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] --> 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] + User[CLI / daemon user] --> Graycode[Graycode composition root] + Graycode --> Eyrie[Eyrie 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] Eyrie --> Eagle Harrier --> Eagle Shrike --> Eagle @@ -59,7 +63,7 @@ flowchart TB Merlin --> Eagle end - Hawk --> Daemon[Hawk daemon API] + Graycode --> Daemon[Graycode daemon API] Daemon --> OpenAPI[api/openapi.yaml] OpenAPI --> Sparrow[Sparrow Go SDK snapshot] OpenAPI --> Robin[Robin Python SDK snapshot] @@ -68,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[Hawk Cloud Worker] + BFF -->|private typed Service Binding| Cloud[Graycode Cloud Worker] Cloud --> Control[(Control-plane D1)] Cloud -->|same D1 transaction| Outbox[(Usage outbox)] Outbox -->|immediate + scheduled retry| Queue[Cloudflare Queue] @@ -76,21 +80,21 @@ flowchart TB Queue --> Archive[(R2 archive)] end - Hawk -. explicit opt-in; fail-open .-> Cloud + Graycode -. explicit opt-in; fail-open .-> Cloud ``` ## Ownership rules | Boundary | Owner | Rule | |---|---|---| -| Product orchestration | Hawk | Engines never orchestrate Hawk or one another. | -| Provider runtime | Eyrie | Hawk imports its supported `engine` facade. | +| Product orchestration | Graycode | Engines never orchestrate Graycode or one another. | +| Provider runtime | Eyrie | Graycode imports its supported `engine` facade. | | Shared data contracts | Eagle | Neutral types only; no product behavior. | -| 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. | +| 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. | | 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 | Hawk Cloud | Organizations, projects, devices, usage, billing, graph ledger, and audit. | +| Hosted control plane | Graycode 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. | @@ -108,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 `hawk.graph/v1`. Hawk Cloud independently +3. Local graph projections use `graycode.graph/v1`. Graycode 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` @@ -119,12 +123,12 @@ must use `directory`/`github_repo`; UI copy may use `product_name`. ## Release and development workflow -- Run `make workspace` in Hawk to regenerate the parent `go.work`. +- Run `make workspace` in Graycode 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 Hawk and separately checks +- SDK CI checks its snapshot byte-for-byte against Graycode 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. @@ -134,17 +138,17 @@ must use `directory`/`github_repo`; UI copy may use `product_name`. ### Coordinated Go publication gate 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. +published `origin/main`. Do not merge or release Graycode against the older Eyrie +pseudo-version: it still exposes the retired `graycode-core-contracts` types and is +not type-compatible with Graycode's Eagle boundary. 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/eyrie@`. -3. Update Hawk's Eyrie requirement, run `GOWORK=off go mod tidy`, and remove any +3. Update Graycode's Eyrie requirement, run `GOWORK=off go mod tidy`, and remove any transition excludes no longer required by the published engine graphs. -4. Require Hawk's `public-modules` and `release-parity` CI jobs to pass before - merging or tagging Hawk. +4. Require Graycode's `public-modules` and `release-parity` CI jobs to pass before + merging or tagging Graycode. This publication is intentionally not performed by the source implementation: remote branch merges and tags are externally visible release operations. @@ -155,11 +159,11 @@ remote branch merges and tags are externally visible release operations. 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 `HawkCloudService` entrypoint and +3. Deploy `graycode-cloud` with the named `GraycodeCloudService` 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. Hawk/CLI device start and - poll traffic continues to target Hawk Cloud. +5. Point the web frontend API hostname at the BFF. Graycode/CLI device start and + poll traffic continues to target Graycode 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 fa73e78c..5dbb0b75 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/hawk-architecture-baseline.md`. +`docs/architecture/graycode-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 (hawk repo) +### 1. Add VS Code Extension Integration (graycode repo) **Effort:** Large (2-3 weeks) **Priority:** HIGH **What:** -- Create `hawk-vscode` repo for VS Code extension -- Use Hawk SDK for communication +- Create `graycode-vscode` repo for VS Code extension +- Use Graycode 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 hawk-vscode -cd hawk-vscode +mkdir graycode-vscode +cd graycode-vscode # Initialize git init -go mod init github.com/GrayCodeAI/hawk-vscode +go mod init github.com/GrayCodeAI/graycode-cli-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": "hawk-code", - "displayName": "Hawk Code", + "name": "graycode", + "displayName": "Graycode", "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": "hawk.code.activate", - "title": "Activate Hawk Code" + "command": "graycode.activate", + "title": "Activate Graycode" } ], "menus": { "commandPalette": [ { - "command": "hawk.code.activate", + "command": "graycode.activate", "when": "editorLangId" } ] @@ -94,22 +94,22 @@ EOF # Add extension scaffold cat > extension/src/extension.ts << 'EOF' import * as vscode from 'vscode'; -import * as hawksdk from '@graycodeai/hawksdk'; +import * as graycodesdk from '@graycodeai/graycodesdk'; export function activate(context: vscode.ExtensionContext) { - const client = new hawksdk.Client(); + const client = new graycodesdk.Client(); // Register completion provider vscode.languages.registerCompletionItemProvider( 'go', - new HawkCompletionProvider(client), + new GraycodeCompletionProvider(client), 'g', 'h' ); // Register hover provider vscode.languages.registerHoverProvider( 'go', - new HawkHoverProvider(client) + new GraycodeHoverProvider(client) ); } EOF @@ -123,7 +123,7 @@ vsce publish **Repository Structure:** ``` -hawk-vscode/ +graycode-vscode/ ├── extension/ # VS Code extension code │ ├── src/ │ │ ├── extension.ts @@ -140,25 +140,25 @@ hawk-vscode/ --- -### 2. Add Extension Marketplace (hawk repo) +### 2. Add Extension Marketplace (graycode repo) **Effort:** Medium (1-2 weeks) **Priority:** HIGH **What:** - Create marketplace for community extensions -- Add extension discovery endpoint to hawk +- Add extension discovery endpoint to graycode - Create `starling` integration - Add version compatibility checking **Why:** - Top agents have 100+ extensions -- Build ecosystem around Hawk +- Build ecosystem around Graycode - Increase adoption **Implementation Plan:** ```go -// Add to hawk +// Add to graycode package marketplace // Extension represents a community extension @@ -305,12 +305,12 @@ cat >> SKILL.md << 'EOF' To discover available extensions, use: ``` -hawk extensions list +graycode extensions list ``` To install an extension: ``` -hawk extensions install +graycode extensions install ``` EOF ``` @@ -431,7 +431,7 @@ go build forum.go --- -### 5. Add More Extension Points (hawk) +### 5. Add More Extension Points (graycode) **Effort:** Small (3-5 days) **Priority:** MEDIUM @@ -514,7 +514,7 @@ func main() { --- -### 7. Add Debugging Support (hawk) +### 7. Add Debugging Support (graycode) **Effort:** Large (2-3 weeks) **Priority:** LOW @@ -530,7 +530,7 @@ func main() { --- -### 8. Add Web UI for Monitoring (hawk) +### 8. Add Web UI for Monitoring (graycode) **Effort:** Small (3-5 days) **Priority:** LOW @@ -579,7 +579,7 @@ func main() { ## Detailed Repo-Specific Roadmap -### **hawk** (Main Repo) - Primary Product +### **graycode** (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/hawk-architecture-baseline.md`. +Current architecture status: see `docs/architecture/graycode-architecture-baseline.md`. --- @@ -657,7 +657,7 @@ Current architecture status: see `docs/architecture/hawk-architecture-baseline.m 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/hawk-architecture-baseline.md`. +in `docs/architecture/graycode-architecture-baseline.md`. --- @@ -667,7 +667,7 @@ in `docs/architecture/hawk-architecture-baseline.md`. |------|--------|------------| | Marketplace competition | High | Focus on quality extensions | | Community building | Medium | Active engagement, good docs | -| Development velocity | Medium | Prioritize hawk SDK first | +| Development velocity | Medium | Prioritize graycode SDK first | | Adoption curve | Medium | VS Code extension is key | --- diff --git a/docs/OTEL-CONVENTIONS.md b/docs/OTEL-CONVENTIONS.md index efb61781..5fd41e99 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: hawk, eyrie, harrier, shrike, swift +Applies to: graycode, 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 @@ -23,7 +23,7 @@ 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 Eyrie implementation detail; Hawk must not import or compose it +decorator is an Eyrie implementation detail; Graycode must not import or compose it directly. - `eyrie/internal/observability/observability.go` provides a stdlib-only, @@ -34,9 +34,9 @@ directly. of hard-coding strings. A pinning test (`genai_semconv_test.go`) guards the exact key values. -When adding tracing to Hawk, propagate swift context through the Engine call and +When adding tracing to Graycode, 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 +Graycode wraps product turns and tools. Harrier, Shrike, and Swift instrument only their own operations. ## Span kinds and names @@ -113,8 +113,8 @@ Mapping: - **eyrie** — owns provider/model/usage spans behind `eyrie/engine`; align attribute keys to `gen_ai.*` over time. -- **hawk** — daemon/orchestrator. Already has OTel hooks - (`HAWK_CODE_ENABLE_TELEMETRY`, `HAWK_CODE_OTEL_SHUTDOWN_TIMEOUT_MS`). Emit +- **graycode** — daemon/orchestrator. Already has OTel hooks + (`GRAYCODE_ENABLE_TELEMETRY`, `GRAYCODE_OTEL_SHUTDOWN_TIMEOUT_MS`). Emit `agent.id` and `session.id` on agent-turn spans; propagate them downstream to eyrie via context so provider spans inherit the same IDs. - **harrier** — memory service. Add `embeddings` spans with `gen_ai.system` + diff --git a/docs/PERMISSION-MODEL-IMPROVEMENTS.md b/docs/PERMISSION-MODEL-IMPROVEMENTS.md index 9f21f8f4..cc4f67a3 100644 --- a/docs/PERMISSION-MODEL-IMPROVEMENTS.md +++ b/docs/PERMISSION-MODEL-IMPROVEMENTS.md @@ -1,6 +1,6 @@ -# Hawk Permission Model — Improvements (2026-08-07) +# Graycode Permission Model — Improvements (2026-08-07) -This document describes the improvements made to hawk's permission, isolation, +This document describes the improvements made to graycode'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 `hawk-seatbelt-*.sb` files from +**Solution:** `seatbelt.go` `init()` removes orphaned `graycode-seatbelt-*.sb` files from `os.TempDir()` at process startup. --- diff --git a/docs/SECURITY-DEVELOPER.md b/docs/SECURITY-DEVELOPER.md index 4cf2d5d5..6f1a0c7a 100644 --- a/docs/SECURITY-DEVELOPER.md +++ b/docs/SECURITY-DEVELOPER.md @@ -1,13 +1,13 @@ -# Hawk developer security model +# Graycode developer security model -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. +This document describes how graycode 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). -- Hawk does not read API keys from `.env`, shell env, or plaintext files. +- Graycode 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. +- Graycode 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 hawk and eyrie handle API keys and agent isolation f | Write | Read | Remove | |-------|------|--------| -| `/config` paste flow → `eyrie/engine.Engine.SaveCredential` | `Engine.ResolveCredential` (secret store only) | `/config key remove` or `hawk credentials remove` | +| `/config` paste flow → `eyrie/engine.Engine.SaveCredential` | `Engine.ResolveCredential` (secret store only) | `/config key remove` or `graycode credentials remove` | -On startup, Hawk asks the Eyrie engine facade to migrate legacy -`~/.hawk/env` / `~/.hawk/.env` values into the secret store and delete those +On startup, Graycode asks the Eyrie engine facade to migrate legacy +`~/.graycode/env` / `~/.graycode/.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: `hawk credentials status`, `hawk path`, or `hawk preflight`. +Check status: `graycode credentials status`, `graycode path`, or `graycode preflight`. ## First-run flow (`/config`) @@ -32,7 +32,7 @@ Check status: `hawk credentials status`, `hawk path`, or `hawk preflight`. User pastes API key in /config | v -Hawk /config -> Eyrie engine credential service (OS secret store) +Graycode /config -> Eyrie engine credential service (OS secret store) | v Eyrie engine discover/apply (credentials from store, not JSON body) @@ -46,20 +46,20 @@ User picks model -> settings.json (canonical id only) Remove a stored key: `/config key remove` (interactive picker). -## Hawk to Eyrie +## Graycode to Eyrie -- **Control plane**: Hawk calls only `eyrie/engine`; no lower Eyrie package is a +- **Control plane**: Graycode 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**: Hawk sends model intent, messages, and tool definitions; Eyrie +- **Chat**: Graycode sends model intent, messages, and tool definitions; Eyrie resolves the gateway and reads secrets internally. ## Agent isolation ``` +------------------+ +------------------+ -| Hawk TUI/host | | Docker sandbox | +| Graycode TUI/host | | Docker sandbox | | Keychain access | | Commands only | | /config paste | | project mount | +------------------+ +------------------+ @@ -69,25 +69,25 @@ Remove a stored key: `/config key remove` (interactive picker). ``` When the container is ready, `session.ContainerExecutor` runs agent commands in -Docker. Hawk fails closed when Docker is unavailable; it never falls back to +Docker. Graycode fails closed when Docker is unavailable; it never falls back to host command execution. -The sandbox image has an independent compatibility version embedded in Hawk. +The sandbox image has an independent compatibility version embedded in Graycode. Startup first checks the local Docker image cache, then anonymously pulls the -public `graycodeai/hawk-sandbox` image. If the registry cannot be reached, Hawk +public `graycodeai/graycode-sandbox` image. If the registry cannot be reached, Graycode 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 Hawk env files, Eyrie's configured `provider.json`, +- **Read** tool: legacy Graycode env files, Eyrie's configured `provider.json`, `~/.ssh/*`, etc. -- **Bash**: `printenv`, `env`, reading hawk env paths, echoing `*_API_KEY` variables. +- **Bash**: `printenv`, `env`, reading graycode env paths, echoing `*_API_KEY` variables. ## Migration -- **Legacy env files**: startup migration imports `~/.hawk/env` and - `~/.hawk/.env` into the OS secret store, then deletes the plaintext files. +- **Legacy env files**: startup migration imports `~/.graycode/env` and + `~/.graycode/.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. @@ -99,27 +99,27 @@ required for users, and neither provisioning path enables host execution. Eyrie owns the provider-state path. Resolution order is: 1. `EYRIE_CONFIG_DIR/provider.json` -2. `HAWK_CONFIG_DIR/provider.json` (compatibility fallback) -3. the platform user-config directory under `hawk/provider.json` +2. `GRAYCODE_CONFIG_DIR/provider.json` (compatibility fallback) +3. the platform user-config directory under `graycode/provider.json` -Hawk's Read/Edit/Write and Bash safety checks protect the resolved path, +Graycode'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 (hawk does not load provider API keys from env): +Non-secret overrides only (graycode does not load provider API keys from env): | Variable | Meaning | |----------|---------| -| `HAWK_CONFIG_DIR` | Override hawk config directory | +| `GRAYCODE_CONFIG_DIR` | Override graycode 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 -- Hawk: `internal/config/eyrie_engine.go`, `internal/tool/safety.go`, +- Graycode: `internal/config/eyrie_engine.go`, `internal/tool/safety.go`, `internal/storage/paths.go`, `cmd/credentials.go` - 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 c3d1fe39..bcdfec5b 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,6 +1,6 @@

-# bird hawk Architecture +# bird graycode Architecture **AI Coding Agent for Your Terminal** @@ -14,19 +14,19 @@ ## target Overview -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. +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. -Detailed planning docs for the Hawk product architecture live in [`docs/architecture/`](architecture/README.md). +Detailed planning docs for the Graycode product architecture live in [`docs/architecture/`](architecture/README.md). --- ## blocks Layered Architecture ``` -hawk/ +graycode/ ├── api/openapi.yaml file-text Daemon REST API contract (OpenAPI 3.1) ├── cmd/ terminal Cobra CLI commands (200+ files) -│ ├── hawk/main.go zap Entry point — calls cmd.Execute() +│ ├── graycode/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 @@ hawk/ └── (ecosystem siblings live at ../ in the graycode-eco workspace; see docs/architecture/ecosystem-design.md) ``` -Legacy note: `hawk/shared/types` has been removed. Shared cross-repo severity +Legacy note: `graycode/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: `HAWK_DAEMON_PORT` | -| **Auth** | Bearer token or `X-API-Key`. Set via `HAWK_DAEMON_API_KEY` | +| **Port** | `:4590` (default). Override: `GRAYCODE_DAEMON_PORT` | +| **Auth** | Bearer token or `X-API-Key`. Set via `GRAYCODE_DAEMON_API_KEY` |
radio Endpoint Summary @@ -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 **hawk never talks to LLM APIs directly** — all calls go through eyrie. +> lightbulb **graycode never talks to LLM APIs directly** — all calls go through eyrie. --- @@ -125,9 +125,9 @@ Tool Call → ` — persisted Hawk session; -- `hawk/task-request//` — user task request, content represented by +- `graycode/session/` — persisted Graycode session; +- `graycode/task-request//` — user task request, content represented by SHA-256 only; -- `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; +- `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; - `harrier/memory/` — retrieved memory knowledge; -- `hawk/code-chunk/` — retrieved code-index knowledge; +- `graycode/code-chunk/` — retrieved code-index knowledge; - `merlin/report/` and `merlin/finding/` — website audit quality facts; - `kestrel/review/` and `kestrel/finding/` — code-review quality @@ -116,7 +116,7 @@ Edges: - `contains` — session/task hierarchy and task-to-tool containment; - `depends_on` — blocking task dependency; -- `references` — related tasks and Hawk-to-Swift checkpoint linkage; +- `references` — related tasks and Graycode-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`. -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 +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 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 `hawk.graph/v1` envelope, but it is mission-scoped rather than +portable `graycode.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 -hawk graph export [session-id] +graycode 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_HAWK_SESSION_ID=` at its actual session-start +`SWIFT_TAG_GRAYCODE_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 -Hawk export, and incomplete checkpoint enumeration produces a session link +Graycode 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 `hawk.graph/v1` envelope. The handler owns transport concerns -only; the Hawk composition root injects the existing graph builder, so CLI, +returns the typed `graycode.graph/v1` envelope. The handler owns transport concerns +only; the Graycode 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 `hawk cloud login` or `hawk cloud connect`, a +After connecting a project with `graycode cloud login` or `graycode cloud connect`, a completed session snapshot can be uploaded explicitly: ```bash -hawk cloud graph sync [session-id] +graycode 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. -Hawk does not upload prompt bodies, response bodies, tool arguments, tool +Graycode 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 Hawk session: +pretended to be a Graycode session: ```bash -hawk graph export --mission-dir -hawk cloud graph sync --mission-dir +graycode graph export --mission-dir +graycode cloud graph sync --mission-dir ``` -The CLI reads only `mission-graph.json`, validates its `hawk.graph/v1` schema +The CLI reads only `mission-graph.json`, validates its `graycode.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,7 +227,7 @@ The central tool-execution seam automatically records: - aggregate results from `VerifyPlanExecution`. - Harrier subgraphs selected by direct, graph-budget, global, proactive, and shared-memory retrieval; -- Hawk code-index chunks selected through Harrier-backed code search. +- Graycode 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. @@ -237,40 +237,40 @@ The central tool-execution seam automatically records: - Eyrie model route and usage reported for persisted-session turns. Mission execution now also persists a portable graph artifact for local mission -runs. Plain `hawk mission` runs emit mission and feature execution nodes; the -graph-driven `hawk mission --from-tasks` path additionally emits operations +runs. Plain `graycode mission` runs emit mission and feature execution nodes; the +graph-driven `graycode 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 Hawk's neutral aggregate verification observation. The +quality topology and Graycode'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 Hawk cost accounting and limits remain authoritative +pre-turn guard. Existing Graycode 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 --hawk-session ` surface through the permitted +`graph correlation --graycode-session ` surface through the permitted `cli.NewRootCmd()` boundary. It: -- matches only the `SWIFT_TAG_HAWK_SESSION_ID` value stored at Swift's actual +- matches only the `SWIFT_TAG_GRAYCODE_SESSION_ID` value stored at Swift's actual session-start boundary; -- returns the authoritative Swift session and checkpoint IDs for that Hawk ID; +- returns the authoritative Swift session and checkpoint IDs for that Graycode ID; - never guesses identity from the current branch, HEAD, timestamps, or prompt similarity; - returns an empty match set rather than a speculative match. -Hawk now consumes this surface through `cli.NewRootCmd()`, validates the schema -and echoed Hawk identity, bounds the response, and emits: +Graycode now consumes this surface through `cli.NewRootCmd()`, validates the schema +and echoed Graycode identity, bounds the response, and emits: -- Hawk session `references` Swift session; -- Hawk session `references` each authoritative Swift checkpoint; +- Graycode session `references` Swift session; +- Graycode session `references` each authoritative Swift checkpoint; - Swift session `produced` Swift checkpoint. Malformed, mismatched, oversized, unavailable, or incomplete responses fail diff --git a/docs/architecture/hawk-architecture-baseline.md b/docs/architecture/graycode-architecture-baseline.md similarity index 82% rename from docs/architecture/hawk-architecture-baseline.md rename to docs/architecture/graycode-architecture-baseline.md index 24310753..6d2e5c7d 100644 --- a/docs/architecture/hawk-architecture-baseline.md +++ b/docs/architecture/graycode-architecture-baseline.md @@ -1,10 +1,10 @@ -# Hawk Architecture Baseline +# Graycode Architecture Baseline **Status:** Phase 0 baseline **Date:** 2026-08-04 **Baseline commit:** `69ce83f55f9098623e5a891e8c52be636db89c7c` -This document records the architecture that exists in the Hawk repository at +This document records the architecture that exists in the Graycode 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. `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. +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. 5. `spec.md` for behavioral requirements and agent-loop semantics. -Hawk is the main Go CLI/product and workspace entry point in a +Graycode 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 `hawk/external/` vendor tree. +published module pins. There is no current `graycode/external/` vendor tree. ## Target product graph @@ -31,7 +31,7 @@ published module pins. There is no current `hawk/external/` vendor tree. users / SDKs / skills / daemon clients | v - hawk + graycode / | \ eyrie harrier shrike swift kestrel merlin @@ -42,25 +42,25 @@ users / SDKs / skills / daemon clients The graph is intentionally directional: -- Hawk owns user-facing orchestration, sessions, tools, permissions, +- Graycode owns user-facing orchestration, sessions, tools, permissions, composition, and public product surfaces. - 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 - Hawk internals or one another. + Graycode internals or one another. - Core contracts contain stable cross-repository vocabulary and DTOs, not runtime orchestration. -- SDKs and skills consume Hawk surfaces rather than support-engine internals. +- SDKs and skills consume Graycode surfaces rather than support-engine internals. ## Current implementation state ### Complete or enforced -- 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. +- Graycode production code uses Eyrie through the `eyrie/engine` facade. +- Kestrel and Merlin are integrated through Graycode bridge packages. +- Support-engine sibling imports and imports of Graycode internals are guarded. - The AST/package-graph guard reports production boundary violations with - file/line diagnostics across Hawk and available support repositories. + file/line diagnostics across Graycode 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`; Eyrie @@ -87,7 +87,7 @@ 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. -- Hawk's Harrier and Shrike implementation imports are now concentrated in +- Graycode'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 Eyrie @@ -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.newConfiguredHawkSession`; the +- Non-interactive entry points now share `cmd.newConfiguredGraycodeSession`; the interactive TUI intentionally retains a lightweight startup path followed by deferred heavy configuration to protect first-frame latency. @@ -112,15 +112,15 @@ The graph is intentionally directional: ### ADR-B01 — Keep the multi-repository ecosystem -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 +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 local integration without removing independent ownership and release boundaries. ### ADR-B02 — Preserve the Eyrie boundary Provider implementation, catalog metadata, credential mapping, and protocol -adapters remain owned by Eyrie. Hawk may own product policy and user-facing +adapters remain owned by Eyrie. Graycode 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 Hawk from implementation +release needs. A facade is justified when it isolates Graycode 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 Hawk-owned facades. The next +and Shrike implementation imports behind narrow Graycode-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/hawk-architecture-v1-definition-of-done.md b/docs/architecture/graycode-architecture-v1-definition-of-done.md similarity index 73% rename from docs/architecture/hawk-architecture-v1-definition-of-done.md rename to docs/architecture/graycode-architecture-v1-definition-of-done.md index 6df39127..660217c4 100644 --- a/docs/architecture/hawk-architecture-v1-definition-of-done.md +++ b/docs/architecture/graycode-architecture-v1-definition-of-done.md @@ -1,6 +1,6 @@ -# Hawk Architecture v1 — Definition of Done +# Graycode Architecture v1 — Definition of Done -This document defines **realistic v1 complete** for the Hawk main CLI and its +This document defines **realistic v1 complete** for the Graycode 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 (`hawk`) +- one product surface (`graycode`) - 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] `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 +- [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 ### Forbidden edges stay forbidden -- [x] no support repo imports `hawk/internal/*` -- [x] no support repo imports removed `hawk/shared/types` +- [x] no support repo imports `graycode/internal/*` +- [x] no support repo imports removed `graycode/shared/types` - [x] no SDK/skills repo references support engines as primary dependencies -- [x] Hawk production code imports Eyrie only through `eyrie/engine` -- [x] Hawk's graph/projection imports are documented as explicit integration +- [x] Graycode production code imports Eyrie only through `eyrie/engine` +- [x] Graycode's graph/projection imports are documented as explicit integration surfaces and do not create engine-to-engine dependencies ### `eagle` (implemented packages only) @@ -49,7 +49,7 @@ 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 Hawk +- [x] `events/` — normalized audit/swift event subset used by Graycode - [x] `policy/` — permission and guardian decision contracts Adoption bar: @@ -58,16 +58,16 @@ Adoption bar: - [x] `shrike/types` compatibility shim removed from the local ecosystem - [x] `eyrie`, `harrier`, and `swift` remain contract-free unless they gain a true cross-repo type -### Hawk integration seams +### Graycode 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] 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 +- [x] Graycode owns runtime DTOs in `internal/types` and translates them to `eyrie/engine` in `internal/engine` +- [x] `graycode swift ...` remains a Graycode-mounted subcommand, not a competing product surface ### Enforcement -- [x] Hawk CI runs ecosystem, shared-types, eyrie-client, and peer-coupling guards +- [x] Graycode 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 Hawk integration are merged to `main` -- [ ] published module versions used by Hawk match the merged contract changes +- [ ] open architecture PRs for engines, consumers, and Graycode integration are merged to `main` +- [ ] published module versions used by Graycode 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 Hawk internal event struct into `eagle/events` +- moving every Graycode 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,7 +108,7 @@ Remove compatibility shims only when: ## Verify locally -From `hawk`: +From `graycode`: ```bash make ecosystem-guard contracts-guard eyrie-client-guard eyrie-engine-guard peer-guard @@ -125,7 +125,7 @@ go test ./... -count=1 ## Related docs -- `hawk-product-architecture.md` — target shape and phases -- `hawk-dependency-rules.md` — allowed and forbidden edges +- `graycode-product-architecture.md` — target shape and phases +- `graycode-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/hawk-capability-seams.md b/docs/architecture/graycode-capability-seams.md similarity index 96% rename from docs/architecture/hawk-capability-seams.md rename to docs/architecture/graycode-capability-seams.md index e5206291..dce2eb91 100644 --- a/docs/architecture/hawk-capability-seams.md +++ b/docs/architecture/graycode-capability-seams.md @@ -1,7 +1,7 @@ # Capability seams Deepseek-harness keeps every side effect behind a named, replaceable seam with a -strict owner. Hawk adopts the same discipline without the Cordis dependency +strict owner. Graycode 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/graycode-contract-spec.md index 9f9898a2..198127db 100644 --- a/docs/architecture/graycode-contract-spec.md +++ b/docs/architecture/graycode-contract-spec.md @@ -1,10 +1,10 @@ -# Hawk Core Contracts Spec +# Graycode Core Contracts Spec ## Purpose -`eagle` is the shared language spoken by Hawk and its engines. +`eagle` is the shared language spoken by Graycode and its engines. -It exists to remove cross-repo dependency on Hawk internals and to prevent engines from importing each other. +It exists to remove cross-repo dependency on Graycode internals and to prevent engines from importing each other. ## Scope @@ -25,7 +25,7 @@ Not allowed: - runtime logic - storage implementations - engine implementations -- Hawk application internals +- Graycode application internals ## Package layout @@ -87,25 +87,25 @@ list in sync with the code (it is the inventory the dependency rules assume). ## Migration order -1. inventory cross-repo shared types in `hawk/shared` and `hawk/internal/types` +1. inventory cross-repo shared types in `graycode/shared` and `graycode/internal/types` 2. move stable shared types into contracts 3. update `kestrel` 4. update `merlin` -5. update other repos that rely on Hawk-exported shared types -6. leave product-only types inside Hawk +5. update other repos that rely on Graycode-exported shared types +6. leave product-only types inside Graycode ## Current status - severity and finding contracts are live in `eagle` -- review result contracts now exist in `eagle/review` -- verification result contracts now exist in `eagle/verify` +- review result contracts now exist in `contracts/review` +- verification result contracts now exist in `contracts/verify` - tool contracts now exist in `eagle/tools` - event contracts now exist in `eagle/events` - policy contracts now exist in `eagle/policy` - portable graph contracts now exist in `eagle/graph` -- 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 +- 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 `eyrie/engine` contract at the integration edge ## Versioning rule diff --git a/docs/architecture/hawk-current-vs-proposed.md b/docs/architecture/graycode-current-vs-proposed.md similarity index 73% rename from docs/architecture/hawk-current-vs-proposed.md rename to docs/architecture/graycode-current-vs-proposed.md index 7effb34b..131e3a8b 100644 --- a/docs/architecture/hawk-current-vs-proposed.md +++ b/docs/architecture/graycode-current-vs-proposed.md @@ -1,16 +1,16 @@ -# Hawk Current vs Proposed Architecture +# Graycode Current vs Proposed Architecture ## Purpose This document explains the connected repository design. The canonical -machine-readable inventory is [`hawk/ecosystem.yaml`](../../ecosystem.yaml); +machine-readable inventory is [`graycode/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 | |---|---| -| `hawk` | Hawk product and orchestration root | +| `graycode` | Graycode product and orchestration root | | `eyrie` | Eyrie provider runtime | | `harrier` | Harrier memory engine | | `shrike` | Shrike token/context 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 Hawk Cloud deployment | +| `graycode-platform` | GrayCode web, BFF, and Graycode Cloud deployment | ## Current workspace @@ -34,7 +34,7 @@ only the nine Go modules marked `workspace: true` in the manifest: ```text graycode-eco/ -├── hawk # product / composition root +├── graycode # product / composition root ├── eagle # neutral contracts foundation ├── falcon # MCP foundation ├── eyrie # Eyrie provider engine @@ -48,17 +48,17 @@ graycode-eco/ ├── wren # TypeScript SDK ├── starling # skills/extensions ├── owl # architecture tooling -└── graycode-platform # web/BFF/Hawk Cloud platform +└── graycode-platform # web/BFF/Graycode Cloud platform ``` -There is no `hawk/external` vendor tree in the current workspace. Local +There is no `graycode/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 ──> hawk <── skill surface ── starling +sparrow / robin / wren ── HTTP/OpenAPI ──> graycode <── skill surface ── starling │ ├── eyrie/engine ├── harrier (Harrier) @@ -71,7 +71,7 @@ sparrow / robin / wren ── HTTP/OpenAPI ──> hawk <── skill surface engines ──> eagle harrier / kestrel / merlin ──> falcon ──> mark3labs/mcp-go -hawk ── optional, fail-open HTTP ──> graycode-platform/apps/worker +graycode ── 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: -- Hawk is the only orchestrator and product integration root. -- Engines are peers and do not import Hawk internals or one another. +- Graycode is the only orchestrator and product integration root. +- Engines are peers and do not import Graycode internals or one another. - Eagle contains only neutral, implementation-light contracts. - Falcon contains only reusable MCP transport/handler scaffolding. -- 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. +- 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. ## 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 Hawk-centered dependency direction all exist. +contract parity checks, and Graycode-centered dependency direction all exist. The remaining proposed work is boundary refinement rather than repository reorganization: -1. publish the Eagle-migrated Eyrie revision and update Hawk's standalone pin; -2. remove the transitional `hawk-core-contracts` dependency from the published +1. publish the Eagle-migrated Eyrie revision and update Graycode's standalone pin; +2. remove the transitional `graycode-core-contracts` dependency from the published module graph; -3. decide whether graph/projection packages should remain explicit Hawk +3. decide whether graph/projection packages should remain explicit Graycode 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 -hawk -> eyrie / harrier / shrike / swift / kestrel / merlin / eagle +graycode -> eyrie / harrier / shrike / swift / kestrel / merlin / eagle engines -> eagle # only for shared contracts harrier / kestrel / merlin -> falcon -sparrow / robin / wren -> Hawk public HTTP/OpenAPI surface -starling -> Hawk skill/plugin surface -graycode-platform <-> Hawk via authenticated HTTP/Service Binding only +sparrow / robin / wren -> Graycode public HTTP/OpenAPI surface +starling -> Graycode skill/plugin surface +graycode-platform <-> Graycode 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 -> hawk/internal/* +engine -> graycode/internal/* SDK -> engine skills -> engine -any Hawk engine -> graycode-platform code +any Graycode engine -> graycode-platform code any Go module -> graycode-platform code ``` -The sanctioned platform connection is runtime-only and HTTP-based: Hawk may -send explicitly enabled usage or graph requests to the deployed Hawk Cloud +The sanctioned platform connection is runtime-only and HTTP-based: Graycode may +send explicitly enabled usage or graph requests to the deployed Graycode Cloud Worker, and local execution must remain usable when that request fails. ## Recommendation diff --git a/docs/architecture/hawk-dependency-rules.md b/docs/architecture/graycode-dependency-rules.md similarity index 72% rename from docs/architecture/hawk-dependency-rules.md rename to docs/architecture/graycode-dependency-rules.md index f6560a26..7c5bedc6 100644 --- a/docs/architecture/hawk-dependency-rules.md +++ b/docs/architecture/graycode-dependency-rules.md @@ -1,13 +1,13 @@ -# Hawk Dependency Rules +# Graycode Dependency Rules -`graycode-eco` is only a local parent folder. `hawk` is the main CLI and the +`graycode-eco` is only a local parent folder. `graycode` 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 ── Hawk daemon API ──> hawk <── skill API ── starling +sparrow / robin / wren ── Graycode daemon API ──> graycode <── skill API ── starling │ ├── eyrie/engine ├── harrier (Harrier) @@ -22,7 +22,7 @@ engines ──> eagle when a shared contract is required ``` The canonical list of all 15 repositories is in -[`hawk/ecosystem.yaml`](../../ecosystem.yaml). `owl/ecosystem.json` is a +[`graycode/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 Hawk, Eagle, or an engine. +It remains upstream-only and must not import Graycode, Eagle, or an engine. ## Forbidden graph ```text engine -> engine -engine -> hawk/internal/* -engine -> hawk/shared/* +engine -> graycode/internal/* +engine -> graycode/shared/* SDK -> engine skills -> engine any Go module -> graycode-platform code @@ -47,9 +47,9 @@ any Go module -> graycode-platform code ## Rules -### 1. Hawk is the orchestrator +### 1. Graycode is the orchestrator -Only Hawk coordinates the support engines and owns the user-facing CLI, +Only Graycode coordinates the support engines and owns the user-facing CLI, daemon, sessions, tools, policy, and product workflows. ### 2. Engines are peers @@ -60,18 +60,18 @@ Shrike, `swift` is Swift, `kestrel` is Kestrel, and `merlin` is Merlin. ### 3. Provider logic stays behind Eyrie -Hawk production code reaches provider credentials, catalogs, routing, and +Graycode 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. Hawk schemas stay Hawk-owned +### 4. Graycode schemas stay Graycode-owned -Hawk conversation persistence and CLI/JSON output are explicit projections, +Graycode conversation persistence and CLI/JSON output are explicit projections, not aliases or direct serialization of Eyrie DTOs. ### 5. Graph integrations are explicit surfaces -Hawk currently imports selected engine-owned graph/projection packages for +Graycode 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 Hawk Go module graph. Hawk may call the +`graycode-platform` is outside the Graycode Go module graph. Graycode 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 -Hawk supplies effective gateway settings while constructing an Eyrie Engine. +Graycode supplies effective gateway settings while constructing an Eyrie Engine. Product code must not mutate Eyrie process-global gateway state. ## Enforcement -Hawk CI runs manifest validation, no-local-replace checks, Eagle parity, +Graycode 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 Hawk's public +guards. SDK contract tests compare their OpenAPI snapshots with Graycode's public daemon contract. diff --git a/docs/architecture/graycode-ecosystem-summary.md b/docs/architecture/graycode-ecosystem-summary.md index 9b85ef44..db75949b 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. `hawk` is the main CLI and the -only primary Hawk product; the other repositories provide capabilities, +`graycode-eco` is only a local parent folder. `graycode` is the main CLI and the +only primary Graycode product; the other repositories provide capabilities, contracts, integrations, tooling, or optional platform services. ## Repository layers @@ -10,7 +10,7 @@ contracts, integrations, tooling, or optional platform services. API consumers/extensions sparrow robin wren starling \ | | / - hawk + graycode (main CLI and daemon) | Support engines @@ -21,7 +21,7 @@ Foundations Outside the Go runtime graph owl architecture tooling - graycode-platform web, BFF, and Hawk Cloud Worker + graycode-platform web, BFF, and Graycode Cloud Worker ``` Product labels map to directories as follows: `harrier`/Harrier, `shrike`/Shrike, @@ -30,20 +30,20 @@ Product labels map to directories as follows: `harrier`/Harrier, `shrike`/Shrike ## Dependency direction ```text -hawk -> eyrie / harrier / shrike / swift / kestrel / merlin / eagle +graycode -> eyrie / harrier / shrike / swift / kestrel / merlin / eagle engines -> eagle # when shared contracts are needed harrier / kestrel / merlin -> falcon -sparrow / robin / wren -> Hawk daemon API -starling -> Hawk skill/plugin API +sparrow / robin / wren -> Graycode daemon API +starling -> Graycode skill/plugin API ``` -Forbidden edges are engine-to-engine, engine-to-Hawk-internal, SDK-to-engine, +Forbidden edges are engine-to-engine, engine-to-Graycode-internal, SDK-to-engine, skills-to-engine, and any Go-module dependency on GrayCode Platform. ## Runtime and hosted plane ```text -Hawk main CLI +Graycode main CLI ├── Eyrie provider execution ├── Harrier memory ├── Shrike token/context management @@ -51,20 +51,20 @@ Hawk main CLI ├── Kestrel review └── Merlin verification -hawk ── optional authenticated HTTP ──> graycode-platform/apps/worker +graycode ── 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 Hawk Go module graph. -Hawk's cloud usage path is fail-open; graph synchronization is explicit. +not a repository. GrayCode Platform remains outside the Graycode Go module graph. +Graycode's cloud usage path is fail-open; graph synchronization is explicit. ## Repository roles | Repository | Role | Direct dependency rule | |---|---|---| -| `hawk` | Main CLI, daemon, orchestration, policy | Integrates engines and contracts | +| `graycode` | 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 | @@ -73,10 +73,10 @@ Hawk'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 | Hawk API contract | -| `robin` | Python SDK | Hawk API contract | -| `wren` | TypeScript SDK | Hawk API contract | -| `starling` | Skills/extensions | Hawk skill surface | +| `sparrow` | Go SDK | Graycode API contract | +| `robin` | Python SDK | Graycode API contract | +| `wren` | TypeScript SDK | Graycode API contract | +| `starling` | Skills/extensions | Graycode skill surface | | `owl` | Architecture explorer | Generated read-only projection | | `graycode-platform` | Web/BFF/cloud | HTTP and Service Binding only | @@ -86,5 +86,5 @@ 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 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. +`graycode-core-contracts` dependency from the standalone graph, and decide whether +Graycode's graph/projection packages need additional engine-owned facades. diff --git a/docs/architecture/hawk-harness.md b/docs/architecture/graycode-harness.md similarity index 81% rename from docs/architecture/hawk-harness.md rename to docs/architecture/graycode-harness.md index 9f093845..37de59d6 100644 --- a/docs/architecture/hawk-harness.md +++ b/docs/architecture/graycode-harness.md @@ -1,11 +1,11 @@ -# Hawk Harness +# Graycode Harness -Hawk treats the agent runtime as a product boundary around provider output. +Graycode treats the agent runtime as a product boundary around provider output. ## Tool-Call Path 1. Eyrie normalizes provider protocol responses. -2. Hawk validates and resolves tool metadata. +2. Graycode 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 -Eyrie's verification package; Hawk owns host-level UX, permissions, persistence, +Eyrie's verification package; Graycode owns host-level UX, permissions, persistence, and review contracts. ## Security Rule diff --git a/docs/architecture/hawk-product-architecture.md b/docs/architecture/graycode-product-architecture.md similarity index 67% rename from docs/architecture/hawk-product-architecture.md rename to docs/architecture/graycode-product-architecture.md index 5130ca76..88b5c1d3 100644 --- a/docs/architecture/hawk-product-architecture.md +++ b/docs/architecture/graycode-product-architecture.md @@ -1,23 +1,23 @@ -# Hawk Product Architecture +# Graycode Product Architecture ## Product statement -Hawk is the model-agnostic main AI coding-agent CLI from GrayCodeAI. +Graycode 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. -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. +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. -For model execution specifically: **Hawk is the face and composition layer; -Eyrie is the engine.** Hawk owns the conversation and product experience while +For model execution specifically: **Graycode is the face and composition layer; +Eyrie is the engine.** Graycode 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 Hawk model agnostic. -- Keep Hawk CLI-first and local-first. +- Keep Graycode model agnostic. +- Keep Graycode CLI-first and local-first. - Keep provider integration pluggable. - Keep support engines isolated from each other. - Make swift, review, and verification first-class. @@ -25,7 +25,7 @@ catalog state through model selection and normalized generation/streaming. ## Target repo set -- `hawk` +- `graycode` - `eyrie` - `harrier` - `shrike` @@ -39,7 +39,7 @@ catalog state through model selection and normalized generation/streaming. - `falcon` - `wren` - `owl` -- `graycode-platform` (outside the Hawk runtime module graph) +- `graycode-platform` (outside the Graycode 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,7 +51,7 @@ Merlin. The product labels remain useful in CLI and user-facing documentation. Users / SDKs / Skills | v - HAWK + GRAYCODE | +-----------------------------+ | | @@ -71,7 +71,7 @@ Users / SDKs / Skills Current implementation in the workspace: ```text -hawk +graycode -> eyrie -> harrier -> shrike @@ -83,7 +83,7 @@ hawk support engines -> eagle when they need shared contracts -> falcon where MCP serving is shared - x-> Hawk internals or each other + x-> Graycode internals or each other ``` Proposed steady-state architecture: @@ -92,7 +92,7 @@ Proposed steady-state architecture: SDKs / Skills / future integrations | v - Hawk + Graycode | +------------+------------+------------+------------+------------+------------+ | | | | | | | @@ -107,17 +107,17 @@ SDKs / Skills / future integrations This means: -- Hawk is the product and orchestration boundary +- Graycode 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 Hawk, not the engines directly +- SDKs and community skills consume Graycode, not the engines directly - `graycode-platform` provides the optional hosted plane through HTTP and a - private Service Binding; it is not imported by Hawk or any engine + private Service Binding; it is not imported by Graycode or any engine -## Hawk responsibilities +## Graycode responsibilities -Hawk owns: +Graycode owns: - CLI entrypoints - session lifecycle @@ -129,7 +129,7 @@ Hawk owns: - engine coordination - public integration surfaces -Hawk does not own: +Graycode does not own: - provider-specific implementation details - engine-specific business logic @@ -169,32 +169,32 @@ Hawk does not own: - review findings - risk detection - code quality analysis -- review-engine-local output converted into shared `eagle/review` contracts at product boundaries +- review-engine-local output converted into shared `contracts/review` contracts at product boundaries ### `merlin` - verification checks - test/assertion normalization - final pass/fail validation -- verification-engine-local output converted into shared `eagle/verify` contracts at product boundaries +- verification-engine-local output converted into shared `contracts/verify` contracts at product boundaries ## Primary runtime flow -1. User invokes `hawk`. -2. Hawk loads product settings, policy, and workspace state, then creates an +1. User invokes `graycode`. +2. Graycode 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 +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 `eyrie`. +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. 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 +and returns normalized events. No production Graycode package imports a lower +Eyrie package, and no Eyrie engine DTO is used as Graycode's persistent or CLI schema. ## Implementation phases @@ -206,41 +206,41 @@ schema. ### Phase 2 - add `eagle` -- move shared types out of Hawk internals +- move shared types out of Graycode internals Status: - completed - shared contracts now exist for `types`, `review`, `verify`, `tools`, `events`, and `policy` ### Phase 3 -- remove engine imports of Hawk internals +- remove engine imports of Graycode internals - remove engine-to-engine coupling Status: - completed for current workspace boundaries -- local/CI guards now block support-repo imports of `hawk/internal/*` and removed legacy `hawk/shared/types` +- local/CI guards now block support-repo imports of `graycode/internal/*` and removed legacy `graycode/shared/types` ### Phase 4 -- harden orchestration boundaries in Hawk +- harden orchestration boundaries in Graycode - formalize provider, swift, review, and verify integration points Status: - completed for the local runtime boundary -- Hawk owns runtime DTOs and review/verify product-boundary contracts -- Hawk's `ChatClient` anti-corruption port translates only to `eyrie/engine` +- Graycode owns runtime DTOs and review/verify product-boundary contracts +- Graycode'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 Hawk public interfaces only +- align SDKs and skills to Graycode public interfaces only Status: -- policy is now explicit and guarded in Hawk docs +- policy is now explicit and guarded in Graycode 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 `hawk/shared/types` +- remove legacy `graycode/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: -- `hawk` is the only product surface +- `graycode` is the only product surface - engines depend only on `eagle` -- shared types no longer live in Hawk internals as a cross-repo API +- shared types no longer live in Graycode 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/hawk-provider-abstraction.md b/docs/architecture/graycode-provider-abstraction.md similarity index 71% rename from docs/architecture/hawk-provider-abstraction.md rename to docs/architecture/graycode-provider-abstraction.md index 6ac4a47e..1d006050 100644 --- a/docs/architecture/hawk-provider-abstraction.md +++ b/docs/architecture/graycode-provider-abstraction.md @@ -1,17 +1,17 @@ -# Hawk Provider Abstraction +# Graycode Provider Abstraction ## Goal -Hawk must remain model agnostic. +Graycode must remain model agnostic. -That means Hawk should support multiple providers without leaking provider-specific assumptions across the product. +That means Graycode should support multiple providers without leaking provider-specific assumptions across the product. ## Design principle Provider-specific code lives behind Eyrie's stable `eyrie/engine` host facade. -Hawk is the face and composition root; Eyrie is the engine. +Graycode is the face and composition root; Eyrie is the engine. -Hawk decides: +Graycode decides: - what capability the task needs - the semantic intent (`fast`, `balanced`, `reasoning`, `economical`) @@ -40,9 +40,9 @@ Hawk decides: - error classification - timeout/cancellation support -## Hawk-facing abstraction +## Graycode-facing abstraction -Hawk depends on its small `ChatClient` product port and adapts it only to +Graycode 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 Hawk packages +- no direct vendor SDK imports in unrelated Graycode packages - no provider-specific branches inside review/verify logic - no model-specific assumptions inside session persistence -- keep task-semantic policy inside Hawk orchestration +- keep task-semantic policy inside Graycode orchestration - keep provider/deployment routing, health, retry, and fallback inside Eyrie -- Hawk production integrations use `github.com/GrayCodeAI/eyrie/engine` +- Graycode 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 -- Hawk-owned JSON, session, and conversation schemas are explicit projections; +- Graycode-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/hawk-repo-roles.md b/docs/architecture/graycode-repo-roles.md similarity index 62% rename from docs/architecture/hawk-repo-roles.md rename to docs/architecture/graycode-repo-roles.md index 38481836..5f41f91a 100644 --- a/docs/architecture/hawk-repo-roles.md +++ b/docs/architecture/graycode-repo-roles.md @@ -1,8 +1,8 @@ -# Hawk Repo Roles +# Graycode Repo Roles ## Product repo -### `hawk` +### `graycode` Main CLI and product repository. It is the orchestration root; `graycode-eco` is only the local parent folder for the independent repositories. @@ -17,31 +17,31 @@ Owns: - engine coordination - user-facing model/configuration presentation and stable CLI schemas -Hawk is the only primary product surface. Users interact with Hawk, not with six +Graycode is the only primary product surface. Users interact with Graycode, not with six separate end-user products. ## Support engines ### `eyrie` -Hawk provider engine. Its public host boundary is `eyrie/engine`, which owns +Graycode 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. Hawk production +transport, resilience, and normalized generation/streaming. Graycode production code has zero imports of Eyrie's lower-level packages. ### `harrier` (Harrier) -Hawk memory engine. +Graycode memory engine. ### `shrike` (Shrike) -Hawk context and token engine. +Graycode context and token engine. ### `swift` (Swift) -Hawk audit and replay engine. +Graycode audit and replay engine. ### `kestrel` (Kestrel) -Hawk review engine. +Graycode review engine. ### `merlin` (Merlin) -Hawk verification engine. +Graycode verification engine. All six support engines are peers: @@ -52,22 +52,22 @@ All six support engines are peers: - `kestrel` (Kestrel) - `merlin` (Merlin) -They should stay isolated from each other and are coordinated by Hawk; +They should stay isolated from each other and are coordinated by Graycode; they may depend on `eagle` where a shared vocabulary is required. ## Ecosystem repos ### `sparrow` -Go integration surface for Hawk public APIs/contracts. +Go integration surface for Graycode public APIs/contracts. ### `robin` -Python integration surface for Hawk public APIs/contracts. +Python integration surface for Graycode public APIs/contracts. ### `wren` -TypeScript integration surface for Hawk public APIs/contracts. +TypeScript integration surface for Graycode public APIs/contracts. ### `starling` -Reusable Hawk skills, recipes, and extension packs. +Reusable Graycode 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, hawk, or graycode-platform. +engines, graycode, or graycode-platform. ## Tooling and platform ### `owl` Read-only architecture visualization tooling. It consumes the generated -projection of `hawk/ecosystem.yaml`; it is not a runtime dependency. +projection of `graycode/ecosystem.yaml`; it is not a runtime dependency. ### `graycode-platform` -Separate web, browser-BFF, and Hawk Cloud repository. Its Worker deployment is -named `graycode-cloud`, but it connects to Hawk only through authenticated +Separate web, browser-BFF, and Graycode Cloud repository. Its Worker deployment is +named `graycode-cloud`, but it connects to Graycode only through authenticated runtime HTTP/Service Binding and is never a Go dependency. ## Role rules -- Users should feel they are using `hawk`, not six unrelated tools. +- Users should feel they are using `graycode`, 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 Hawk, but should not bypass Hawk to reach engines directly. +- SDKs and skills extend Graycode, but should not bypass Graycode to reach engines directly. diff --git a/docs/architecture/hawk-review-verify-lifecycle.md b/docs/architecture/graycode-review-verify-lifecycle.md similarity index 84% rename from docs/architecture/hawk-review-verify-lifecycle.md rename to docs/architecture/graycode-review-verify-lifecycle.md index 9802da61..b50711c9 100644 --- a/docs/architecture/hawk-review-verify-lifecycle.md +++ b/docs/architecture/graycode-review-verify-lifecycle.md @@ -1,8 +1,8 @@ -# Hawk Review and Verify Lifecycle +# Graycode Review and Verify Lifecycle ## Goal -Review and verification should be standard parts of Hawk's workflow, not optional bolt-ons. +Review and verification should be standard parts of Graycode's workflow, not optional bolt-ons. ## Roles @@ -54,7 +54,7 @@ Run `merlin` for: ## Decision policy -Hawk should define when review and verification are: +Graycode should define when review and verification are: - required - recommended diff --git a/docs/architecture/hawk-swift-event-model.md b/docs/architecture/graycode-swift-event-model.md similarity index 95% rename from docs/architecture/hawk-swift-event-model.md rename to docs/architecture/graycode-swift-event-model.md index ccd33400..de092ab7 100644 --- a/docs/architecture/hawk-swift-event-model.md +++ b/docs/architecture/graycode-swift-event-model.md @@ -1,4 +1,4 @@ -# Hawk Swift Event Model +# Graycode Swift Event Model ## Goal @@ -71,7 +71,7 @@ In the local CLI world, swift primarily supports: - debugging - reproduction - replay -- audit of what Hawk changed and why +- audit of what Graycode changed and why ## Long-term use diff --git a/docs/architecture/plan.md b/docs/architecture/plan.md index 09275039..cdfb06d2 100644 --- a/docs/architecture/plan.md +++ b/docs/architecture/plan.md @@ -1,8 +1,8 @@ -# Hawk Architecture - Technical Plan +# Graycode Architecture - Technical Plan ## Overview -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. +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. ## Architecture Decisions diff --git a/docs/architecture/spec.md b/docs/architecture/spec.md index 4f18daaa..706fecf4 100644 --- a/docs/architecture/spec.md +++ b/docs/architecture/spec.md @@ -1,8 +1,8 @@ -# Hawk Architecture Specification +# Graycode Architecture Specification ## Problem Statement -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. +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. ## Scope @@ -20,15 +20,15 @@ Hawk is an AI-powered coding agent for the terminal. This specification defines ### REQ-1: Repository Structure -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 +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 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: hawk, eagle, falcon, eyrie, harrier (Harrier), shrike (Shrike), swift (Swift), kestrel (Kestrel), and merlin (Merlin) | +| Parent `go.work` | Resolves the nine local Go siblings: graycode, 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 | @@ -64,7 +64,7 @@ The `internal/` directory SHALL contain the following packages: | `system/` | Bus, shutdown, retention, cron, staleness | | `storage/` | State directory management | | `snapshot/` | File snapshots for undo | -| `hawk-skills/` | Bundled skills (32 skills) | +| `graycode-skills/` | Bundled skills (32 skills) | ### REQ-3: Engine Sub-Systems @@ -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` | Hawk ChatClient port, engine adapter coordination, compact | +| `chat_service.go` | Graycode 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 -Hawk CLI/TUI + conversation + tools +Graycode CLI/TUI + conversation + tools | - Hawk-owned ports/DTOs + Graycode-owned ports/DTOs | v eyrie/engine credentials -> catalog -> routing -> generate/stream ``` -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 +No production Graycode package may import a lower Eyrie package. Custom gateways +are supplied per Engine instance, and Eyrie DTOs are not Graycode persistence or CLI output schemas. ### REQ-4: Agent Loop Lifecycle diff --git a/docs/architecture/tasks.md b/docs/architecture/tasks.md index 40d608b1..f0712a7b 100644 --- a/docs/architecture/tasks.md +++ b/docs/architecture/tasks.md @@ -1,7 +1,7 @@ -# Hawk Architecture - Implementation Tasks +# Graycode Architecture - Implementation Tasks > **Historical.** This was the initial architecture implementation checklist. -> It is superseded by `hawk-architecture-v1-definition-of-done.md`, which +> It is superseded by `graycode-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 b684bb70..6cd0b041 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -12,11 +12,11 @@ Platform/provider capability metadata is separate: [`platform-capabilities.json` ```jsonc { - "components": ["hawk", "eyrie", ...], // canonical eco roster - "dependencies": { "hawk": ["eyrie", ...] }, // who depends on who + "components": ["graycode", "eyrie", ...], // canonical eco roster + "dependencies": { "graycode": ["eyrie", ...] }, // who depends on who "matrices": [ - { "name": "stable", "components": { "hawk": "0.1.0", "eyrie": "0.1.0", ... } }, - { "name": "next", "components": { "hawk": "main", "eyrie": "main", ... } } + { "name": "stable", "components": { "graycode": "0.1.0", "eyrie": "0.1.0", ... } }, + { "name": "next", "components": { "graycode": "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 hawk repo. +1. Reads `testdata/compatibility-matrix.json` from the graycode 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 hawk 0.4 with eyrie 0.2" — you +- **Bug reports become triageable.** "I ran graycode 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: `hawk`'s own `go.mod` directly pins a couple -of shared leaf dependencies (currently `eagle`), and several sibling-repository +Separate from the matrix above: `graycode`'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 `hawk` pins wins in `hawk`'s own build — but if a +selection means whatever `graycode` pins wins in `graycode`'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 `hawk/go.mod` against each +step) reports any such mismatch by comparing `graycode/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 Hawk-only checkout simply reports no local consumers to compare. +present; a Graycode-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 35b84986..f6fbfbe7 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 (hawk, eyrie, harrier, shrike, swift) + a new gallery web property + a unified docs site. +**Scope:** Multi-month effort spanning all 5 repos (graycode, 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` (hawk P1) and `TOP20_COMPARISON.md:227` (cross-cutting P1). +1. **Centralized extension gallery / plugin marketplace** — `TOP20_COMPARISON.md:65` (graycode 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 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`. +- **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`. - **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.** `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`). +- **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`). -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/`). +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/`, `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 **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`. +- **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`. - **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 `~/.hawk/skills` / `.hawk/skills` layout (`registry.go:201-203`). +- **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`). - **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 ▼ ▼ ┌───────────────────────────────┐ ┌──────────────────────────────┐ -│ hawk CLI (Go) │ │ Gallery + Docs site (static) │ +│ graycode 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/{hawk,eyrie,...} │ +│ cmd/skills_cmd.go (+ ext cmd)│ │ - /docs/{graycode,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 `hawk/internal/plugin/skill_loader.go:51` (`parseSkillFrontMatter`) and discovered at `hawk/internal/tool/skill.go:62` (`discoverSkills`). We keep this working verbatim. +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. **(b) Full — `extension.yaml`** (new, for multi-asset bundles): ```yaml -apiVersion: hawk.extension/v1 +apiVersion: graycode.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://hawkskills.dev/extensions/terraform-pro +homepage: https://graycodeskills.dev/extensions/terraform-pro keywords: [terraform, iac, devops] compat: - minHawkVersion: "1.2.0" # maps to ManifestV2.MinHawkVersion (manifest_v2.go:20) + minGraycodeVersion: "1.2.0" # maps to ManifestV2.MinGraycodeVersion (manifest_v2.go:20) provides: skills: [ ./SKILL.md ] commands: [ ./commands/plan.md, ./commands/apply.md ] # slash commands @@ -125,7 +125,7 @@ signing: signature: "signatures/terraform-pro.sig" ``` -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. +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. ### 3.3 Registry index format v2 (G3) @@ -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 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. +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. ### 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 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). +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). 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: hawk ext install GrayCodeAI/starling terraform-pro +user: graycode 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: hawk 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 ~/.hawk/skills/ (and commands/hooks/mcp dirs) + → copy assets to ~/.graycode/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: hawk ext install GrayCodeAI/starling terraform-pro **Flow B — Gallery browse & install (no account, no server)** ``` -browser → hawkskills.dev/extensions (static Docusaurus/Mintlify page) +browser → graycodeskills.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: hawk ext install (copy button) + → "Install" shows: graycode 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 hawk, eyrie, + pull README.md / ARCHITECTURE.md / docs/** from each of graycode, 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` | `hawk/internal/plugin/registry.go:60` | +| Registry fetch + 1h cache | `RegistryClient.FetchIndex` | `graycode/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 | `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` | +| 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` | | Hook bundling | `ManifestHook{Event,Command,Priority}` | `manifest_v2.go:32` | -| 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` | +| 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` | | 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):** -- `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`. +- `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`. - 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. `hawk-docs`). +- The gallery + docs site (new repo, e.g. `graycode-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 `.hawk/skills` / `~/.hawk/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 `.graycode/skills` / `~/.graycode/skills` (project then user), exactly the precedence `RuleDiscoverer` already honors. --- @@ -257,20 +257,20 @@ 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`; `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. +- **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. ### P1 — Gallery + Docs site (the visible surface) -- **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.1** Stand up the static site (decision in §6). Pages: `/extensions` (gallery), per-extension detail, `/docs/{graycode,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** `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. +- **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. ### P2 — Ecosystem maturity & publisher self-service -- **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.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.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: 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 hawk +### New runtime dependencies for graycode - 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 hawk'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 graycode'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 `hawk skills audit`). + 3. **Hidden-Unicode / homoglyph audit** (`audit.go`): catches prompt-injection-via-invisible-characters in `SKILL.md`/prompts (already `graycode 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 hawk's binary; rotation requires a hawk release. P2 cosign/sigstore would move trust to a transparency log, reducing reliance on bundled keys. +- **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. --- ## 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 `hawk-docs` repo with the 5 repos as submodules? (Submodules complicate contributor flow but isolate doc build from skill content.) +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 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). +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). 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,12 +342,12 @@ 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, `hawk 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, `graycode 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** | `hawk ext update`, `hawk ext verify`, atomic replace | **2 ew** | -| **P2 — publisher self-service** | `hawk ext publish` scaffolder + validators, PR automation | **3 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** | | **P2 — trust tiers + sigstore upgrade** | verified-publisher badges, optional cosign/sigstore keyless + transparency log | **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** | @@ -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` -- `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` +- `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` - `TOP20_COMPARISON.md:65,74,83,181,227,232,240,241` diff --git a/docs/design/HAWK-CLOUD-SAAS.md b/docs/design/GRAYCODE-CLOUD-SAAS.md similarity index 92% rename from docs/design/HAWK-CLOUD-SAAS.md rename to docs/design/GRAYCODE-CLOUD-SAAS.md index 3ad9d42b..3eb45086 100644 --- a/docs/design/HAWK-CLOUD-SAAS.md +++ b/docs/design/GRAYCODE-CLOUD-SAAS.md @@ -1,4 +1,4 @@ -# Hawk Cloud — Hosted Execution Plane (Design Doc) +# Graycode Cloud — Hosted Execution Plane (Design Doc) **Status:** Draft / Proposal **Author:** Platform team @@ -9,17 +9,17 @@ ## 1. Overview & Competitive Context -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` +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` 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 -(`hawk/internal/sandbox/container.go`). +(`graycode/internal/sandbox/container.go`). -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 +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 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 hawk P0 table (lines 27-37) and P2 table +From `TOP20_COMPARISON.md`, the graycode 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 hawk 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 `/hawk/internal/auth/auth.go`" (`TOP20_COMPARISON.md:72`). +token store at `/graycode/internal/auth/auth.go`" (`TOP20_COMPARISON.md:72`). 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`). Hawk Cloud and eyrie multi-tenancy share +budgets (`TOP20_COMPARISON.md:95-96`). Graycode 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,27 +68,27 @@ 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 Hawk Cloud's versioned server catalog, enforce + price normalized usage with Graycode 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 hawk's existing sandbox interface. + microVMs (E2B/Daytona/Firecracker) behind graycode'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 hawk remains +- **Not** replacing the local/offline single-binary mode. Local graycode 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. Eyrie remains the provider engine behind - Hawk's `eyrie/engine` integration; the plane calls a Hawk agent worker, which + Graycode's `eyrie/engine` integration; the plane calls a Graycode 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). @@ -103,7 +103,7 @@ once and shows how both consume it. ``` ┌────────────────────────────────────────┐ - Browser UI ──┐ │ Hawk Cloud │ + Browser UI ──┐ │ Graycode Cloud │ IDE (ACP) ──┼──TLS──▶│ │ CLI (token)──┘ │ ┌──────────────┐ ┌────────────────┐ │ │ │ Edge / API │ │ Identity svc │ │ @@ -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. Hawk Cloud owns this authority; Eyrie -supplies normalized model and token usage through Hawk's Engine boundary. +hard-limit enforcement, and invoicing. Graycode Cloud owns this authority; Eyrie +supplies normalized model and token usage through Graycode'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 hawk agent loop, unchanged, +(`internal/engine/stream.go:20`). This is the existing graycode 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 Hawk Cloud + -- kind ∈ {llm, sandbox}; cost_usd is priced by Graycode Cloud ``` `meter_events.cost_usd` is computed from normalized model/token dimensions by -Hawk Cloud's versioned pricing catalog. Client-side estimates may be displayed +Graycode Cloud's versioned pricing catalog. Client-side estimates may be displayed or audited, but never affect credits or invoices. ### 3.3 API Surface @@ -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** — -hawk already implements RFC 8628 device flow end-to-end +graycode already implements RFC 8628 device flow end-to-end (`internal/auth/device_flow.go`: `RequestCode`, `PollForToken`, -`exchangeCode`). Hawk Cloud stands up the *server* side of these grants; the +`exchangeCode`). Graycode 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 Hawk Cloud reuses it | +| Existing asset | File | How Graycode 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). | -| 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. | +| Eyrie normalized usage | `eyrie/engine` response and stream usage DTOs through Graycode'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 `forwardToHawk` (`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 `forwardToGraycode` (`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) @@ -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 Hawk Cloud catalog, persist the Postgres credit ledger, and + the versioned Graycode 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 hawk against a +**Exit criteria:** a paying single team can log in via OAuth, run graycode against a cloud sandbox, see streamed output and diffs in the browser, and get cut off when credits hit zero. @@ -399,7 +399,7 @@ credits hit zero. | 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 eyrie). Stripe SDK is permissive. | | Database | **Buy** managed Postgres | Standard. | -| 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. | +| Metering enforcement | **Build in Graycode 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 hawk never phones home; +1. **Cloud is opt-in and isolated from local mode.** Local graycode 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 hawk's isolation model) carries over. Sandboxes default to + (already graycode'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`). @@ -454,7 +454,7 @@ 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 hawk's + Daytona = richer dev-environment model. Which maps better onto graycode'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? diff --git a/docs/ecosystem-message-flow.md b/docs/ecosystem-message-flow.md index 72ca6dff..67140077 100644 --- a/docs/ecosystem-message-flow.md +++ b/docs/ecosystem-message-flow.md @@ -1,11 +1,11 @@ # Ecosystem message flow (eyrie · harrier · shrike) -How one user message travels through hawk and the GrayCodeAI ecosystem libraries. +How one user message travels through graycode and the GrayCodeAI ecosystem libraries. ## Overview ``` -User prompt (TUI or hawk exec) +User prompt (TUI or graycode exec) │ ▼ ┌───────────────────┐ @@ -26,7 +26,7 @@ User prompt (TUI or hawk exec) │ ▼ ┌───────────────────┐ ┌─────────────┐ -│ Hawk ChatClient │────►│ eyrie/engine│ catalog, credentials, routing +│ Graycode ChatClient │────►│ eyrie/engine│ catalog, credentials, routing │ port + adapter │ │ generate/ │────► provider API └─────────┬─────────┘ │ stream │ └─────────────┘ @@ -48,19 +48,19 @@ User prompt (TUI or hawk exec) ## Step by step -### 1. Session start (`hawk` or `hawk exec`) +### 1. Session start (`graycode` or `graycode exec`) -- **eyrie**: The Hawk composition root creates an `eyrie/engine.Engine` with +- **eyrie**: The Graycode 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 Hawk's `ChatClient` port. -- **harrier**: `configureSession` creates `HarrierBridge` → opens `~/.harrier/data/harrier.db`. If missing, hawk runs without persistent memory. + builds transport behind Graycode's `ChatClient` port. +- **harrier**: `configureSession` creates `HarrierBridge` → opens `~/.harrier/data/harrier.db`. If missing, graycode runs without persistent memory. - **shrike**: No startup step — linked at compile time. ### 2. System prompt assembly -- Hawk templates (`internal/prompts/templates/*.md`) define behavior, tools, and practices. -- Project `AGENTS.md` is appended via `hawkconfig.BuildContextWithDirs`. +- Graycode templates (`internal/prompts/templates/*.md`) define behavior, tools, and practices. +- Project `AGENTS.md` is appended via `graycodeconfig.BuildContextWithDirs`. - **harrier**: `Memory.Recall` injects relevant graph nodes into the system prompt. ### 3. User message → agent loop (`internal/engine/stream.go`) @@ -68,7 +68,7 @@ User prompt (TUI or hawk exec) Each turn: 1. **harrier** — recall memories matching the latest user message (token budget ~2000). -2. **eyrie** — Hawk's adapter calls engine generate/stream with Hawk-owned tool +2. **eyrie** — Graycode's adapter calls engine generate/stream with Graycode-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. **eyrie** — if shrike reduction is insufficient, hawk calls the LLM to summarize, then keeps recent messages. +2. **eyrie** — if shrike reduction is insufficient, graycode calls the LLM to summarize, then keeps recent messages. ### 5. Token accounting @@ -87,14 +87,14 @@ When messages exceed limits (`internal/engine/compact.go`): ## Verify locally ```bash -hawk doctor # ecosystem panel + eyrie preflight + harrier status -hawk harrier # merlin memory graph -./scripts/smoke-hawk.sh # build + quick tests +graycode doctor # ecosystem panel + eyrie preflight + harrier status +graycode harrier # merlin memory graph +./scripts/smoke-graycode.sh # build + quick tests ``` ## Module layout -| Module | Role in hawk | Required? | +| Module | Role in graycode | Required? | |--------|----------------|-----------| | **eyrie** | LLM APIs, catalog, credentials, routing | Yes | | **harrier** | SQLite memory graph at `~/.harrier/data/` | No (degrades gracefully) | @@ -104,7 +104,7 @@ The support repositories are independent sibling checkouts: `eyrie`, `harrier` (Harrier), and `shrike` (Shrike), with the parent `go.work` wiring the local Go workspace. -Production Hawk code imports Eyrie only through `eyrie/engine`. Conversation -history, WAL/resume, permissions, and tool execution remain in Hawk; provider +Production Graycode code imports Eyrie only through `eyrie/engine`. Conversation +history, WAL/resume, permissions, and tool execution remain in Graycode; provider credentials, discovery, selection, transport, resilience, and normalized streaming remain in Eyrie. diff --git a/docs/ecosystem-remediation-plan.md b/docs/ecosystem-remediation-plan.md index eb01e3f7..abc9089e 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 (hawk + swift) +## 1. charmbracelet v1/v2 Dependency Duplication (graycode + swift) **Status:** ✅ Done (2026-07-11) -**Impact:** High — contributed ~20-30MB to hawk binary size +**Impact:** High — contributed ~20-30MB to graycode binary size ### Outcome -- Hawk and Swift imports migrated to `charm.land/*/v2` (`bubbles`, `bubbletea`, +- Graycode and Swift imports migrated to `charm.land/*/v2` (`bubbles`, `bubbletea`, `lipgloss`, `huh`, `glamour`). - Direct `github.com/charmbracelet/{bubbles,bubbletea,lipgloss}` requires removed - from hawk/swift `go.mod`. + from graycode/swift `go.mod`. - Binary size gate tightened: **110MB → 80MB** (`make size-check`). -- Verified build: hawk binary **~75 MB** (under gate) after migration. +- Verified build: graycode binary **~75 MB** (under gate) after migration. -Commits (hawk): `2890c74` (migrate), `ccfa286` (API fixups), `7a42c1e` (size gate). +Commits (graycode): `2890c74` (migrate), `ccfa286` (API fixups), `7a42c1e` (size gate). ### Residual notes @@ -38,13 +38,13 @@ Commits (hawk): `2890c74` (migrate), `ccfa286` (API fixups), `7a42c1e` (size gat (`replace github.com/GrayCodeAI/harrier => ../..`). - Core `github.com/GrayCodeAI/harrier` no longer requires `charmbracelet/{bubbles,bubbletea,lipgloss}`. -- Hawk embeds only the library packages (`engine`, `storage`, `graph`, …), so - the default hawk binary does not pull the demo TUI module. +- Graycode embeds only the library packages (`engine`, `storage`, `graph`, …), so + the default graycode 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 Hawk's `go.mod` must resolve to a +- The local `harrier` (Harrier) checkout and Graycode'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 `hawk` fetch the registry from a CDN or GitHub +3. **Runtime fetch:** Have `graycode` fetch the registry from a CDN or GitHub raw URL rather than embedding it. ### Risk -If `registry.json` is read offline by hawk, removing it requires adding a -fetch-or-cache mechanism. Verify how hawk consumes the registry before +If `registry.json` is read offline by graycode, removing it requires adding a +fetch-or-cache mechanism. Verify how graycode consumes the registry before removing from git. --- diff --git a/docs/intelligent-cli.md b/docs/intelligent-cli.md index 721dda26..f578c6ca 100644 --- a/docs/intelligent-cli.md +++ b/docs/intelligent-cli.md @@ -1,6 +1,6 @@ # Intelligent CLI capabilities -Hawk keeps the startup tool schema small while making the full capability +Graycode 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 -hawk tools -hawk tools --json +graycode tools +graycode 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 8043a300..828757d4 100644 --- a/docs/mcp-servers.md +++ b/docs/mcp-servers.md @@ -1,10 +1,10 @@ # MCP Server Configuration -hawk supports connecting to external MCP (Model Context Protocol) servers to extend its capabilities with additional tools, resources, and prompts. +graycode 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: `~/.hawk/settings.json`, project: `.hawk/settings.json`). +MCP servers are configured in `settings.json` (global: `~/.graycode/settings.json`, project: `.graycode/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 hawk settings +# Add to graycode settings ``` **settings.json:** @@ -76,7 +76,7 @@ npm install -g my-jogyo ### harrier (Memory Engine) -hawk's built-in memory engine. Configured automatically when harrier is installed. +graycode's built-in memory engine. Configured automatically when harrier is installed. ```json { @@ -93,7 +93,7 @@ hawk's built-in memory engine. Configured automatically when harrier is installe ### kestrel (Code Review) -hawk's built-in code review engine. +graycode's built-in code review engine. ```json { @@ -110,7 +110,7 @@ hawk's built-in code review engine. ### merlin (Security Audit) -hawk's built-in security scanner. +graycode's built-in security scanner. ```json { @@ -129,16 +129,16 @@ hawk's built-in security scanner. ```bash # Add an MCP server -hawk mcp add [args...] +graycode mcp add [args...] # List configured servers -hawk mcp list +graycode mcp list # Remove a server -hawk mcp remove +graycode mcp remove # Test a server connection -hawk mcp test +graycode mcp test ``` ## Troubleshooting @@ -149,8 +149,8 @@ hawk mcp test - Run the command manually to check for errors **Tools not appearing:** -- Restart hawk after adding a new server -- Check `hawk mcp test ` for connection errors +- Restart graycode after adding a new server +- Check `graycode 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 007c5600..f381f925 100644 --- a/docs/monitoring-guide.md +++ b/docs/monitoring-guide.md @@ -1,6 +1,6 @@ # Monitoring Guide -This guide covers how to monitor hawk's daemon and CLI for production health, +This guide covers how to monitor graycode's daemon and CLI for production health, performance, and security. ## 1. Daemon Health & Readiness @@ -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: $HAWK_DAEMON_API_KEY" \ +curl -H "X-API-Key: $GRAYCODE_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: $HAWK_DAEMON_API_KEY" \ +curl -H "X-API-Key: $GRAYCODE_DAEMON_API_KEY" \ http://localhost:4590/v1/metrics ``` @@ -51,9 +51,9 @@ Available metrics: | Metric | Type | Description | |--------|------|-------------| -| `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 | +| `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 | | `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 HAWK_CODE_ENABLE_TELEMETRY=1 +export GRAYCODE_ENABLE_TELEMETRY=1 export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 -hawk daemon start +graycode 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: `hawk-code`) and version. +- Set the service name (default: `graycode`) and version. ### Configuration | Environment Variable | Default | Description | |---------------------|---------|-------------| -| `HAWK_CODE_ENABLE_TELEMETRY` | `0` | Set to `1` to enable OTP telemetry | +| `GRAYCODE_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 | -| `HAWK_CODE_OTEL_SHUTDOWN_TIMEOUT_MS` | `2000` | Shutdown timeout in milliseconds | +| `GRAYCODE_OTEL_SHUTDOWN_TIMEOUT_MS` | `2000` | Shutdown timeout in milliseconds | ### OTel Conventions -hawk follows the OpenTelemetry semantic conventions for traces. See +graycode 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 `~/.hawk/state/daemon.log` by +The daemon writes structured SLOG logs to `~/.graycode/state/daemon.log` by default. Control the log level via: ```bash -hawk daemon start --log-level DEBUG +graycode 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 `~/.hawk/state/securitylog/security_events.jsonl`. +tamper-evident log at `~/.graycode/state/securitylog/security_events.jsonl`. Verify the audit log integrity: ```bash # The securitylog package provides a verify command -go run ./cmd/hawk securitylog verify +go run ./cmd/graycode securitylog verify ``` ## 5. Prometheus Scraping @@ -142,12 +142,12 @@ go run ./cmd/hawk securitylog verify ```yaml services: - hawk: - image: ghcr.io/graycodeai/hawk-daemon:latest + graycode: + image: ghcr.io/graycodeai/graycode-daemon:latest ports: - "4590:4590" environment: - - HAWK_DAEMON_API_KEY=secret + - GRAYCODE_DAEMON_API_KEY=secret labels: - "prometheus.io/scrape=true" - "prometheus.io/port=4590" @@ -160,14 +160,14 @@ services: apiVersion: v1 kind: Service metadata: - name: hawk-daemon + name: graycode-daemon annotations: prometheus.io/scrape: "true" prometheus.io/port: "4590" prometheus.io/path: "/v1/metrics" spec: selector: - app: hawk-daemon + app: graycode-daemon ports: - port: 4590 targetPort: 4590 @@ -175,18 +175,18 @@ spec: apiVersion: apps/v1 kind: Deployment metadata: - name: hawk-daemon + name: graycode-daemon spec: template: spec: containers: - - name: hawk - image: ghcr.io/graycodeai/hawk-daemon:latest + - name: graycode + image: ghcr.io/graycodeai/graycode-daemon:latest env: - - name: HAWK_DAEMON_API_KEY + - name: GRAYCODE_DAEMON_API_KEY valueFrom: secretKeyRef: - name: hawk-secret + name: graycode-secret key: api-key ports: - containerPort: 4590 @@ -208,26 +208,26 @@ spec: | Alert | Condition | Severity | |-------|-----------|----------| -| Daemon down | `hawk_daemon_uptime_seconds` does not increase for 2+ minutes | critical | +| Daemon down | `graycode_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 | `hawk_daemon_chat_concurrency_used` sustained at max for 5+ minutes | warning | -| No active sessions | `hawk_daemon_active_sessions` = 0 during business hours | warning | +| 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 | ## 7. Systemd Logging When running under systemd, logs from stderr/stdout are captured by journald: ```bash -journalctl -u hawk-daemon -f +journalctl -u graycode-daemon -f ``` The daemon also writes its own structured log to -`~/.hawk/state/daemon.log`: +`~/.graycode/state/daemon.log`: ```bash -tail -f ~/.hawk/state/daemon.log +tail -f ~/.graycode/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 HAWK_FEATURE_=1 +export GRAYCODE_FEATURE_=1 ``` | Flag | Default | Description | @@ -251,5 +251,5 @@ export HAWK_FEATURE_=1 List all registered flags: ```bash -hawk features +graycode features ``` diff --git a/docs/operations-checklist.md b/docs/operations-checklist.md index 60c77346..58d3f04e 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 hawk in a production +Use this checklist when deploying or upgrading graycode in a production environment. Each item links to the relevant configuration option or documentation section. ## Pre-Deployment -- [ ] **API key is set** — `HAWK_DAEMON_API_KEY` environment variable is +- [ ] **API key is set** — `GRAYCODE_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 HAWK_DAEMON_API_KEY=$(openssl rand -base64 32) + export GRAYCODE_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 Hawk bound to loopback or + flags. If a reverse proxy terminates TLS, keep Graycode 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 - `HAWK_DAEMON_MAX_CONCURRENT`. + `GRAYCODE_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 `~/.hawk/state/daemon.log`. +- [ ] **Log retention** — Daemon logs at `~/.graycode/state/daemon.log`. Configure log rotation (logrotate, journald retention) to prevent disk exhaustion. -- [ ] **State directory backed up** — The `~/.hawk/state/` directory contains +- [ ] **State directory backed up** — The `~/.graycode/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 `HAWK_CODE_ENABLE_TELEMETRY=1` +- [ ] **Telemetry enabled (optional)** — Set `GRAYCODE_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: 'hawk-daemon' - bearer_token: '' + - job_name: 'graycode-daemon' + bearer_token: '' static_configs: - - targets: ['hawk-daemon:4590'] + - targets: ['graycode-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 - `~/.hawk/state/daemon.key` for convenience. **Remove this file in + `~/.graycode/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/hawk securitylog verify + go run ./cmd/graycode 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/hawk # should say "not a dynamic executable" + ldd /usr/local/bin/graycode # 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: $HAWK_DAEMON_API_KEY" http://localhost:4590/v1/metrics + curl -H "X-API-Key: $GRAYCODE_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 `~/.hawk/state/` to a safe location. +1. **Back up state** — Copy `~/.graycode/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 `hawk daemon start` +3. **Install new binary** — Replace the binary and run `graycode 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-GRAYCODE-ECO-PORT-PLAN.md index fd529286..0f01bb2d 100644 --- a/docs/plans/FULL-GROK-ECO-TO-GRAYCODE-ECO-PORT-PLAN.md +++ b/docs/plans/FULL-GROK-ECO-TO-GRAYCODE-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 hawk into a Rust monorepo. +**Not meaning:** Copy Rust crates, depend on Grok binaries, or collapse graycode 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,8 +18,8 @@ ## 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 hawk / engines / contracts / cloud / skills. -3. **Prefer wire-first** — Hawk already has partial types; complete wiring before greenfield. +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. 4. **Privacy-first defaults** — Grok Mixpanel/Sentry defaults become **opt-in** OTEL/privacy-safe telemetry. 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. @@ -67,7 +67,7 @@ Critical path remains **months**; **complete parity** including TUI polish and e ```text grok-eco/grok-build (one Rust workspace) │ - ├─► hawk product CLI/TUI/engine/tools/hooks/plugins/ACP + ├─► graycode product CLI/TUI/engine/tools/hooks/plugins/ACP ├─► eagle pure DTOs (tool/spawn/hooks/policy) ├─► eyrie LLM routing/stream/retry/catalog/auth credentials ├─► harrier memory graph + dream UX @@ -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 | 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-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-workspace-types` | 9.2k | Workspace wire types | contracts | Partial | M | | -| `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-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 | **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-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-memory` | 9.7k | Cross-session memory | **harrier** | Done* | M | Port UX only | -| `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-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-config-types` | 2.7k | Config DTOs | contracts/config | Partial | 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-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-hooks-plugins-types` | 1.2k | Hooks/plugins ACP DTOs | contracts | Port | S | | -| `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-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 + 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-grok-announcements` | 0.4k | Release announcements | graycode tips/notify | Port | S | | +| `xai-grok-auth` | 0.4k | Auth seam | eyrie + graycode 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 | 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-tracing-macros` | 0.2k | Log macros | graycode observability | N/A | XS | | +| `xai-grok-env` | 0.2k | Backend env presets | eyrie/graycode | Partial | XS | | +| `xai-prompt-queue` | 0.2k | Prompt queue types | graycode | Port | S | | | `xai-mixpanel` | 0.1k | Mixpanel client | — | **Skip** | — | Privacy; OTEL opt-in | -| `xai-grok-version` | 0.1k | Version | hawk VERSION | Done | — | | +| `xai-grok-version` | 0.1k | Version | graycode 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 hawk | Port (Y2+) | XL | Only if multi-host tools wanted | +| `xai-computer-hub-sdk` | 14k | Remote tool server SDK | new or graycode | 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 + hawk | Port (Y2) | L | | -| `xai-tool-runtime` | 5.4k | Tool trait runtime | hawk tool | Partial | L | | +| `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-types` | 3.6k | Spawn/task types | **contracts** | Port | M | **P0** | -| `xai-grok-compaction` | 6.8k | Compaction engine | hawk engine + shrike | Partial | L | | +| `xai-grok-compaction` | 6.8k | Compaction engine | graycode 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-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-proto-build` | — | Build tooling | — | Skip | — | Rust build | -\*Done* = domain covered by hawk engine with different API. +\*Done* = domain covered by graycode engine with different API. --- @@ -180,7 +180,7 @@ grok-eco/grok-build (one Rust workspace) ### 3.1 Grok Build native tools -| Grok tool | Hawk equivalent | Status | Port work | +| Grok tool | Graycode 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 | Hawk | Action | +| Compat surface | Graycode | Action | |----------------|------|--------| -| 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 | +| 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 | | Cursor skills scan | missing | **Port** | --- ## 4. Slash commands & UX (pager) -Grok slash modules (port checklist → hawk `/` commands or CLI): +Grok slash modules (port checklist → graycode `/` commands or CLI): -| Grok slash | Status in hawk | Action | +| Grok slash | Status in graycode | Action | |------------|----------------|--------| | help | Partial | Port completeness | | model / effort | Partial | Port effort levels | @@ -259,17 +259,17 @@ Grok slash modules (port checklist → hawk `/` commands or CLI): --- -## 5. User-guide docs (port as hawk user-guide) +## 5. User-guide docs (port as graycode user-guide) | Grok doc | Port to | |----------|---------| -| 01–05 essentials | `hawk/docs/user-guide/` | +| 01–05 essentials | `graycode/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 | eyrie + hawk | +| 11 custom models | eyrie + graycode | | 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 → hawk `/` commands or CLI): These are “small” crates/modules but required for **full** port claims: -| Item | Grok home | Hawk action | Effort | +| Item | Grok home | Graycode 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 hawk swift | S | +| Swift classifier | shell | map to graycode 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, 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 | +| **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 | **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 → hawk; IT tier; config layer order | +| **Q6** | Managed policy (signed) graycode-cloud → graycode; 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 hawk issues +- Diff audit bot: scan Grok public releases → open graycode 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:** hawk can import types; no engine deps. +**DoD:** graycode can import types; no engine deps. ### PACK-02: Spawn control plane (6–8 weeks) -**Repo:** `hawk` +**Repo:** `graycode` - [ ] 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 HAWK_PLUGIN_ROOT/DATA +- [ ] Plugin env GRAYCODE_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) -- [ ] hawk apply layers +- [ ] graycode 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 hawk Bubble Tea: +Track Grok pager features against graycode 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 -### `hawk` (majority) +### `graycode` (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. @@ -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 hawk composition matches Grok “review/merlin” product moments if any. +No direct Grok crates; keep peer engines; ensure graycode composition matches Grok “review/merlin” product moments if any. ### `falcon` @@ -608,7 +608,7 @@ Dashboard/usage/marketplace web UI only. | Replace eyrie with xai-grok-sampler | Multi-provider better | | ratatui widgets | Bubble Tea stack | | `xai-proto-build` | Rust build | -| Closed contribution policy | Hawk is open | +| Closed contribution policy | Graycode is open | Skipping is **documented completion**, not unfinished work. @@ -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 Hawk’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 Graycode’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 ba36435b..3440d632 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:** `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) +**Primary product:** `graycode` +**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/Graycode Cloud; outside the Graycode Go runtime graph) --- @@ -13,21 +13,21 @@ ### Goal -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). +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 (eyrie multi-provider, harrier graph memory, shrike compression, mission mode, contracts, cloud ledger). ### Non-goals -- Rewrite Hawk in Rust or monorepo-collapse engines into hawk. +- Rewrite Graycode in Rust or monorepo-collapse engines into graycode. - Vendor lock-in to a single LLM host auth model. - 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** — Hawk already has many *types* and *partial systems* that are not exposed on the Agent tool or unified. +1. **Close wiring gaps first** — Graycode 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**; hawk remains the only product orchestrator. +4. **Keep engines peer-independent**; graycode remains the only product orchestrator. --- @@ -110,7 +110,7 @@ This is the single highest-ROI fix: **wire what already exists**, then extend. ```text ┌──────────────────────────────┐ - │ hawk TUI / CLI / daemon/ACP │ + │ graycode TUI / CLI / daemon/ACP │ └──────────────┬───────────────┘ │ ┌─────────────────────────┼─────────────────────────┐ @@ -133,7 +133,7 @@ This is the single highest-ROI fix: **wire what already exists**, then extend. eyrie harrier shrike swift kestrel/merlin ``` -**New core package (proposed):** `hawk/internal/spawn` (or expand `engine/agent`) owning: +**New core package (proposed):** `graycode/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 HAWK_PLUGIN_ROOT/DATA | Missing | Full | +| C5 | Plugin env GRAYCODE_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 hawk core + fractional engine/cloud; scale parallelizes quarters. +Assumes ~1–2 senior Go engineers on graycode 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; hawk can depend on new pseudo-version without engine imports. +- `go test ./...` green; graycode can depend on new pseudo-version without engine imports. --- ### Phase 2 — Spawn controller & Agent tool (4–6 weeks) ★ highest ROI -**Repo:** `hawk` +**Repo:** `graycode` **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 `.hawk/worktrees/` using shared helper from mission worker | -| 6 | Persist child session transcript under `~/.hawk/subagents//` | +| 5 | Isolation worktree: create under `.graycode/worktrees/` using shared helper from mission worker | +| 6 | Persist child session transcript under `~/.graycode/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 hawk +1. Contracts bump in graycode 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:** `hawk` +**Repo:** `graycode` **Packages:** `internal/sandbox`, `internal/trust` (new), `internal/mcp`, `internal/hooks`, `internal/plugin` #### 3.1 sandbox.toml ```toml -# ~/.hawk/sandbox.toml +# ~/.graycode/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 `.hawk/sandbox.toml` **additive only** (new names only). +2. Load project `.graycode/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:** `~/.hawk/trusted_folders.toml` +**Store:** `~/.graycode/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:** `hawk` +**Repo:** `graycode` **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 HAWK_* | +| command | shell with timeout, env GRAYCODE_* | | 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 -- `~/.hawk/hooks/*.json` -- `.hawk/hooks/*.json` (trust required) +- `~/.graycode/hooks/*.json` +- `.graycode/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:** `hawk`, `starling` +**Repos:** `graycode`, `starling` #### 5.1 Plugin layout (convention) @@ -603,19 +603,19 @@ Loader merges components; tools optional. 1. Session meta / SDK 2. `--plugin-dir` -3. Project `.hawk/plugins` (trust) -4. User `~/.hawk/plugins` +3. Project `.graycode/plugins` (trust) +4. User `~/.graycode/plugins` 5. Config extra paths #### 5.3 Marketplace | Component | Owner | |-----------|--------| -| 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 | +| 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 | | 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 `.hawk/plans/` or specs | +| Plan subagent | Default tool for “design only”; writes plan file under `.graycode/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:** `hawk`, `sparrow`, `robin` +**Repos:** `graycode`, `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 | `hawk agent serve --bind` WS | +| Optional | `graycode agent serve --bind` WS | **Exit criteria** -- Zed/VS Code can drive hawk ACP for multi-turn with permissions. +- Zed/VS Code can drive graycode 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`), `hawk`, optional `eyrie` +**Repos:** `graycode-platform/apps/worker` (deployed as `graycode-cloud`), `graycode`, optional `eyrie` | Piece | Detail | |-------|--------| | Policy document | models allow/deny, capabilities, sandbox deny, max budget, tool denylist | -| Signing | Ed25519 envelope; hawk verifies before apply | -| Apply path | `~/.hawk/managed_policy.json` layers under user config | +| Signing | Ed25519 envelope; graycode verifies before apply | +| Apply path | `~/.graycode/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 + 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 | +| 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 | **Exit criteria** @@ -756,20 +756,20 @@ Build on existing enterprise policyInput (model/capability lists). | Concept family | Primary | Secondary | Contracts? | |----------------|---------|-----------|------------| -| 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 | +| 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 | | Token | shrike | — | no change | -| Providers | eyrie | hawk | no change | -| Review engines | kestrel/merlin | hawk | findings already | +| Providers | eyrie | graycode | no change | +| Review engines | kestrel/merlin | graycode | 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_hawk | +| Signature change of AgentSpawnFn breaks plugins | High | Adapter release; version min_graycode | | 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 | |------|-------|-------|-------| -| Hawk core (Go) | 1.5 FTE | 1.5 FTE | 1 FTE | +| Graycode 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 → Hawk names +## 14. Appendix B — Mapping Grok names → Graycode names -| Grok | Hawk | +| Grok | Graycode | |------|------| | `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` | `~/.hawk/sandbox.toml` | -| folder trust | `~/.hawk/trusted_folders.toml` | +| `sandbox.toml` | `~/.graycode/sandbox.toml` | +| folder trust | `~/.graycode/trusted_folders.toml` | | `/loop` | `/loop` over CronScheduler | -| plugin marketplace | `hawk plugins` + community registry | +| plugin marketplace | `graycode plugins` + community registry | | managed_config | managed policy via graycode-cloud | -| ACP | `hawk acp` | +| ACP | `graycode acp` | --- diff --git a/docs/plans/SPEC_DRIVEN_PHASE2_PLAN.md b/docs/plans/SPEC_DRIVEN_PHASE2_PLAN.md index 10f6aafa..0b3eecd8 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 .hawk/specs/ changes + - Stage .graycode/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 4f7b94dc..0f2d06a5 100644 --- a/docs/plans/YEAR-0-ACTIVE.md +++ b/docs/plans/YEAR-0-ACTIVE.md @@ -1,4 +1,4 @@ -# Year 0 Active Track (Grok → Hawk) +# Year 0 Active Track (Grok → Graycode) **Status:** Active **Date:** 2026-07-16 @@ -14,7 +14,7 @@ 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 hawk monorepo | +| Map capabilities to graycode-eco repos | Collapse engines into graycode monorepo | | Wire existing modes/budgets first | Rebuild eyrie/harrier/shrike as Grok clones | | Privacy-first telemetry (OTEL opt-in) | Port Mixpanel defaults | @@ -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 (`.hawk/plugins`, `.hawk/hooks`; MCP/LSP follow same AllowLoadPath) +- [x] Folder trust gates project hooks / plugins (`.graycode/plugins`, `.graycode/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 `hawk/docs/user-guide/` (completed July 2026) +- [x] User-guide docs `01`–`24` under `graycode/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 | |-----|---------|---------| -| `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 | +| `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 | Implementation: `internal/flags/y0.go`. diff --git a/docs/plans/codex-adoption-plan.md b/docs/plans/codex-adoption-plan.md index 9e7bb922..8659d540 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 hawk; remaining +Status: Audited. Core ideas already implemented natively in graycode; 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 hawk's security and +The audit found that every codex-rs capability relevant to graycode's security and runtime model already has a native Go implementation, several of them deeper -than codex's equivalents because they build on Hawk's independent ecosystem +than codex's equivalents because they build on Graycode'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 | hawk implementation | Decision | +| codex-rs crate/concept | graycode implementation | Decision | |---|---|---| -| `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 | +| `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` | eyrie credential store in OS keychain across 28 providers | Keep graycode (broader) | +| `model-provider(-info)`, `models-manager`, `ollama`, `lmstudio` | sibling `eyrie` 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 | | `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: `hawk status` (text and `--json`) now resolves the +- Status transparency: `graycode 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`): hawk already provides a +- **Agent identity signing** (`agent-identity`): graycode 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. - Hawk Cloud already provides sync/review surfaces; a queue protocol would + Graycode 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`, `hawk verify` green. +- `make vet`, `make lint`, `graycode 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 07e58ebd..1bc57c4b 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 Hawk working tree where the existing architecture +Status: Implemented in the Graycode 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 Hawk skills | +| `awesome-agents` | Apache-2.0 example applications | Reference only; do not merge into Graycode 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 Hawk already provides native equivalents +The Kimi Code comparison confirmed that Graycode 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`. -- Hawk's existing permission engine already supports ordered allow/deny rules +- Graycode'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. -- Hawk's existing goal tracker already provides durable objective state, +- Graycode'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 Hawk's stronger Harrier memory, Shrike token controls, Swift replay, Kestrel +replace Graycode's stronger Harrier memory, Shrike token controls, Swift replay, Kestrel review, Merlin auditing, or Eyrie provider runtime. ### 1. Skill metadata interoperability -Hawk's smart-skill parser accepts both hyphenated and community-schema +Graycode'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 -Hawk's existing Unicode audit remains the runtime security scanner. A new +Graycode'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 -`hawk skills audit` now reports both Unicode and structural findings. This is a +`graycode 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 -Hawk already has `internal/feature/taste` with confidence, sample count, +Graycode 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. Hawk now +CommandCodeAI's strongest product contribution is discoverability. Graycode now documents its existing capabilities in: - `docs/user-guide/26-learned-preferences.md` - `docs/user-guide/27-workflows.md` -- `docs/architecture/hawk-harness.md` +- `docs/architecture/graycode-harness.md` - `docs/user-guide/28-workflow-budgets.md` These cover slash/shell/file-context input, headless review, MCP-backed @@ -112,21 +112,21 @@ tool, depth, time, token, and cost budgets. ## Deliberately Not Adopted - CommandCodeAI provider adapters: Eyrie owns provider protocols and routing. -- BaseAI remote pipes: incompatible with Hawk's local authority and durable +- BaseAI remote pipes: incompatible with Graycode's local authority and durable event model. -- BaseAI `lowdb` JSON memory: weaker than Harrier and Hawk persistence. +- BaseAI `lowdb` JSON memory: weaker than Harrier and Graycode 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 Hawk's code-intelligence boundary. +- Media/UI/status repositories: outside Graycode's code-intelligence boundary. - CommandCodeAI branding, proprietary model claims, and undocumented services. ## Verification Plan -1. Run formatting and static checks on all changed Hawk Go files. +1. Run formatting and static checks on all changed Graycode Go files. 2. Run focused parser, validator, taste, engine, and command tests. -3. Run the full Hawk test suite and vet. +3. Run the full Graycode test suite and vet. 4. Repeat the focused and full checks independently. 5. Merlin the final diff, worktree, and sibling-repository status. 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. +through its own feature branch and PR before updating Graycode's module pin. diff --git a/docs/plans/dsh-harness-gap-port-plan.md b/docs/plans/dsh-harness-gap-port-plan.md index f079d5b3..9d2ec67e 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) | Hawk gap | Port status | +| # | DSH package(s) | Graycode 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` | hawk exposes ACP server instead of DSH JSON-RPC SDK | deferred (keep ACP) | +| 24 | `sdk/client`, `sdk/protocol`, `sdk/server`, `python/sdk` | graycode 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): `$HAWK_HOME`-style home (`~/.hawk`), +- Resolved once per process (memoized): `$GRAYCODE_HOME`-style home (`~/.graycode`), `.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 hawk home data dir. + filesystem `Store` under the graycode 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 — hawk has no Cordis bus; records reach the +coordinator stays out of scope — graycode 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 - (`HAWK_CODE_ENABLE_TELEMETRY=1` + `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT`); - wired in `cmd/hawk/main.go` and `cmd/daemon.go`. + (`GRAYCODE_ENABLE_TELEMETRY=1` + `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT`); + wired in `cmd/graycode/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 `hawk verify` — run **twice** (the second pass re-runs the full touched +and `graycode 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`) -- `hawk verify`: exit 0 +- `graycode 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; `hawk verify`: exit 0 +- `make lint`: 0 issues; `graycode 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/hawk/main.go, cmd/daemon.go, internal/observability/otellog): clean +- `gofmt -l` (cmd/graycode/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** -- `hawk verify`: exit 0 +- `graycode 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; `hawk verify`: exit 0 +- `make lint`: 0 issues; `graycode 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 5a1c0d47..fc3f3699 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 hawk +# Adopt durable-design ideas from deepseek-harness in graycode 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 hawk | +| dsh file | Extract | Maps to graycode | | --- | --- | --- | | `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/hawk-capability-seams.md` (Phase 2) | +| `docs/capability-seams.md` | owner / impl / consumer roles | `docs/architecture/graycode-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/hawk-capability-seams.md`: owner / impl / consumer table. +- `docs/architecture/graycode-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 Hawk against the real deepseek-harness source, not the +These numbers compare Graycode 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`. -Hawk is a **deliberate subset**. It ports the session-log spine, the fail-closed +Graycode 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, Hawk has roughly **20–30%** +product-wide plugin catalogue. Measured head-to-head, Graycode has roughly **20–30%** of upstream by feature surface, and roughly **80–90%** of the plan's stated scope. -| Surface | DSH | Hawk today | Gap | +| Surface | DSH | Graycode 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 | @@ -429,4 +429,4 @@ UI additions (Safari support, outside pointer handling) or ACP content admission ## Gates -Each phase: `make ci` + `hawk verify`; no direct commits to `main`. +Each phase: `make ci` + `graycode 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 eb8e7858..73bc8b8e 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 hawk +# Wave 2 — high-value deepseek-harness RFC ports for graycode 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 | Hawk gap | Status | +| # | DSH source | Graycode 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 hawk-native form of DSH's + time.Duration`, 0 = no declaration) — the graycode-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. Hawk's +`toolPairingBalancedBefore` / `toolPairingBalancedAfter` helpers. Graycode'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). Hawk already has the vocabulary — `internal/sandbox/mode.go` +contribution). Graycode 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 hawk's + the prompt assembler exactly like DSH's three templates, but with graycode'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 hawk's session logs. +`session-query/session-query-sqlite` semantics over graycode'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`. Hawk has the ACP **server** (Wave 1 `internal/acp/server.go` — +`subagent-codex`. Graycode 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 hawk's mission mode +session/request_permission); this phase adds the client so graycode'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 - hawk constraint); Windows via `ConPTY` later, outside this phase. + graycode 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 hawk is Docker-only and fails closed without Docker. This phase gives +on Windows graycode 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 hawk's fs, `textDocument/didOpen` (version 1, full text), the + through graycode'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 -`hawk verify`, run **twice** (second pass re-runs the full touched suite after +`graycode 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 f74dec38..47d94ac3 100644 --- a/docs/plans/engine-refactor-plan.md +++ b/docs/plans/engine-refactor-plan.md @@ -1,20 +1,20 @@ -# hawk/engine sub-package split — analysis and migration plan +# graycode/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 hawk's +> split is multi-PR work that should land incrementally to keep graycode's > build green at every step. ## The problem ``` -hawk/engine/ +graycode/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. hawk's engine +~110K lines split across **dozens** of internal packages. graycode'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. ``` -hawk/engine/ +graycode/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 hawk -that imports `engine.Foo`. To keep hawk green at every commit: +The split is high-risk because it touches every other package in graycode +that imports `engine.Foo`. To keep graycode 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/hawk/engine" + import "github.com/GrayCodeAI/graycode-cli/engine" type Strategy = engine.CompactStrategy var Default = engine.DefaultCompactStrategy ``` - Hawk's external callers can start migrating to the new import paths. + Graycode'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 hawk green at every commit: coordinator package only. This approach scales: each PR is small, reviewable, and individually -revertible. No "big bang" merge that paralyses hawk for a week. +revertible. No "big bang" merge that paralyses graycode 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/hawk/engine/` +New code should import `github.com/GrayCodeAI/graycode-cli/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/hawk/engine" +import "github.com/GrayCodeAI/graycode-cli/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 dbf82802..9a281bb1 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 — hawk +# Plan: Fix Critical + High-Impact Review Findings — graycode > 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 `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, +A deep code review of `eyrie` 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, H6, H7, H8, H9) broken into a sequence of small, reviewable PRs. -## Scope (hawk) +## Scope (graycode) | ID | Severity | Title | File(s) | Effort | |----|----------|-------|---------|--------| @@ -99,7 +99,7 @@ H6, H7, H8, H9) broken into a sequence of small, reviewable PRs. - H10 from eyrie: `//nolint:errcheck` on type-assertion that can panic. - M1–M20 medium items. -- L-tier quick wins (e.g. `cosmenticFlags` typo, `cmd/.hawk/` leaked state). +- L-tier quick wins (e.g. `cosmenticFlags` typo, `cmd/.graycode/` 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 `~/.hawk/.env` while +the error. If migration fails, secrets may remain in `~/.graycode/.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 -`HAWK_MULTIAGENT_POLLING=1` to revert. +`GRAYCODE_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` `DefaultHawkPolicy` defaults +**Bug**: `internal/sandbox/seatbelt.go:70-108` `DefaultGraycodePolicy` 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/hawk +go build ./cmd/graycode 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 -- **eyrie PR 4 (C2 — Vertex fix)** and **hawk PR 4 (H6 — Session +- **eyrie PR 4 (C2 — Vertex fix)** and **graycode PR 4 (H6 — Session decomposition)** are independent. - **eyrie PR 8 (H4 — EyrieError)** is a prerequisite for any future - hawk-side `errors.As(err, &eyrieErr)` use (currently none). No + graycode-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 7bd70b9b..7c2c2930 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 Hawk's native Go architecture +Status: Implemented in Graycode'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 Hawk and its independent ecosystem repositories. It is an -adoption plan, not a code-porting plan. Hawk should reimplement compatible +`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 behavior in Go and preserve its existing provider, memory, review, audit, session, and security boundaries. ## Executive Decision -Hawk should adopt the following `fx` ideas: +Graycode 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 @@ Hawk 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 Hawk's existing daemon and ACP surfaces. +7. A narrow embeddable host API over Graycode's existing daemon and ACP surfaces. -Hawk should not copy `fx` source code, replace its Go runtime with Zig, create a +Graycode 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 Hawk Capabilities +## Existing Graycode Capabilities -The comparison found that Hawk already provides the foundation for most `fx` +The comparison found that Graycode already provides the foundation for most `fx` features: -| `fx` capability | Hawk implementation | Current decision | +| `fx` capability | Graycode implementation | Current decision | |---|---|---| -| Native single binary | Go static binary and cross-platform release builds | Keep Hawk implementation | -| Agent loop | `internal/engine` | Keep Hawk implementation | +| Native single binary | Go static binary and cross-platform release builds | Keep Graycode implementation | +| Agent loop | `internal/engine` | Keep Graycode 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 `eyrie` | Do not add provider logic to Hawk | +| Provider runtime | sibling `eyrie` | Do not add provider logic to Graycode | ## Priority Model @@ -63,7 +63,7 @@ when their original command, path, or workspace has changed. ### Scope and ownership -- Primary implementation: Hawk `internal/permissions` and +- Primary implementation: Graycode `internal/permissions` and `internal/engine/safety`. - Shared contract changes, if needed: sibling `eagle/policy`. - User-facing commands: `cmd` and the existing slash-command surface. @@ -88,12 +88,12 @@ when their original command, path, or workspace has changed. ### Proposed interfaces ```text -hawk permissions list [--json] -hawk permissions revoke -hawk permissions reset [--scope user|project] +graycode permissions list [--json] +graycode permissions revoke +graycode permissions reset [--scope user|project] ``` -The exact command names may follow existing Hawk conventions, but the typed +The exact command names may follow existing Graycode 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: Hawk root or `eagle` if consumed cross-repo. +- Snapshot contract: Graycode 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 -hawk_version +graycode_version session_id workspace git_branch @@ -193,8 +193,8 @@ identifiers rather than raw secrets. ### Required interfaces ```text -hawk status -hawk status --json +graycode status +graycode 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 Hawk's existing +Add `fx`-style terminal byte and resize recording to complement Graycode's existing agent-session swift and replay features. ### Ownership - Preferred home: sibling `swift` (Swift) if the capability is intended for reuse by other agents. -- Hawk integration: `internal/swift` or the TUI composition layer. +- Graycode 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 -hawk swift record --output -hawk swift replay -hawk swift replay --frames -hawk swift replay --json +graycode swift record --output +graycode swift replay +graycode swift replay --frames +graycode 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 Hawk's richer policy +Expose familiar `fx` permission names without weakening Graycode's richer policy engine. ### Mapping -| Compatibility name | Hawk behavior | +| Compatibility name | Graycode 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 Hawk's +These are aliases or presentation-layer modes, not a replacement for Graycode'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 Hawk tier and hard safety constraints. +2. Display the effective Graycode 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 Hawk representation, not an ambiguous alias. +4. Persist the canonical Graycode 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 Hawk's continuable +Adopt `fx`'s useful child-agent visibility while retaining Graycode'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 Hawk's existing MCP and skills architecture. +without changing Graycode's existing MCP and skills architecture. ### Required behavior @@ -344,7 +344,7 @@ without changing Hawk's existing MCP and skills architecture. ### Goal Offer a supported embedding boundary inspired by `fx`'s ACP and WASM surfaces, -without committing Hawk to a WASM rewrite. +without committing Graycode to a WASM rewrite. ### First implementation @@ -417,11 +417,11 @@ make test-race make vet make lint make security -hawk verify +graycode verify ``` For sibling-repository changes, run that repository's own tests and boundary checks before -updating the Hawk pointer. Do not modify provider protocols or adapters in Hawk; +updating the Graycode pointer. Do not modify provider protocols or adapters in Graycode; those belong in the Eyrie repository. ## Delivery Sequence @@ -456,7 +456,7 @@ those belong in the Eyrie 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 Hawk without coupling tape parsing to the +- Integrate recording controls into Graycode without coupling tape parsing to the agent loop. ### Milestone 5: Host integration and trust UX @@ -469,18 +469,18 @@ those belong in the Eyrie repository. ## Deliberately Rejected - Copying Zig implementation code from `vercel-labs/fx`. -- Replacing Hawk's Go runtime or independent ecosystem repositories. +- Replacing Graycode'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 Hawk's code intelligence, review, audit, token, or provider features +- Removing Graycode'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 Hawk can provide the operational clarity of +The adoption is successful when Graycode 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 Hawk 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 Hawk memory, provider, review, audit, MCP, ACP, and session behavior +- Existing Graycode memory, provider, review, audit, MCP, ACP, and session behavior remains intact. ## Implemented Scope @@ -504,7 +504,7 @@ The adoption is successful when Hawk 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. Hawk's subprocess LSP +implemented surfaces, not missing baseline features. Graycode'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 6744fb2d..e3b747ba 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 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 +Goose's audit against graycode found that most of its runtime concepts already have +a native graycode implementation (providers via eyrie, 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 sandboxing model. -## Existing Hawk Capabilities +## Existing Graycode Capabilities -| Goose package/concept | Hawk implementation | Decision | +| Goose package/concept | Graycode implementation | Decision | |---|---|---| -| 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 | +| Provider abstraction (~36) | sibling `eyrie` (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 | | 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, hawk-native adoptions. +- **P0:** Security-relevant, bounded, graycode-native adoptions. - **P1:** High-value product improvements. - **Defer:** Larger or cross-cutting changes needing an RFC. @@ -114,7 +114,7 @@ strict size/depth budgets — matching goose `hints/import_files.rs`. ### Goal -Upgrade hawk's context compaction to a structured summary with a progressive +Upgrade graycode'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): hawk's +- Goose's SQLite session store (`usage_ledger`, token/cost schema): graycode'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 eyrie's local-model path. -- Recipe security scanner / cron recipes: hawk already has schedule/cron. +- Recipe security scanner / cron recipes: graycode already has schedule/cron. ## Verification - `go test ./...` full suite. -- `make vet`, `make lint`, `hawk verify`. +- `make vet`, `make lint`, `graycode verify`. - Focused tests for the env filter, AGENTS.md references, and compaction retry. - markdownlint on this document. diff --git a/docs/plans/hawk-contracts-migration-backlog.md b/docs/plans/graycode-contracts-migration-backlog.md similarity index 66% rename from docs/plans/hawk-contracts-migration-backlog.md rename to docs/plans/graycode-contracts-migration-backlog.md index 4edbc9e1..9a7dc8a3 100644 --- a/docs/plans/hawk-contracts-migration-backlog.md +++ b/docs/plans/graycode-contracts-migration-backlog.md @@ -1,8 +1,8 @@ -# Plan: Hawk Contracts Migration Backlog +# Plan: Graycode Contracts Migration Backlog > Status: locally complete > Scope: graycode-eco ecosystem architecture cleanup after introducing `eagle` -> Goal: keep `hawk` as the product while moving stable cross-repo contracts out of Hawk internals +> Goal: keep `graycode` as the product while moving stable cross-repo contracts out of Graycode 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 Hawk `internal/types/severity.go` to re-export from contracts -- removed `hawk/shared/types` after local ecosystem migration +- switched Graycode `internal/types/severity.go` to re-export from contracts +- removed `graycode/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,14 +31,14 @@ These items are already completed in the current workspace. ### Tool contract migration - added `eagle/tools` -- switched Hawk session persistence to provider-neutral tool contracts +- switched Graycode session persistence to provider-neutral tool contracts - added runtime/session conversion helpers - added `eagle/tools/tool_test.go` -- centralized message slice conversion in `hawk/internal/session` -- removed direct lower-level provider message reconstruction from Hawk +- centralized message slice conversion in `graycode/internal/session` +- removed direct lower-level provider message reconstruction from Graycode cmd/session restore paths -- replaced the provider-owned message alias with a Hawk-owned runtime DTO -- added explicit Hawk-owned runtime DTOs; the final boundary translates them +- replaced the provider-owned message alias with a Graycode-owned runtime DTO +- added explicit Graycode-owned runtime DTOs; the final boundary translates them through `eyrie/engine` ### Event contract migration @@ -61,7 +61,7 @@ These items are already completed in the current workspace. - 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 Hawk, kestrel, merlin, and external workspace copies +- updated docs across Graycode, kestrel, merlin, and external workspace copies - added standalone boundary guards in `kestrel` and `merlin` - added standalone boundary guards in `shrike`, `eyrie`, `harrier`, and `swift` - updated support repo READMEs with ecosystem boundary rules @@ -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 Hawk review persistence to neutral review contracts -- switched Hawk review/merlin bridge paths to return neutral review/verify contracts +- switched Graycode review persistence to neutral review contracts +- switched Graycode 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: -- Hawk local integration snapshot points at architecture-aligned support-repo revisions +- Graycode local integration snapshot points at architecture-aligned support-repo revisions Still external: -- confirm released module versions used by Hawk match the merged contract changes +- confirm released module versions used by Graycode match the merged contract changes -### 3. Remove Hawk production dependency on lower Eyrie packages +### 3. Remove Graycode production dependency on lower Eyrie packages Current state: - session persistence uses neutral tool contracts -- 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 +- Graycode now owns the runtime message DTO in `internal/types.EyrieMessage` +- 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 `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: Hawk owns the product DTO/port seam and Eyrie owns the engine, +- completed: Graycode 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` -- Hawk consumes neutral review/verification contracts at persistence and bridge boundaries +- Graycode 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 `hawk/shared/types` +### 6. Remove `graycode/shared/types` Completed for the local ecosystem. Current status: -- Hawk no longer ships `hawk/shared/types` +- Graycode no longer ships `graycode/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 Hawk orchestration logic into contracts +- move Graycode orchestration logic into contracts - move sandbox manager internals into contracts -- move every event struct in Hawk into contracts -- force Lark/Gitant architecture into Hawk contracts +- move every event struct in Graycode into contracts +- force Lark/Gitant architecture into Graycode contracts ## Recommended PR order ### PR 1 -- completed: moved chat options/request DTOs behind Hawk-owned runtime adapters +- completed: moved chat options/request DTOs behind Graycode-owned runtime adapters ### PR 2 -- completed: moved provider/config interfaces behind Hawk-owned transport adapters +- completed: moved provider/config interfaces behind Graycode-owned transport adapters ### PR 3 -- completed: removed all lower-level Eyrie imports from Hawk production code and +- completed: removed all lower-level Eyrie imports from Graycode production code and replaced the compatibility allowlist with a zero-exception guard ### PR 4 -- completed: added neutral review/verification result contracts and wired Hawk bridge/persistence edges +- completed: added neutral review/verification result contracts and wired Graycode bridge/persistence edges ### PR 5 -- completed: removed `hawk/shared/types` from the local ecosystem and kept a legacy import guard +- completed: removed `graycode/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 `hawk/internal/*` -- support repos do not import `hawk/shared/types` +- support repos do not import `graycode/internal/*` +- support repos do not import `graycode/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 f433c118..97639023 100644 --- a/docs/plans/minimax-adoption-plan.md +++ b/docs/plans/minimax-adoption-plan.md @@ -1,14 +1,14 @@ -# MiniMax-AI → hawk Adoption Plan +# MiniMax-AI → graycode 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 Hawk ecosystem (Hawk plus independent repositories: Eyrie, Eagle, +repos into the Graycode ecosystem (Graycode plus independent repositories: Eyrie, Eagle, Falcon, Kestrel, and Merlin). ## Findings summary -Deep code review of 6 MiniMax-AI repos against Hawk's existing sibling +Deep code review of 6 MiniMax-AI repos against Graycode's existing sibling repositories and internals produced these adoptions, ordered by value: @@ -16,9 +16,9 @@ internals produced these adoptions, ordered by value: |---|---|---|---| | 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 | 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) | +| 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) | | 6 | MiniMax-MCP / JS | — | Not adoptable (media generation, out of scope) | ## 1. Eyrie provider-conformance metrics + CI wiring (highest value) @@ -93,17 +93,17 @@ This makes the orphaned harness operational and enables CI gating. ## 2. Curate MiniMax/skills content into starling -### 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`). +### 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`). - 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 hawk's convention (`globs`, `alwaysApply`). + repo), adapting frontmatter to graycode'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 (hawk) +### Verified current state (graycode) - `internal/mcp` implements its own JSON-RPC client + server and does NOT use the shared `falcon` scaffolding (architectural divergence). -- hawk has `internal/attachment/image.go` for image decode, and `ScreenshotTool` +- graycode 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 (hawk) -- hawk has `WebSearchTool` (6-provider cascade: Brave/SearXNG/DeepSeek/Exa/ +### Verified current state (graycode) +- graycode 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 (hawk) -- hawk's `Session.agentLoop` (`internal/engine/stream.go:133`) is a complete +### Verified current state (graycode) +- graycode'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 hawk's loop: on cancellation +- Add `_cleanup_incomplete_messages` equivalent to graycode'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,16 +175,16 @@ 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; 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. + 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. - The `verify` harness's live-token path: optional, off by default. ## Execution order 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 +3. graycode image-source normalization helper + tests +4. graycode Jina browse extractor + tests 5. Agent-loop cancellation/summarization patterns 6. starling content curation (follow-up PR in separate repo) @@ -192,10 +192,10 @@ This makes the orphaned harness operational and enables CI gating. - [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 +- [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 compaction; no duplicate summarizer was added. - [ ] 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 c1a755bb..673286c2 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 hawk while -auditing the Pi agent harness against Hawk and its independent ecosystem +This plan records the features identified as genuinely missing from graycode while +auditing the Pi agent harness against Graycode and its independent ecosystem repositories. It is -an adoption plan, not a code-porting plan: hawk reimplements compatible behavior +an adoption plan, not a code-porting plan: graycode reimplements compatible behavior in Go and preserves its existing provider, session, sandbox, permission, observability, and protocol boundaries. ## Executive Decision -Hawk should adopt the following Pi features: +Graycode should adopt the following Pi features: 1. A Go telemetry conformance suite that verifies emitted OpenTelemetry spans and attributes match the documented schema. @@ -25,21 +25,21 @@ Hawk should adopt the following Pi features: 5. Kitty graphics protocol support for terminal images. 6. Session lease/ownership semantics in the daemon protocol. -Hawk should not copy Pi's TypeScript code, replace its Go runtime, adopt Pi's +Graycode 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 Hawk Capabilities +## Existing Graycode Capabilities -The audit found that hawk already provides the foundation for most Pi features: +The audit found that graycode already provides the foundation for most Pi features: -| Pi package | Hawk implementation | Current decision | +| Pi package | Graycode implementation | Current decision | |---|---|---| -| `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-ai` (multi-provider) | sibling `eyrie` 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-tui` differential rendering | Bubble Tea v2 full-frame redraw | Adopt line-diff engine | | `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 | @@ -57,7 +57,7 @@ The audit found that hawk already provides the foundation for most Pi features: 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 Hawk and its independent +contract, so the schema cannot silently drift across Graycode and its independent ecosystem repositories. ### Scope and ownership @@ -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 hawk and eyrie test suites so CI +4. Wire the conformance harness into the graycode 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 hawk emits is covered by the schema. +- Every span graycode 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 hawk and eyrie CI. +- The conformance harness runs green in graycode and eyrie CI. ## P0: Agent-Runtime Eval Harness ### Goal -Evaluate the full hawk agent end-to-end (real session, tool loop, planning, +Evaluate the full graycode 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 hawk and eyrie CI. +- Cross-repo telemetry conformance in graycode and eyrie CI. ### Security tests @@ -291,11 +291,11 @@ make test-race make vet make lint make security -hawk verify +graycode verify ``` For sibling-repository changes, run that repository's own tests and boundary checks before -updating the Hawk pointer. +updating the Graycode pointer. ## Delivery Sequence @@ -310,7 +310,7 @@ updating the Hawk pointer. - [x] Define the typed span/attribute schema. - [x] Implement the conformance harness. -- [x] Wire hawk and eyrie CI. +- [x] Wire graycode and eyrie CI. - [x] Add schema-drift regression tests. ### Milestone 2: Agent-runtime eval (P0) @@ -318,7 +318,7 @@ updating the Hawk 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 `hawk eval` with a loop mode. +- [x] Extend `graycode eval` with a loop mode. ### Milestone 3: Differential renderer (P1) @@ -344,19 +344,19 @@ updating the Hawk pointer. ## Deliberately Deferred - Copying Pi's TypeScript code or adopting its custom CBOR RPC protocol in place - of hawk's ACP/MCP/daemon stack. -- Replacing hawk's native permission/sandbox model with Pi's container-only + of graycode's ACP/MCP/daemon stack. +- Replacing graycode's native permission/sandbox model with Pi's container-only approach. -- Porting Pi's extension system verbatim; hawk's plugin/hook/skills model already +- Porting Pi's extension system verbatim; graycode'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 hawk closes each confirmed gap while retaining +The adoption is successful when graycode closes each confirmed gap while retaining its stronger architecture: -- Telemetry spans always conform to the documented schema across hawk and its +- Telemetry spans always conform to the documented schema across graycode 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 ab392916..60c03b97 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 hawk's current UI framework: +cannot be adopted cleanly in graycode'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 `hawk eval loop` output. +- Wire it into the existing `evalloop` package and `graycode 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. `hawk eval loop --report` prints the comparative summary. +4. `graycode 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`, `hawk verify`. +- `make vet`, `make lint`, `graycode 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 04a02e52..63b654e4 100644 --- a/docs/plans/qwen-code-adoption-plan.md +++ b/docs/plans/qwen-code-adoption-plan.md @@ -1,17 +1,17 @@ # Qwen Code Adoption Plan -Status: Implemented selectively in the current Hawk feature branch. +Status: Implemented selectively in the current Graycode feature branch. ## Guardrails -Qwen Code is Apache-2.0 TypeScript software with a Gemini-shaped core. Hawk +Qwen Code is Apache-2.0 TypeScript software with a Gemini-shaped core. Graycode will independently reimplement behavioral contracts in Go, preserve the Eyrie -provider boundary, and retain Hawk's event-sourced sessions and OS sandbox. +provider boundary, and retain Graycode's event-sourced sessions and OS sandbox. No Qwen source or dependency is vendored. -## Existing Hawk Capabilities +## Existing Graycode Capabilities -Hawk already has durable sessions, event logging, WAL/recovery, branching, +Graycode 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 Hawk's existing cancellation transcript cleanup and compaction. +4. Preserved Graycode'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 Hawk's typed SpawnRequest. +- Map Markdown subagent frontmatter to Graycode'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 Hawk build, vet, and test suite. +- Full Graycode 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 5b58ae8f..275bf31f 100644 --- a/docs/plans/toolbench-comparison-vs-top20.md +++ b/docs/plans/toolbench-comparison-vs-top20.md @@ -1,17 +1,17 @@ -# Toolbench Comparison: hawk vs the Top-20 OSS Coding Agents (2026) +# Toolbench Comparison: graycode vs the Top-20 OSS Coding Agents (2026) > Status: analysis complete -> Scope: built-in tool inventory of hawk and the leading open-source coding agents +> Scope: built-in tool inventory of graycode and the leading open-source coding agents > Related issue: browser/screenshot automation gap ## Executive summary -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: +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: 1. **Browser / computer-use automation** — now closed (see this plan's outcome). -2. **IDE surfaces** — Cline/Continue/Zed are editor-native; hawk remains terminal-first by design. +2. **IDE surfaces** — Cline/Continue/Zed are editor-native; graycode remains terminal-first by design. -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. +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. ## 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 | |---|---|---| -| hawk | **69** (+Power, +MCP) | all | +| graycode | **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 | -## hawk's current tool set +## graycode'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. hawk 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. graycode previously had only file/shell/web tools. -**Outcome:** added `internal/tool/browser.go` and `internal/tool/screenshot.go`, exposed through hawk's existing `tool.Tool` interface: +**Outcome:** added `internal/tool/browser.go` and `internal/tool/screenshot.go`, exposed through graycode'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 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). +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). -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. +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. ## Category parity vs the field -| Category | hawk | Field leaders | +| Category | graycode | Field leaders | |---|---|---| -| 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 | +| 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 | | 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 `HAWK_LIVE_BROWSER=1`) nav ## Remaining deltas (out of scope here) - VS Code / JetBrains extension (planned in `docs/IMPLEMENTATION-ROADMAP.md`). -- Auto-commit-per-edit discipline à la Aider (hawk uses AGENTS.md branch rules instead). +- Auto-commit-per-edit discipline à la Aider (graycode 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 `HAWK_LIVE_BROWSER=1`. +- `go test ./internal/tool/` — unit tests pass; live browser test passes with `GRAYCODE_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 17931698..46de595d 100644 --- a/docs/platform-capabilities.json +++ b/docs/platform-capabilities.json @@ -51,12 +51,12 @@ "features": ["tools", "resources", "prompts", "sampling"] }, "ecosystem_compatibility": { - "hawk+eyrie": "full", - "hawk+shrike": "full", - "hawk+kestrel": "full", - "hawk+merlin": "full", - "hawk+harrier": "full", - "hawk+swift": "full", + "graycode+eyrie": "full", + "graycode+shrike": "full", + "graycode+kestrel": "full", + "graycode+merlin": "full", + "graycode+harrier": "full", + "graycode+swift": "full", "eyrie+shrike": "token_compression", "kestrel+merlin": "security_review" } diff --git a/docs/plugin-development.md b/docs/plugin-development.md index 4f209f69..bfa75246 100644 --- a/docs/plugin-development.md +++ b/docs/plugin-development.md @@ -2,10 +2,10 @@ ## Overview -Hawk supports three plugin types: +Graycode 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 `~/.hawk/plugins/`. +2. **Managed subprocess plugins** — binary executables managed by `PluginManager`. Supports security scanning, timeout enforcement, and auto-discovery from `~/.graycode/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 (`HAWK_EVENT=file.write`, `HAWK_FILE_PATH=/path/to/file`). +Hooks can specify `priority` (higher runs first). Event data is passed as environment variables (`GRAYCODE_EVENT=file.write`, `GRAYCODE_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 `.hawk/skills//SKILL.md`: +Skills are Markdown files with YAML frontmatter placed in `.graycode/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 hawk, Claude Code, and Codex. +- **Cross-agent compatibility**: Skills follow the Agent Skills spec (agentskills.io) and work with graycode, Claude Code, and Codex. ## Event Hooks @@ -89,27 +89,27 @@ Available events for plugin and skill hooks: | Event | Data | Description | |-------|------|-------------| -| `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 | +| `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 | ## 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 `hawk plugin search `. -4. Users install via `hawk plugin install `. +3. Users discover plugins via `graycode plugin search `. +4. Users install via `graycode plugin install `. 5. Registry installation includes automatic audit and security scanning. ## Local Development ```bash # Create a plugin directory -mkdir -p ~/.hawk/plugins/my-plugin -cd ~/.hawk/plugins/my-plugin +mkdir -p ~/.graycode/plugins/my-plugin +cd ~/.graycode/plugins/my-plugin # Create a manifest cat > plugin.json << 'EOF' @@ -147,14 +147,14 @@ cat > plugin.json << 'EOF' EOF # List installed plugins -hawk plugin list +graycode plugin list # Run a plugin command -hawk plugin exec my-plugin hello +graycode plugin exec my-plugin hello # Create a skill (project-local) -mkdir -p .hawk/skills/my-skill -cat > .hawk/skills/my-skill/SKILL.md << 'EOF' +mkdir -p .graycode/skills/my-skill +cat > .graycode/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 `~/.hawk/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 `~/.graycode/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 82441724..11dd9ea8 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: `hawk/internal/engine/session.go` (the 35-collaborator `Session` struct) +> Scope: `graycode/internal/engine/session.go` (the 35-collaborator `Session` struct) ## Problem -`Session` at `hawk/internal/engine/session.go:42-141` is a god object: +`Session` at `graycode/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.) @@ -90,7 +90,7 @@ backends without an explicit migration and recovery decision. - `RecordSuccess(latency time.Duration)` / `RecordFailure(err error)` - `BuildContinuationConfig() types.ContinuationConfig` -**File:** `hawk/internal/engine/chat_service.go` (~150 LOC) +**File:** `graycode/internal/engine/chat_service.go` (~150 LOC) ### 2. `MemoryService` — owns harrier bridge + recall/remember @@ -104,7 +104,7 @@ backends without an explicit migration and recovery decision. - `RecordFeedback(ctx, content string, action string)` — wraps EnhancedMemory feedback - `ShouldRemember(text string) bool` — heuristic for auto-remember -**File:** `hawk/internal/engine/memory_service.go` (~200 LOC) +**File:** `graycode/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:** `hawk/internal/engine/tool_service.go` (~400 LOC — biggest, owns the 15-stage pipeline) +**File:** `graycode/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:** `hawk/internal/engine/permission_service.go` (~150 LOC) +**File:** `graycode/internal/engine/permission_service.go` (~150 LOC) ### 5. `LifecycleService` — owns the self-improvement loop @@ -148,7 +148,7 @@ backends without an explicit migration and recovery decision. - `DetectDoomLoop(steps []ToolStep) bool` - `InjectSteering(messages []types.EyrieMessage) []types.EyrieMessage` -**File:** `hawk/internal/engine/lifecycle_service.go` (~250 LOC) +**File:** `graycode/internal/engine/lifecycle_service.go` (~250 LOC) ### 6. `PersistenceService` — owns checkpoint, session, harrier snapshot @@ -167,7 +167,7 @@ backends without an explicit migration and recovery decision. - `CheckpointOnCompaction(strategy string, before, after int, manual bool)` - `TokenTracking() (prompt, completion int)`, `RecordAPIUsage(prompt, completion int)` -**File:** `hawk/internal/engine/persistence_service.go` (~300 LOC) +**File:** `graycode/internal/engine/persistence_service.go` (~300 LOC) ## What stays on `Session` diff --git a/docs/terminal-icons.md b/docs/terminal-icons.md index 24be39fa..ad2a70b0 100644 --- a/docs/terminal-icons.md +++ b/docs/terminal-icons.md @@ -1,6 +1,6 @@ # Terminal icons -Hawk uses current Nerd Font Codicon glyphs for interactive terminal output. +Graycode 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) -HAWK_ICONS=nerd ./bin/hawk +GRAYCODE_ICONS=nerd ./bin/graycode # Portable text-only output -HAWK_ICONS=ascii ./bin/hawk +GRAYCODE_ICONS=ascii ./bin/graycode ``` -For the real icons, configure the terminal profile—not Hawk's Go code—with a +For the real icons, configure the terminal profile—not Graycode'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. Hawk applies bold +Font size and glyph scale are controlled by that profile. Graycode 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 d43f8400..a8b473fb 100644 --- a/docs/troubleshooting-guide.md +++ b/docs/troubleshooting-guide.md @@ -1,6 +1,6 @@ # Troubleshooting Guide -A practical guide for diagnosing common hawk daemon and CLI issues. +A practical guide for diagnosing common graycode 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 HAWK_DAEMON_API_KEY=$(openssl rand -base64 32) -hawk daemon start --host 0.0.0.0 --port 4590 +export GRAYCODE_DAEMON_API_KEY=$(openssl rand -base64 32) +graycode daemon start --host 0.0.0.0 --port 4590 ``` Or bind to loopback only (no API key required, but not remotely accessible): ```bash -hawk daemon start --host 127.0.0.1 --port 4590 +graycode 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 `~/.hawk/state/`. +The daemon writes logs, PID files, and the audit log to `~/.graycode/state/`. **Fix:** ```bash -mkdir -p ~/.hawk/state -chmod 750 ~/.hawk/state -# If running under systemd as user 'hawk', ensure ownership: -chown -R hawk:hawk ~/.hawk +mkdir -p ~/.graycode/state +chmod 750 ~/.graycode/state +# If running under systemd as user 'graycode', ensure ownership: +chown -R graycode:graycode ~/.graycode ``` ### "port already in use" @@ -64,7 +64,7 @@ Another process is using port 4590. lsof -i :4590 # Or use a different port -hawk daemon start --port 4591 +graycode daemon start --port 4591 ``` --- @@ -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 `HAWK_MODEL` or provider credentials. +- No model configured — set `GRAYCODE_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 + repositories; from a full parent workspace, run `make setup` in graycode 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 hawk +ps aux | grep graycode # Check logs -journalctl -u hawk-daemon -n 50 -tail -50 ~/.hawk/state/daemon.log +journalctl -u graycode-daemon -n 50 +tail -50 ~/.graycode/state/daemon.log ``` --- @@ -121,7 +121,7 @@ The daemon requires `Authorization: Bearer ` or `X-API-Key: `. **Fix:** ```bash -curl -H "X-API-Key: $HAWK_DAEMON_API_KEY" http://localhost:4590/v1/stats +curl -H "X-API-Key: $GRAYCODE_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 `~/.hawk/state/daemon.key` (permissions 0600). +The API key is written to `~/.graycode/state/daemon.key` (permissions 0600). ```bash -cat ~/.hawk/state/daemon.key +cat ~/.graycode/state/daemon.key ``` **Security note:** Remove this file in production after initial testing: ```bash -rm ~/.hawk/state/daemon.key +rm ~/.graycode/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 `HAWK_DAEMON_MAX_CONCURRENT`) +- **Concurrent chat sessions**: 4 (configurable via `GRAYCODE_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 -hawk daemon start --cors https://app.example.com --cors https://admin.example.com +graycode 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 HAWK_FEATURE_CORS=1 +export GRAYCODE_FEATURE_CORS=1 ``` --- @@ -211,7 +211,7 @@ The metrics endpoint is protected by the same API key authentication as other endpoints. ```bash -curl -H "X-API-Key: $HAWK_DAEMON_API_KEY" http://localhost:4590/v1/metrics +curl -H "X-API-Key: $GRAYCODE_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: $HAWK_DAEMON_API_KEY" \ +curl -H "X-API-Key: $GRAYCODE_DAEMON_API_KEY" \ "http://localhost:4590/v1/metrics?format=json" ``` @@ -240,13 +240,13 @@ curl -H "X-API-Key: $HAWK_DAEMON_API_KEY" \ Telemetry is **opt-in**. You must explicitly enable it: ```bash -export HAWK_CODE_ENABLE_TELEMETRY=1 +export GRAYCODE_ENABLE_TELEMETRY=1 export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 -hawk daemon start +graycode daemon start ``` **Important**: Setting `OTEL_EXPORTER_OTLP_ENDPOINT` alone does **not** -enable telemetry. The `HAWK_CODE_ENABLE_TELEMETRY=1` flag is required. +enable telemetry. The `GRAYCODE_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 -`HAWK_CODE_OTEL_SHUTDOWN_TIMEOUT_MS`) to flush pending spans. +`GRAYCODE_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 `~/.hawk/state/securitylog/`. If the directory +The audit log is stored in `~/.graycode/state/securitylog/`. If the directory doesn't exist or isn't writable: ```bash -mkdir -p ~/.hawk/state/securitylog -chmod 700 ~/.hawk/state/securitylog +mkdir -p ~/.graycode/state/securitylog +chmod 700 ~/.graycode/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 `~/.hawk/state/securitylog/` directory. +of the `~/.graycode/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 -`~/.hawk/state/securitylog/` directory.** +`~/.graycode/state/securitylog/` directory.** --- @@ -306,13 +306,13 @@ Tools that require sandboxing are disabled until the sandbox container is running. Check the sandbox status: ```bash -hawk sandbox status +graycode sandbox status ``` Start the sandbox: ```bash -hawk sandbox start +graycode 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: $HAWK_DAEMON_API_KEY" \ + curl -H "X-API-Key: $GRAYCODE_DAEMON_API_KEY" \ "http://localhost:4590/v1/metrics?format=json" ``` -2. Check concurrent sessions: `hawk_daemon_chat_concurrency_used` +2. Check concurrent sessions: `graycode_daemon_chat_concurrency_used` 3. If at capacity, increase the concurrency limit: ```bash - export HAWK_DAEMON_MAX_CONCURRENT=8 + export GRAYCODE_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 `hawk daemon start --host 0.0.0.0 --port 4590`. +The default entrypoint runs `graycode 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 HAWK_DAEMON_API_KEY=$(openssl rand -base64 32) \ + -e GRAYCODE_DAEMON_API_KEY=$(openssl rand -base64 32) \ -v "$PWD/certs:/certs:ro" \ - ghcr.io/graycodeai/hawk-daemon:latest \ + ghcr.io/graycodeai/graycode-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 hawk-daemon.service: Unit not found" +### "Failed to start graycode-daemon.service: Unit not found" Install the unit file: ```bash -sudo cp packaging/systemd/hawk-daemon.service /etc/systemd/system/ +sudo cp packaging/systemd/graycode-daemon.service /etc/systemd/system/ sudo systemctl daemon-reload -sudo systemctl enable --now hawk-daemon +sudo systemctl enable --now graycode-daemon ``` ### "Permission denied" accessing state directory -The systemd unit runs as user `hawk` with `ProtectSystem=strict` and -`ReadWritePaths=%h/.hawk/state`. Ensure the user's home directory has +The systemd unit runs as user `graycode` with `ProtectSystem=strict` and +`ReadWritePaths=%h/.graycode/state`. Ensure the user's home directory has the correct state: ```bash -sudo -u hawk mkdir -p /home/hawk/.hawk/state -sudo -u hawk chmod 750 /home/hawk/.hawk/state +sudo -u graycode mkdir -p /home/graycode/.graycode/state +sudo -u graycode chmod 750 /home/graycode/.graycode/state ``` ### Daemon not logging to journald If using the systemd unit, logs go to both journald (stdout/stderr) and -`~/.hawk/state/daemon.log`. Check: +`~/.graycode/state/daemon.log`. Check: ```bash -journalctl -u hawk-daemon -f -tail -f ~/.hawk/state/daemon.log +journalctl -u graycode-daemon -f +tail -f ~/.graycode/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 a3c01d0e..6d797361 100644 --- a/docs/user-guide/01-getting-started.md +++ b/docs/user-guide/01-getting-started.md @@ -1,25 +1,25 @@ # Getting Started -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. +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. -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). +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). --- ## Installation -Hawk is currently in active development. Contributor source builds are the primary path while we harden the product in the open. +Graycode 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/hawk && cd hawk -GOWORK=off go build -o hawk ./cmd/hawk -./hawk +git clone https://github.com/GrayCodeAI/graycode-cli && cd graycode-cli +GOWORK=off go build -o graycode ./cmd/graycode +./graycode ``` For full cross-repository development, place the nine Go repositories beside -Hawk in a parent workspace and run `make setup` from `hawk`; `graycode-eco` is +Graycode in a parent workspace and run `make setup` from `graycode-cli`; `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 -./hawk path +./graycode path ``` This checks setup, security, and sandbox readiness. @@ -36,15 +36,15 @@ This checks setup, security, and sandbox readiness. ## First Launch -Start Hawk by running: +Start Graycode by running: ```bash -hawk +graycode ``` -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. +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. -Hawk supports multiple providers: +Graycode 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, Hawk presents a full-screen TUI powered by Bubble Tea with two main areas: +Once authenticated, Graycode presents a full-screen TUI powered by Bubble Tea with two main areas: -- **Scrollback** — the conversation history showing your prompts, Hawk's responses, tool calls, file edits, and more +- **Scrollback** — the conversation history showing your prompts, Graycode'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. Hawk 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. Graycode 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 -Hawk exposes two independent control surfaces: +Graycode 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: `hawk -c` +- Continue the most recent session: `graycode -c` ### Scrollback The scrollback shows: - **User prompts** — your messages, rendered as sticky headers -- **Agent messages** — Hawk's responses with markdown rendering -- **Thinking blocks** — Hawk's reasoning process (collapsible) +- **Agent messages** — Graycode's responses with markdown rendering +- **Thinking blocks** — Graycode's reasoning process (collapsible) - **Tool calls** — file edits, command executions, search results - **Task lists** — TODO items tracking progress ### Tools -Hawk has built-in tools for: +Graycode 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 -hawk +graycode # Submit an initial prompt as the first turn -hawk "fix the failing auth test and run it" +graycode "fix the failing auth test and run it" # Start in a specific project directory -hawk --cwd ~/projects/my-app +graycode --cwd ~/projects/my-app # Resume a previous session -hawk -r abc123 +graycode -r abc123 # Continue the most recent session -hawk -c +graycode -c # Use a specific provider/model -hawk --provider openai --model gpt-4o +graycode --provider openai --model gpt-4o # Isolated worktree for changes -hawk --worktree "refactor module X" +graycode --worktree "refactor module X" # Non-interactive (headless) mode -hawk -p "Explain this codebase" +graycode -p "Explain this codebase" # Full auto mode -hawk exec --auto full "add error handling" +graycode exec --auto full "add error handling" # Dry-run mode (denies all tools) -hawk exec --autonomy dry-run "What would this do?" +graycode exec --autonomy dry-run "What would this do?" ``` --- ## Headless Mode -Run Hawk non-interactively for scripting, CI/CD, and automation: +Run Graycode non-interactively for scripting, CI/CD, and automation: ```bash -hawk -p "Your prompt here" +graycode -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. Hawk 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. Graycode reads these files and injects their contents as a project-instructions message at the start of the conversation: ``` -~/.hawk/AGENTS.md # Global rules (apply to all projects) +~/.graycode/AGENTS.md # Global rules (apply to all projects) /AGENTS.md # Repository-level rules /AGENTS.md # Directory-level rules (highest priority) ``` -Deeper files take precedence. Hawk also reads `CLAUDE.md` files for compatibility. +Deeper files take precedence. Graycode also reads `CLAUDE.md` files for compatibility. --- diff --git a/docs/user-guide/02-authentication.md b/docs/user-guide/02-authentication.md index a8de81e4..182e0c05 100644 --- a/docs/user-guide/02-authentication.md +++ b/docs/user-guide/02-authentication.md @@ -1,14 +1,14 @@ # Authentication -Hawk supports several authentication methods, including API key configuration through the TUI and multi-provider support via Eyrie. +Graycode supports several authentication methods, including API key configuration through the TUI and multi-provider support via Eyrie. --- ## API Key Configuration -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. +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. -Hawk supports multiple providers: +Graycode supports multiple providers: | Provider | ID | Key | |----------|----|-----| @@ -24,10 +24,10 @@ Hawk supports multiple providers: ```bash # Verify credential status -hawk credentials status +graycode credentials status # Run the TUI and use /config to set keys interactively -hawk +graycode ``` ### 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-..." -hawk +graycode ``` 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 -Hawk uses Eyrie for provider routing, health checks, and retry logic. To configure providers: +Graycode uses Eyrie for provider routing, health checks, and retry logic. To configure providers: ```bash # In the TUI, press /config to open provider settings -hawk +graycode # Or validate readiness -hawk path +graycode path ``` ### Deployment-Aware Routing -For deployment-aware routing, set in `.hawk/settings.json`: +For deployment-aware routing, set in `.graycode/settings.json`: ```json { @@ -72,10 +72,10 @@ For deployment-aware routing, set in `.hawk/settings.json`: Or export: ```bash -export HAWK_DEPLOYMENT_ROUTING=true +export GRAYCODE_DEPLOYMENT_ROUTING=true ``` -Hawk will route canonical model IDs through Eyrie's deployment catalog. Refresh the catalog with: +Graycode 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 -// .hawk/settings.json +// .graycode/settings.json { "oidc": { "issuer": "https://acme.okta.com", @@ -99,7 +99,7 @@ Authenticate developers through your own Identity Provider (IdP) — such as Okt } ``` -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`. +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`. ### Required Scopes @@ -116,7 +116,7 @@ When browser-based login isn't possible — for example, on sandboxed VMs, CI ru ### How It Works -1. Hawk runs your command via `sh -c ""` +1. Graycode 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 -// .hawk/settings.json +// .graycode/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 HAWK_AUTH_PROVIDER_COMMAND="/usr/local/bin/my-auth-provider" -export HAWK_AUTH_PROVIDER_LABEL="Acme Corp" +export GRAYCODE_AUTH_PROVIDER_COMMAND="/usr/local/bin/my-auth-provider" +export GRAYCODE_AUTH_PROVIDER_LABEL="Acme Corp" ``` ### Token Refresh -When Hawk needs to refresh an expired token, it re-runs your binary with `HAWK_AUTH_EXPIRED=1` set in the environment: +When Graycode needs to refresh an expired token, it re-runs your binary with `GRAYCODE_AUTH_EXPIRED=1` set in the environment: ```bash #!/bin/sh -if [ "$HAWK_AUTH_EXPIRED" = "1" ]; then +if [ "$GRAYCODE_AUTH_EXPIRED" = "1" ]; then echo "Refreshing token..." >&2 TOKEN=$(my-company-auth --refresh --silent) else @@ -182,7 +182,7 @@ echo "{\"access_token\": \"$TOKEN\", \"expires_in\": 3600}" Check credential status at any time: ```bash -hawk credentials status +graycode credentials status ``` This verifies keychain entries and validates Eyrie's provider status. @@ -191,7 +191,7 @@ This verifies keychain entries and validates Eyrie's provider status. ## Credential Precedence -Hawk resolves credentials in this order: +Graycode 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 -Hawk works with any LLM provider through Eyrie's adapter system: +Graycode 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 9ad63033..3ba2b2ea 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 Hawk TUI. Bindings are built-in and cannot currently be remapped. +Reference for key bindings in the Graycode TUI. Bindings are built-in and cannot currently be remapped. --- ## Input Modes -Hawk has two input modes that control how you navigate the scrollback: +Graycode 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 `~/.hawk/settings.json`: +Or set `vim_mode = true` under `[ui]` in `~/.graycode/settings.json`: ```json { diff --git a/docs/user-guide/04-slash-commands.md b/docs/user-guide/04-slash-commands.md index 0c5c85c5..be019145 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 -hawk skills install go-review +graycode skills install go-review ``` ### `/skills audit` @@ -213,7 +213,7 @@ Security scan installed skills. ``` /skills audit -hawk skills audit +graycode skills audit ``` --- diff --git a/docs/user-guide/05-configuration.md b/docs/user-guide/05-configuration.md index 60da4fb5..942042d0 100644 --- a/docs/user-guide/05-configuration.md +++ b/docs/user-guide/05-configuration.md @@ -1,6 +1,6 @@ # Configuration -Hawk reads configuration from settings files, environment variables, and has defaults for all options. This document covers the common configuration options. +Graycode reads configuration from settings files, environment variables, and has defaults for all options. This document covers the common configuration options. --- @@ -8,19 +8,21 @@ Hawk reads configuration from settings files, environment variables, and has def Configuration is resolved in this order (highest priority first): -1. **CLI flags** (e.g., `--provider`, `--model`) -2. **Environment variables** -3. **User settings** (`~/.hawk/settings.json`) -4. **Project settings** (`.hawk/settings.json`) -5. **Built-in defaults** +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 — + `model`, `provider`, permissions, MCP servers and providers are stripped) +5. **User settings** (`~/.graycode/settings.json`) +6. **Built-in defaults** --- ## Settings File -Location: `~/.hawk/settings.json` +Location: `~/.graycode/settings.json` -This is the main configuration file. Hawk writes to it when you save changes via `/config` or `/autonomy save`. +This is the main configuration file. Graycode writes to it when you save changes via `/config` or `/autonomy save`. ### Basic Settings @@ -51,7 +53,7 @@ This is the main configuration file. Hawk writes to it when you save changes via - `operator` — full tool access for trusted operations - `autonomous` — no permission prompts -**Sandbox profiles** control permissions inside Hawk's mandatory Docker +**Sandbox profiles** control permissions inside Graycode's mandatory Docker execution boundary: - `off` — no additional policy restrictions - `workspace` — filesystem access limited to project directory @@ -110,34 +112,34 @@ Key environment variables for configuration. | Variable | Description | |----------|-------------| -| `HAWK_Y0_FOLDER_TRUST` | Folder trust feature flag (default: `1`) | -| `HAWK_Y0_MARKETPLACE` | Marketplace feature flag (default: `0`) | -| `HAWK_DEPLOYMENT_ROUTING` | Enable deployment-aware routing | +| `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` | ### Paths | Variable | Description | |----------|-------------| -| `HAWK_HOME` | Override config directory (default: `~/.hawk`) | +| `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 | --- ## Project Configuration -Place configuration in `.hawk/` within your repository: +Place configuration in `.graycode/` within your repository: | File | Purpose | |------|---------| -| `.hawk/settings.json` | Project settings (autonomy, rules) | -| `.hawk/sandbox.toml` | Custom sandbox profiles | -| `.hawk/lsp.json` | LSP server configuration | +| `.graycode/settings.json` | Project settings (autonomy, rules) | +| `.graycode/sandbox.toml` | Custom sandbox profiles | +| `.graycode/lsp.json` | LSP server configuration | | `AGENTS.md` | Project instructions | --- ## Sandbox Profiles -Location: `~/.hawk/sandbox.toml` (user) or `.hawk/sandbox.toml` (project) +Location: `~/.graycode/sandbox.toml` (user) or `.graycode/sandbox.toml` (project) Define custom sandbox profiles: @@ -161,7 +163,10 @@ network = "deny" ## MCP Servers -Configure MCP servers in `.hawk/settings.json` or project `.hawk/settings.json`: +Configure MCP servers in global `~/.graycode/settings.json` only — project +`.graycode/settings.json` cannot register MCP servers (stripped by +`projectSafeSettings`; project automation additionally requires folder trust, +see below): ```json { @@ -195,7 +200,7 @@ Folder trust controls whether project automation (hooks, plugins, MCP, LSP) can ### Trust Store -Location: `~/.hawk/trusted_folders.toml` +Location: `~/.graycode/trusted_folders.toml` ```toml [[folders]] diff --git a/docs/user-guide/06-theming.md b/docs/user-guide/06-theming.md index cf1ede1b..e0d4acae 100644 --- a/docs/user-guide/06-theming.md +++ b/docs/user-guide/06-theming.md @@ -1,16 +1,16 @@ # Theming and Appearance Customization -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. +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. --- ## Available Themes -Hawk includes 17 built-in themes, plus an `auto` option that follows your system appearance: +Graycode includes 17 built-in themes, plus an `auto` option that follows your system appearance: | Theme | Description | |-------|-------------| -| **dark** | Neutral dark base with Hawk's Talon Gold accent. Default theme. | +| **dark** | Neutral dark base with Graycode'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, Hawk 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, Graycode 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 `~/.hawk/settings.json`: +Set the theme in `~/.graycode/settings.json`: ```json { @@ -66,7 +66,7 @@ Set the theme in `~/.hawk/settings.json`: ## Auto Theme (System Appearance) -Set `theme: "auto"` to have Hawk follow your operating system's light/dark appearance and switch themes automatically: +Set `theme: "auto"` to have Graycode 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, Hawk checks for appearance changes when you explicitly switch themes (no continuous polling to avoid resource drain). +Once running, Graycode checks for appearance changes when you explicitly switch themes (no continuous polling to avoid resource drain). --- ## Color Support Detection -On startup, Hawk detects your terminal's color capability: +On startup, Graycode detects your terminal's color capability: | Level | Description | |-------|-------------| @@ -107,7 +107,7 @@ On startup, Hawk 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, Hawk uses truecolor mode. When `TERM` contains `256color`, it uses 256-color mode. When `NO_COLOR` is set, Hawk renders in monochrome. +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. --- @@ -192,7 +192,7 @@ View terminal configuration recommendations and current capabilities: /terminal-setup ``` -This shows detected color support, current settings, and tips for optimal Hawk experience. +This shows detected color support, current settings, and tips for optimal Graycode experience. --- diff --git a/docs/user-guide/07-mcp-servers.md b/docs/user-guide/07-mcp-servers.md index 45dae6e3..1977c942 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 Hawk with external tool integrations. They let Hawk interact with any service that implements the MCP standard. +MCP (Model Context Protocol) servers extend Graycode with external tool integrations. They let Graycode interact with any service that implements the MCP standard. --- ## What Are MCP Servers? -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. +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. 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 `.hawk/settings.json` under the `mcp_servers` key. +MCP servers are configured in `.graycode/settings.json` under the `mcp_servers` key. ### stdio Transport (Local Process) -Hawk spawns a local process and communicates over stdin/stdout: +Graycode 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 -hawk mcp list +graycode mcp list # Add a stdio server -hawk mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /path/to/dir +graycode mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /path/to/dir # Add a remote HTTP server -hawk mcp add linear --transport http https://mcp.linear.app/mcp +graycode mcp add linear --transport http https://mcp.linear.app/mcp # Remove a server -hawk mcp remove github +graycode mcp remove github # Diagnose server configuration -hawk mcp doctor -hawk mcp doctor +graycode mcp doctor +graycode mcp doctor ``` -Use `--scope project` to write to `.hawk/settings.json` in the current directory instead of the user config. +Use `--scope project` to write to `.graycode/settings.json` in the current directory instead of the user config. --- ## Project-Scoped MCP Servers -MCP servers can be configured per-project in `.hawk/settings.json`: +MCP servers can be configured per-project in `.graycode/settings.json`: ``` my-project/ - .hawk/ + .graycode/ settings.json src/ ... ``` ```json -// .hawk/settings.json +// .graycode/settings.json { "mcp_servers": { "linear": { @@ -125,7 +125,7 @@ MCP tools are namespaced with the server name to avoid collisions: ## Discovering and Using Tools -Hawk provides built-in tools to work with MCP servers: +Graycode 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 b5e14d20..5e5c67da 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 Hawk 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 Graycode 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 Hawk 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 Graycode 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. Hawk 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. Graycode activates a skill when it applies to your current task. --- ## Skill Locations -Hawk discovers skills from these directories, in priority order: +Graycode discovers skills from these directories, in priority order: | Location | Scope | Priority | |----------|-------|----------| -| `.hawk/skills/`, `.hawk/commands/` | Local (CWD) | Highest | -| `/.hawk/skills/` | Repo | Medium | -| `~/.hawk/skills/` | User | Lowest | +| `.graycode/skills/`, `.graycode/commands/` | Local (CWD) | Highest | +| `/.graycode/skills/` | Repo | Medium | +| `~/.graycode/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: ``` -~/.hawk/skills/ +~/.graycode/skills/ commit/ SKILL.md review-pr/ @@ -103,13 +103,13 @@ Pass arguments after the name: When names collide, use qualified forms: ``` -/local:commit # From ./.hawk/skills/ -/user:commit # From ~/.hawk/skills/ +/local:commit # From ./.graycode/skills/ +/user:commit # From ~/.graycode/skills/ ``` ### Automatic Invocation -Hawk can invoke a skill automatically when it recognizes a relevant task. Write specific descriptions in the `description` and `when-to-use` fields. +Graycode 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 -hawk skills search go +graycode skills search go # Install a skill from a source -hawk skills install go-review +graycode skills install go-review # Audit installed skills for security -hawk skills audit +graycode skills audit ``` --- ## Installing Skills -Hawk ships **no bundled skills** by default. Skills are installed on demand +Graycode ships **no bundled skills** by default. Skills are installed on demand from the separate `GrayCodeAI/starling` repo (or any GitHub repo): ```bash -hawk skills search # find a skill in the registry -hawk skills install [skill] # install after user approval -hawk skills audit # security-scan installed skills +graycode skills search # find a skill in the registry +graycode skills install [skill] # install after user approval +graycode 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 773b065a..e9169b21 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 -Hawk discovers plugins from these locations, in priority order: +Graycode discovers plugins from these locations, in priority order: | Location | Scope | Trust Required | |----------|-------|----------------| -| `.hawk/plugins/` | Project | Yes | -| `~/.hawk/plugins/` | User | No | +| `.graycode/plugins/` | Project | Yes | +| `~/.graycode/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 -hawk plugins list +graycode plugins list # Install a plugin -hawk plugins install user/repo -hawk plugins install user/repo@v1.0 -hawk plugins install /path/to/local/plugin +graycode plugins install user/repo +graycode plugins install user/repo@v1.0 +graycode plugins install /path/to/local/plugin # Install with trust (required for project plugins) -hawk plugins install user/repo --trust +graycode plugins install user/repo --trust # Uninstall a plugin -hawk plugins uninstall my-plugin +graycode plugins uninstall my-plugin # Update plugins -hawk plugins update +graycode plugins update ``` --- @@ -63,15 +63,15 @@ Browse and install plugins from the community marketplace: ```bash # Search marketplace -hawk plugins search go +graycode plugins search go # Install from marketplace -hawk plugins install go-review --trust +graycode plugins install go-review --trust ``` ### Adding Marketplace Sources -Configure additional marketplaces in `~/.hawk/settings.json`: +Configure additional marketplaces in `~/.graycode/settings.json`: ```json { @@ -90,13 +90,13 @@ Configure additional marketplaces in `~/.hawk/settings.json`: ## Trust Model -- **User plugins** (`~/.hawk/plugins/`) — Trusted automatically -- **Project plugins** (`.hawk/plugins/`) — Require explicit trust before activation +- **User plugins** (`~/.graycode/plugins/`) — Trusted automatically +- **Project plugins** (`.graycode/plugins/`) — Require explicit trust before activation Trust is required because plugins can execute code via hooks and MCP servers. To trust a plugin: ```bash -hawk plugins install --trust +graycode plugins install --trust ``` --- diff --git a/docs/user-guide/10-hooks.md b/docs/user-guide/10-hooks.md index 8553ea89..0f503f7e 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 Hawk 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 Graycode 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 Hawk calls when a specific lifecycle event occurs. Hooks can: +A hook is a shell command or HTTP endpoint that Graycode 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 `.hawk/hooks/hooks.json`: +Create hooks in `.graycode/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** (`~/.hawk/hooks/`) — Always trusted -- **Project hooks** (`.hawk/hooks/`) — Require folder trust +- **User hooks** (`~/.graycode/hooks/`) — Always trusted +- **Project hooks** (`.graycode/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 -Hawk sets these variables for every hook: +Graycode sets these variables for every hook: | Variable | Description | |----------|-------------| -| `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 | +| `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 | --- @@ -130,20 +130,20 @@ Hawk sets these variables for every hook: ```bash # List hooks -hawk hooks list +graycode hooks list # Trust project hooks -hawk hooks trust +graycode hooks trust # Untrust project hooks -hawk hooks untrust +graycode hooks untrust ``` --- ## Example: Safe Shell Guard -Create `.hawk/hooks/safety.json`: +Create `.graycode/hooks/safety.json`: ```json { diff --git a/docs/user-guide/11-custom-models.md b/docs/user-guide/11-custom-models.md index f378e8d7..80084c82 100644 --- a/docs/user-guide/11-custom-models.md +++ b/docs/user-guide/11-custom-models.md @@ -1,12 +1,12 @@ # Custom Models -Hawk connects to custom model endpoints through Eyrie for alternative providers, self-hosted models, and overriding built-in settings. +Graycode connects to custom model endpoints through Eyrie for alternative providers, self-hosted models, and overriding built-in settings. --- ## Supported Providers -Hawk works with any LLM provider. Built-in support includes: +Graycode works with any LLM provider. Built-in support includes: | Provider | ID | Key | |----------|-----|-----| @@ -24,7 +24,7 @@ Hawk works with any LLM provider. Built-in support includes: ### CLI Flag ```bash -hawk -m gpt-4o -p "Hello" +graycode -m gpt-4o -p "Hello" ``` ### Slash Command @@ -39,7 +39,7 @@ In the TUI: ### Config Default ```json -// ~/.hawk/settings.json +// ~/.graycode/settings.json { "default_provider": "openai", "default_model": "gpt-4o" @@ -50,7 +50,7 @@ In the TUI: ## Configuring Custom Models -Add custom models in `~/.hawk/settings.json`: +Add custom models in `~/.graycode/settings.json`: ```json { @@ -73,7 +73,7 @@ Add custom models in `~/.hawk/settings.json`: ### Credential Resolution -Hawk resolves credentials in this order: +Graycode resolves credentials in this order: 1. Per-model `api_key` field 2. Environment variable (`env_key`) @@ -130,8 +130,8 @@ ollama pull llama-3.1-70b Enable deployment-aware routing to use Eyrie's model catalog: ```bash -export HAWK_DEPLOYMENT_ROUTING=true -hawk +export GRAYCODE_DEPLOYMENT_ROUTING=true +graycode ``` Or in settings: diff --git a/docs/user-guide/12-project-rules.md b/docs/user-guide/12-project-rules.md index 492b618c..69736fb0 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 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. +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. --- ## Supported File Names -Hawk checks for these filenames in each directory (from repo root to CWD): +Graycode checks for these filenames in each directory (from repo root to CWD): - `AGENTS.md` - `Agents.md` @@ -14,26 +14,26 @@ Hawk checks for these filenames in each directory (from repo root to CWD): - `Claude.md` - `AGENT.md` -Hawk loads all matching files. Deeper files take precedence when instructions conflict. +Graycode loads all matching files. Deeper files take precedence when instructions conflict. --- ## Rules Directories -Hawk also scans for `*.md` files in rules directories: +Graycode also scans for `*.md` files in rules directories: | Location | Purpose | |----------|---------| -| `/.hawk/rules/` | Always scanned | +| `/.graycode/rules/` | Always scanned | | `/.claude/rules/` | Claude compatibility | --- ## How Discovery Works -Hawk scans rules in this order: +Graycode scans rules in this order: -1. **Global rules**: `~/.hawk/AGENTS.md` (applies to all projects) +1. **Global rules**: `~/.graycode/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 Hawk runs in `packages/frontend/`, it loads all instructions. The frontend-specific rules appear later and take precedence. +When Graycode 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 -hawk --rules "Always use TypeScript" -p "Implement feature" +graycode --rules "Always use TypeScript" -p "Implement feature" ``` --- diff --git a/docs/user-guide/13-memory.md b/docs/user-guide/13-memory.md index 9d4bde3a..09ebd387 100644 --- a/docs/user-guide/13-memory.md +++ b/docs/user-guide/13-memory.md @@ -1,12 +1,12 @@ # Memory -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. +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. --- ## What Is Memory? -Without memory, each Hawk session starts fresh. When you enable memory, Hawk can: +Without memory, each Graycode session starts fresh. When you enable memory, Graycode can: - Recall project conventions you explained before - Reuse debugging steps that worked @@ -30,13 +30,13 @@ Toggle memory in the TUI: ### CLI Flag ```bash -hawk --memory +graycode --memory ``` ### Settings ```json -// ~/.hawk/settings.json +// ~/.graycode/settings.json { "memory": { "enabled": true @@ -48,13 +48,13 @@ hawk --memory ## How Memory Is Stored -Memory is stored in harrier's graph database under `~/.hawk/harrier/`. +Memory is stored in harrier's graph database under `~/.graycode/harrier/`. | Location | Scope | Description | |----------|-------|-------------| -| `~/.hawk/harrier/global/` | Global | Cross-project memory | -| `~/.hawk/harrier/workspaces//` | Workspace | Project-specific memory | -| `~/.hawk/harrier/sessions/` | Sessions | Session logs and summaries | +| `~/.graycode/harrier/global/` | Global | Cross-project memory | +| `~/.graycode/harrier/workspaces//` | Workspace | Project-specific memory | +| `~/.graycode/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 Hawk to remember something, or use the slash command: +Ask Graycode to remember something, or use the slash command: ``` /remember always open PR links after pushing ``` -Hawk records entries as durable statements organized by topic. +Graycode records entries as durable statements organized by topic. ### Forget -Ask what Hawk should forget: +Ask what Graycode 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 Hawk remembers: +Ask what Graycode remembers: ``` /what do you remember about auth? ``` -Hawk searches across all memory sources and summarizes. +Graycode searches across all memory sources and summarizes. --- @@ -111,16 +111,16 @@ Or search directly: ### CLI Commands ```bash -hawk harrier # Open harrier UI -hawk harrier search # Search memory -hawk harrier stats # Show memory statistics +graycode harrier # Open harrier UI +graycode harrier search # Search memory +graycode harrier stats # Show memory statistics ``` --- ## First-Turn Injection -On the first turn of each session, Hawk automatically searches memory for content relevant to the current project and injects it as context. +On the first turn of each session, Graycode 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 -Hawk searches memory automatically. Manual search: +Graycode 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 12ce192c..2e9ffbb5 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 Hawk non-interactively from the command line. It accepts a prompt, executes tools, and returns results — ideal for automation and CI/CD. +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. --- @@ -9,10 +9,10 @@ Headless mode runs Hawk non-interactively from the command line. It accepts a pr Pass a prompt to run headless: ```bash -hawk -p "Your prompt here" +graycode -p "Your prompt here" ``` -Hawk processes the prompt and prints the result to stdout. +Graycode processes the prompt and prints the result to stdout. --- @@ -23,7 +23,7 @@ Hawk processes the prompt and prints the result to stdout. Human-readable text: ```bash -hawk -p "Summarize this codebase" +graycode -p "Summarize this codebase" ``` ### json @@ -31,7 +31,7 @@ hawk -p "Summarize this codebase" Single JSON object after completion: ```bash -hawk -p "Summarize this codebase" --output-format json | jq -r '.response' +graycode -p "Summarize this codebase" --output-format json | jq -r '.response' ``` Output includes: @@ -44,7 +44,7 @@ Output includes: NDJSON events in real time: ```bash -hawk -p "Summarize" --output-format stream-json | jq -r 'select(.type=="content") | .content' +graycode -p "Summarize" --output-format stream-json | jq -r 'select(.type=="content") | .content' ``` Event types: @@ -59,20 +59,20 @@ Event types: ### Named Session -Each `hawk -p` creates a fresh session by default. To continue a session: +Each `graycode -p` creates a fresh session by default. To continue a session: ```bash # Get session ID -hawk -p "Initial prompt" --output-format json | jq -r '.sessionId' +graycode -p "Initial prompt" --output-format json | jq -r '.sessionId' # Resume the session -hawk -p "Follow-up" --resume +graycode -p "Follow-up" --resume ``` ### Continue Most Recent ```bash -hawk -p "Continue" --continue +graycode -p "Continue" --continue ``` --- @@ -83,10 +83,10 @@ Restrict available tools: ```bash # Allow only read tools -hawk -p "Explain this" --tools "Read,Grep,LS" +graycode -p "Explain this" --tools "Read,Grep,LS" # Deny specific tools -hawk -p "Review" --disallowed-tools "Bash,WebSearch" +graycode -p "Review" --disallowed-tools "Bash,WebSearch" ``` --- @@ -97,10 +97,10 @@ Control tool permissions: ```bash # Allow shell commands through the explicit tool policy flag -hawk -p "Build" --allowed-tools "Bash(git:*) Bash(npm:*)" +graycode -p "Build" --allowed-tools "Bash(git:*) Bash(npm:*)" # Deny dangerous commands -hawk -p "Clean" --disallowed-tools "Bash(rm:*) Bash(sudo:*)" +graycode -p "Clean" --disallowed-tools "Bash(rm:*) Bash(sudo:*)" ``` --- @@ -110,8 +110,8 @@ hawk -p "Clean" --disallowed-tools "Bash(rm:*) Bash(sudo:*)" Use `--auto` for fully automated runs: ```bash -hawk -p "Format all files" --dangerously-skip-permissions -hawk exec --auto full "Add error handling" +graycode -p "Format all files" --dangerously-skip-permissions +graycode exec --auto full "Add error handling" ``` **Warning:** This grants full autonomy. Use only in trusted environments. @@ -124,14 +124,14 @@ hawk exec --auto full "Add error handling" ```bash #!/bin/bash -hawk -p "Review staged changes for bugs. Reply OK if fine." \ +graycode -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 -hawk -p "Review PR for security issues" \ +graycode -p "Review PR for security issues" \ --output-format json --dangerously-skip-permissions | jq -r '.response' > review.md ``` @@ -139,7 +139,7 @@ hawk -p "Review PR for security issues" \ ```bash for file in src/*.go; do - hawk -p "Format $file" --auto + graycode -p "Format $file" --auto done ``` @@ -149,8 +149,8 @@ done ```bash export XAI_API_KEY="xai-..." # API key -export HAWK_HOME="/path" # Custom config location -export HAWK_LOG_FILE="/tmp/hawk.log" # Log file +export GRAYCODE_HOME="/path" # Custom config location +export GRAYCODE_LOG_FILE="/tmp/graycode.log" # Log file ``` --- diff --git a/docs/user-guide/15-agent-mode.md b/docs/user-guide/15-agent-mode.md index c4133d8c..ee05882d 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 Hawk as an ACP (Agent Client Protocol) server for IDE integration and custom tooling. +Agent mode runs Graycode 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 Hawk as an ACP server over stdio: +Run Graycode as an ACP server over stdio: ```bash -hawk agent stdio +graycode agent stdio ``` Clients include: @@ -31,9 +31,9 @@ Clients include: ### Options ```bash -hawk agent --model gpt-4o stdio -hawk agent --auto stdio -hawk agent --agent-profile path/to/profile.yaml stdio +graycode agent --model gpt-4o stdio +graycode agent --auto stdio +graycode agent --agent-profile path/to/profile.yaml stdio ``` | Flag | Description | @@ -49,7 +49,7 @@ hawk agent --agent-profile path/to/profile.yaml stdio ### WebSocket Server ```bash -hawk agent serve --bind 127.0.0.1:2419 --secret +graycode 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 -Hawk provides `x.ai/*` extension methods: +Graycode provides `x.ai/*` extension methods: | Category | Methods | |----------|---------| @@ -110,7 +110,7 @@ Hawk provides `x.ai/*` extension methods: import { spawn } from "child_process"; // Start ACP server -const proc = spawn("hawk", ["agent", "stdio"]); +const proc = spawn("graycode", ["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 2d3605d3..01026cb7 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 Hawk needs to delegate work, it spawns a child session using the `Agent` tool. The child runs with: +When Graycode 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 `~/.hawk/settings.json`: +Personas are behavioral overlays applied to subagents. Define them in `~/.graycode/settings.json`: ```json { diff --git a/docs/user-guide/17-sessions.md b/docs/user-guide/17-sessions.md index 97b5d47f..9c5670c5 100644 --- a/docs/user-guide/17-sessions.md +++ b/docs/user-guide/17-sessions.md @@ -1,6 +1,6 @@ # Sessions -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. +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. --- @@ -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 `~/.hawk/sessions/`. +Sessions are identified by a unique session ID and stored under `~/.graycode/sessions/`. --- @@ -36,7 +36,7 @@ This clears the current context and starts fresh. Alias: `/exit` -To leave the session but stay in Hawk: +To leave the session but stay in Graycode: ``` /home @@ -58,13 +58,13 @@ Opens a session picker. Select a session to resume. ```bash # Resume specific session -hawk --resume +graycode --resume # Continue most recent -hawk --continue +graycode --continue # New session with specific ID -hawk --session-id -p "prompt" +graycode --session-id -p "prompt" ``` --- @@ -122,10 +122,10 @@ Maintain context across headless calls: ```bash # Start and capture ID -ID=$(hawk -p "First" --output-format json | jq -r '.sessionId') +ID=$(graycode -p "First" --output-format json | jq -r '.sessionId') # Continue -hawk -p "Second" --resume "$ID" +graycode -p "Second" --resume "$ID" ``` --- diff --git a/docs/user-guide/18-sandbox.md b/docs/user-guide/18-sandbox.md index 9f9b2bdb..3ad6a4bc 100644 --- a/docs/user-guide/18-sandbox.md +++ b/docs/user-guide/18-sandbox.md @@ -1,6 +1,6 @@ # Sandbox -Sandbox mode restricts what Hawk and its spawned commands can access on your filesystem using OS-level kernel primitives. +Sandbox mode restricts what Graycode and its spawned commands can access on your filesystem using OS-level kernel primitives. --- @@ -8,13 +8,13 @@ Sandbox mode restricts what Hawk and its spawned commands can access on your fil ```bash # Workspace sandbox (recommended for development) -hawk --sandbox workspace +graycode --sandbox workspace # Read-only mode -hawk --sandbox read-only +graycode --sandbox read-only # Strict mode -hawk --sandbox strict +graycode --sandbox strict ``` --- @@ -24,15 +24,15 @@ hawk --sandbox strict | Profile | FS Read | FS Write | Child Network | Use Case | |---------|---------|----------|---------------|----------| | `off` | All | All | All | No restrictions | -| `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 | +| `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 | --- ## Custom Profiles -Create `.hawk/sandbox.toml`: +Create `.graycode/sandbox.toml`: ```toml [profiles.project] @@ -44,7 +44,7 @@ deny = ["**/.env", "**/*.pem"] Use with: ```bash -hawk --sandbox project +graycode --sandbox project ``` --- @@ -53,15 +53,15 @@ hawk --sandbox project ### workspace (recommended) -Read anywhere, write only to the project directory and Hawk's config. Allows network access for LLM calls and web search. +Read anywhere, write only to the project directory and Graycode's config. Allows network access for LLM calls and web search. ### read-only -Read anywhere, write only to `~/.hawk/` and temp directories. Good for code exploration without risk of modification. +Read anywhere, write only to `~/.graycode/` 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, `~/.hawk/`, and temp. +Most restrictive. Read only CWD and essential system paths. Write only to CWD, `~/.graycode/`, and temp. --- diff --git a/docs/user-guide/19-plan-mode.md b/docs/user-guide/19-plan-mode.md index 39b8155e..fe089e2e 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 Hawk 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 Graycode 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, Hawk: +When plan mode is active, Graycode: 1. Reads and searches the codebase to understand patterns 2. Designs an implementation approach -3. Writes the plan to a file under `.hawk/specs/` +3. Writes the plan to a file under `.graycode/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 `.hawk/specs//plan.md`: +Plans are written to `.graycode/specs//plan.md`: - **Context** — Why the change is needed - **Approach** — Recommended implementation strategy @@ -56,7 +56,7 @@ Plans are written to `.hawk/specs//plan.md`: ## Plan Approval -When planning completes, Hawk opens a preview with action bar: +When planning completes, Graycode 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 c96e0628..d258c6bb 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 -Hawk runs long-lived processes without blocking the conversation. This covers background commands, `/loop`, and the `monitor` tool. +Graycode 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 7ef8ee0e..de4b21dd 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 -Hawk runs as a full-screen TUI powered by Bubble Tea. This covers terminal compatibility and common fixes. +Graycode 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 -Hawk detects these terminals: +Graycode 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 `~/.hawk/settings.json`: +**Fix**: Set in `~/.graycode/settings.json`: ```json { "terminal": { "alt_screen": "always" } } @@ -128,7 +128,7 @@ set -g set-clipboard on ## Diagnostics -Run in Hawk: +Run in Graycode: ``` /terminal-setup diff --git a/docs/user-guide/22-permissions-and-safety.md b/docs/user-guide/22-permissions-and-safety.md index c2b7437c..50662e3c 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 -Hawk can read files, edit code, and run shell commands. The permission system controls what the agent is allowed to do. +Graycode 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 c6f5aa09..e31a9168 100644 --- a/docs/user-guide/23-dashboard.md +++ b/docs/user-guide/23-dashboard.md @@ -1,6 +1,6 @@ # Dashboard and HUD -The Hawk dashboard provides system status and monitoring information. +The Graycode dashboard provides system status and monitoring information. --- @@ -18,7 +18,7 @@ In the TUI: ## Ecosystem Status -Shows the status of all Hawk components: +Shows the status of all Graycode components: | Component | Status | |-----------|--------| @@ -34,7 +34,7 @@ Shows the status of all Hawk components: Check readiness to chat: ```bash -hawk path +graycode path ``` This verifies: diff --git a/docs/user-guide/24-monitoring-usage.md b/docs/user-guide/24-monitoring-usage.md index 40f3e29b..8d8e4e51 100644 --- a/docs/user-guide/24-monitoring-usage.md +++ b/docs/user-guide/24-monitoring-usage.md @@ -1,6 +1,6 @@ # Monitoring and Usage -Hawk tracks token usage, costs, and session activity for monitoring and debugging. +Graycode 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 -hawk usage -hawk doctor # Full health report +graycode usage +graycode doctor # Full health report ``` Usage shows: @@ -24,12 +24,12 @@ Usage shows: ## Telemetry -Hawk can send anonymous usage telemetry. +Graycode can send anonymous usage telemetry. ### Enable/Disable ```json -// ~/.hawk/settings.json +// ~/.graycode/settings.json { "telemetry": { "enabled": false @@ -40,7 +40,7 @@ Hawk can send anonymous usage telemetry. Or: ```bash -hawk --telemetry # Enable +graycode --telemetry # Enable haw --no-telemetry # Disable ``` @@ -77,7 +77,7 @@ Accessible via `/ecosystem` in TUI. ## Cloud Integration -Hawk Cloud provides managed usage tracking: +Graycode 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 f40480c7..00aa9778 100644 --- a/docs/user-guide/25-shell-completions.md +++ b/docs/user-guide/25-shell-completions.md @@ -1,15 +1,15 @@ # Shell Completions -hawk ships completion scripts for **bash**, **zsh**, **fish**, and **PowerShell**, +graycode 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: -hawk completion install bash -hawk completion install zsh -hawk completion install fish +graycode completion install bash +graycode completion install zsh +graycode completion install fish ``` ## Manual Setup @@ -18,50 +18,50 @@ hawk completion install fish ```bash # Load for current session: -source <(hawk completion bash) +source <(graycode completion bash) # Persist (Linux): -hawk completion bash > ~/.local/share/bash-completion/completions/hawk +graycode completion bash > ~/.local/share/bash-completion/completions/graycode # Persist (macOS with Homebrew): -hawk completion bash > /opt/homebrew/etc/bash_completion.d/hawk +graycode completion bash > /opt/homebrew/etc/bash_completion.d/graycode ``` ### Zsh ```bash # Load for current session: -source <(hawk completion zsh) +source <(graycode completion zsh) # Persist: -hawk completion zsh > "${fpath[1]}/_hawk" +graycode completion zsh > "${fpath[1]}/_graycode" ``` ### Fish ```bash # Load for current session: -hawk completion fish | source +graycode completion fish | source # Persist: -hawk completion fish > ~/.config/fish/completions/hawk.fish +graycode completion fish > ~/.config/fish/completions/graycode.fish ``` ### PowerShell ```powershell # Load for current session: -hawk completion powershell | Out-String | Invoke-Expression +graycode completion powershell | Out-String | Invoke-Expression # Persist: add to your $PROFILE -hawk completion powershell > hawk.ps1 -. ./hawk.ps1 +graycode completion powershell > graycode.ps1 +. ./graycode.ps1 ``` ## JSON Spec (IDE Integration) ```bash -hawk completion json +graycode 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 -`hawk completion install` resolves the correct path automatically: +`graycode completion install` resolves the correct path automatically: | Shell | Linux | macOS (Homebrew) | |-------|-------|-------------------| -| 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 | +| 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 | diff --git a/docs/user-guide/26-learned-preferences.md b/docs/user-guide/26-learned-preferences.md index 962ec899..2d03573c 100644 --- a/docs/user-guide/26-learned-preferences.md +++ b/docs/user-guide/26-learned-preferences.md @@ -1,6 +1,6 @@ # Learned Preferences -Hawk can learn coding-style tendencies from feedback through its taste system. +Graycode 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 76e7a7a8..07ae36bb 100644 --- a/docs/user-guide/27-workflows.md +++ b/docs/user-guide/27-workflows.md @@ -2,7 +2,7 @@ ## Interactive Input -- `/command` invokes a Hawk slash command. +- `/command` invokes a Graycode 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 -hawk review run HEAD --output-format json --max-turns 8 +graycode 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 `hawk mcp`. +2. Merlin configured MCP servers with `graycode 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. Hawk trims incomplete tool turns before a cancelled session +from interruptions. Graycode 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 1f2ab15e..9ad6b4c5 100644 --- a/docs/user-guide/28-workflow-budgets.md +++ b/docs/user-guide/28-workflow-budgets.md @@ -1,6 +1,6 @@ # Workflow Budgets -Hawk exposes several independent limits. Configure the smallest useful scope +Graycode 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 049a3e8e..82a66501 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 (`hawk`, `harrier`, `swift`) +### Go binaries (`graycode`, `harrier`, `swift`) - `VERSION` at the repo root. - A version package (`internal/version` for binaries, or `main` itself) declares @@ -90,7 +90,7 @@ func init() { pattern = "^(?P[^\\s]+)" [tool.hatch.build.targets.wheel] - force-include = { "VERSION" = "hawk/VERSION" } + force-include = { "VERSION" = "graycode/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 fc7871fa..74e8f3d4 100644 --- a/ecosystem.yaml +++ b/ecosystem.yaml @@ -1,135 +1,61 @@ schema_version: 1 # Canonical repository inventory for the GrayCodeAI ecosystem. Repository -# directories and GitHub repository names use the bird codenames; product_name -# records the user-facing capability. Tooling must consume this file instead of -# carrying its own repo-name list. +# directories and GitHub repository names are listed per repository; +# product_name records the user-facing capability. Tooling must consume this +# file instead of carrying its own repo-name list. Only repositories that +# exist are listed: removed siblings stay out until they are restored. contracts: - eagle_version: v0.0.0-20260902153929-5877bed17503 - portable_graph_schema: hawk.graph/v1 + portable_graph_schema: graycode.graph/v1 cloud_graph_schema: graycode-cloud.graph/v1 usage_event: usage.recorded.v1 repositories: - - directory: hawk - github_repo: hawk - product_name: Hawk + - directory: graycode-cli + github_repo: graycode-cli + product_name: Graycode kind: product language: go - module: github.com/GrayCodeAI/hawk + module: github.com/GrayCodeAI/graycode-cli workspace: true - tracks_eagle: true - - directory: eagle - github_repo: eagle - product_name: Eagle Contracts - kind: foundation - language: go - module: github.com/GrayCodeAI/eagle - workspace: true - tracks_eagle: false - - - directory: falcon - github_repo: falcon - product_name: Falcon MCP Kit - kind: foundation - language: go - module: github.com/GrayCodeAI/falcon - workspace: true - tracks_eagle: false - - - directory: eyrie - github_repo: eyrie + - directory: graycode-router + github_repo: graycode-router product_name: Eyrie kind: engine language: go module: github.com/GrayCodeAI/eyrie workspace: true - tracks_eagle: false facade: github.com/GrayCodeAI/eyrie/engine - - directory: harrier - github_repo: harrier - product_name: Harrier - kind: engine - language: go - module: github.com/GrayCodeAI/harrier - workspace: true - tracks_eagle: false - facade: github.com/GrayCodeAI/harrier/engine - - - directory: shrike - github_repo: shrike - product_name: Shrike - kind: engine - language: go - module: github.com/GrayCodeAI/shrike - workspace: true - tracks_eagle: false - facade: github.com/GrayCodeAI/shrike - - - directory: swift - github_repo: swift - product_name: Swift - kind: engine - language: go - module: github.com/GrayCodeAI/swift - workspace: true - tracks_eagle: false - facade: github.com/GrayCodeAI/swift/cli - - - directory: kestrel - github_repo: kestrel - product_name: Kestrel - kind: engine - language: go - module: github.com/GrayCodeAI/kestrel - workspace: true - tracks_eagle: false - facade: github.com/GrayCodeAI/kestrel - - - directory: merlin - github_repo: merlin - product_name: Merlin - kind: engine - language: go - module: github.com/GrayCodeAI/merlin - workspace: true - tracks_eagle: false - facade: github.com/GrayCodeAI/merlin - - directory: sparrow github_repo: sparrow - product_name: Hawk SDK for Go + product_name: Graycode SDK for Go kind: sdk language: go module: github.com/GrayCodeAI/sparrow workspace: false - tracks_eagle: false - directory: robin github_repo: robin - product_name: Hawk SDK for Python + product_name: Graycode SDK for Python kind: sdk language: python workspace: false - tracks_eagle: false - directory: wren github_repo: wren - product_name: Hawk SDK for TypeScript + product_name: Graycode SDK for TypeScript kind: sdk language: typescript workspace: false - tracks_eagle: false - directory: starling github_repo: starling - product_name: Hawk Community Skills + product_name: Graycode Community Skills kind: extension language: python workspace: false - tracks_eagle: false - directory: owl github_repo: owl @@ -137,7 +63,6 @@ repositories: kind: tooling language: javascript workspace: false - tracks_eagle: false - directory: graycode-platform github_repo: graycode-platform @@ -145,4 +70,3 @@ repositories: kind: platform language: typescript workspace: false - tracks_eagle: false diff --git a/examples/README.md b/examples/README.md index bc809e22..6553d637 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,26 +1,26 @@ -# Hawk Examples +# Graycode Examples -Hawk is an AI coding agent that understands your codebase. +Graycode is an AI coding agent that understands your codebase. ## Basic Usage ### Start a chat session ```bash -hawk chat +graycode chat > Explain how the authentication system works ``` ### Generate code ```bash -hawk "Add input validation to the user registration endpoint" +graycode "Add input validation to the user registration endpoint" ``` ### Review changes ```bash -hawk review +graycode review ``` ## Advanced Examples @@ -28,52 +28,52 @@ hawk review ### Use with skills ```bash -hawk --skill code-review "Review my latest changes" +graycode --skill code-review "Review my latest changes" ``` ### Analyze codebase ```bash -hawk analyze --depth full +graycode analyze --depth full ``` ### Fix issues ```bash -hawk fix --auto +graycode fix --auto ``` ### Headless agent in CI -Copy [hawk-ci-exec.yml](github/hawk-ci-exec.yml) to run `hawk exec --ephemeral --json` +Copy [graycode-ci-exec.yml](github/graycode-ci-exec.yml) to run `graycode exec --ephemeral --json` on pull requests (summarize diff, risk list, or your own prompt). Pin your -hawk install step and provider secrets before enabling the job. +graycode install step and provider secrets before enabling the job. ### Report CI delivery context -Copy [hawk-delivery-context.yml](github/hawk-delivery-context.yml) to your -repository to report GitHub Actions runs to Hawk Cloud. Create a dedicated, +Copy [graycode-delivery-context.yml](github/graycode-delivery-context.yml) to your +repository to report GitHub Actions runs to Graycode 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 -Hawk can use MCP servers for extended capabilities: +Graycode can use MCP servers for extended capabilities: ```bash # With harrier for persistent memory harrier setup -hawk chat +graycode chat # With swift for session capture swift start -hawk "refactor the API layer" +graycode "refactor the API layer" swift stop ``` ## Configuration -Create `.hawk/config.json`: +Create `.graycode/config.json`: ```json { diff --git a/examples/github/hawk-ci-exec.yml b/examples/github/graycode-ci-exec.yml similarity index 56% rename from examples/github/hawk-ci-exec.yml rename to examples/github/graycode-ci-exec.yml index e15e3510..5e8deae4 100644 --- a/examples/github/hawk-ci-exec.yml +++ b/examples/github/graycode-ci-exec.yml @@ -1,15 +1,15 @@ -# Hawk CI agent (headless) +# Graycode CI agent (headless) # -# Copy to .github/workflows/hawk-ci-exec.yml. +# Copy to .github/workflows/graycode-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: -# - hawk binary on PATH (pin a release or install from your org's package) +# - graycode 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 hawk build requires container isolation +# - Docker available on the runner if your graycode build requires container isolation # -name: Hawk CI Exec +name: Graycode CI Exec on: pull_request: @@ -26,27 +26,27 @@ permissions: pull-requests: read jobs: - hawk-exec: + graycode-exec: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - # Install hawk with your org's preferred mechanism, for example: + # Install graycode with your org's preferred mechanism, for example: # - curl -fsSL https://…/install.sh | sh - # - go install github.com/GrayCodeAI/hawk/cmd/hawk@vX.Y.Z + # - go install github.com/GrayCodeAI/graycode-cli/cmd/graycode@vX.Y.Z # - download a release asset into $GITHUB_PATH - - name: Ensure hawk is available + - name: Ensure graycode is available run: | - if ! command -v hawk >/dev/null 2>&1; then - echo "Install hawk before this step (see comments in this workflow)." + if ! command -v graycode >/dev/null 2>&1; then + echo "Install graycode before this step (see comments in this workflow)." exit 1 fi - hawk version || hawk --version || true + graycode version || graycode --version || true - name: Trust workspace (folder trust) - run: hawk trust add --reason "github-actions ci" || true + run: graycode 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 }} - HAWK_PROMPT: ${{ github.event.inputs.prompt || 'Summarize the changes in this PR (git log/diff) and list risks.' }} + GRAYCODE_PROMPT: ${{ github.event.inputs.prompt || 'Summarize the changes in this PR (git log/diff) and list risks.' }} run: | set -euo pipefail - PROMPT="${HAWK_PROMPT}" - hawk exec \ + PROMPT="${GRAYCODE_PROMPT}" + graycode exec \ --ephemeral \ --json \ --auto full \ --max-turns 20 \ - "$PROMPT" | tee hawk-result.json + "$PROMPT" | tee graycode-result.json - name: Upload agent result if: always() uses: actions/upload-artifact@v4 with: - name: hawk-result - path: hawk-result.json + name: graycode-result + path: graycode-result.json diff --git a/examples/github/graycode-delivery-context.yml b/examples/github/graycode-delivery-context.yml new file mode 100644 index 00000000..fd01e0e6 --- /dev/null +++ b/examples/github/graycode-delivery-context.yml @@ -0,0 +1,47 @@ +# 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/hawk-issue-resolver.yml b/examples/github/graycode-issue-resolver.yml similarity index 55% rename from examples/github/hawk-issue-resolver.yml rename to examples/github/graycode-issue-resolver.yml index 9a1724de..922d2402 100644 --- a/examples/github/hawk-issue-resolver.yml +++ b/examples/github/graycode-issue-resolver.yml @@ -1,16 +1,16 @@ -# Hawk Issue Resolver +# Graycode Issue Resolver # -# Copy this file to .github/workflows/hawk-issue-resolver.yml in your repository. +# Copy this file to .github/workflows/graycode-issue-resolver.yml in your repository. # -# It runs the Hawk agent in two scenarios: -# 1. Automation mode - when an issue is labeled `hawk-agent`, Hawk reads the +# It runs the Graycode agent in two scenarios: +# 1. Automation mode - when an issue is labeled `graycode-agent`, Graycode reads the # issue title + body and attempts to resolve it autonomously. -# 2. Interactive mode - when someone comments "@hawk ..." on an issue or PR, -# Hawk replies conversationally to the request. +# 2. Interactive mode - when someone comments "@graycode ..." on an issue or PR, +# Graycode replies conversationally to the request. # -# The mode is auto-detected by `hawk exec` from the GitHub event payload, so a +# The mode is auto-detected by `graycode exec` from the GitHub event payload, so a # single composite action handles both paths. -name: Hawk Issue Resolver +name: Graycode Issue Resolver on: issues: @@ -26,12 +26,12 @@ permissions: pull-requests: write jobs: - hawk: - # Run on the `hawk-agent` label, or on any comment mentioning @hawk. + graycode: + # Run on the `graycode-agent` label, or on any comment mentioning @graycode. if: >- - (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')) + (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')) runs-on: ubuntu-latest steps: - name: Checkout @@ -39,11 +39,11 @@ jobs: with: fetch-depth: 0 - - name: Run Hawk - id: hawk - uses: GrayCodeAI/hawk/.github/actions/hawk@main + - name: Run Graycode + id: graycode + uses: GrayCodeAI/graycode-cli/.github/actions/graycode@main with: - # Leave prompt empty so Hawk derives it from the triggering event. + # Leave prompt empty so Graycode 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 Hawk response as a comment - if: ${{ steps.hawk.outputs.response != '' }} + - name: Post Graycode response as a comment + if: ${{ steps.graycode.outputs.response != '' }} uses: actions/github-script@v7 with: script: | - const body = `🦅 **Hawk**\n\n${{ toJSON(steps.hawk.outputs.response) }}`; + const body = `🦅 **Graycode**\n\n${{ toJSON(steps.graycode.outputs.response) }}`; const issueNumber = context.issue.number; if (issueNumber) { await github.rest.issues.createComment({ diff --git a/examples/github/hawk-delivery-context.yml b/examples/github/hawk-delivery-context.yml deleted file mode 100644 index 343218d5..00000000 --- a/examples/github/hawk-delivery-context.yml +++ /dev/null @@ -1,47 +0,0 @@ -# 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/flake.nix b/flake.nix index 205bbd9b..574034ff 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "Hawk - AI coding agent powered by eyrie"; + description = "Graycode - AI coding agent powered by eyrie"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; @@ -53,8 +53,8 @@ ) siblings)} ''; - hawk = pkgs.buildGoModule rec { - pname = "hawk"; + graycode = pkgs.buildGoModule rec { + pname = "graycode"; version = "0.1.0"; 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/hawk"; + homepage = "https://github.com/GrayCodeAI/graycode-cli"; license = licenses.mit; maintainers = [ ]; }; @@ -104,8 +104,8 @@ in { packages = { - default = hawk; - inherit hawk; + default = graycode; + inherit graycode; }; devShells.default = pkgs.mkShell { @@ -119,14 +119,14 @@ ]; shellHook = '' - echo "Hawk development shell" + echo "Graycode development shell" echo "Go version: $(go version)" ''; }; apps.default = { type = "app"; - program = "${hawk}/bin/hawk"; + program = "${graycode}/bin/graycode"; }; }); } diff --git a/go.mod b/go.mod index a63b788c..8dae49f5 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/GrayCodeAI/hawk +module github.com/GrayCodeAI/graycode-cli go 1.26.6 @@ -11,7 +11,6 @@ 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/eagle v0.0.0-20260902153929-5877bed17503 github.com/GrayCodeAI/eyrie v0.2.3-0.20260902140659-6bc3068cd48a github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7 github.com/GrayCodeAI/kestrel v0.0.0-20260902154440-1b4c8cf7ea62 @@ -53,7 +52,6 @@ require ( // by the current engine module commits. Remove these excludes after the next // ordered Eagle -> Falcon -> engine release. exclude ( - github.com/GrayCodeAI/eagle v0.1.13 github.com/GrayCodeAI/falcon v0.1.4 github.com/GrayCodeAI/falcon v0.1.5 github.com/GrayCodeAI/falcon v0.1.6-0.20260825010843-82c1c610efe3 diff --git a/go.sum b/go.sum index a61353a8..08eb5214 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/eagle v0.0.0-20260902153929-5877bed17503 h1:n5y1Xpf+xzwpYixeyYg4TsZWnbEp3/dJ7RitphksnBE= -github.com/GrayCodeAI/eagle v0.0.0-20260902153929-5877bed17503/go.mod h1:wjWP4o2xfIAoPx2JdizFYvlsLMjufmKy7anToKjEj3U= github.com/GrayCodeAI/eyrie v0.2.3-0.20260902140659-6bc3068cd48a h1:6TnQbyidnPMBBfHBhIldTWzYQZx+bparWuWm1ft2q+s= github.com/GrayCodeAI/eyrie v0.2.3-0.20260902140659-6bc3068cd48a/go.mod h1:gphUZ6Vcml7zyXfKU+704HZcxtHbJLFqgLDzke/rvFc= github.com/GrayCodeAI/harrier v0.0.0-20260902154449-d52fa214feb7 h1:X5lmXWlBdPVyk7lMAX7M0FiRr9o16Sq7yIjfN3mnXGU= diff --git a/install.sh b/install.sh index 2649d5ee..5af2d8a0 100755 --- a/install.sh +++ b/install.sh @@ -2,16 +2,16 @@ set -e # Versioned install target. -# 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}" +# 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}" if [ "$1" = "--prefix" ]; then - HAWK_HOME="$2" + GRAYCODE_HOME="$2" shift 2 fi -REPO="GrayCodeAI/hawk" -BINARY="hawk" +REPO="GrayCodeAI/graycode-cli" +BINARY="graycode" 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 hawk v${LATEST} for ${OS}/${ARCH}..." +echo "Downloading graycode 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 hawk has mmap'd (same SIGKILL rationale). +# ever replacing a binary a running graycode has mmap'd (same SIGKILL rationale). # # Windows lacks reliable non-admin symlinks, so the launcher is a plain copy. -BINDIR="$HAWK_HOME/bin" +BINDIR="$GRAYCODE_HOME/bin" mkdir -p "$BINDIR" if [ "$OS" = "windows" ]; then - mv -f "$TMP/$BIN_NAME" "$BINDIR/hawk-$VERSION.exe" - cp -f "$BINDIR/hawk-$VERSION.exe" "$BINDIR/hawk.exe" + mv -f "$TMP/$BIN_NAME" "$BINDIR/graycode-$VERSION.exe" + cp -f "$BINDIR/graycode-$VERSION.exe" "$BINDIR/graycode.exe" echo "" - echo "Installed hawk v$VERSION to $BINDIR/hawk-$VERSION.exe" - echo "Linked launcher: $BINDIR/hawk.exe" + echo "Installed graycode v$VERSION to $BINDIR/graycode-$VERSION.exe" + echo "Linked launcher: $BINDIR/graycode.exe" else - mv -f "$TMP/$BIN_NAME" "$BINDIR/hawk-$VERSION" - ln -sf "hawk-$VERSION" "$BINDIR/hawk.tmp" \ - && mv -f "$BINDIR/hawk.tmp" "$BINDIR/hawk" + mv -f "$TMP/$BIN_NAME" "$BINDIR/graycode-$VERSION" + ln -sf "graycode-$VERSION" "$BINDIR/graycode.tmp" \ + && mv -f "$BINDIR/graycode.tmp" "$BINDIR/graycode" echo "" - echo "Installed hawk v$VERSION to $BINDIR/hawk-$VERSION (linked: $BINDIR/hawk)" + echo "Installed graycode v$VERSION to $BINDIR/graycode-$VERSION (linked: $BINDIR/graycode)" 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 hawk sessions to pick up the new binary — the old" +echo "Restart any running graycode 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 706426ac..7d4b5e14 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) // --- Error factory for testing --- diff --git a/internal/acp/client_test.go b/internal/acp/client_test.go index a128b6f2..8997b003 100644 --- a/internal/acp/client_test.go +++ b/internal/acp/client_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/engine" ) // mockSession creates a minimal engine session for testing. diff --git a/internal/acp/content.go b/internal/acp/content.go index ce2bd2db..8b1820d1 100644 --- a/internal/acp/content.go +++ b/internal/acp/content.go @@ -17,7 +17,7 @@ import ( "regexp" "strings" - "github.com/GrayCodeAI/hawk/internal/attachment" + "github.com/GrayCodeAI/graycode-cli/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 1bf4bba2..4e9de6ff 100644 --- a/internal/acp/content_test.go +++ b/internal/acp/content_test.go @@ -16,7 +16,7 @@ import ( "testing" "time" - "github.com/GrayCodeAI/hawk/internal/attachment" + "github.com/GrayCodeAI/graycode-cli/internal/attachment" ) // pngFixture encodes a 4x3 NRGBA raster as PNG bytes. diff --git a/internal/acp/server.go b/internal/acp/server.go index b3a22769..a5330149 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 hawk, exposing +// Package acp implements an Agent Client Protocol (ACP) server for graycode, 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 hawk's local tools; client fs routing is intentionally out of scope. +// use graycode's local tools; client fs routing is intentionally out of scope. package acp import ( @@ -20,10 +20,10 @@ import ( "sync" "time" - "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" + "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" ) // ProtocolVersion is the ACP protocol version this server implements. @@ -186,8 +186,8 @@ func (s *Server) handle(ctx context.Context, msg rpcMessage) { "audio": false, }, }, - // Hawk control-plane metadata for IDE clients that want it. - "hawkCapabilities": map[string]any{ + // Graycode control-plane metadata for IDE clients that want it. + "graycodeCapabilities": 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, - // Hawk extensions (ignored by clients that only read sessionId). - "hawk": map[string]any{ + // Graycode extensions (ignored by clients that only read sessionId). + "graycode": 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 17865094..3046f9d9 100644 --- a/internal/acp/server_test.go +++ b/internal/acp/server_test.go @@ -10,9 +10,9 @@ import ( "testing" "time" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/session" - "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/graycode-cli/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("HAWK_SESSIONS_DIR", tempDir) + t.Setenv("GRAYCODE_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("HAWK_SESSIONS_DIR", tempDir) + t.Setenv("GRAYCODE_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 0428df7c..6e5a990e 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{ - ".hawk/verify/environment.json": "{not json", + ".graycode/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 576be01b..ee264e14 100644 --- a/internal/appverify/manifest.go +++ b/internal/appverify/manifest.go @@ -6,13 +6,13 @@ import ( "os" "path/filepath" - "github.com/GrayCodeAI/hawk/internal/safewrite" + "github.com/GrayCodeAI/graycode-cli/internal/safewrite" ) // ManifestPath returns the location of the persisted verify environment -// manifest for a project: /.hawk/verify/environment.json. +// manifest for a project: /.graycode/verify/environment.json. func ManifestPath(root string) string { - return filepath.Join(root, ".hawk", "verify", "environment.json") + return filepath.Join(root, ".graycode", "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 19b17fc5..c2882780 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 = ".hawk/verify/artifacts" +const EvidenceDir = ".graycode/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 ad3ddd6e..2b9ff69b 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -13,8 +13,8 @@ import ( "runtime" "strings" - "github.com/GrayCodeAI/hawk/internal/safewrite" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/safewrite" + "github.com/GrayCodeAI/graycode-cli/internal/storage" ) // TokenStore manages authentication tokens. @@ -27,25 +27,6 @@ func NewTokenStore() *TokenStore { return &TokenStore{tokens: make(map[string]string)} } -// Load loads tokens from secure storage. -// Deprecated: Use SecureStorage directly to load tokens. This stub always -// returns an empty token map. Migrate callers to SecureStorage.Get/Set. -func (t *TokenStore) Load() error { - // Stub: no-op. Existing callers that relied on this get an empty token - // map. New code should use SecureStorage directly. - t.tokens = make(map[string]string) - return nil -} - -// Save saves tokens to secure storage. -// Deprecated: Use SecureStorage directly to persist tokens. This stub is a -// no-op. Migrate callers to SecureStorage.Set. -func (t *TokenStore) Save() error { - // Stub: no-op. Tokens held in-memory only; they are lost on process exit. - // Use SecureStorage for persistent, OS-keychain-backed storage. - return nil -} - // Get returns a token for a provider. func (t *TokenStore) Get(provider string) string { return t.tokens[provider] diff --git a/internal/auth/auth_extra_test.go b/internal/auth/auth_extra_test.go index b30c9eed..a093f272 100644 --- a/internal/auth/auth_extra_test.go +++ b/internal/auth/auth_extra_test.go @@ -23,33 +23,6 @@ func TestNewTokenStore(t *testing.T) { } } -func TestTokenStore_Load(t *testing.T) { - ts := NewTokenStore() - // Pre-populate - ts.tokens["test"] = "old" - err := ts.Load() - if err != nil { - t.Fatalf("Load() error: %v", err) - } - // Load should reset to empty - if len(ts.tokens) != 0 { - t.Errorf("expected empty tokens after Load, got %d", len(ts.tokens)) - } -} - -func TestTokenStore_Save(t *testing.T) { - ts := NewTokenStore() - ts.tokens["test"] = "secret" - err := ts.Save() - if err != nil { - t.Fatalf("Save() error: %v", err) - } - // Save is a no-op stub, tokens should still be in memory - if ts.tokens["test"] != "secret" { - t.Error("token should still be in memory after Save") - } -} - func TestTokenStore_Get(t *testing.T) { ts := NewTokenStore() ts.tokens["provider1"] = "token1" @@ -100,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("HAWK_CONFIG_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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 e4a6b4cc..cdb32061 100644 --- a/internal/auth/auth_test.go +++ b/internal/auth/auth_test.go @@ -6,7 +6,7 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/storage" ) func TestTokenStore(t *testing.T) { @@ -82,17 +82,6 @@ func TestTokenStore(t *testing.T) { } } }) - - t.Run("load and save are no-ops", func(t *testing.T) { - t.Parallel() - store := NewTokenStore() - if err := store.Load(); err != nil { - t.Errorf("Load() error = %v", err) - } - if err := store.Save(); err != nil { - t.Errorf("Save() error = %v", err) - } - }) } func TestGenerateNonce(t *testing.T) { @@ -130,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("hawk-test") + ss := NewSecureStorage("graycode-test") if ss == nil { t.Fatal("NewSecureStorage returned nil") } - if ss.service != "hawk-test" { - t.Errorf("service = %q, want %q", ss.service, "hawk-test") + if ss.service != "graycode-test" { + t.Errorf("service = %q, want %q", ss.service, "graycode-test") } }) @@ -143,7 +132,7 @@ func TestSecureStorage(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) - ss := NewSecureStorage("hawk-test") + ss := NewSecureStorage("graycode-test") _, err := ss.getFile("nonexistent") if err == nil { t.Error("getFile() should return error for missing file") @@ -154,7 +143,7 @@ func TestSecureStorage(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) - ss := NewSecureStorage("hawk-test") + ss := NewSecureStorage("graycode-test") if err := ss.setFile("anthropic", "sk-test-token"); err != nil { t.Fatalf("setFile() error = %v", err) } @@ -172,7 +161,7 @@ func TestSecureStorage(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) - ss := NewSecureStorage("hawk-test") + ss := NewSecureStorage("graycode-test") if err := ss.setFile("provider", "old-token"); err != nil { t.Fatal(err) } @@ -193,7 +182,7 @@ func TestSecureStorage(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) - ss := NewSecureStorage("hawk-test") + ss := NewSecureStorage("graycode-test") if err := ss.setFile("test", "secret"); err != nil { t.Fatal(err) } @@ -213,7 +202,7 @@ func TestSecureStorage(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", dir) - ss := NewSecureStorage("hawk-test") + ss := NewSecureStorage("graycode-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 b8b05d80..4c6efcc9 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("HAWK_CONFIG_DIR", tmpDir) + t.Setenv("GRAYCODE_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("HAWK_CONFIG_DIR", tmpDir) + t.Setenv("GRAYCODE_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("HAWK_CONFIG_DIR", tmpDir) + t.Setenv("GRAYCODE_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("HAWK_CONFIG_DIR", tmpDir) + t.Setenv("GRAYCODE_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("HAWK_CONFIG_DIR", tmpDir) + t.Setenv("GRAYCODE_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 9100e372..e4de2fd6 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 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 +// 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 // 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 / `hawk init`). When no runner is wired, +// init analysis (the init-deep skill / `graycode 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/hawk/internal/config" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/config" + "github.com/GrayCodeAI/graycode-cli/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 = "HAWK_DISABLE_AUTO_INIT" +const disableEnv = "GRAYCODE_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 hawk. -var contextFiles = []string{"AGENTS.md", "HAWK.md", "CLAUDE.md", "CONTEXT.md"} +// the convention files recognized elsewhere in graycode. +var contextFiles = []string{"AGENTS.md", "GRAYCODE.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 HAWK_DISABLE_AUTO_INIT -> skip. +// 1. Disabled via GRAYCODE_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 60d92022..335507bd 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, "HAWK.md"), []byte("# x"), 0o644); err != nil { + if err := os.WriteFile(filepath.Join(root, "GRAYCODE.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("HAWK_STATE_DIR", stateFile) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", filepath.Join(t.TempDir(), "state")) + t.Setenv("GRAYCODE_STATE_DIR", filepath.Join(t.TempDir(), "state")) } diff --git a/internal/bench/bench_test.go b/internal/bench/bench_test.go index 4f41a07b..4bfcb120 100644 --- a/internal/bench/bench_test.go +++ b/internal/bench/bench_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 HAWK_BENCH_PROVIDER / HAWK_BENCH_API_KEY, which is why this +// and is gated by GRAYCODE_BENCH_PROVIDER / GRAYCODE_BENCH_API_KEY, which is why this // test only runs when that env is set — see TestBenchmark_SWE_benchHeadless). var sWEbenchSmokeTasks = []taskFixture{ { @@ -81,13 +81,13 @@ func runTask(t *testing.B, fix taskFixture, events []types.EyrieStreamEvent) (te } // TestBenchmark_SWE_benchHeadless is a compile + smoke gate for the headless -// agent loop. It is skipped unless HAWK_BENCH_HEADLESS=1 is set so it never +// agent loop. It is skipped unless GRAYCODE_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 `hawk bench`. +// harness execution lives in bench_suite.go and is invoked via `graycode bench`. func TestBenchmark_SWE_benchHeadless(t *testing.T) { - if v := os.Getenv("HAWK_BENCH_HEADLESS"); v != "1" { + if v := os.Getenv("GRAYCODE_BENCH_HEADLESS"); v != "1" { // TODO: track scheduling the headless benchmark smoke in CI. - t.Skip("set HAWK_BENCH_HEADLESS=1 to run agent-loop benchmark smoke") + t.Skip("set GRAYCODE_BENCH_HEADLESS=1 to run agent-loop benchmark smoke") } b := &testing.B{} for _, fix := range sWEbenchSmokeTasks { diff --git a/internal/bench/suite.go b/internal/bench/suite.go index 07588127..329a6fc3 100644 --- a/internal/bench/suite.go +++ b/internal/bench/suite.go @@ -9,7 +9,7 @@ import ( "path/filepath" "time" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 hawk build benchmark - if hawkResult, err := runHawkBuildBench(projectDir); err == nil { - suite.Results = append(suite.Results, hawkResult) + // Run graycode build benchmark + if graycodeResult, err := runGraycodeBuildBench(projectDir); err == nil { + suite.Results = append(suite.Results, graycodeResult) } return suite, nil @@ -103,21 +103,21 @@ func runShrikeBench(projectDir string) ([]BenchmarkResult, error) { return []BenchmarkResult{result}, nil } -// 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") +// 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") } start := time.Now() cmd := exec.CommandContext(context.Background(), "go", "build", "-o", "/dev/null", ".") - cmd.Dir = hawkDir + cmd.Dir = graycodeDir err := cmd.Run() duration := time.Since(start) return BenchmarkResult{ - Name: "hawk/build", + Name: "graycode/build", Duration: duration, Score: float64(duration.Milliseconds()), Metric: "time", diff --git a/internal/bridge/kestrel/bridge.go b/internal/bridge/kestrel/bridge.go index 76845e0c..c34ef95f 100644 --- a/internal/bridge/kestrel/bridge.go +++ b/internal/bridge/kestrel/bridge.go @@ -5,19 +5,19 @@ import ( "sync" "time" - graphcontracts "github.com/GrayCodeAI/eagle/graph" - reviewcontracts "github.com/GrayCodeAI/eagle/review" - eagletypes "github.com/GrayCodeAI/eagle/types" - "github.com/GrayCodeAI/hawk/internal/graphjournal" - "github.com/GrayCodeAI/hawk/internal/types" + 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" kestrelLib "github.com/GrayCodeAI/kestrel" kestrelgraph "github.com/GrayCodeAI/kestrel/graph" "github.com/GrayCodeAI/kestrel/qualitygraph" kestrelreview "github.com/GrayCodeAI/kestrel/review" ) -// EyrieAdapter implements kestrel's Provider interface using hawk's eyrie client. -// It translates between kestrel.Message/kestrel.ChatOpts and Hawk runtime DTOs. +// EyrieAdapter implements kestrel's Provider interface using graycode's eyrie client. +// It translates between kestrel.Message/kestrel.ChatOpts and Graycode runtime DTOs. type EyrieAdapter struct { client types.ChatProvider provider string @@ -70,7 +70,7 @@ func (a *EyrieAdapter) Chat(ctx context.Context, messages []kestrelLib.Message, }, nil } -// Bridge connects hawk to the kestrel code-review library. +// Bridge connects graycode to the kestrel code-review library. // If initialization fails, all operations degrade gracefully and return // empty results rather than errors. type Bridge struct { @@ -90,7 +90,7 @@ type GraphObservation struct { MaxFindings int } -// NewBridge creates a bridge to the kestrel library using the given Hawk +// NewBridge creates a bridge to the kestrel library using the given Graycode // 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 { @@ -133,7 +133,7 @@ func (b *Bridge) ReviewContracts(ctx context.Context, diff string) (*reviewcontr if err != nil { return nil, err } - return toEagleResult(kestrelLib.ToContractResult(result)), nil + return toContractResult(kestrelLib.ToContractResult(result)), nil } // ReviewContractsObserved reviews a diff, journals Kestrel's portable quality @@ -170,9 +170,9 @@ func (b *Bridge) ReviewContractsObserved( observation.ToolCallID, stage, "kestrel", - toEagleNodes(export.Nodes), - toEagleEdges(export.Edges), - toEagleEvents(export.Events), + toContractNodes(export.Nodes), + toContractEdges(export.Edges), + toContractEvents(export.Events), observedAt, ); err != nil { return nil, err @@ -190,7 +190,7 @@ func (b *Bridge) ReviewContractsObserved( ); err != nil { return nil, err } - return toEagleResult(contractResult), nil + return toContractResult(contractResult), nil } // Describe generates a PR description from a unified diff string. @@ -218,86 +218,86 @@ func (b *Bridge) Improve(ctx context.Context, diff string) (*kestrelLib.ImproveR } // The following helpers convert Kestrel's vendored contract types into -// Hawk's eagle/* contract types (and the reverse for scope). The definitions +// Graycode'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 { return kestrelgraph.Scope{TenantID: s.TenantID, ProjectID: s.ProjectID, RepositoryID: s.RepositoryID} } -func toEagleNodes(nodes []kestrelgraph.Node) []graphcontracts.Node { +func toContractNodes(nodes []kestrelgraph.Node) []graphcontracts.Node { out := make([]graphcontracts.Node, len(nodes)) for i, n := range nodes { - out[i] = toEagleNode(n) + out[i] = toContractNode(n) } return out } -func toEagleNode(n kestrelgraph.Node) graphcontracts.Node { +func toContractNode(n kestrelgraph.Node) graphcontracts.Node { return graphcontracts.Node{ ID: n.ID, Kind: graphcontracts.NodeKind(n.Kind), - Scope: toEagleScope(n.Scope), + Scope: toContractScope(n.Scope), CreatedAt: n.CreatedAt, EffectiveAt: n.EffectiveAt, - Provenance: toEagleProvenance(n.Provenance), + Provenance: toContractProvenance(n.Provenance), Attributes: n.Attributes, } } -func toEagleEdges(edges []kestrelgraph.Edge) []graphcontracts.Edge { +func toContractEdges(edges []kestrelgraph.Edge) []graphcontracts.Edge { out := make([]graphcontracts.Edge, len(edges)) for i, e := range edges { - out[i] = toEagleEdge(e) + out[i] = toContractEdge(e) } return out } -func toEagleEdge(e kestrelgraph.Edge) graphcontracts.Edge { +func toContractEdge(e kestrelgraph.Edge) graphcontracts.Edge { return graphcontracts.Edge{ ID: e.ID, Kind: graphcontracts.EdgeKind(e.Kind), - From: toEagleRef(e.From), - To: toEagleRef(e.To), - Scope: toEagleScope(e.Scope), + From: toContractRef(e.From), + To: toContractRef(e.To), + Scope: toContractScope(e.Scope), CreatedAt: e.CreatedAt, EffectiveAt: e.EffectiveAt, - Provenance: toEagleProvenance(e.Provenance), + Provenance: toContractProvenance(e.Provenance), Attributes: e.Attributes, } } -func toEagleEvents(events []kestrelgraph.Event) []graphcontracts.Event { +func toContractEvents(events []kestrelgraph.Event) []graphcontracts.Event { out := make([]graphcontracts.Event, len(events)) for i, ev := range events { - out[i] = toEagleEvent(ev) + out[i] = toContractEvent(ev) } return out } -func toEagleEvent(ev kestrelgraph.Event) graphcontracts.Event { +func toContractEvent(ev kestrelgraph.Event) graphcontracts.Event { return graphcontracts.Event{ ID: ev.ID, Type: graphcontracts.EventType(ev.Type), - Subject: toEagleRef(ev.Subject), - Scope: toEagleScope(ev.Scope), + Subject: toContractRef(ev.Subject), + Scope: toContractScope(ev.Scope), OccurredAt: ev.OccurredAt, CorrelationID: ev.CorrelationID, CausationID: ev.CausationID, IdempotencyKey: ev.IdempotencyKey, - Provenance: toEagleProvenance(ev.Provenance), + Provenance: toContractProvenance(ev.Provenance), } } -func toEagleRef(r kestrelgraph.Ref) graphcontracts.Ref { +func toContractRef(r kestrelgraph.Ref) graphcontracts.Ref { return graphcontracts.Ref{Kind: graphcontracts.NodeKind(r.Kind), ID: r.ID} } -func toEagleScope(s kestrelgraph.Scope) graphcontracts.Scope { +func toContractScope(s kestrelgraph.Scope) graphcontracts.Scope { return graphcontracts.Scope{TenantID: s.TenantID, ProjectID: s.ProjectID, RepositoryID: s.RepositoryID} } -func toEagleProvenance(p kestrelgraph.Provenance) graphcontracts.Provenance { +func toContractProvenance(p kestrelgraph.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} @@ -305,28 +305,28 @@ func toEagleProvenance(p kestrelgraph.Provenance) graphcontracts.Provenance { return graphcontracts.Provenance{Producer: p.Producer, Version: p.Version, SourceID: p.SourceID, Evidence: evidence} } -func toEagleResult(r *kestrelreview.Result) *reviewcontracts.Result { +func toContractResult(r *kestrelreview.Result) *reviewcontracts.Result { if r == nil { return nil } return &reviewcontracts.Result{ - Findings: toEagleFindings(r.Findings), - Comments: toEagleComments(r.Comments), - Stats: toEagleStats(r.Stats), + Findings: toContractFindings(r.Findings), + Comments: toContractComments(r.Comments), + Stats: toContractStats(r.Stats), Report: r.Report, - FailOn: eagletypes.Severity(r.FailOn), + FailOn: typescontracts.Severity(r.FailOn), FailOnSet: r.FailOnSet, - SASTFusion: toEagleSASTFusion(r.SASTFusion), - ConfidenceBreakdown: toEagleConfidenceBreakdown(r.ConfidenceBreakdown), + SASTFusion: toContractSASTFusion(r.SASTFusion), + ConfidenceBreakdown: toContractConfidenceBreakdown(r.ConfidenceBreakdown), } } -func toEagleFindings(findings []kestrelreview.Finding) []reviewcontracts.Finding { +func toContractFindings(findings []kestrelreview.Finding) []reviewcontracts.Finding { out := make([]reviewcontracts.Finding, len(findings)) for i, f := range findings { out[i] = reviewcontracts.Finding{ Concern: f.Concern, - Severity: eagletypes.Severity(f.Severity), + Severity: typescontracts.Severity(f.Severity), File: f.File, Line: f.Line, EndLine: f.EndLine, @@ -341,7 +341,7 @@ func toEagleFindings(findings []kestrelreview.Finding) []reviewcontracts.Finding return out } -func toEagleComments(comments []kestrelreview.InlineComment) []reviewcontracts.InlineComment { +func toContractComments(comments []kestrelreview.InlineComment) []reviewcontracts.InlineComment { out := make([]reviewcontracts.InlineComment, len(comments)) for i, c := range comments { out[i] = reviewcontracts.InlineComment{ @@ -355,10 +355,10 @@ func toEagleComments(comments []kestrelreview.InlineComment) []reviewcontracts.I return out } -func toEagleStats(s kestrelreview.Stats) reviewcontracts.Stats { - bySeverity := make(map[eagletypes.Severity]int, len(s.BySeverity)) +func toContractStats(s kestrelreview.Stats) reviewcontracts.Stats { + bySeverity := make(map[typescontracts.Severity]int, len(s.BySeverity)) for sev, count := range s.BySeverity { - bySeverity[eagletypes.Severity(sev)] = count + bySeverity[typescontracts.Severity(sev)] = count } return reviewcontracts.Stats{ FilesReviewed: s.FilesReviewed, @@ -375,24 +375,24 @@ func toEagleStats(s kestrelreview.Stats) reviewcontracts.Stats { } } -func toEagleSASTFusion(f *kestrelreview.SASTFusionResult) *reviewcontracts.SASTFusionResult { +func toContractSASTFusion(f *kestrelreview.SASTFusionResult) *reviewcontracts.SASTFusionResult { if f == nil { return nil } return &reviewcontracts.SASTFusionResult{ - Confirmed: toEagleFindings(f.Confirmed), - Dismissed: toEagleFindings(f.Dismissed), - Unaddressed: toEagleFindings(f.Unaddressed), + Confirmed: toContractFindings(f.Confirmed), + Dismissed: toContractFindings(f.Dismissed), + Unaddressed: toContractFindings(f.Unaddressed), } } -func toEagleConfidenceBreakdown(c *kestrelreview.ConfidenceBreakdown) *reviewcontracts.ConfidenceBreakdown { +func toContractConfidenceBreakdown(c *kestrelreview.ConfidenceBreakdown) *reviewcontracts.ConfidenceBreakdown { if c == nil { return nil } return &reviewcontracts.ConfidenceBreakdown{ - High: toEagleFindings(c.High), - Medium: toEagleFindings(c.Medium), - Low: toEagleFindings(c.Low), + High: toContractFindings(c.High), + Medium: toContractFindings(c.Medium), + Low: toContractFindings(c.Low), } } diff --git a/internal/bridge/kestrel/bridge_test.go b/internal/bridge/kestrel/bridge_test.go index de53a65d..ea4e86a4 100644 --- a/internal/bridge/kestrel/bridge_test.go +++ b/internal/bridge/kestrel/bridge_test.go @@ -5,12 +5,12 @@ import ( "testing" "time" - graphcontracts "github.com/GrayCodeAI/eagle/graph" - "github.com/GrayCodeAI/hawk/internal/graphjournal" + graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" + "github.com/GrayCodeAI/graycode-cli/internal/graphjournal" ) func TestReviewContractsObservedRecordsQualityGraph(t *testing.T) { - t.Setenv("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) bridge := &Bridge{} at := time.Date(2026, time.July, 25, 13, 0, 0, 0, time.UTC) result, err := bridge.ReviewContractsObserved( diff --git a/internal/bridge/merlin/bridge.go b/internal/bridge/merlin/bridge.go index 968af449..f9a2864f 100644 --- a/internal/bridge/merlin/bridge.go +++ b/internal/bridge/merlin/bridge.go @@ -5,17 +5,17 @@ import ( "sync" "time" - graphcontracts "github.com/GrayCodeAI/eagle/graph" - eagletypes "github.com/GrayCodeAI/eagle/types" - verifycontracts "github.com/GrayCodeAI/eagle/verify" - "github.com/GrayCodeAI/hawk/internal/graphjournal" + 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" merlinLib "github.com/GrayCodeAI/merlin" merlingraph "github.com/GrayCodeAI/merlin/graph" "github.com/GrayCodeAI/merlin/qualitygraph" merlinverify "github.com/GrayCodeAI/merlin/verify" ) -// Bridge connects hawk to the merlin site-auditing library. +// Bridge connects graycode to the merlin site-auditing library. // If initialization fails, all operations degrade gracefully and return // empty results rather than errors. type Bridge struct { @@ -24,7 +24,7 @@ type Bridge struct { ready bool } -// GraphObservation identifies an opt-in Hawk quality-graph journal record. +// GraphObservation identifies an opt-in Graycode quality-graph journal record. type GraphObservation struct { SessionID string ToolCallID string @@ -77,7 +77,7 @@ func (b *Bridge) RunContracts(ctx context.Context, target string, opts ...merlin if err != nil { return nil, err } - return toEagleReport(merlinLib.ToContractReport(report)), nil + return toContractReport(merlinLib.ToContractReport(report)), nil } // RunContractsObserved performs a scan, journals Merlin's portable quality @@ -114,9 +114,9 @@ func (b *Bridge) RunContractsObserved( observation.ToolCallID, stage, "merlin", - toEagleNodes(export.Nodes), - toEagleEdges(export.Edges), - toEagleEvents(export.Events), + toContractNodes(export.Nodes), + toContractEdges(export.Edges), + toContractEvents(export.Events), observedAt, ); err != nil { return nil, err @@ -134,90 +134,90 @@ func (b *Bridge) RunContractsObserved( ); err != nil { return nil, err } - return toEagleReport(contractReport), nil + return toContractReport(contractReport), nil } -// The following helpers convert Merlin's vendored contract types into Hawk's -// eagle/* contract types (and the reverse for scope). The definitions are +// The following helpers convert Merlin's vendored contract types into Graycode'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 toMerlinScope(s graphcontracts.Scope) merlingraph.Scope { return merlingraph.Scope{TenantID: s.TenantID, ProjectID: s.ProjectID, RepositoryID: s.RepositoryID} } -func toEagleNodes(nodes []merlingraph.Node) []graphcontracts.Node { +func toContractNodes(nodes []merlingraph.Node) []graphcontracts.Node { out := make([]graphcontracts.Node, len(nodes)) for i, n := range nodes { - out[i] = toEagleNode(n) + out[i] = toContractNode(n) } return out } -func toEagleNode(n merlingraph.Node) graphcontracts.Node { +func toContractNode(n merlingraph.Node) graphcontracts.Node { return graphcontracts.Node{ ID: n.ID, Kind: graphcontracts.NodeKind(n.Kind), - Scope: toEagleScope(n.Scope), + Scope: toContractScope(n.Scope), CreatedAt: n.CreatedAt, EffectiveAt: n.EffectiveAt, - Provenance: toEagleProvenance(n.Provenance), + Provenance: toContractProvenance(n.Provenance), Attributes: n.Attributes, } } -func toEagleEdges(edges []merlingraph.Edge) []graphcontracts.Edge { +func toContractEdges(edges []merlingraph.Edge) []graphcontracts.Edge { out := make([]graphcontracts.Edge, len(edges)) for i, e := range edges { - out[i] = toEagleEdge(e) + out[i] = toContractEdge(e) } return out } -func toEagleEdge(e merlingraph.Edge) graphcontracts.Edge { +func toContractEdge(e merlingraph.Edge) graphcontracts.Edge { return graphcontracts.Edge{ ID: e.ID, Kind: graphcontracts.EdgeKind(e.Kind), - From: toEagleRef(e.From), - To: toEagleRef(e.To), - Scope: toEagleScope(e.Scope), + From: toContractRef(e.From), + To: toContractRef(e.To), + Scope: toContractScope(e.Scope), CreatedAt: e.CreatedAt, EffectiveAt: e.EffectiveAt, - Provenance: toEagleProvenance(e.Provenance), + Provenance: toContractProvenance(e.Provenance), Attributes: e.Attributes, } } -func toEagleEvents(events []merlingraph.Event) []graphcontracts.Event { +func toContractEvents(events []merlingraph.Event) []graphcontracts.Event { out := make([]graphcontracts.Event, len(events)) for i, ev := range events { - out[i] = toEagleEvent(ev) + out[i] = toContractEvent(ev) } return out } -func toEagleEvent(ev merlingraph.Event) graphcontracts.Event { +func toContractEvent(ev merlingraph.Event) graphcontracts.Event { return graphcontracts.Event{ ID: ev.ID, Type: graphcontracts.EventType(ev.Type), - Subject: toEagleRef(ev.Subject), - Scope: toEagleScope(ev.Scope), + Subject: toContractRef(ev.Subject), + Scope: toContractScope(ev.Scope), OccurredAt: ev.OccurredAt, CorrelationID: ev.CorrelationID, CausationID: ev.CausationID, IdempotencyKey: ev.IdempotencyKey, - Provenance: toEagleProvenance(ev.Provenance), + Provenance: toContractProvenance(ev.Provenance), } } -func toEagleRef(r merlingraph.Ref) graphcontracts.Ref { +func toContractRef(r merlingraph.Ref) graphcontracts.Ref { return graphcontracts.Ref{Kind: graphcontracts.NodeKind(r.Kind), ID: r.ID} } -func toEagleScope(s merlingraph.Scope) graphcontracts.Scope { +func toContractScope(s merlingraph.Scope) graphcontracts.Scope { return graphcontracts.Scope{TenantID: s.TenantID, ProjectID: s.ProjectID, RepositoryID: s.RepositoryID} } -func toEagleProvenance(p merlingraph.Provenance) graphcontracts.Provenance { +func toContractProvenance(p merlingraph.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} @@ -225,7 +225,7 @@ func toEagleProvenance(p merlingraph.Provenance) graphcontracts.Provenance { return graphcontracts.Provenance{Producer: p.Producer, Version: p.Version, SourceID: p.SourceID, Evidence: evidence} } -func toEagleReport(r *merlinverify.Report) *verifycontracts.Report { +func toContractReport(r *merlinverify.Report) *verifycontracts.Report { if r == nil { return nil } @@ -233,7 +233,7 @@ func toEagleReport(r *merlinverify.Report) *verifycontracts.Report { for i, f := range r.Findings { findings[i] = verifycontracts.Finding{ Check: f.Check, - Severity: eagletypes.Severity(f.Severity), + Severity: typescontracts.Severity(f.Severity), URL: f.URL, Element: f.Element, Message: f.Message, @@ -241,9 +241,9 @@ func toEagleReport(r *merlinverify.Report) *verifycontracts.Report { Evidence: f.Evidence, } } - bySeverity := make(map[eagletypes.Severity]int, len(r.Stats.BySeverity)) + bySeverity := make(map[typescontracts.Severity]int, len(r.Stats.BySeverity)) for sev, count := range r.Stats.BySeverity { - bySeverity[eagletypes.Severity(sev)] = count + bySeverity[typescontracts.Severity(sev)] = count } return &verifycontracts.Report{ Target: r.Target, @@ -257,7 +257,7 @@ func toEagleReport(r *merlinverify.Report) *verifycontracts.Report { }, CrawledURLs: r.CrawledURLs, Duration: r.Duration, - FailOn: eagletypes.Severity(r.FailOn), + FailOn: typescontracts.Severity(r.FailOn), FailOnSet: r.FailOnSet, } } diff --git a/internal/bridge/merlin/bridge_test.go b/internal/bridge/merlin/bridge_test.go index 50b28881..de9c9b90 100644 --- a/internal/bridge/merlin/bridge_test.go +++ b/internal/bridge/merlin/bridge_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - graphcontracts "github.com/GrayCodeAI/eagle/graph" - "github.com/GrayCodeAI/hawk/internal/graphjournal" + graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" + "github.com/GrayCodeAI/graycode-cli/internal/graphjournal" ) func TestNewBridge(t *testing.T) { @@ -39,7 +39,7 @@ func TestBridge_Ready(t *testing.T) { } func TestRunContractsObservedRecordsQualityGraph(t *testing.T) { - t.Setenv("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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 d3bfac9b..8f3102d1 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 hawk, replacing the subprocess-based approach. +// functionality for use by graycode, replacing the subprocess-based approach. // -// Since swift and hawk are separate Go modules, this package uses +// Since swift and graycode 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 d069d5e6..7532e245 100644 --- a/internal/catalogtest/install.go +++ b/internal/catalogtest/install.go @@ -20,7 +20,7 @@ var ( // Call from TestMain; returns cleanup to unset env. func InstallGlobal() (cleanup func()) { globalOnce.Do(func() { - dir, err := os.MkdirTemp("", "hawk-catalog-*") + dir, err := os.MkdirTemp("", "graycode-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. diff --git a/internal/circuitbreaker/circuitbreaker.go b/internal/circuitbreaker/circuitbreaker.go index cd342e05..ffdeda15 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. Hawk uses it to stop runaway auto-compaction (an +// failure re-opens it. Graycode 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 bfadae73..7cac4b01 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 hawk, eyrie, shrike, harrier, etc. +// Useful for finding relationships between graycode, 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 hawk calls a symbol in eyrie, this traces that cross-repo dependency. +// If a symbol in graycode 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, hawk calling eyrie functions. +// For example, graycode 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 af9f5831..94a3adab 100644 --- a/internal/config/catalog_api.go +++ b/internal/config/catalog_api.go @@ -6,7 +6,7 @@ import ( "strings" llm "github.com/GrayCodeAI/eyrie/llm" - gw "github.com/GrayCodeAI/hawk/internal/provider/gateway" + gw "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) type GatewayStatus struct { diff --git a/internal/config/catalog_compat_test.go b/internal/config/catalog_compat_test.go index a7909d1c..8ff45f9a 100644 --- a/internal/config/catalog_compat_test.go +++ b/internal/config/catalog_compat_test.go @@ -9,7 +9,7 @@ import ( ) // CompiledCatalogV1 is retained only for lower-level migration/security tests. -// Production Hawk code consumes engine DTOs exclusively. +// Production Graycode 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 { diff --git a/internal/config/catalog_gateways_test.go b/internal/config/catalog_gateways_test.go index 5cf36e7d..71b579f3 100644 --- a/internal/config/catalog_gateways_test.go +++ b/internal/config/catalog_gateways_test.go @@ -7,7 +7,7 @@ import ( "github.com/GrayCodeAI/eyrie/catalog" "github.com/GrayCodeAI/eyrie/credentials" - "github.com/GrayCodeAI/hawk/internal/catalogtest" + "github.com/GrayCodeAI/graycode-cli/internal/catalogtest" ) func TestIsCatalogCacheRequired(t *testing.T) { diff --git a/internal/config/catalog_health.go b/internal/config/catalog_health.go index 8c294ab0..0c566a37 100644 --- a/internal/config/catalog_health.go +++ b/internal/config/catalog_health.go @@ -71,12 +71,12 @@ func catalogHealthReportUncached(ctx context.Context) CatalogHealth { Source: status.Source, Error: status.Error, } if !h.Exists && h.Error == "" { - h.Error = "cache missing — hawk will discover automatically on start" + h.Error = "cache missing — graycode will discover automatically on start" } return h } -// FormatCatalogHealth returns human-readable catalog status for hawk doctor. +// FormatCatalogHealth returns human-readable catalog status for graycode doctor. func FormatCatalogHealth(h CatalogHealth) string { var b strings.Builder b.WriteString("Model catalog (eyrie):\n") @@ -91,7 +91,7 @@ func FormatCatalogHealth(h CatalogHealth) string { } b.WriteString(fmt.Sprintf(" models: %d deployments: %d offerings: %d\n", h.Models, h.Deployments, h.Offerings)) if h.Stale { - b.WriteString(fmt.Sprintf(" stale: yes (after %s) — hawk refreshes automatically on start\n", h.StaleAfter.UTC().Format(time.RFC3339))) + b.WriteString(fmt.Sprintf(" stale: yes (after %s) — graycode refreshes automatically on start\n", h.StaleAfter.UTC().Format(time.RFC3339))) } else if !h.StaleAfter.IsZero() { b.WriteString(fmt.Sprintf(" stale: no (until %s)\n", h.StaleAfter.UTC().Format(time.RFC3339))) } @@ -106,7 +106,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 hawk preflight or /config — hawk refreshes the catalog automatically" + return "check network access, then graycode preflight or /config — graycode refreshes the catalog automatically" } // EnsureCatalogAvailable returns an error when the production catalog cache is missing or empty. diff --git a/internal/config/catalog_health_test.go b/internal/config/catalog_health_test.go index 48b638d6..4fc2e5f3 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/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) func TestCatalogEmptyHint_NoCredentials(t *testing.T) { diff --git a/internal/config/catalog_startup.go b/internal/config/catalog_startup.go index 506521a2..94b75341 100644 --- a/internal/config/catalog_startup.go +++ b/internal/config/catalog_startup.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/env" + "github.com/GrayCodeAI/graycode-cli/internal/env" ) type gatewayModelCount struct { @@ -78,7 +78,7 @@ func CatalogReady(ctx context.Context) bool { return h.Error == "" && h.Models > 0 && !h.Stale } -// CatalogStartupOptions controls automatic catalog refresh at hawk startup. +// CatalogStartupOptions controls automatic catalog refresh at graycode 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 hawk auto-discovers when the cache is missing, empty, or stale. +// By default graycode 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) { @@ -180,7 +180,7 @@ 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 (HAWK_AUTO_REFRESH_CATALOG=0)") + return fmt.Errorf("automatic catalog refresh is disabled (GRAYCODE_AUTO_REFRESH_CATALOG=0)") } return AutoRefreshCatalog(ctx, nil, false) } @@ -209,7 +209,7 @@ func StartupCatalogPrefetch(ctx context.Context) { }() } -// DiscoverCatalogAfterSetup runs during optional hawk setup after API keys are saved. +// DiscoverCatalogAfterSetup runs during optional graycode 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 hawk preflight or /config." + return "Check network access and stored keys (" + CredentialStoreName() + "). Run graycode preflight or /config." } func autoRefreshCatalogEnabled() bool { - switch strings.ToLower(strings.TrimSpace(env.Getenv("HAWK_AUTO_REFRESH_CATALOG"))) { + switch strings.ToLower(strings.TrimSpace(env.Getenv("GRAYCODE_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("HAWK_CATALOG_REFRESH_ALWAYS"))) { + switch strings.ToLower(strings.TrimSpace(env.Getenv("GRAYCODE_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 fa3863c3..48b37cda 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/hawk/internal/catalogtest" - hawkconfig "github.com/GrayCodeAI/hawk/internal/config" + "github.com/GrayCodeAI/graycode-cli/internal/catalogtest" + graycodeconfig "github.com/GrayCodeAI/graycode-cli/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 := hawkconfig.CatalogHealthReport(context.Background()) + h := graycodeconfig.CatalogHealthReport(context.Background()) var buf bytes.Buffer - err := hawkconfig.PrepareCatalogForSession(context.Background(), &buf, hawkconfig.CatalogStartupOptions{ + err := graycodeconfig.PrepareCatalogForSession(context.Background(), &buf, graycodeconfig.CatalogStartupOptions{ ForceRefresh: true, }) // With ForceRefresh, remote may fail; if we had models before, we tolerate failure. @@ -28,7 +28,7 @@ func TestPrepareCatalogForSession_StaleCacheRefreshFailureContinues(t *testing.T func TestCatalogCachePathForDisplay_RespectsEnv(t *testing.T) { custom := filepath.Join(t.TempDir(), "custom.json") t.Setenv("EYRIE_MODEL_CATALOG_PATH", custom) - if got := hawkconfig.CatalogCachePathForDisplay(); got != custom { + if got := graycodeconfig.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 6c98d153..12fc773d 100644 --- a/internal/config/catalog_startup_test.go +++ b/internal/config/catalog_startup_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/catalogtest" + "github.com/GrayCodeAI/graycode-cli/internal/catalogtest" ) func TestCatalogReady_MissingCache(t *testing.T) { @@ -50,11 +50,11 @@ func TestCatalogNeedsAutoRefresh_Fresh(t *testing.T) { } func TestAutoRefreshCatalogEnabled(t *testing.T) { - t.Setenv("HAWK_AUTO_REFRESH_CATALOG", "false") + t.Setenv("GRAYCODE_AUTO_REFRESH_CATALOG", "false") if autoRefreshCatalogEnabled() { t.Fatal("expected disabled") } - t.Setenv("HAWK_AUTO_REFRESH_CATALOG", "") + t.Setenv("GRAYCODE_AUTO_REFRESH_CATALOG", "") if !autoRefreshCatalogEnabled() { t.Fatal("expected enabled by default") } diff --git a/internal/config/config.go b/internal/config/config.go index 31f26f55..63044728 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/intelligence/memory" + "github.com/GrayCodeAI/graycode-cli/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 6b56328d..13577d58 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" - "github.com/GrayCodeAI/hawk/internal/testutil" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/internal/testutil" ) func TestLoadAgentsMD(t *testing.T) { @@ -169,7 +169,7 @@ func TestLoadSettingsUsesUserConfigOnly(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) configDir := filepath.Join(home, "config") - t.Setenv("HAWK_CONFIG_DIR", configDir) + t.Setenv("GRAYCODE_CONFIG_DIR", configDir) t.Setenv("EYRIE_CONFIG_DIR", filepath.Join(home, "eyrie")) if err := os.MkdirAll(configDir, 0o755); err != nil { t.Fatal(err) @@ -183,7 +183,7 @@ func TestLoadSettingsUsesUserConfigOnly(t *testing.T) { 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 hawk settings.json, got %q", settings.Model) + t.Fatalf("model must not remain in graycode 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) } - // Hawk: API keys rejected from settings file + // Graycode: 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,7 +242,7 @@ func TestLoadSettingsPreservesRejectedLegacySelection(t *testing.T) { home := t.TempDir() configDir := filepath.Join(home, "config") t.Setenv("HOME", home) - t.Setenv("HAWK_CONFIG_DIR", configDir) + t.Setenv("GRAYCODE_CONFIG_DIR", configDir) t.Setenv("EYRIE_CONFIG_DIR", filepath.Join(home, "eyrie")) if err := os.MkdirAll(configDir, 0o700); err != nil { t.Fatal(err) diff --git a/internal/config/credentials_store.go b/internal/config/credentials_store.go index f03d7c74..17659779 100644 --- a/internal/config/credentials_store.go +++ b/internal/config/credentials_store.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) // PersistAPIKey saves a provider API key via eyrie (OS secret store). @@ -136,7 +136,7 @@ func ConfiguredCredentialProviders() []string { return configuredCredentialProvidersCached(context.Background()) } -// FormatCredentialCLIStatus returns hawk credentials status output (providers, not raw env names). +// FormatCredentialCLIStatus returns graycode credentials status output (providers, not raw env names). func FormatCredentialCLIStatus(ctx context.Context) string { if ctx == nil { ctx = context.Background() diff --git a/internal/config/credentials_store_test.go b/internal/config/credentials_store_test.go index d13b5b70..2f1067b9 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/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) func TestRemoveStoredCredential_ByProvider(t *testing.T) { diff --git a/internal/config/deployment_status.go b/internal/config/deployment_status.go index 620ae7bb..128a5f48 100644 --- a/internal/config/deployment_status.go +++ b/internal/config/deployment_status.go @@ -10,7 +10,7 @@ func ResolveCanonicalModel(model string) string { return CanonicalModelID(context.Background(), strings.TrimSpace(model)) } -// DeploymentStatusReport returns hawk deployment routing diagnostics. +// DeploymentStatusReport returns graycode deployment routing diagnostics. func DeploymentStatusReport(ctx context.Context, activeModel string) (string, error) { engine, err := newEyrieEngine() if err != nil { diff --git a/internal/config/deployments_ui_test.go b/internal/config/deployments_ui_test.go index 77338e48..c62f7b37 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("HAWK_DEPLOYMENT_ROUTING", "") + t.Setenv("GRAYCODE_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 81557ead..1742d8f9 100644 --- a/internal/config/developer_path.go +++ b/internal/config/developer_path.go @@ -7,14 +7,14 @@ import ( "path/filepath" "strings" - "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/token" - "github.com/GrayCodeAI/hawk/internal/tool" + "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/token" + "github.com/GrayCodeAI/graycode-cli/internal/tool" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) // PathCheckStatus is pass, warn, or fail for one readiness row. @@ -62,7 +62,7 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { checks = append(checks, PathCheck{ Section: "Setup", Name: "credentials", Status: PathFail, Detail: "No provider credentials configured", - FixHint: "Run hawk and /config to paste an API key (or configure Ollama)", + FixHint: "Run graycode and /config to paste an API key (or configure Ollama)", Blocking: true, }) } @@ -93,13 +93,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 hawk models refresh after adding credentials", + FixHint: "Run graycode models refresh after adding credentials", }) default: checks = append(checks, PathCheck{ Section: "Setup", Name: "catalog", Status: PathWarn, Detail: CatalogEmptyHint(ctx), - FixHint: "Add credentials then run hawk models refresh", + FixHint: "Add credentials then run graycode models refresh", }) } @@ -123,7 +123,7 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { checks = append(checks, PathCheck{ Section: "Security", Name: "provider.json", Status: PathFail, Detail: detail, - FixHint: "Run hawk start once (MigrateProviderSecrets) or remove secret fields manually", + FixHint: "Run graycode start once (MigrateProviderSecrets) or remove secret fields manually", Blocking: true, }) } else { @@ -138,20 +138,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 hawk credentials migrate", + FixHint: "Run graycode credentials migrate", }) } else { checks = append(checks, PathCheck{ Section: "Security", Name: "plaintext env", Status: PathPass, - Detail: "No ~/.hawk/env or ~/.hawk/.env files", + Detail: "No ~/.graycode/env or ~/.graycode/.env files", Blocking: true, }) } - hawkDir := home.MustDir() + graycodeDir := home.MustDir() provPath := ProviderStateSecurityStatus().Path if provPath == "" { - provPath = filepath.Join(hawkDir, ".hawk", "provider.json") + provPath = filepath.Join(graycodeDir, ".graycode", "provider.json") } if reason := tool.IsSensitivePath(provPath); reason != "" { checks = append(checks, PathCheck{ @@ -198,7 +198,7 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { } checks = append(checks, PathCheck{ Section: "Ecosystem", Name: "eyrie", Status: status, - Detail: "Preflight not ready — see hawk preflight", + Detail: "Preflight not ready — see graycode preflight", FixHint: "Complete /config (credentials + model)", Blocking: true, }) @@ -219,7 +219,7 @@ func EvaluateDeveloperPath(ctx context.Context) DeveloperPathReport { }) } - sample := token.CountTokensFast("hawk developer path readiness") + sample := token.CountTokensFast("graycode 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), @@ -244,10 +244,10 @@ func anyBlockingFail(checks []PathCheck, section string) bool { func developerPathNextStep(r DeveloperPathReport, setup SetupState) string { if r.Ready { - return "Ready — run hawk and start chatting" + return "Ready — run graycode and start chatting" } if !setup.HasCredentials { - return "Run hawk → /config → paste API key (or Ollama local)" + return "Run graycode → /config → paste API key (or Ollama local)" } if !setup.HasModel { return "Run /config → pick a model from the catalog" @@ -255,14 +255,14 @@ func developerPathNextStep(r DeveloperPathReport, setup SetupState) string { if !r.SecureReady { return "Fix security items above (provider.json secrets, read guard)" } - return "Run hawk preflight for details, then /config if needed" + return "Run graycode preflight for details, then /config if needed" } // FormatDeveloperPathReport renders the developer path readiness report for CLI/TUI. func FormatDeveloperPathReport(ctx context.Context) string { r := EvaluateDeveloperPath(ctx) var b strings.Builder - b.WriteString("Developer path (hawk · eyrie · shrike · harrier)\n\n") + b.WriteString("Developer path (graycode · eyrie · shrike · harrier)\n\n") status := "NEEDS SETUP" switch { @@ -291,7 +291,7 @@ func FormatDeveloperPathReport(ctx context.Context) string { } b.WriteString("Next: " + r.NextStep + "\n") - b.WriteString("\nDocs: docs/DEVELOPER-PATH.md · docs/SECURITY-DEVELOPER.md · hawk doctor · hawk preflight\n") + b.WriteString("\nDocs: docs/DEVELOPER-PATH.md · docs/SECURITY-DEVELOPER.md · graycode doctor · graycode preflight\n") return strings.TrimRight(b.String(), "\n") } @@ -317,12 +317,12 @@ func providerJSONHasSecretsOnDisk() (bool, string) { } func plaintextCredentialFilesPresent() (bool, []string) { - hawkDir := filepath.Join(home.MustDir(), ".hawk") + graycodeDir := filepath.Join(home.MustDir(), ".graycode") var paths []string for _, name := range []string{"env", ".env"} { - p := filepath.Join(hawkDir, name) + p := filepath.Join(graycodeDir, name) if _, err := os.Stat(p); err == nil { - paths = append(paths, "~/.hawk/"+name) + paths = append(paths, "~/.graycode/"+name) } } return len(paths) > 0, paths diff --git a/internal/config/developer_path_test.go b/internal/config/developer_path_test.go index a84c48c6..ad300a14 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/hawk/internal/provider/gateway" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) func TestEvaluateDeveloperPath_FreshInstall(t *testing.T) { diff --git a/internal/config/ecosystem_report.go b/internal/config/ecosystem_report.go index 21b62ae5..61eacb4b 100644 --- a/internal/config/ecosystem_report.go +++ b/internal/config/ecosystem_report.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/hawk/internal/intelligence/memory" - "github.com/GrayCodeAI/hawk/internal/token" + "github.com/GrayCodeAI/graycode-cli/internal/intelligence/memory" + "github.com/GrayCodeAI/graycode-cli/internal/token" ) // EcosystemReport is the structured view of the ecosystem panel. @@ -63,7 +63,7 @@ func BuildEcosystemReport(ctx context.Context, provider, model string) Ecosystem // shrike r.Shrike.Embedded = true - r.Shrike.SampleTokens = token.CountTokensFast("hawk context compression pipeline") + r.Shrike.SampleTokens = token.CountTokensFast("graycode context compression pipeline") return r } @@ -79,7 +79,7 @@ func FormatEcosystemPanel(ctx context.Context, provider, model string) string { if cat.Exists { eyrieLine += fmt.Sprintf("catalog %d models", cat.Models) } else { - eyrieLine += "catalog missing (run hawk models refresh)" + eyrieLine += "catalog missing (run graycode models refresh)" } pre := EnginePreflightReport(ctx) if pre.Ready { @@ -109,7 +109,7 @@ func FormatEcosystemPanel(ctx context.Context, provider, model string) string { } // shrike — token counting and context compression (always embedded) - sample := token.CountTokensFast("hawk context compression pipeline") + sample := token.CountTokensFast("graycode context compression pipeline") b.WriteString(fmt.Sprintf(" shrike: embedded · token/compress pipeline OK (sample=%d tokens)\n", sample)) return strings.TrimRight(b.String(), "\n") diff --git a/internal/config/envmanager.go b/internal/config/envmanager.go index ce3290a7..981ae83d 100644 --- a/internal/config/envmanager.go +++ b/internal/config/envmanager.go @@ -11,7 +11,7 @@ import ( "strings" "sync" - "github.com/GrayCodeAI/hawk/internal/env" + "github.com/GrayCodeAI/graycode-cli/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 3749c519..fe3c9fc4 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["HAWK_MODEL"] = &EnvVar{ - Key: "HAWK_MODEL", + em.Vars["GRAYCODE_MODEL"] = &EnvVar{ + Key: "GRAYCODE_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, "HAWK_MODEL") { - t.Fatal("output should contain HAWK_MODEL") + if !strings.Contains(output, "GRAYCODE_MODEL") { + t.Fatal("output should contain GRAYCODE_MODEL") } if !strings.Contains(output, "claude-sonnet-4-6") { t.Fatal("output should show non-secret value in full") diff --git a/internal/config/eyrie_apply.go b/internal/config/eyrie_apply.go index 6ed88efc..df52c20f 100644 --- a/internal/config/eyrie_apply.go +++ b/internal/config/eyrie_apply.go @@ -5,10 +5,10 @@ import ( "fmt" "time" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) -// ApplyCredentialsResult is Hawk's UI-safe view of an Eyrie catalog/routing +// ApplyCredentialsResult is Graycode'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 { diff --git a/internal/config/eyrie_engine.go b/internal/config/eyrie_engine.go index c480e938..d579a7e2 100644 --- a/internal/config/eyrie_engine.go +++ b/internal/config/eyrie_engine.go @@ -3,7 +3,7 @@ package config import ( "context" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) type ( @@ -19,7 +19,7 @@ func newEyrieEngine() (*gateway.Gateway, error) { func NewEyrieEngine() (*gateway.Gateway, error) { return newEyrieEngine() } // NewEyrieEngineForSettings composes a fresh gateway for one effective -// Hawk settings snapshot. It performs no package-global registration and does +// Graycode settings snapshot. It performs no package-global registration and does // not mutate provider environment variables. func NewEyrieEngineForSettings(settings Settings) (*gateway.Gateway, error) { return gateway.New(context.Background(), gatewayCustomGateways(settings.CustomProviders)) @@ -140,7 +140,7 @@ func EngineDeploymentSummary(ctx context.Context, model string) (gateway.Deploym return gw.DeploymentSummary(ctx, model) } -// newEyrieEngine is Hawk's default composition root for Eyrie's stable host +// newEyrieEngine is Graycode's default composition root for Eyrie's stable host // facade. Command paths that support --settings must use // NewEyrieEngineForSettings instead of relying on this global-settings default. diff --git a/internal/config/eyrie_engine_test.go b/internal/config/eyrie_engine_test.go index 4d7c79b7..809d955b 100644 --- a/internal/config/eyrie_engine_test.go +++ b/internal/config/eyrie_engine_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) func TestNewEyrieEngineForSettingsIsInvocationScoped(t *testing.T) { diff --git a/internal/config/eyrie_selection.go b/internal/config/eyrie_selection.go index 3cf28054..c7a99f84 100644 --- a/internal/config/eyrie_selection.go +++ b/internal/config/eyrie_selection.go @@ -4,7 +4,7 @@ import ( "context" "strings" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) type ( @@ -30,7 +30,7 @@ func EffectiveSelectionWithSettings(ctx context.Context, settings Settings, opts return engine.EffectiveSelection(ctx, opts) } -// ActiveModel returns the selected model from eyrie provider.json (not hawk settings). +// ActiveModel returns the selected model from eyrie provider.json (not graycode settings). func ActiveModel(ctx context.Context) string { if ctx == nil { ctx = context.Background() @@ -97,7 +97,7 @@ func SetActiveSelection(ctx context.Context, provider, modelID string) error { return engine.SetSelection(ctx, provider, modelID) } -// migrateStoredModelProvider moves model/provider from ~/.hawk/settings.json into eyrie once. +// migrateStoredModelProvider moves model/provider from ~/.graycode/settings.json into eyrie once. func migrateStoredModelProvider(s *Settings) { if s == nil { return diff --git a/internal/config/main_test.go b/internal/config/main_test.go index e3567512..ec4fab17 100644 --- a/internal/config/main_test.go +++ b/internal/config/main_test.go @@ -4,8 +4,8 @@ import ( "os" "testing" - "github.com/GrayCodeAI/hawk/internal/catalogtest" - "github.com/GrayCodeAI/hawk/internal/testutil" + "github.com/GrayCodeAI/graycode-cli/internal/catalogtest" + "github.com/GrayCodeAI/graycode-cli/internal/testutil" ) func TestMain(m *testing.M) { diff --git a/internal/config/milestone_verify_test.go b/internal/config/milestone_verify_test.go index 7fbb1767..3eeb12a6 100644 --- a/internal/config/milestone_verify_test.go +++ b/internal/config/milestone_verify_test.go @@ -13,18 +13,18 @@ import ( "github.com/GrayCodeAI/eyrie/credentials" ) -// isolateMilestoneTest uses a temp HOME and HAWK_CONFIG_DIR so verification does not touch the user machine. +// isolateMilestoneTest uses a temp HOME and GRAYCODE_CONFIG_DIR so verification does not touch the user machine. func isolateMilestoneTest(t *testing.T) string { t.Helper() home := t.TempDir() - hawkDir := filepath.Join(home, ".hawk") - if err := os.MkdirAll(hawkDir, 0o700); err != nil { + graycodeDir := filepath.Join(home, ".graycode") + if err := os.MkdirAll(graycodeDir, 0o700); err != nil { t.Fatal(err) } t.Setenv("HOME", home) - t.Setenv("HAWK_CONFIG_DIR", hawkDir) - t.Setenv("EYRIE_CONFIG_DIR", hawkDir) - return hawkDir + t.Setenv("GRAYCODE_CONFIG_DIR", graycodeDir) + t.Setenv("EYRIE_CONFIG_DIR", graycodeDir) + return graycodeDir } func TestVerify_ProviderJSONOnDiskHasNoSecrets(t *testing.T) { @@ -46,11 +46,11 @@ func TestVerify_ProviderJSONOnDiskHasNoSecrets(t *testing.T) { } func TestVerify_MigrateProviderSecretsStripsDisk(t *testing.T) { - hawkDir := isolateMilestoneTest(t) + graycodeDir := isolateMilestoneTest(t) store := &credentials.MapStore{} credentials.SetDefaultStore(store) t.Cleanup(func() { credentials.SetDefaultStore(nil) }) - path := filepath.Join(hawkDir, "provider.json") + path := filepath.Join(graycodeDir, "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) { - hawkDir := isolateMilestoneTest(t) + graycodeDir := 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(hawkDir, "provider.json") + path := filepath.Join(graycodeDir, "provider.json") if _, err := os.Stat(path); err == nil { data, _ := os.ReadFile(path) if strings.Contains(string(data), secret) { @@ -137,7 +137,7 @@ func TestVerify_EvaluateSetupFlow(t *testing.T) { t.Fatal("expected setup still needed until model selected") } - providerPath := filepath.Join(os.Getenv("HOME"), ".hawk", "provider.json") + providerPath := filepath.Join(os.Getenv("HOME"), ".graycode", "provider.json") cfg := &eyriecfg.ProviderConfig{ ActiveProvider: "anthropic", ActiveModel: "claude-sonnet-4-20250514", diff --git a/internal/config/security_test.go b/internal/config/security_test.go index 4fa8d21e..53f264ec 100644 --- a/internal/config/security_test.go +++ b/internal/config/security_test.go @@ -87,7 +87,7 @@ 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("HAWK_CONFIG_DIR", dir) + t.Setenv("GRAYCODE_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 d9ab5dcc..6c44df79 100644 --- a/internal/config/settings.go +++ b/internal/config/settings.go @@ -13,27 +13,27 @@ import ( "sync" "time" - "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/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/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func fetchModelsViaRuntime(ctx context.Context, provider string) ([]EngineModel, error) { return ListEngineModels(ctx, provider, false) } -// Settings holds hawk configuration. -// Hawk: no API keys stored here. Secrets come from the OS secret store via eyrie. +// Settings holds graycode configuration. +// Graycode: 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 eyrie provider.json. - // Hawk does not persist model/provider here; use SetActiveModel / SetActiveProvider. + // Graycode 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 hawk identifies itself in git commits. +// Attribution controls how graycode 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 hawk global settings path from internal storage config, not external input + data, err := os.ReadFile(path) // #nosec G304 -- path is the graycode 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 Hawk's user config settings.json. +// LoadGlobalSettings loads only Graycode'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 .hawk/settings.json. +// directory looking for .graycode/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 .hawk/settings.json file. Returns nil if none is found. The nearest +// a .graycode/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, ".hawk", "settings.json") + candidate := filepath.Join(dir, ".graycode", "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) - // Hawk: API key status comes from OS secret store, not settings file + // Graycode: 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 Hawk user config. -// Hawk: API keys are NOT stored in settings.json. Use /config and the OS secret store. +// 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. func SetGlobalSetting(key, value string) error { s := LoadGlobalSettings() normalized := normalizeSettingKey(key) - // Hawk: reject API key persistence to disk + // Graycode: 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 } // ───────────────────────────────────────────────────────────── -// Hawk: API keys from OS secret store only (no .env) +// Graycode: API keys from OS secret store only (no .env) // ───────────────────────────────────────────────────────────── // ProviderAPIKeyEnv returns the API key env var for a provider (registry first for setup gateways). @@ -758,7 +758,7 @@ func providerCredentialEnvAliases(provider string) []string { // Live model catalog fetch from eyrie // ───────────────────────────────────────────────────────────── -// FetchModelsForProvider returns models from the eyrie catalog (dynamic; no hawk hardcoded lists). +// FetchModelsForProvider returns models from the eyrie catalog (dynamic; no graycode 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 hawk catalog data. + // Custom OpenAI-compatible providers: single model from settings, not graycode 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 eyrie catalog (check API keys; hawk will refresh automatically on next start)", provider) + return nil, fmt.Errorf("no models found for provider %s in eyrie catalog (check API keys; graycode will refresh automatically on next start)", provider) } // FetchModelsForProviderWithSettings resolves cached models using one diff --git a/internal/config/settings_status_test.go b/internal/config/settings_status_test.go index 2fc290e6..7523a18e 100644 --- a/internal/config/settings_status_test.go +++ b/internal/config/settings_status_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) func TestEnvKeyStatusUsesEyrieCredentialStatus(t *testing.T) { diff --git a/internal/config/settings_test.go b/internal/config/settings_test.go index 99c5601e..280988b7 100644 --- a/internal/config/settings_test.go +++ b/internal/config/settings_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/provider/routing" + "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" ) func TestMergeSettings_ModelOverride(t *testing.T) { diff --git a/internal/config/setup_status_test.go b/internal/config/setup_status_test.go index 3a5223c4..916ba342 100644 --- a/internal/config/setup_status_test.go +++ b/internal/config/setup_status_test.go @@ -7,7 +7,7 @@ import ( "github.com/GrayCodeAI/eyrie/catalog" "github.com/GrayCodeAI/eyrie/credentials" - "github.com/GrayCodeAI/hawk/internal/testutil" + "github.com/GrayCodeAI/graycode-cli/internal/testutil" ) func TestHasConfiguredDeployment_FromStore(t *testing.T) { diff --git a/internal/config/validator.go b/internal/config/validator.go index 696d36e3..2829cde0 100644 --- a/internal/config/validator.go +++ b/internal/config/validator.go @@ -62,7 +62,7 @@ func ValidateSettings(s Settings) ValidationResult { if activeProvider == "" { activeProvider = ActiveProvider(context.Background()) } - // Hawk: validate API key is in the OS secret store (not in settings) + // Graycode: 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 ab281a32..32850017 100644 --- a/internal/config/validator_test.go +++ b/internal/config/validator_test.go @@ -5,7 +5,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) func TestValidateSettingsValid(t *testing.T) { diff --git a/internal/config/xiaomi_setup.go b/internal/config/xiaomi_setup.go index 23e24064..e85c18e9 100644 --- a/internal/config/xiaomi_setup.go +++ b/internal/config/xiaomi_setup.go @@ -1,19 +1,3 @@ package config const ProviderXiaomiTokenPlan = "xiaomi_mimo_token_plan" // #nosec G101 -- provider ID string, not a credential - -// NeedsXiaomiTokenPlanRegion reports whether the Token Plan gateway still needs a cluster pick. -func NeedsXiaomiTokenPlanRegion(providerID string) bool { - return NeedsGatewayRegion(providerID) -} - -// SetXiaomiTokenPlanRegion persists region (cn, sgp, ams). Eyrie reads the -// provider state directly when probing; Hawk does not mutate process env. -func SetXiaomiTokenPlanRegion(region string) error { - return SetGatewayRegion(ProviderXiaomiTokenPlan, region) -} - -// XiaomiTokenPlanRegionLabel returns the saved cluster id for UI (cn, sgp, ams) or "" if unset. -func XiaomiTokenPlanRegionLabel() string { - return GatewayRegionLabel(ProviderXiaomiTokenPlan) -} diff --git a/internal/config/xiaomi_setup_test.go b/internal/config/xiaomi_setup_test.go index b0b83bfd..21cac067 100644 --- a/internal/config/xiaomi_setup_test.go +++ b/internal/config/xiaomi_setup_test.go @@ -7,10 +7,10 @@ import ( eyriecfg "github.com/GrayCodeAI/eyrie/config" ) -func TestSetXiaomiTokenPlanRegion_ClearsStaleBaseHost(t *testing.T) { +func TestSetGatewayRegion_XiaomiClearsStaleBaseHost(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", t.TempDir()) - t.Setenv("HAWK_CONFIG_DIR", dir) + t.Setenv("GRAYCODE_CONFIG_DIR", dir) t.Setenv("EYRIE_CONFIG_DIR", dir) t.Setenv("XIAOMI_MIMO_TOKEN_PLAN_BASE_URL", "https://caller-owned.example.test/v1") cfg := &eyriecfg.ProviderConfig{ @@ -21,7 +21,7 @@ func TestSetXiaomiTokenPlanRegion_ClearsStaleBaseHost(t *testing.T) { if err := eyriecfg.SaveProviderConfig(cfg, ""); err != nil { t.Fatal(err) } - if err := SetXiaomiTokenPlanRegion("sgp"); err != nil { + if err := SetGatewayRegion(ProviderXiaomiTokenPlan, "sgp"); err != nil { t.Fatal(err) } loaded := eyriecfg.LoadProviderConfig("") @@ -29,31 +29,27 @@ func TestSetXiaomiTokenPlanRegion_ClearsStaleBaseHost(t *testing.T) { t.Fatalf("region = %q", loaded.XiaomiMimoTokenPlanRegion) } if got := os.Getenv("XIAOMI_MIMO_TOKEN_PLAN_BASE_URL"); got != "https://caller-owned.example.test/v1" { - t.Fatalf("SetXiaomiTokenPlanRegion mutated process env: %q", got) + t.Fatalf("SetGatewayRegion mutated process env: %q", got) } - // want := "https://token-plan-sgp.xiaomimimo.com/v1" - // if loaded.XiaomiMimoTokenPlanBaseURL != want { - // t.Fatalf("base = %q, want %s", loaded.XiaomiMimoTokenPlanBaseURL, want) - // } } -func TestNeedsXiaomiTokenPlanRegion_InvalidAndMissing(t *testing.T) { +func TestNeedsGatewayRegion_XiaomiInvalidAndMissing(t *testing.T) { dir := t.TempDir() t.Setenv("HOME", t.TempDir()) - t.Setenv("HAWK_CONFIG_DIR", dir) + t.Setenv("GRAYCODE_CONFIG_DIR", dir) t.Setenv("EYRIE_CONFIG_DIR", dir) - if !NeedsXiaomiTokenPlanRegion(ProviderXiaomiTokenPlan) { + if !NeedsGatewayRegion(ProviderXiaomiTokenPlan) { t.Fatal("expected true when no config file") } if err := eyriecfg.SaveProviderConfig(&eyriecfg.ProviderConfig{Version: "1", XiaomiMimoTokenPlanRegion: "tokyo"}, ""); err != nil { t.Fatal(err) } - if !NeedsXiaomiTokenPlanRegion(ProviderXiaomiTokenPlan) { + if !NeedsGatewayRegion(ProviderXiaomiTokenPlan) { t.Fatal("expected true for invalid region") } - _ = SetXiaomiTokenPlanRegion("cn") - if NeedsXiaomiTokenPlanRegion(ProviderXiaomiTokenPlan) { + _ = SetGatewayRegion(ProviderXiaomiTokenPlan, "cn") + if NeedsGatewayRegion(ProviderXiaomiTokenPlan) { t.Fatal("expected false after valid region set") } } diff --git a/internal/config/zai_setup.go b/internal/config/zai_setup.go index 17df0053..0b77dc4b 100644 --- a/internal/config/zai_setup.go +++ b/internal/config/zai_setup.go @@ -4,19 +4,3 @@ const ( ProviderZAIPayg = "zai_payg" ProviderZAICoding = "zai_coding" ) - -// NeedsZAIRegion reports whether the Z.AI gateway still needs a region pick for the chosen plan. -func NeedsZAIRegion(providerID string) bool { - return NeedsGatewayRegion(providerID) -} - -// SetZAIRegion persists the region (international or cn) for the given Z.AI -// gateway. Eyrie reads provider state directly without process-env mutation. -func SetZAIRegion(providerID, region string) error { - return SetGatewayRegion(providerID, region) -} - -// ZAIRegionLabel returns the saved region label or "". -func ZAIRegionLabel(providerID string) string { - return GatewayRegionLabel(providerID) -} diff --git a/internal/container/lifecycle.go b/internal/container/lifecycle.go index cb2d31de..10004385 100644 --- a/internal/container/lifecycle.go +++ b/internal/container/lifecycle.go @@ -1,4 +1,4 @@ -// Package container provides Docker container lifecycle management for hawk's +// Package container provides Docker container lifecycle management for graycode'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 4638166f..f19e4ecc 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 hawk's context layer. It builds an import/refer +// repository overview for graycode'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 // -// hawk ships a second package, internal/intelligence/repomap, that exposes +// graycode 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 1f9411f1..ff217d58 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": {}, - ".hawk": {}, + ".graycode": {}, "dist": {}, "build": {}, "target": {}, diff --git a/internal/context/rules.go b/internal/context/rules.go index 344115ab..e020fe10 100644 --- a/internal/context/rules.go +++ b/internal/context/rules.go @@ -10,8 +10,8 @@ import ( "sort" "strings" - homepkg "github.com/GrayCodeAI/hawk/internal/home" - "github.com/GrayCodeAI/hawk/internal/storage" + homepkg "github.com/GrayCodeAI/graycode-cli/internal/home" + "github.com/GrayCodeAI/graycode-cli/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/HawkCode/HAWK.md"} + return []string{"/Library/Application Support/Graycode/GRAYCODE.md"} default: // Linux and other unix-like systems. - return []string{"/etc/hawk-code/HAWK.md"} + return []string{"/etc/graycode/GRAYCODE.md"} } } @@ -55,7 +55,7 @@ var DefaultRuleSources = []RuleSource{ {".cursor/rules", 4, true}, {".github/instructions", 5, true}, {"AGENTS.md", 10, false}, - {"HAWK.md", 11, false}, + {"GRAYCODE.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 062fa2a7..1f745302 100644 --- a/internal/context/rules_test.go +++ b/internal/context/rules_test.go @@ -135,7 +135,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("# Hawk"), 0o644) + os.WriteFile(filepath.Join(dir, ".agents", "rules", "a.md"), []byte("# Graycode"), 0o644) os.WriteFile(filepath.Join(dir, ".claude", "rules", "b.md"), []byte("# Claude"), 0o644) sub := filepath.Join(dir, "src") os.MkdirAll(sub, 0o755) @@ -146,16 +146,16 @@ func TestRuleDiscoverer_SourcePriority(t *testing.T) { rules := rd.Discover(target) // .agents/rules (priority 1) should come before .claude/rules (priority 3) - var hawkIdx, claudeIdx int + var graycodeIdx, claudeIdx int for i, r := range rules { if r.Source == ".agents/rules" { - hawkIdx = i + graycodeIdx = i } if r.Source == ".claude/rules" { claudeIdx = i } } - if hawkIdx >= claudeIdx { + if graycodeIdx >= claudeIdx { t.Error(".agents/rules should have higher precedence than .claude/rules") } } @@ -175,12 +175,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, "HAWK.md"), []byte("# Project Policy"), 0o644) + os.WriteFile(filepath.Join(dir, "GRAYCODE.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-HAWK.md") + managed := filepath.Join(dir, "managed-GRAYCODE.md") os.WriteFile(managed, []byte("# Org Policy"), 0o644) rd := NewRuleDiscoverer(dir) @@ -196,17 +196,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 HAWK.md regardless of project precedence. + // Managed must outrank the project GRAYCODE.md regardless of project precedence. for i, r := range rules { - if r.Source == "HAWK.md" && i == 0 { - t.Error("project HAWK.md should not outrank managed tier") + if r.Source == "GRAYCODE.md" && i == 0 { + t.Error("project GRAYCODE.md should not outrank managed tier") } } } func TestRuleDiscoverer_ManagedTierMissing(t *testing.T) { dir := t.TempDir() - os.WriteFile(filepath.Join(dir, "HAWK.md"), []byte("# Project"), 0o644) + os.WriteFile(filepath.Join(dir, "GRAYCODE.md"), []byte("# Project"), 0o644) target := filepath.Join(dir, "main.go") os.WriteFile(target, []byte("package main"), 0o644) @@ -244,7 +244,7 @@ func TestStripHTMLComments(t *testing.T) { func TestRuleDiscoverer_StripsHTMLCommentsOnLoad(t *testing.T) { dir := t.TempDir() - os.WriteFile(filepath.Join(dir, "HAWK.md"), []byte("# Rules\n\nUse tabs."), 0o644) + os.WriteFile(filepath.Join(dir, "GRAYCODE.md"), []byte("# Rules\n\nUse tabs."), 0o644) target := filepath.Join(dir, "main.go") os.WriteFile(target, []byte("package main"), 0o644) @@ -253,7 +253,7 @@ func TestRuleDiscoverer_StripsHTMLCommentsOnLoad(t *testing.T) { found := false for _, r := range rules { - if r.Source == "HAWK.md" { + if r.Source == "GRAYCODE.md" { found = true if strings.Contains(r.Content, "internal note") { t.Errorf("HTML comment not stripped from loaded content: %q", r.Content) @@ -264,7 +264,7 @@ func TestRuleDiscoverer_StripsHTMLCommentsOnLoad(t *testing.T) { } } if !found { - t.Fatal("HAWK.md rule not loaded") + t.Fatal("GRAYCODE.md rule not loaded") } } diff --git a/internal/context/walkup.go b/internal/context/walkup.go index 8838ca99..b6663829 100644 --- a/internal/context/walkup.go +++ b/internal/context/walkup.go @@ -1,4 +1,4 @@ -// Package context provides hierarchical context discovery for hawk. +// Package context provides hierarchical context discovery for graycode. // 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/hawk/internal/hooks" + "github.com/GrayCodeAI/graycode-cli/internal/hooks" ) const ( @@ -23,7 +23,7 @@ const ( // DefaultConventionFiles lists the files to discover at each directory level. var DefaultConventionFiles = []string{ - "AGENTS.md", "HAWK.md", "CLAUDE.md", "CONTEXT.md", + "AGENTS.md", "GRAYCODE.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 f8c4a09b..b9086cab 100644 --- a/internal/context/walkup_test.go +++ b/internal/context/walkup_test.go @@ -5,7 +5,7 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/hawk/internal/hooks" + "github.com/GrayCodeAI/graycode-cli/internal/hooks" ) func TestWalkUpDiscoverer_FindsAgentsMd(t *testing.T) { diff --git a/internal/contracts/agent/hooks.go b/internal/contracts/agent/hooks.go new file mode 100644 index 00000000..ff23bdc4 --- /dev/null +++ b/internal/contracts/agent/hooks.go @@ -0,0 +1,70 @@ +// 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. +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, +// and SDKs agree on wire/event vocabulary without importing engines. +const ( + HookPreToolUse = "PreToolUse" + HookPostToolUse = "PostToolUse" + HookUserPromptSubmit = "UserPromptSubmit" + HookSessionStart = "SessionStart" + HookSessionEnd = "SessionEnd" + HookStop = "Stop" + HookSubagentStart = "SubagentStart" + HookSubagentStop = "SubagentStop" + HookNotification = "Notification" + HookPermissionRequest = "PermissionRequest" + HookPreCompact = "PreCompact" + HookFailure = "Failure" +) + +// VendorHookAliases maps common third-party hook names to canonical Graycode names. +// Keys are lower-case for case-insensitive lookup. +var VendorHookAliases = map[string]string{ + "pretooluse": HookPreToolUse, + "pre_tool_use": HookPreToolUse, + "posttooluse": HookPostToolUse, + "post_tool_use": HookPostToolUse, + "userpromptsubmit": HookUserPromptSubmit, + "user_prompt_submit": HookUserPromptSubmit, + "sessionstart": HookSessionStart, + "session_start": HookSessionStart, + "sessionend": HookSessionEnd, + "session_end": HookSessionEnd, + "stop": HookStop, + "subagentstart": HookSubagentStart, + "subagent_start": HookSubagentStart, + "subagentstop": HookSubagentStop, + "subagent_stop": HookSubagentStop, + "notification": HookNotification, + "permissionrequest": HookPermissionRequest, + "permission_request": HookPermissionRequest, + "precompact": HookPreCompact, + "pre_compact": HookPreCompact, + "failure": HookFailure, + "onerror": HookFailure, + "on_error": HookFailure, +} + +// CanonicalHookEvent returns the Graycode canonical event name for s, or "" if unknown. +func CanonicalHookEvent(s string) string { + if s == "" { + return "" + } + switch s { + case HookPreToolUse, HookPostToolUse, HookUserPromptSubmit, HookSessionStart, + HookSessionEnd, HookStop, HookSubagentStart, HookSubagentStop, + HookNotification, HookPermissionRequest, HookPreCompact, HookFailure: + return s + } + if c, ok := VendorHookAliases[strings.ToLower(strings.TrimSpace(s))]; ok { + return c + } + return "" +} diff --git a/internal/contracts/agent/spawn.go b/internal/contracts/agent/spawn.go new file mode 100644 index 00000000..47ba0d88 --- /dev/null +++ b/internal/contracts/agent/spawn.go @@ -0,0 +1,240 @@ +// 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. +// Package agent defines shared DTOs for typed subagent spawn across graycode-eco. +// +// Stdlib only. No engine, CLI, or storage imports. +package agent + +import ( + "fmt" + "strings" +) + +// CapabilityMode limits what tools a subagent may use. +type CapabilityMode string + +const ( + CapReadOnly CapabilityMode = "read-only" + CapReadWrite CapabilityMode = "read-write" + CapExecute CapabilityMode = "execute" + CapAll CapabilityMode = "all" +) + +// IsolationMode selects filesystem isolation for a subagent. +type IsolationMode string + +const ( + IsoNone IsolationMode = "none" + IsoWorktree IsolationMode = "worktree" +) + +// SubagentType selects the built-in subagent profile. +type SubagentType string + +const ( + TypeGeneralPurpose SubagentType = "general-purpose" + TypeExplore SubagentType = "explore" + TypePlan SubagentType = "plan" +) + +// Thoroughness levels for explore subagents. +const ( + ThoroughnessQuick = "quick" + ThoroughnessMedium = "medium" + ThoroughnessVeryThorough = "very-thorough" +) + +// Spawn status values for SpawnResult.Status. +const ( + StatusRunning = "running" + StatusCompleted = "completed" + StatusFailed = "failed" +) + +// SpawnRequest is the cross-repo contract for spawning a subagent. +type SpawnRequest struct { + Prompt string `json:"prompt"` + Description string `json:"description,omitempty"` + SubagentType string `json:"subagent_type,omitempty"` + CapabilityMode string `json:"capability_mode,omitempty"` + Isolation string `json:"isolation,omitempty"` + ResumeFrom string `json:"resume_from,omitempty"` + CWD string `json:"cwd,omitempty"` + Model string `json:"model,omitempty"` + Background bool `json:"background,omitempty"` + Thoroughness string `json:"thoroughness,omitempty"` + ParentSession string `json:"parent_session,omitempty"` +} + +// SpawnResult is the cross-repo contract returned after spawn completes or is accepted. +type SpawnResult struct { + SubagentID string `json:"subagent_id,omitempty"` + SubagentType string `json:"subagent_type,omitempty"` + Status string `json:"status,omitempty"` + Output string `json:"output,omitempty"` + Summary string `json:"summary,omitempty"` + ToolCalls int `json:"tool_calls,omitempty"` + Turns int `json:"turns,omitempty"` + DurationMs int64 `json:"duration_ms,omitempty"` + WorktreePath string `json:"worktree_path,omitempty"` + Persona string `json:"persona,omitempty"` + Error string `json:"error,omitempty"` +} + +// ParseSubagentType normalizes aliases (e.g. "general" → general-purpose). +// Empty input defaults to explore (conservative read-oriented default). +func ParseSubagentType(s string) (SubagentType, error) { + switch strings.ToLower(strings.TrimSpace(s)) { + case "", "explore": + return TypeExplore, nil + case "plan": + return TypePlan, nil + case "general", "general-purpose", "general_purpose", "generalpurpose": + return TypeGeneralPurpose, nil + default: + return "", fmt.Errorf("agent: unknown subagent_type %q", s) + } +} + +// ParseCapabilityMode normalizes capability aliases. +// Empty input returns CapReadOnly when defaultFromType is empty; callers +// should prefer DefaultCapabilityForType. +func ParseCapabilityMode(s string) (CapabilityMode, error) { + switch strings.ToLower(strings.TrimSpace(s)) { + case "", "read-only", "readonly", "read_only", "ro": + return CapReadOnly, nil + case "read-write", "readwrite", "read_write", "rw": + return CapReadWrite, nil + case "execute", "exec": + return CapExecute, nil + case "all", "full": + return CapAll, nil + default: + return "", fmt.Errorf("agent: unknown capability_mode %q", s) + } +} + +// ParseIsolationMode normalizes isolation aliases. Empty → none. +func ParseIsolationMode(s string) (IsolationMode, error) { + switch strings.ToLower(strings.TrimSpace(s)) { + case "", "none", "off", "false": + return IsoNone, nil + case "worktree", "wt", "git-worktree": + return IsoWorktree, nil + default: + return "", fmt.Errorf("agent: unknown isolation %q", s) + } +} + +// ParseThoroughness normalizes explore thoroughness. Empty → medium. +func ParseThoroughness(s string) (string, error) { + switch strings.ToLower(strings.TrimSpace(s)) { + case "", "medium", "med", "default": + return ThoroughnessMedium, nil + case "quick", "fast": + return ThoroughnessQuick, nil + case "very-thorough", "very_thorough", "verythorough", "deep": + return ThoroughnessVeryThorough, nil + default: + return "", fmt.Errorf("agent: unknown thoroughness %q", s) + } +} + +// DefaultCapabilityForType returns the capability implied by a subagent type +// when the request does not set capability_mode. +func DefaultCapabilityForType(t SubagentType) CapabilityMode { + switch t { + case TypeGeneralPurpose: + return CapAll + case TypePlan, TypeExplore: + return CapReadOnly + default: + return CapReadOnly + } +} + +// Normalized is a validated, alias-resolved spawn request. +type Normalized struct { + Prompt string + Description string + SubagentType SubagentType + CapabilityMode CapabilityMode + Isolation IsolationMode + ResumeFrom string + CWD string + Model string + Background bool + Thoroughness string + ParentSession string +} + +// Normalize validates and resolves aliases on r. +// +// Rules: +// - prompt is required unless resume_from is set +// - cwd and isolation=worktree are mutually exclusive +// - thoroughness only applies to explore (ignored otherwise after parse) +// - empty capability_mode uses DefaultCapabilityForType +func (r SpawnRequest) Normalize() (Normalized, error) { + prompt := strings.TrimSpace(r.Prompt) + resume := strings.TrimSpace(r.ResumeFrom) + if prompt == "" && resume == "" { + return Normalized{}, fmt.Errorf("agent: prompt is required unless resume_from is set") + } + + st, err := ParseSubagentType(r.SubagentType) + if err != nil { + return Normalized{}, err + } + + var capMode CapabilityMode + if strings.TrimSpace(r.CapabilityMode) == "" { + capMode = DefaultCapabilityForType(st) + } else { + capMode, err = ParseCapabilityMode(r.CapabilityMode) + if err != nil { + return Normalized{}, err + } + } + + iso, err := ParseIsolationMode(r.Isolation) + if err != nil { + return Normalized{}, err + } + + cwd := strings.TrimSpace(r.CWD) + if cwd != "" && iso == IsoWorktree { + return Normalized{}, fmt.Errorf("agent: cwd and isolation=worktree are mutually exclusive") + } + + thorough := ThoroughnessMedium + if st == TypeExplore { + thorough, err = ParseThoroughness(r.Thoroughness) + if err != nil { + return Normalized{}, err + } + } else if strings.TrimSpace(r.Thoroughness) != "" { + // Explicit thoroughness on non-explore is an error to catch model mistakes. + return Normalized{}, fmt.Errorf("agent: thoroughness is only valid for explore subagents") + } + + return Normalized{ + Prompt: prompt, + Description: strings.TrimSpace(r.Description), + SubagentType: st, + CapabilityMode: capMode, + Isolation: iso, + ResumeFrom: resume, + CWD: cwd, + Model: strings.TrimSpace(r.Model), + Background: r.Background, + Thoroughness: thorough, + ParentSession: strings.TrimSpace(r.ParentSession), + }, nil +} + +// Validate is an alias for Normalize when only the error is needed. +func (r SpawnRequest) Validate() error { + _, err := r.Normalize() + return err +} diff --git a/internal/contracts/events/events.go b/internal/contracts/events/events.go new file mode 100644 index 00000000..bb3599c2 --- /dev/null +++ b/internal/contracts/events/events.go @@ -0,0 +1,36 @@ +// 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. +package events + +import "time" + +// ToolEvent represents a normalized tool event emitted by Graycode workflows. +type ToolEvent struct { + ToolName string `json:"tool_name"` + ToolInput map[string]interface{} `json:"tool_input,omitempty"` + CWD string `json:"cwd,omitempty"` + Timestamp time.Time `json:"timestamp"` + SessionID string `json:"session_id,omitempty"` + Transcript string `json:"transcript,omitempty"` +} + +// TraceEvent represents a normalized trace record for model/runtime activity. +type TraceEvent struct { + ID string `json:"id"` + Name string `json:"name"` + Input string `json:"input,omitempty"` + Output string `json:"output,omitempty"` + Model string `json:"model,omitempty"` + StartTime time.Time `json:"start_time"` + EndTime time.Time `json:"end_time,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` + Usage *UsageInfo `json:"usage,omitempty"` +} + +// UsageInfo captures token and cost information for a trace event. +type UsageInfo struct { + PromptTokens int `json:"prompt_tokens"` + CompletionTokens int `json:"completion_tokens"` + TotalTokens int `json:"total_tokens"` + CostUSD float64 `json:"cost_usd,omitempty"` +} diff --git a/internal/contracts/graph/graph.go b/internal/contracts/graph/graph.go new file mode 100644 index 00000000..826c5eaf --- /dev/null +++ b/internal/contracts/graph/graph.go @@ -0,0 +1,338 @@ +// 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. +// Package graph defines the portable graph vocabulary shared across graycode-eco. +// +// The package contains data contracts only. Individual repositories retain +// ownership of their graph storage, projections, and runtime behavior. +// +// Graph Engineering Patterns: +// This package implements the foundational patterns from LangGraph, +// Microsoft AutoGen, and Google ADK for multi-agent orchestration as +// nodes, edges, and shared state. +package graph + +import ( + "fmt" + "strings" + "time" +) + +// NodeType represents the specific type/behavior of a node in the graph +type NodeType string + +const ( + // NodeTypeAgent represents an autonomous agent that can make decisions + NodeTypeAgent NodeType = "agent" + // NodeTypeTool represents a tool/function that agents can invoke + NodeTypeTool NodeType = "tool" + // NodeTypeFunction represents a function node (deterministic operation) + NodeTypeFunction NodeType = "function" + // NodeTypeStart represents the entry point of a graph + NodeTypeStart NodeType = "start" + // NodeTypeEnd represents the termination point of a graph + NodeTypeEnd NodeType = "end" + // NodeTypeRouter represents a conditional routing node + NodeTypeRouter NodeType = "router" + // NodeTypeQuality represents a code quality analysis node + NodeTypeQuality NodeType = "quality" + // NodeTypeExecution represents an execution journal node + NodeTypeExecution NodeType = "execution" + // NodeTypeOperations represents an operations orchestration node + NodeTypeOperations NodeType = "operations" + // NodeTypeSystem is an alias for NodeKindSystem for backward compatibility + NodeTypeSystem NodeType = "system" +) + +// NodeKind classifies a node by the ecosystem view to which it belongs. +type NodeKind string + +const ( + NodeSystem NodeKind = "system" + NodeKnowledge NodeKind = "knowledge" + NodeExecution NodeKind = "execution" + NodePolicy NodeKind = "policy" + NodeQuality NodeKind = "quality" + NodeOperations NodeKind = "operations" +) + +// ParseNodeKind normalizes a graph node kind. +func ParseNodeKind(s string) (NodeKind, error) { + switch NodeKind(strings.ToLower(strings.TrimSpace(s))) { + case NodeSystem, NodeKnowledge, NodeExecution, NodePolicy, NodeQuality, NodeOperations: + return NodeKind(strings.ToLower(strings.TrimSpace(s))), nil + default: + return "", fmt.Errorf("graph: unknown node kind %q", s) + } +} + +// ParseNodeType normalizes a graph node type. +func ParseNodeType(s string) (NodeType, error) { + switch NodeType(strings.ToLower(strings.TrimSpace(s))) { + case NodeTypeAgent, NodeTypeTool, NodeTypeFunction, NodeTypeStart, NodeTypeEnd, NodeTypeRouter, NodeTypeQuality, NodeTypeExecution, NodeTypeOperations: + return NodeType(strings.ToLower(strings.TrimSpace(s))), nil + default: + return "", fmt.Errorf("graph: unknown node type %q", s) + } +} + +// NodeSpec describes a node in an orchestration graph. +type NodeSpec struct { + ID string `json:"id"` + Type NodeType `json:"type"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Config map[string]string `json:"config,omitempty"` +} + +// EdgeKind identifies a portable relationship between two graph nodes. +type EdgeKind string + +const ( + EdgeContains EdgeKind = "contains" + EdgeDependsOn EdgeKind = "depends_on" + EdgeReferences EdgeKind = "references" + EdgeProduced EdgeKind = "produced" + EdgeGovernedBy EdgeKind = "governed_by" + EdgeValidatedBy EdgeKind = "validated_by" +) + +// EdgeCondition represents a conditional edge in orchestration graphs. +// When non-empty, the edge is only followed if the condition evaluates to true. +type EdgeCondition struct { + Expression string `json:"expression,omitempty"` + Variables map[string]string `json:"variables,omitempty"` +} + +// EdgeSpec describes an edge in an orchestration graph. +type EdgeSpec struct { + From string `json:"from"` + To string `json:"to"` + Condition *EdgeCondition `json:"condition,omitempty"` + Weight float64 `json:"weight,omitempty"` +} + +// GraphSpec describes a complete orchestration graph. +type GraphSpec struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + Nodes []NodeSpec `json:"nodes"` + Edges []EdgeSpec `json:"edges"` + Metadata map[string]string `json:"metadata,omitempty"` +} + +// ParseEdgeKind normalizes a graph edge kind. +func ParseEdgeKind(s string) (EdgeKind, error) { + switch EdgeKind(strings.ToLower(strings.TrimSpace(s))) { + case EdgeContains, EdgeDependsOn, EdgeReferences, EdgeProduced, EdgeGovernedBy, EdgeValidatedBy: + return EdgeKind(strings.ToLower(strings.TrimSpace(s))), nil + default: + return "", fmt.Errorf("graph: unknown edge kind %q", s) + } +} + +// EventType identifies a lifecycle event for a graph subject. +type EventType string + +const ( + EventCreated EventType = "created" + EventUpdated EventType = "updated" + EventTransitioned EventType = "transitioned" + EventObserved EventType = "observed" + EventDeleted EventType = "deleted" +) + +// ParseEventType normalizes a graph event type. +func ParseEventType(s string) (EventType, error) { + switch EventType(strings.ToLower(strings.TrimSpace(s))) { + case EventCreated, EventUpdated, EventTransitioned, EventObserved, EventDeleted: + return EventType(strings.ToLower(strings.TrimSpace(s))), nil + default: + return "", fmt.Errorf("graph: unknown event type %q", s) + } +} + +// Scope limits a graph fact to its authorized ecosystem boundary. Empty fields +// are valid for local-only or global facts. +type Scope struct { + TenantID string `json:"tenant_id,omitempty"` + ProjectID string `json:"project_id,omitempty"` + RepositoryID string `json:"repository_id,omitempty"` +} + +// Ref identifies a graph node without embedding its mutable attributes. +type Ref struct { + Kind NodeKind `json:"kind"` + ID string `json:"id"` +} + +// Validate reports whether r can safely identify a graph node. +func (r Ref) Validate() error { + if _, err := ParseNodeKind(string(r.Kind)); err != nil { + return err + } + if strings.TrimSpace(r.ID) == "" { + return fmt.Errorf("graph: reference ID is required") + } + return nil +} + +// ArtifactRef points to immutable evidence outside the contract payload. +type ArtifactRef struct { + URI string `json:"uri"` + Digest string `json:"digest,omitempty"` + MediaType string `json:"media_type,omitempty"` +} + +// Provenance identifies the producer and evidence for a graph fact. +type Provenance struct { + Producer string `json:"producer"` + Version string `json:"version,omitempty"` + SourceID string `json:"source_id,omitempty"` + Evidence []ArtifactRef `json:"evidence,omitempty"` +} + +// Validate reports whether p can support an auditable graph fact. +func (p Provenance) Validate() error { + if strings.TrimSpace(p.Producer) == "" { + return fmt.Errorf("graph: provenance producer is required") + } + for i, evidence := range p.Evidence { + if strings.TrimSpace(evidence.URI) == "" { + return fmt.Errorf("graph: evidence[%d] URI is required", i) + } + } + return nil +} + +// Node is a typed, temporal graph fact. Attributes are intentionally bounded +// to strings; large or sensitive data belongs in ArtifactRef evidence. +type Node struct { + ID string `json:"id"` + Kind NodeKind `json:"kind"` + Scope Scope `json:"scope,omitempty"` + CreatedAt time.Time `json:"created_at"` + EffectiveAt time.Time `json:"effective_at,omitempty"` + Provenance Provenance `json:"provenance"` + Attributes map[string]string `json:"attributes,omitempty"` +} + +// Validate reports whether n satisfies the minimum shared graph contract. +func (n Node) Validate() error { + if strings.TrimSpace(n.ID) == "" { + return fmt.Errorf("graph: node ID is required") + } + if _, err := ParseNodeKind(string(n.Kind)); err != nil { + return err + } + if n.CreatedAt.IsZero() { + return fmt.Errorf("graph: node created_at is required") + } + return n.Provenance.Validate() +} + +// Validate reports whether s satisfies the minimum node spec contract. +func (s NodeSpec) Validate() error { + if strings.TrimSpace(s.ID) == "" { + return fmt.Errorf("graph: node spec ID is required") + } + if _, err := ParseNodeType(string(s.Type)); err != nil { + return err + } + return nil +} + +// Validate reports whether s satisfies the minimum edge spec contract. +func (s EdgeSpec) Validate() error { + if strings.TrimSpace(s.From) == "" { + return fmt.Errorf("graph: edge spec from is required") + } + if strings.TrimSpace(s.To) == "" { + return fmt.Errorf("graph: edge spec to is required") + } + return nil +} + +// Validate reports whether s satisfies the minimum graph spec contract. +func (s GraphSpec) Validate() error { + if strings.TrimSpace(s.ID) == "" { + return fmt.Errorf("graph: graph spec ID is required") + } + if len(s.Nodes) == 0 { + return fmt.Errorf("graph: graph spec must have at least one node") + } + for i, node := range s.Nodes { + if err := node.Validate(); err != nil { + return fmt.Errorf("graph: node[%d]: %w", i, err) + } + } + for i, edge := range s.Edges { + if err := edge.Validate(); err != nil { + return fmt.Errorf("graph: edge[%d]: %w", i, err) + } + } + return nil +} + +// Edge is a typed, temporal relationship between two graph nodes. +type Edge struct { + ID string `json:"id"` + Kind EdgeKind `json:"kind"` + From Ref `json:"from"` + To Ref `json:"to"` + Scope Scope `json:"scope,omitempty"` + CreatedAt time.Time `json:"created_at"` + EffectiveAt time.Time `json:"effective_at,omitempty"` + Provenance Provenance `json:"provenance"` + Attributes map[string]string `json:"attributes,omitempty"` +} + +// Validate reports whether e satisfies the minimum shared graph contract. +func (e Edge) Validate() error { + if strings.TrimSpace(e.ID) == "" { + return fmt.Errorf("graph: edge ID is required") + } + if _, err := ParseEdgeKind(string(e.Kind)); err != nil { + return err + } + if err := e.From.Validate(); err != nil { + return fmt.Errorf("graph: edge from: %w", err) + } + if err := e.To.Validate(); err != nil { + return fmt.Errorf("graph: edge to: %w", err) + } + if e.CreatedAt.IsZero() { + return fmt.Errorf("graph: edge created_at is required") + } + return e.Provenance.Validate() +} + +// Event records an immutable lifecycle observation for a graph subject. +type Event struct { + ID string `json:"id"` + Type EventType `json:"type"` + Subject Ref `json:"subject"` + Scope Scope `json:"scope,omitempty"` + OccurredAt time.Time `json:"occurred_at"` + CorrelationID string `json:"correlation_id,omitempty"` + CausationID string `json:"causation_id,omitempty"` + IdempotencyKey string `json:"idempotency_key,omitempty"` + Provenance Provenance `json:"provenance"` +} + +// Validate reports whether e satisfies the minimum shared graph event contract. +func (e Event) Validate() error { + if strings.TrimSpace(e.ID) == "" { + return fmt.Errorf("graph: event ID is required") + } + if _, err := ParseEventType(string(e.Type)); err != nil { + return err + } + if err := e.Subject.Validate(); err != nil { + return fmt.Errorf("graph: event subject: %w", err) + } + if e.OccurredAt.IsZero() { + return fmt.Errorf("graph: event occurred_at is required") + } + return e.Provenance.Validate() +} diff --git a/internal/contracts/harness/harness.go b/internal/contracts/harness/harness.go new file mode 100644 index 00000000..080d8617 --- /dev/null +++ b/internal/contracts/harness/harness.go @@ -0,0 +1,97 @@ +// 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. +package harness + +import ( + "time" + + contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" +) + +// Dimension represents one of the 5 core Agent Work Loop dimensions. +type Dimension string + +const ( + DimensionFeedforward Dimension = "Feedforward Guidance" + DimensionFeedback Dimension = "Feedback Sensors" + DimensionTaskUnderstanding Dimension = "Task Understanding" + DimensionStepPlanning Dimension = "Step Planning & Execution" + DimensionVerification Dimension = "Verification & Safeguards" +) + +// EvidenceState describes whether an evidence mechanism is Present, Partial, Missing, or Unobserved. +type EvidenceState string + +const ( + EvidenceStatePresent EvidenceState = "Present" + EvidenceStatePartial EvidenceState = "Partial" + EvidenceStateMissing EvidenceState = "Missing" + EvidenceStateUnobserved EvidenceState = "Unobserved" +) + +// Finding represents a single actionable evaluation discovery in the neutral harness contract. +type Finding struct { + ID string `json:"id"` + Dimension Dimension `json:"dimension"` + Severity contracts.Severity `json:"severity"` + Title string `json:"title"` + Description string `json:"description"` + Impact string `json:"impact"` + EvidenceSource string `json:"evidence_source"` + EvidenceState EvidenceState `json:"evidence_state"` + ExpectedOutcome string `json:"expected_outcome"` + ScopedRepair string `json:"scoped_repair"` + ValidationRoute string `json:"validation_route"` +} + +// DimensionScore holds aggregated scoring for a single Agent Work Loop dimension. +type DimensionScore struct { + Dimension Dimension `json:"dimension"` + Score int `json:"score"` // 0 to 100 + State EvidenceState `json:"state"` + Summary string `json:"summary"` + FindingsCount int `json:"findings_count"` +} + +// AssetsDetected lists the project harness assets detected during evaluation. +type AssetsDetected struct { + AgentsMD bool `json:"agents_md"` + AgentsMDPath string `json:"agents_md_path,omitempty"` + ZeroMD bool `json:"zero_md"` + ZeroMDPath string `json:"zero_md_path,omitempty"` + Skills []string `json:"skills"` + SpecsCount int `json:"specs_count"` + Linters []string `json:"linters"` + TestRunners []string `json:"test_runners"` + Hooks []string `json:"hooks"` + SandboxPolicy string `json:"sandbox_policy"` + AutonomyTier string `json:"autonomy_tier"` + MerlinBridge bool `json:"merlin_bridge"` + KestrelBridge bool `json:"kestrel_bridge"` +} + +// Report is the neutral cross-repo contract for Graycode Agent Harness evaluations. +type Report struct { + TargetPath string `json:"target_path"` + GeneratedAt time.Time `json:"generated_at"` + OverallScore int `json:"overall_score"` // 0 to 100 + OverallStatus string `json:"overall_status"` + Dimensions map[Dimension]DimensionScore `json:"dimensions"` + Findings []Finding `json:"findings"` + Assets AssetsDetected `json:"assets"` + Summary string `json:"summary"` +} + +// MaxSeverity returns the highest severity finding in the harness report. +func (r *Report) MaxSeverity() contracts.Severity { + if r == nil { + return contracts.SeverityInfo + } + max := contracts.SeverityInfo + for _, f := range r.Findings { + if f.Severity > max { + max = f.Severity + } + } + return max +} diff --git a/internal/contracts/policy/policy.go b/internal/contracts/policy/policy.go new file mode 100644 index 00000000..ebae6b4f --- /dev/null +++ b/internal/contracts/policy/policy.go @@ -0,0 +1,129 @@ +// 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. +package policy + +import ( + "fmt" + "strings" +) + +// Risk is the severity of a permission or policy verdict. +type Risk int + +const ( + RiskLow Risk = iota + RiskMedium + RiskHigh + RiskBlocked +) + +// String returns a human-readable risk name. +func (r Risk) String() string { + switch r { + case RiskLow: + return "low" + case RiskMedium: + return "medium" + case RiskHigh: + return "high" + case RiskBlocked: + return "blocked" + default: + return fmt.Sprintf("Risk(%d)", int(r)) + } +} + +// ParseRisk parses a risk name (case-insensitive) into a Risk value. +func ParseRisk(s string) (Risk, error) { + switch strings.ToLower(strings.TrimSpace(s)) { + case "low": + return RiskLow, nil + case "medium", "med", "moderate": + return RiskMedium, nil + case "high", "hi": + return RiskHigh, nil + case "blocked", "block", "deny", "denied", "forbidden": + return RiskBlocked, nil + default: + return RiskMedium, fmt.Errorf("policy: unknown risk %q", s) + } +} + +// PermissionVerdict is the unified outcome type for permission subsystems. +type PermissionVerdict struct { + Allowed bool `json:"allowed"` + Reason string `json:"reason,omitempty"` + Rule string `json:"rule,omitempty"` + Risk Risk `json:"risk"` + Confidence float64 `json:"confidence,omitempty"` + Source string `json:"source,omitempty"` +} + +// Allow returns a permissive verdict. +func Allow(reason string) PermissionVerdict { + return PermissionVerdict{ + Allowed: true, + Reason: reason, + Risk: RiskLow, + Confidence: 1.0, + Source: "default", + } +} + +// Deny returns a reject verdict with the given reason and rule. +func Deny(reason, rule string) PermissionVerdict { + return PermissionVerdict{ + Allowed: false, + Reason: reason, + Rule: rule, + Risk: RiskBlocked, + Confidence: 1.0, + Source: "rules", + } +} + +// RequireApproval returns a "needs human approval" verdict. +func RequireApproval(reason, rule string, risk Risk) PermissionVerdict { + return PermissionVerdict{ + Allowed: false, + Reason: reason, + Rule: rule, + Risk: risk, + Confidence: 0.5, + Source: "guardian", + } +} + +// IsZero reports whether v is the zero value. +func (v PermissionVerdict) IsZero() bool { + return !v.Allowed && v.Reason == "" && v.Rule == "" && + v.Risk == 0 && v.Confidence == 0 && v.Source == "" +} + +// String returns a one-line summary for logs. +func (v PermissionVerdict) String() string { + action := "DENY" + if v.Allowed { + action = "ALLOW" + } + if v.Rule != "" { + return fmt.Sprintf("[%s] %s (%s, risk=%s, conf=%.2f): %s", + v.Source, action, v.Rule, v.Risk, v.Confidence, v.Reason) + } + return fmt.Sprintf("[%s] %s (risk=%s, conf=%.2f): %s", + v.Source, action, v.Risk, v.Confidence, v.Reason) +} + +// GuardianDecision is a provider-neutral automatic permission review response. +type GuardianDecision struct { + Allowed bool `json:"allowed"` + Reason string `json:"reason"` + Confidence float64 `json:"confidence"` +} + +// PermissionRequest represents a user-facing approval request. +type PermissionRequest struct { + ToolName string `json:"tool_name"` + ToolID string `json:"tool_id,omitempty"` + Summary string `json:"summary,omitempty"` +} diff --git a/internal/contracts/review/review.go b/internal/contracts/review/review.go new file mode 100644 index 00000000..c860fcdd --- /dev/null +++ b/internal/contracts/review/review.go @@ -0,0 +1,144 @@ +// 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. +package review + +import ( + "fmt" + "strings" + "time" + + contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" +) + +// Finding is the neutral review finding contract shared across Graycode and review engines. +type Finding struct { + Concern string `json:"concern"` + Severity contracts.Severity `json:"severity"` + File string `json:"file"` + Line int `json:"line"` + EndLine int `json:"end_line,omitempty"` + Message string `json:"message"` + Fix string `json:"fix,omitempty"` + Reasoning string `json:"reasoning,omitempty"` + CWE string `json:"cwe,omitempty"` + Confidence float64 `json:"confidence"` + SASTSource bool `json:"sast_source,omitempty"` +} + +// Validate reports whether the finding satisfies the minimum contract +// invariants: a non-blank Message, a non-negative Line, and a Confidence +// within [0, 1]. It returns a descriptive error naming the first violated +// field. +func (f Finding) Validate() error { + if strings.TrimSpace(f.Message) == "" { + return fmt.Errorf("finding message is empty") + } + if f.Line < 0 { + return fmt.Errorf("finding line %d is negative", f.Line) + } + if f.Confidence < 0 || f.Confidence > 1 { + return fmt.Errorf("finding confidence %v is outside [0, 1]", f.Confidence) + } + return nil +} + +// InlineComment is a review finding mapped to a concrete diff position. +type InlineComment struct { + Path string `json:"path"` + StartLine int `json:"start_line"` + EndLine int `json:"end_line,omitempty"` + Body string `json:"body"` + Suggestion string `json:"suggestion,omitempty"` +} + +// Stats captures review execution metrics. +type Stats struct { + FilesReviewed int `json:"files_reviewed"` + HunksAnalyzed int `json:"hunks_analyzed"` + FindingsTotal int `json:"findings_total"` + BySeverity map[contracts.Severity]int `json:"by_severity"` + ByConcern map[string]int `json:"by_concern"` + TokensUsed int `json:"tokens_used"` + DurationPerConcern map[string]time.Duration `json:"duration_per_concern"` + AverageConfidence float64 `json:"average_confidence"` + HighConfidenceCount int `json:"high_confidence_count"` + LowConfidenceCount int `json:"low_confidence_count"` + // LLMErrors records non-fatal provider errors encountered during + // analysis; findings may be partial when it is non-empty. + LLMErrors []string `json:"llm_errors,omitempty"` +} + +// ConfidenceBreakdown groups review findings by confidence band. +type ConfidenceBreakdown struct { + High []Finding `json:"high"` + Medium []Finding `json:"medium"` + Low []Finding `json:"low"` +} + +// SASTFusionResult tracks how the LLM handled SAST findings during a review. +// Only populated when SAST-LLM fusion is active (preAnalysis enabled). +type SASTFusionResult struct { + Confirmed []Finding `json:"confirmed"` + Dismissed []Finding `json:"dismissed"` + Unaddressed []Finding `json:"unaddressed"` +} + +// Result is the neutral review result contract. +type Result struct { + Findings []Finding `json:"findings"` + Comments []InlineComment `json:"comments"` + Stats Stats `json:"stats"` + Report string `json:"report"` + FailOn contracts.Severity `json:"fail_on"` + // FailOnSet reports whether FailOn was explicitly configured via + // SetFailOn. When it is false, Failed() treats SeverityCritical as the + // effective threshold: an unset FailOn must not fail the review on + // informational findings just because SeverityInfo is the zero value. + FailOnSet bool `json:"fail_on_set,omitempty"` + SASTFusion *SASTFusionResult `json:"sast_fusion,omitempty"` + ConfidenceBreakdown *ConfidenceBreakdown `json:"confidence_breakdown,omitempty"` +} + +// SetFailOn sets the fail threshold used by Failed. Set the threshold +// through this method rather than assigning FailOn directly, so that the +// threshold is recorded as explicitly configured. +func (r *Result) SetFailOn(sev contracts.Severity) { + r.FailOn = sev + r.FailOnSet = true +} + +// Failed reports whether any finding meets or exceeds the configured fail threshold. +// When the threshold was never set — a zero Result, or a Result whose FailOn +// field was assigned directly — SeverityCritical is used as the effective +// threshold, matching the kestrel and merlin engine defaults. Set the +// threshold via SetFailOn to make an explicit choice (including Info) take +// effect. +func (r *Result) Failed() bool { + if r == nil { + return false + } + threshold := r.FailOn + if !r.FailOnSet { + threshold = contracts.SeverityCritical + } + for _, f := range r.Findings { + if f.Severity.AtLeast(threshold) { + return true + } + } + return false +} + +// MaxSeverity returns the highest severity present in the result. +func (r *Result) MaxSeverity() contracts.Severity { + if r == nil { + return contracts.SeverityInfo + } + max := contracts.SeverityInfo + for _, f := range r.Findings { + if f.Severity > max { + max = f.Severity + } + } + return max +} diff --git a/internal/contracts/types/finding.go b/internal/contracts/types/finding.go new file mode 100644 index 00000000..5c1d17a4 --- /dev/null +++ b/internal/contracts/types/finding.go @@ -0,0 +1,175 @@ +// 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. +package types + +import ( + "fmt" + "strings" + "time" +) + +// Finding represents a unified analysis concern sourced from Graycode support engines. +type Finding struct { + ID string `json:"id"` + Source string `json:"source"` + Concern string `json:"concern"` + Severity Severity `json:"severity"` + File string `json:"file,omitempty"` + URL string `json:"url,omitempty"` + Line int `json:"line,omitempty"` + EndLine int `json:"end_line,omitempty"` + Message string `json:"message"` + CWE string `json:"cwe,omitempty"` + Confidence float64 `json:"confidence"` + Fix string `json:"fix,omitempty"` + Reasoning string `json:"reasoning,omitempty"` + Tags []string `json:"tags,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` + CreatedAt time.Time `json:"created_at"` +} + +// Validate reports whether the finding satisfies the minimum contract +// invariants: a non-blank Message, a non-negative Line, and a Confidence +// within [0, 1]. It returns a descriptive error naming the first violated +// field. +func (f Finding) Validate() error { + if strings.TrimSpace(f.Message) == "" { + return fmt.Errorf("finding message is empty") + } + if f.Line < 0 { + return fmt.Errorf("finding line %d is negative", f.Line) + } + if f.Confidence < 0 || f.Confidence > 1 { + return fmt.Errorf("finding confidence %v is outside [0, 1]", f.Confidence) + } + return nil +} + +// FindingSlice is sortable by severity descending and confidence descending. +type FindingSlice []Finding + +func (s FindingSlice) Len() int { return len(s) } + +func (s FindingSlice) Less(i, j int) bool { + if s[i].Severity != s[j].Severity { + return s[i].Severity > s[j].Severity + } + return s[i].Confidence > s[j].Confidence +} + +func (s FindingSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// FilterBySource returns findings whose Source matches the given value. +func (s FindingSlice) FilterBySource(source string) FindingSlice { + out := make(FindingSlice, 0, len(s)) + for _, f := range s { + if f.Source == source { + out = append(out, f) + } + } + return out +} + +// FilterBySeverity returns findings whose Severity is at least min. +func (s FindingSlice) FilterBySeverity(min Severity) FindingSlice { + out := make(FindingSlice, 0, len(s)) + for _, f := range s { + if f.Severity.AtLeast(min) { + out = append(out, f) + } + } + return out +} + +// FilterByConfidence returns findings whose Confidence is >= min. +func (s FindingSlice) FilterByConfidence(min float64) FindingSlice { + out := make(FindingSlice, 0, len(s)) + for _, f := range s { + if f.Confidence >= min { + out = append(out, f) + } + } + return out +} + +// ByFile groups findings by their File field. +func (s FindingSlice) ByFile() map[string]FindingSlice { + m := make(map[string]FindingSlice, len(s)) + for _, f := range s { + m[f.File] = append(m[f.File], f) + } + return m +} + +// FindingSummary provides aggregate counts over a set of findings. +type FindingSummary struct { + Total int `json:"total"` + BySource map[string]int `json:"by_source"` + BySeverity map[string]int `json:"by_severity"` + AvgConfidence float64 `json:"avg_confidence"` +} + +// Summary returns a FindingSummary for the slice. +func (s FindingSlice) Summary() FindingSummary { + bySrc := make(map[string]int) + bySev := make(map[string]int) + var confSum float64 + + for _, f := range s { + bySrc[f.Source]++ + bySev[f.Severity.String()]++ + confSum += f.Confidence + } + + avg := 0.0 + if len(s) > 0 { + avg = confSum / float64(len(s)) + } + + return FindingSummary{ + Total: len(s), + BySource: bySrc, + BySeverity: bySev, + AvgConfidence: avg, + } +} + +// FindingFromKestrel constructs a Finding from a kestrel review result. +func FindingFromKestrel( + concern, file string, + line int, + message, cwe string, + sev Severity, + confidence float64, +) Finding { + return Finding{ + ID: fmt.Sprintf("kestrel:%s:%s:%d", concern, file, line), + Source: "kestrel", + Concern: concern, + Severity: sev, + File: file, + Line: line, + Message: message, + CWE: cwe, + Confidence: confidence, + CreatedAt: time.Now(), + } +} + +// FindingFromMerlin constructs a Finding from an merlin analysis result. +func FindingFromMerlin( + concern, url, message string, + sev Severity, + tags []string, +) Finding { + return Finding{ + ID: fmt.Sprintf("merlin:%s:%s", concern, url), + Source: "merlin", + Concern: concern, + Severity: sev, + URL: url, + Message: message, + Tags: tags, + CreatedAt: time.Now(), + } +} diff --git a/internal/contracts/types/severity.go b/internal/contracts/types/severity.go new file mode 100644 index 00000000..5eef2182 --- /dev/null +++ b/internal/contracts/types/severity.go @@ -0,0 +1,85 @@ +// 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. +package types + +import ( + "fmt" + "strings" +) + +// Severity represents the impact level of a finding. +type Severity int + +const ( + SeverityInfo Severity = iota + SeverityLow + SeverityMedium + SeverityHigh + SeverityCritical +) + +var severityNames = [...]string{"info", "low", "medium", "high", "critical"} + +func (s Severity) String() string { + if int(s) >= 0 && int(s) < len(severityNames) { + return severityNames[s] + } + return "unknown" +} + +// ParseSeverity converts a string to a Severity. +// +// Deprecated: ParseSeverity fails open — unknown input (typos such as +// "critcal", empty strings, arbitrary text) silently maps to SeverityInfo, +// so a malformed value is indistinguishable from a legitimate "info". +// Callers handling untrusted input should use ParseSeverityStrict, which +// reports unknown values as errors instead. +func ParseSeverity(s string) Severity { + sev, _ := ParseSeverityStrict(s) + return sev +} + +// ParseSeverityStrict converts a string to a Severity, reporting unknown +// values as errors instead of failing open to SeverityInfo. Matching is +// case-insensitive and ignores surrounding whitespace, exactly like +// ParseSeverity; the two accept the same set of valid names. +func ParseSeverityStrict(s string) (Severity, error) { + switch strings.ToLower(strings.TrimSpace(s)) { + case "critical": + return SeverityCritical, nil + case "high": + return SeverityHigh, nil + case "medium": + return SeverityMedium, nil + case "low": + return SeverityLow, nil + case "info": + return SeverityInfo, nil + default: + return SeverityInfo, fmt.Errorf("unknown severity %q (want one of info, low, medium, high, critical)", s) + } +} + +// AtLeast returns true if s >= threshold. +func (s Severity) AtLeast(threshold Severity) bool { + return s >= threshold +} + +// TokenSeverity defines rule severity for compression error patterns. +type TokenSeverity string + +const ( + TokenSeverityCritical TokenSeverity = "critical" + TokenSeverityHigh TokenSeverity = "high" + TokenSeverityMedium TokenSeverity = "medium" + TokenSeverityLow TokenSeverity = "low" +) + +// AuditSeverity indicates how dangerous a security audit finding is. +type AuditSeverity string + +const ( + AuditSeverityCritical AuditSeverity = "CRITICAL" + AuditSeverityWarning AuditSeverity = "WARNING" + AuditSeverityInfo AuditSeverity = "INFO" +) diff --git a/internal/contracts/verify/verify.go b/internal/contracts/verify/verify.go new file mode 100644 index 00000000..d9269ec4 --- /dev/null +++ b/internal/contracts/verify/verify.go @@ -0,0 +1,86 @@ +// 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. +package verify + +import ( + "time" + + contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/types" +) + +// Finding is the neutral verification finding contract shared across Graycode and verification engines. +type Finding struct { + Check string `json:"check"` + Severity contracts.Severity `json:"severity"` + URL string `json:"url"` + Element string `json:"element,omitempty"` + Message string `json:"message"` + Fix string `json:"fix,omitempty"` + Evidence string `json:"evidence,omitempty"` +} + +// Stats captures verification execution metrics. +type Stats struct { + PagesScanned int `json:"pages_scanned"` + FindingsTotal int `json:"findings_total"` + BySeverity map[contracts.Severity]int `json:"by_severity"` + ByCheck map[string]int `json:"by_check"` + DurationPerCheck map[string]time.Duration `json:"duration_per_check"` +} + +// Report is the neutral verification report contract. +type Report struct { + Target string `json:"target"` + Findings []Finding `json:"findings"` + Stats Stats `json:"stats"` + CrawledURLs int `json:"crawled_urls"` + Duration time.Duration `json:"duration"` + FailOn contracts.Severity `json:"fail_on"` + // FailOnSet reports whether FailOn was explicitly configured via + // SetFailOn. When it is false, Failed() treats SeverityCritical as the + // effective threshold: an unset FailOn must not fail the report on + // informational findings just because SeverityInfo is the zero value. + FailOnSet bool `json:"fail_on_set,omitempty"` +} + +// SetFailOn sets the fail threshold used by Failed. Set the threshold +// through this method rather than assigning FailOn directly, so that the +// threshold is recorded as explicitly configured. +func (r *Report) SetFailOn(sev contracts.Severity) { + r.FailOn = sev + r.FailOnSet = true +} + +// Failed reports whether any finding meets or exceeds the configured fail threshold. +// When the threshold was never set — a zero Report, or a Report whose FailOn +// field was assigned directly — SeverityCritical is used as the effective +// threshold, mirroring review.Result. +func (r *Report) Failed() bool { + if r == nil { + return false + } + threshold := r.FailOn + if !r.FailOnSet { + threshold = contracts.SeverityCritical + } + for _, f := range r.Findings { + if f.Severity.AtLeast(threshold) { + return true + } + } + return false +} + +// MaxSeverity returns the highest severity present in the report. +func (r *Report) MaxSeverity() contracts.Severity { + if r == nil { + return contracts.SeverityInfo + } + max := contracts.SeverityInfo + for _, f := range r.Findings { + if f.Severity > max { + max = f.Severity + } + } + return max +} diff --git a/internal/crash/crash.go b/internal/crash/crash.go index ab67d962..2acefb19 100644 --- a/internal/crash/crash.go +++ b/internal/crash/crash.go @@ -31,7 +31,7 @@ import ( "sync" "time" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/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("hawk crash report\n") + b.WriteString("graycode 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 23ecf882..989600e1 100644 --- a/internal/crash/crash_extra_test.go +++ b/internal/crash/crash_extra_test.go @@ -9,7 +9,7 @@ import ( "time" ) -const testEnvStateDir = "HAWK_STATE_DIR" +const testEnvStateDir = "GRAYCODE_STATE_DIR" // --- WriteReport tests --- @@ -112,7 +112,7 @@ func TestFormatReport_FullFields(t *testing.T) { } result := formatReport(r) - if !strings.Contains(result, "hawk crash report") { + if !strings.Contains(result, "graycode 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 62d1a7a0..c5e999a9 100644 --- a/internal/crash/crash_test.go +++ b/internal/crash/crash_test.go @@ -5,14 +5,14 @@ import ( "path/filepath" "testing" - "github.com/GrayCodeAI/hawk/internal/crash" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/crash" + "github.com/GrayCodeAI/graycode-cli/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 HAWK_STATE_DIR override production uses. +// the same GRAYCODE_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) { - // HAWK_STATE_DIR must be set before Install/crash writes anything; mirror + // GRAYCODE_STATE_DIR must be set before Install/crash writes anything; mirror // the convention storage's own tests use. dir := t.TempDir() - t.Setenv("HAWK_STATE_DIR", dir) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", dir) + t.Setenv("GRAYCODE_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 f9c0f279..d38b5a1b 100644 --- a/internal/crash/crash_unix.go +++ b/internal/crash/crash_unix.go @@ -56,7 +56,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("hawk signal report\nsignal: %s\nreason: %s\ntimestamp: %s\n\n%s\n", + content := fmt.Sprintf("graycode 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 795ef63c..85bd6b46 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/eagle/graph" - "github.com/GrayCodeAI/hawk/internal/testutil" + graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" + "github.com/GrayCodeAI/graycode-cli/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 38276ed3..3acd03e9 100644 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -22,12 +22,12 @@ import ( "sync" "time" - "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" + "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" ) 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 hawk version into the daemon. +// SetVersion propagates the canonical graycode version into the daemon. func SetVersion(v string) { version = v } -// Server is the hawk daemon HTTP server for programmatic/CI access. +// Server is the graycode daemon HTTP server for programmatic/CI access. type Server struct { addr string mux *http.ServeMux @@ -99,7 +99,7 @@ type Server struct { readyMu sync.RWMutex readyFn func() (bool, string) - // graphFactory projects a persisted Hawk session into the portable, + // graphFactory projects a persisted Graycode 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("hawk daemon started", "addr", actualAddr) + slog.Info("graycode daemon started", "addr", actualAddr) return actualAddr, nil } @@ -382,7 +382,7 @@ func (s *Server) warnInsecureAuthConfig() { return } slog.Warn( - "hawk daemon started without API key authentication; only loopback access allowed", + "graycode 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.", ) @@ -540,11 +540,11 @@ func (s *Server) cancelSession(sessionID string) bool { return true } -// maxConcurrentFromEnv reads HAWK_DAEMON_MAX_CONCURRENT (clamped to >= 1) so +// maxConcurrentFromEnv reads GRAYCODE_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("HAWK_DAEMON_MAX_CONCURRENT"); raw != "" { + if raw := os.Getenv("GRAYCODE_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 *hawksession.Session + var saved *graycodesession.Session if requestedID != "" { var loadErr error - saved, loadErr = hawksession.Load(sessionID) + saved, loadErr = graycodesession.Load(sessionID) if loadErr != nil { - if !errors.Is(loadErr, hawksession.ErrNotFound) { + if !errors.Is(loadErr, graycodesession.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(hawksession.ToRuntimeMessages(saved.Messages)) + sess.LoadMessages(graycodesession.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 (HAWK_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 (GRAYCODE_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 *hawksession.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 *graycodesession.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-Hawk-Session-ID", sessionID) + w.Header().Set("X-Graycode-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 *hawksession.Session, start time.Time) { +func writeJSONResponse(s *Server, w http.ResponseWriter, events <-chan engine.StreamEvent, sessionID string, req ChatRequest, sess *engine.Session, saved *graycodesession.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-Hawk-Session-ID", sessionID) + w.Header().Set("X-Graycode-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 hawksession.ValidID(id) + return graycodesession.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 *hawksession.Session, startedAt time.Time) error { +func persistDaemonSession(id string, req ChatRequest, sess *engine.Session, previous *graycodesession.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 hawksession.Save(&hawksession.Session{ + return graycodesession.Save(&graycodesession.Session{ ID: id, Model: sess.Model(), Provider: sess.Provider(), Agent: req.Agent, CWD: req.CWD, Name: name, - Messages: hawksession.FromRuntimeMessages(sess.RawMessages()), + Messages: graycodesession.FromRuntimeMessages(sess.RawMessages()), Events: sess.JournalWire(), CreatedAt: createdAt, }) } -func (s *Server) trackSession(id string, req ChatRequest, previous *hawksession.Session, startedAt time.Time, turns int) { +func (s *Server) trackSession(id string, req ChatRequest, previous *graycodesession.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 3dac3ca5..e7807a2f 100644 --- a/internal/daemon/daemon_test.go +++ b/internal/daemon/daemon_test.go @@ -14,10 +14,10 @@ import ( "testing" "time" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/session" - "github.com/GrayCodeAI/hawk/internal/storage" - "github.com/GrayCodeAI/hawk/internal/testutil" + "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" ) func startTestDaemon(t *testing.T, srv *Server) string { @@ -238,7 +238,7 @@ func TestDaemon_RejectsUnknownFields(t *testing.T) { } func TestDaemon_Chat_WithEngine(t *testing.T) { - t.Setenv("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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-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")) + 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")) } factoryReq := <-seen @@ -371,7 +371,7 @@ func TestDaemon_ChatPersistsRetrievableSessionAndRequestMetadata(t *testing.T) { } func TestDaemon_ChatContinuationReusesDurableSession(t *testing.T) { - t.Setenv("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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-Hawk-Session-ID") + id := resp.Header.Get("X-Graycode-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 18e67980..d051aa6f 100644 --- a/internal/daemon/discord.go +++ b/internal/daemon/discord.go @@ -11,7 +11,7 @@ import ( "github.com/bwmarrin/discordgo" ) -// DiscordGateway bridges hawk to Discord via the official Gateway (WebSocket), +// DiscordGateway bridges graycode 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 forwardToHawk + client *http.Client // for forwardToGraycode 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 hawk.") + reply("Paired. You can now chat with graycode.") } else { reply("Pairing failed: invalid code.") } @@ -151,7 +151,7 @@ func (g *DiscordGateway) handleMessage(ctx context.Context, senderID, channelID, return } - resp, err := forwardToHawk(ctx, g.client, g.daemonAddr, g.apiKey, text) + resp, err := forwardToGraycode(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 46b96657..26946f01 100644 --- a/internal/daemon/e2e_test.go +++ b/internal/daemon/e2e_test.go @@ -8,8 +8,8 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/feature" - "github.com/GrayCodeAI/hawk/internal/testutil" + "github.com/GrayCodeAI/graycode-cli/internal/feature" + "github.com/GrayCodeAI/graycode-cli/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), "hawk_daemon_active_sessions") { - t.Error("expected hawk_daemon_active_sessions metric in output") + 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_uptime_seconds") { - t.Error("expected hawk_daemon_uptime_seconds metric in output") + if !strings.Contains(string(body), "graycode_daemon_uptime_seconds") { + t.Error("expected graycode_daemon_uptime_seconds metric in output") } // Test JSON format too. diff --git a/internal/daemon/gateway.go b/internal/daemon/gateway.go index 739537a1..b272f1e0 100644 --- a/internal/daemon/gateway.go +++ b/internal/daemon/gateway.go @@ -11,11 +11,11 @@ import ( "sync" ) -// forwardToHawk posts a prompt to the daemon's /v1/chat endpoint and returns the +// forwardToGraycode 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 forwardToHawk(ctx context.Context, client *http.Client, daemonAddr, apiKey, prompt string) (string, error) { +func forwardToGraycode(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 forwardToHawk(ctx context.Context, client *http.Client, daemonAddr, apiKey, } // Gateway is a bidirectional messaging bridge between an external chat platform -// (Telegram, Discord, Slack, ...) and the hawk daemon. Implementations forward +// (Telegram, Discord, Slack, ...) and the graycode 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 ffe340b7..832bd254 100644 --- a/internal/daemon/gateway_test.go +++ b/internal/daemon/gateway_test.go @@ -211,7 +211,7 @@ func TestAuthorizer_PairingAndAllowlist(t *testing.T) { } } -func TestForwardToHawk(t *testing.T) { +func TestForwardToGraycode(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 TestForwardToHawk(t *testing.T) { })) defer ts.Close() - reply, err := forwardToHawk(context.Background(), ts.Client(), ts.URL, "key123", "ping") + reply, err := forwardToGraycode(context.Background(), ts.Client(), ts.URL, "key123", "ping") if err != nil { - t.Fatalf("forwardToHawk: %v", err) + t.Fatalf("forwardToGraycode: %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) { - hawk := newIPv4GatewayServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - writeJSON(w, http.StatusOK, ChatResponse{Response: "hawk-reply"}) + graycode := newIPv4GatewayServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + writeJSON(w, http.StatusOK, ChatResponse{Response: "graycode-reply"}) })) - defer hawk.Close() + defer graycode.Close() - g := newDiscordGateway(DiscordConfig{Token: "bot", PairingCode: "code"}, hawk.URL, "") + g := newDiscordGateway(DiscordConfig{Token: "bot", PairingCode: "code"}, graycode.URL, "") var sent []string send := func(s string) error { sent = append(sent, s); return nil } - // Unauthorized non-pair message -> "Unauthorized", no hawk call. + // Unauthorized non-pair message -> "Unauthorized", no graycode 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 hawk. + // Authorized -> forwarded to graycode. 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] != "hawk-reply" { - t.Errorf("sent[3]=%q want hawk-reply", sent[3]) + if sent[3] != "graycode-reply" { + t.Errorf("sent[3]=%q want graycode-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 dfb9b493..95fd2708 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/hawk/internal/testutil" + "github.com/GrayCodeAI/graycode-cli/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: "hawk.graph/v1", + SchemaVersion: "graycode.graph/v1", Digest: "abc123", Facts: 4, - GraphJSON: `{"schema_version":"hawk.graph/v1","nodes":[]}`, + GraphJSON: `{"schema_version":"graycode.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 != "hawk.graph/v1" { + if got.ProjectID != "proj" || got.SessionID != "sess" || got.SchemaVersion != "graycode.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 3782dba3..11b2b37c 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/hawk/internal/testutil" + "github.com/GrayCodeAI/graycode-cli/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("HAWK_DAEMON_MAX_CONCURRENT", "1") + t.Setenv("GRAYCODE_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 70a5b5a9..ebda2e1b 100644 --- a/internal/daemon/middleware.go +++ b/internal/daemon/middleware.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/feature" + "github.com/GrayCodeAI/graycode-cli/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 2c29af36..75ca86c1 100644 --- a/internal/daemon/middleware_test.go +++ b/internal/daemon/middleware_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/feature" - "github.com/GrayCodeAI/hawk/internal/testutil" + "github.com/GrayCodeAI/graycode-cli/internal/feature" + "github.com/GrayCodeAI/graycode-cli/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 e6c1d7a5..ab5016cf 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: "hawk", Turns: 3, LastUsed: now}) + s.sessions.Store("working-1", &Session{ID: "working-1", Agent: "graycode", 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 2eee3f19..30c3550d 100644 --- a/internal/daemon/routes_graph.go +++ b/internal/daemon/routes_graph.go @@ -9,8 +9,8 @@ import ( "time" "unicode" - "github.com/GrayCodeAI/hawk/internal/executiongraph" - "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" + "github.com/GrayCodeAI/graycode-cli/internal/session" ) const ( @@ -26,7 +26,7 @@ type GraphRequest struct { GeneratedAt time.Time } -// GraphFactory projects one persisted session into Hawk's portable graph. +// GraphFactory projects one persisted session into Graycode'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_test.go b/internal/daemon/routes_graph_read_test.go index 79826c5f..5f693078 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/hawk/internal/executiongraph" + "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" ) func getGraphRead(t *testing.T, addr, path string) *http.Response { diff --git a/internal/daemon/routes_graph_sync.go b/internal/daemon/routes_graph_sync.go index 07a23c1c..f7157918 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/eagle/graph" - "github.com/GrayCodeAI/hawk/internal/executiongraph" + graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" + "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" ) // POST /v1/graph/sync lets a producer (a future ecosystem repo) push portable -// `*.graph/v1` facts into Hawk. It mirrors the cloud plane's /v1/graph/sync +// `*.graph/v1` facts into Graycode. 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. @@ -179,7 +179,7 @@ func (s *Server) handleGraphSync(w http.ResponseWriter, r *http.Request) { // validateGraphExport checks a portable graph against the shared `*.graph/v1` // contract: schema version, fact-count bounds, per-fact validity (reusing -// eagle/graph), unique identities, and self-contained topology (edges and +// contracts/graph), unique identities, and self-contained topology (edges and // events may only reference nodes present in the same export). func validateGraphExport(export executiongraph.Export) error { if !graphSchemaVersionPattern.MatchString(export.SchemaVersion) { diff --git a/internal/daemon/routes_graph_sync_test.go b/internal/daemon/routes_graph_sync_test.go index 380e859d..8160ed4b 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/eagle/graph" - "github.com/GrayCodeAI/hawk/internal/executiongraph" - "github.com/GrayCodeAI/hawk/internal/testutil" + graphcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/graph" + "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" + "github.com/GrayCodeAI/graycode-cli/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 3794ee62..9c20f25c 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/hawk/internal/executiongraph" - "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/graycode-cli/internal/executiongraph" + "github.com/GrayCodeAI/graycode-cli/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=hawk&swift_checkpoint=012345abcdef&swift_checkpoint=fedcba987654", + "/v1/sessions/session-1/graph?repository=graycode&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 != "hawk" { + if captured.SessionID != "session-1" || captured.RepositoryID != "graycode" { 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=hawk%0Aother", + url: "/v1/sessions/session-1/graph?repository=graycode%0Aother", code: "invalid_repository", }, { diff --git a/internal/daemon/routes_lease.go b/internal/daemon/routes_lease.go index 60d60eb3..f4f18d02 100644 --- a/internal/daemon/routes_lease.go +++ b/internal/daemon/routes_lease.go @@ -6,7 +6,7 @@ import ( "errors" "net/http" - "github.com/GrayCodeAI/hawk/internal/session" + "github.com/GrayCodeAI/graycode-cli/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 6738c866..39e81a6d 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/hawk/internal/session" - "github.com/GrayCodeAI/hawk/internal/testutil" + "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/graycode-cli/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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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 ccaa3baa..7e0d45df 100644 --- a/internal/daemon/routes_metrics.go +++ b/internal/daemon/routes_metrics.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/observability/metrics" + "github.com/GrayCodeAI/graycode-cli/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(fmt.Sprintf("# TYPE hawk_daemon_active_sessions gauge\n")) - sb.WriteString(fmt.Sprintf("hawk_daemon_active_sessions %d\n", activeSessions)) + sb.WriteString(fmt.Sprintf("# TYPE graycode_daemon_active_sessions gauge\n")) + sb.WriteString(fmt.Sprintf("graycode_daemon_active_sessions %d\n", activeSessions)) // Concurrency slots used - sb.WriteString(fmt.Sprintf("# TYPE hawk_daemon_chat_concurrency_used gauge\n")) - sb.WriteString(fmt.Sprintf("hawk_daemon_chat_concurrency_used %d\n", len(s.concurrencySem))) + sb.WriteString(fmt.Sprintf("# TYPE graycode_daemon_chat_concurrency_used gauge\n")) + sb.WriteString(fmt.Sprintf("graycode_daemon_chat_concurrency_used %d\n", len(s.concurrencySem))) // Uptime - sb.WriteString(fmt.Sprintf("# TYPE hawk_daemon_uptime_seconds gauge\n")) - sb.WriteString(fmt.Sprintf("hawk_daemon_uptime_seconds %.0f\n", time.Since(s.startedAt).Seconds())) + sb.WriteString(fmt.Sprintf("# TYPE graycode_daemon_uptime_seconds gauge\n")) + sb.WriteString(fmt.Sprintf("graycode_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 1a1874d4..96075e41 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 `hawk review run` subprocesses +// reviewSem bounds the number of concurrent `graycode 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 hawk review run. + // Trigger review asynchronously via graycode 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(), "hawk", args...).Run() // #nosec G204 -- binary is fixed "hawk"; args are validated (SHA regex, no "--" prefix, printable charset) + _ = exec.CommandContext(context.Background(), "graycode", args...).Run() // #nosec G204 -- binary is fixed "graycode"; 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 hawk review status and return output. - out, err := exec.CommandContext(context.Background(), "hawk", "review", "status").Output() + // Run graycode review status and return output. + out, err := exec.CommandContext(context.Background(), "graycode", "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 e6363568..c136f743 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/hawk/internal/testutil" + "github.com/GrayCodeAI/graycode-cli/internal/testutil" ) // newReviewTestServer starts a daemon. RegisterReviewRoutes() is already @@ -119,7 +119,7 @@ func TestDaemon_ReviewStatus(t *testing.T) { } defer resp.Body.Close() - // `hawk review status` shells out to the hawk binary; in a sandboxed + // `graycode review status` shells out to the graycode 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 40a72d21..7205da07 100644 --- a/internal/daemon/routes_sessions.go +++ b/internal/daemon/routes_sessions.go @@ -8,8 +8,8 @@ import ( "path/filepath" "strconv" - "github.com/GrayCodeAI/hawk/internal/session" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/graycode-cli/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 9b9fa839..4dcdc6b6 100644 --- a/internal/daemon/routes_sessions_test.go +++ b/internal/daemon/routes_sessions_test.go @@ -7,15 +7,15 @@ import ( "testing" contracts "github.com/GrayCodeAI/eyrie/tools" - "github.com/GrayCodeAI/hawk/internal/session" - "github.com/GrayCodeAI/hawk/internal/testutil" + "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/graycode-cli/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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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 5adacfcc..b18ac010 100644 --- a/internal/daemon/routes_stats.go +++ b/internal/daemon/routes_stats.go @@ -5,7 +5,7 @@ import ( "strconv" "time" - analytics "github.com/GrayCodeAI/hawk/internal/observability" + analytics "github.com/GrayCodeAI/graycode-cli/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 6b154d7e..6852e329 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/hawk/internal/observability" - "github.com/GrayCodeAI/hawk/internal/testutil" + analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" + "github.com/GrayCodeAI/graycode-cli/internal/testutil" ) func TestDaemon_Stats_Aggregation(t *testing.T) { - t.Setenv("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_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 9d435b15..bb96b15d 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/hawk/internal/status" + "github.com/GrayCodeAI/graycode-cli/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.HawkVersion = version + snapshot.GraycodeVersion = 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 1ac7993d..eee0661c 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 -// forwardToHawk, so it needs the resolved address even though it replies via the +// forwardToGraycode, 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 hawk.") + g.reply(ctx, ev.Channel, threadTS, "Paired. You can now chat with graycode.") } 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 := forwardToHawk(ctx, g.client, g.daemonAddr, g.apiKey, text) + reply, err := forwardToGraycode(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 a816de47..b30fe793 100644 --- a/internal/daemon/telegram.go +++ b/internal/daemon/telegram.go @@ -1,5 +1,5 @@ -// Package daemon provides a Telegram gateway for hawk. -// Allows users to interact with hawk via Telegram bot messages. +// Package daemon provides a Telegram gateway for graycode. +// Allows users to interact with graycode via Telegram bot messages. package daemon import ( @@ -15,13 +15,13 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/stt" + "github.com/GrayCodeAI/graycode-cli/internal/stt" ) -// TelegramGateway connects hawk to a Telegram bot. +// TelegramGateway connects graycode to a Telegram bot. type TelegramGateway struct { Token string - DaemonAddr string // hawk daemon address to forward messages to + DaemonAddr string // graycode 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 hawk.") + tg.reply(ctx, msg.Chat.ID, "Paired. You can now chat with graycode.") } 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 hawk daemon - response, err := tg.forwardToHawk(ctx, prompt) + // Forward to graycode daemon + response, err := tg.forwardToGraycode(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) forwardToHawk(ctx context.Context, prompt string) (string, error) { +func (tg *TelegramGateway) forwardToGraycode(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) forwardToHawk(ctx context.Context, prompt string) (st // 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 forwardToHawk response", "error", err) + slog.Warn("partial read in forwardToGraycode 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 3697c050..9d44c57f 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 + hawk +// telegramMockAPI captures sendMessage calls and serves a fake Telegram + graycode // 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 hawk daemon /v1/chat. - hawk := newIPv4TelegramServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - writeJSON(w, http.StatusOK, ChatResponse{Response: "hawk-reply"}) + // 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"}) })) - defer hawk.Close() + defer graycode.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"}, - hawk.URL, "apikey", + graycode.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-hawk goes to the real mock server; let it through. + // forward-to-graycode 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 hawk call. + // 1. Unauthorized non-pair message -> "Unauthorized" reply, no graycode 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 -> hawk reply forwarded. + // 4. Now authorized -> graycode 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] != "hawk-reply" { - t.Errorf("send[3]=%q want hawk-reply", sends[3]) + if sends[3] != "graycode-reply" { + t.Errorf("send[3]=%q want graycode-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 hawk. - hawkCalled := false - hawk := newIPv4TelegramServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - hawkCalled = true + // 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 writeJSON(w, http.StatusOK, ChatResponse{Response: "ok"}) })) - defer hawk.Close() + defer graycode.Close() var sends []string - tg := NewTelegramGateway("tok", hawk.URL) + tg := NewTelegramGateway("tok", graycode.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 hawkCalled { - t.Fatal("bare constructor must not forward unauthorized messages to hawk") + if graycodeCalled { + t.Fatal("bare constructor must not forward unauthorized messages to graycode") } } @@ -166,14 +166,14 @@ func TestTelegramSafeFileName(t *testing.T) { } func TestTelegramVoiceWithoutTranscriberFallsBackToText(t *testing.T) { - hawk := newIPv4TelegramServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - writeJSON(w, http.StatusOK, ChatResponse{Response: "hawk-reply"}) + graycode := newIPv4TelegramServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + writeJSON(w, http.StatusOK, ChatResponse{Response: "graycode-reply"}) })) - defer hawk.Close() + defer graycode.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"}}, hawk.URL, "k") + tg := newTelegramGatewayFromConfig(TelegramConfig{Token: "tok", AllowList: []string{"user"}}, graycode.URL, "k") tg.handleMessage(context.Background(), &TelegramMessage{ Text: "hello", Chat: struct { diff --git a/internal/deps/deps.go b/internal/deps/deps.go index 7e594fc9..fb1b128f 100644 --- a/internal/deps/deps.go +++ b/internal/deps/deps.go @@ -1,4 +1,4 @@ -// Package deps documents the dependency injection pattern used across hawk. +// Package deps documents the dependency injection pattern used across graycode. // // # Pattern // diff --git a/internal/engine/adaptive_prompt.go b/internal/engine/adaptive_prompt.go index 4d54dcdf..2546d357 100644 --- a/internal/engine/adaptive_prompt.go +++ b/internal/engine/adaptive_prompt.go @@ -8,8 +8,8 @@ import ( "sync" "time" - "github.com/GrayCodeAI/hawk/internal/storage" - "github.com/GrayCodeAI/hawk/internal/textutil" + "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/graycode-cli/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 Hawk user state. +// NewAdaptivePrompt creates an adaptive prompt backed by Graycode 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 bd0e8da3..2d089955 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/hawk/internal/testutil" + "github.com/GrayCodeAI/graycode-cli/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 bc21fc51..24be93bd 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/hawk/internal/provider/routing" + "github.com/GrayCodeAI/graycode-cli/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 hawk uses. +// DefaultSections returns the built-in prompt sections that graycode uses. func DefaultSections(ctx PromptBuildContext) []PromptSection { sections := []PromptSection{ { Name: "identity", - 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.", + 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.", Priority: 1, }, { diff --git a/internal/engine/adaptive_system_prompt_test.go b/internal/engine/adaptive_system_prompt_test.go index 11a5fd87..a139cca9 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/hawk/internal/provider/routing" + "github.com/GrayCodeAI/graycode-cli/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 hawk.", Priority: 1}) + b.AddSection(PromptSection{Name: "identity", Content: "You are graycode.", 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 hawk v2.", Priority: 1}) + b.AddSection(PromptSection{Name: "identity", Content: "You are graycode 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 hawk v2." { + if b.Sections[0].Content != "You are graycode 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 hawk."}, + {Name: "identity", Content: "You are graycode."}, {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 hawk.") { + if !strings.Contains(result, "You are graycode.") { 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 3bdbddcc..016eed4f 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/eagle/agent" + agentcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/agent" - "github.com/GrayCodeAI/hawk/internal/taskruntime" + "github.com/GrayCodeAI/graycode-cli/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 9ab95d95..78421786 100644 --- a/internal/engine/agent_reexports.go +++ b/internal/engine/agent_reexports.go @@ -4,20 +4,14 @@ package engine import ( - "context" - - "github.com/GrayCodeAI/hawk/internal/engine/agent" + "github.com/GrayCodeAI/graycode-cli/internal/engine/agent" ) type ( - SubAgentMode = agent.SubAgentMode - SubAgentConfig = agent.SubAgentConfig - SubAgentBudget = agent.SubAgentBudget - // Deprecated: use Session.SpawnController() and BackgroundAgentManager - // (taskruntime-backed) instead. BackgroundAgentPool is retained for - // compatibility with older callers and tests. - BackgroundAgentPool = agent.BackgroundAgentPool - BackgroundResult = agent.BackgroundResult + SubAgentMode = agent.SubAgentMode + SubAgentConfig = agent.SubAgentConfig + SubAgentBudget = agent.SubAgentBudget + BackgroundResult = agent.BackgroundResult ) const ( @@ -46,16 +40,3 @@ func FilterToolsForMode(mode SubAgentMode, available []string) []string { } func DefaultTurnsForMode(mode SubAgentMode) int { return agent.DefaultTurnsForMode(mode) } func IsReadOnlyMode(mode SubAgentMode) bool { return agent.IsReadOnlyMode(mode) } - -// Deprecated: prefer Session.SpawnController().SpawnBackground for async -// sub-agents. Retained for compatibility. -func NewBackgroundAgentPool() *BackgroundAgentPool { return agent.NewBackgroundAgentPool() } - -// Deprecated: prefer Session.SpawnController().SpawnBackground for async -// sub-agents. Retained for compatibility. -func NewBackgroundAgentPoolWithContext(ctx context.Context) *BackgroundAgentPool { - return agent.NewBackgroundAgentPoolWithContext(ctx) -} - -// Deprecated: prefer SpawnController for background result formatting. -func FormatResults(results []BackgroundResult) string { return agent.FormatResults(results) } diff --git a/internal/engine/agent_resume_test.go b/internal/engine/agent_resume_test.go index be5d5f12..380d6c42 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/eagle/agent" - "github.com/GrayCodeAI/hawk/internal/session" - "github.com/GrayCodeAI/hawk/internal/storage" - "github.com/GrayCodeAI/hawk/internal/tool" + 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" ) func TestSubAgentResume_ReplaysTranscriptMessages(t *testing.T) { diff --git a/internal/engine/agent_session_tool.go b/internal/engine/agent_session_tool.go index e4e6cab9..85f1f262 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/eagle/agent" + agentcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/agent" - 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" + 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" ) // 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: "hawk", + Provider: "graycode", 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 90a74418..2322c00b 100644 --- a/internal/engine/arc_test.go +++ b/internal/engine/arc_test.go @@ -3,7 +3,7 @@ package engine import ( "testing" - "github.com/GrayCodeAI/hawk/internal/conversationarc" + "github.com/GrayCodeAI/graycode-cli/internal/conversationarc" ) func TestSessionArcAccessors(t *testing.T) { diff --git a/internal/engine/architect.go b/internal/engine/architect.go index 544dfdec..bec83136 100644 --- a/internal/engine/architect.go +++ b/internal/engine/architect.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/hawk/internal/provider/routing" + "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" ) // ArchitectConfig configures the two-model architect/editor pipeline. diff --git a/internal/engine/assumptions.go b/internal/engine/assumptions.go index 1c1f7bda..61a3cfb9 100644 --- a/internal/engine/assumptions.go +++ b/internal/engine/assumptions.go @@ -6,7 +6,7 @@ import ( "strings" "sync" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 4b0aa133..9ee719c4 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/hawk/internal/engine" + "github.com/GrayCodeAI/graycode-cli/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 909eca0a..bd6d1e8a 100644 --- a/internal/engine/async/engine_test.go +++ b/internal/engine/async/engine_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/GrayCodeAI/hawk/internal/engine" - "github.com/GrayCodeAI/hawk/internal/tool" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/engine" + "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) // fakeClient is a scripted ChatClient for exercising the engine loop. diff --git a/internal/engine/auto_commit.go b/internal/engine/auto_commit.go index 1f813ccb..481bdb82 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 "hawk: " + description + return "graycode: " + description } - return fmt.Sprintf("hawk: auto-commit %s", time.Now().Format("15:04:05")) + return fmt.Sprintf("graycode: 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 0a4765ba..cae7685f 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/hawk/internal/tool" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/graycode-cli/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 7a17ebf6..135719fd 100644 --- a/internal/engine/background_runner.go +++ b/internal/engine/background_runner.go @@ -5,10 +5,10 @@ import ( "fmt" "time" - agentcontracts "github.com/GrayCodeAI/eagle/agent" + agentcontracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/agent" - "github.com/GrayCodeAI/hawk/internal/taskruntime" - "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/graycode-cli/internal/taskruntime" + "github.com/GrayCodeAI/graycode-cli/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 d6e5c0fd..38bf2d86 100644 --- a/internal/engine/branching/cascade.go +++ b/internal/engine/branching/cascade.go @@ -6,8 +6,8 @@ import ( "sync" "time" - "github.com/GrayCodeAI/hawk/internal/engine/cost" - "github.com/GrayCodeAI/hawk/internal/provider/routing" + "github.com/GrayCodeAI/graycode-cli/internal/engine/cost" + "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" ) // CascadeRouter selects the optimal model for each request based on task complexity. diff --git a/internal/engine/branching/cascade_test.go b/internal/engine/branching/cascade_test.go index 5602674e..f3a5673d 100644 --- a/internal/engine/branching/cascade_test.go +++ b/internal/engine/branching/cascade_test.go @@ -3,7 +3,7 @@ package branching import ( "testing" - "github.com/GrayCodeAI/hawk/internal/provider/routing" + "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" ) const testProvider = "anthropic" diff --git a/internal/engine/branching/shadow.go b/internal/engine/branching/shadow.go index fad7c38f..32cb5fe1 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("", "hawk-shadow-*") + dir, err := os.MkdirTemp("", "graycode-shadow-*") if err != nil { return nil, fmt.Errorf("shadow workspace: create temp dir: %w", err) } diff --git a/internal/engine/cache_gate_test.go b/internal/engine/cache_gate_test.go index 96ff6ea9..7653498b 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func TestCacheDecisionNonAnthropicOff(t *testing.T) { diff --git a/internal/engine/cache_planner.go b/internal/engine/cache_planner.go index f9640ace..0efa6196 100644 --- a/internal/engine/cache_planner.go +++ b/internal/engine/cache_planner.go @@ -4,7 +4,7 @@ import ( "encoding/json" "strings" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) // Prompt-cache segment planning, extending the caveman-style break-even gate diff --git a/internal/engine/cache_planner_test.go b/internal/engine/cache_planner_test.go index 65316ced..30b6b1f7 100644 --- a/internal/engine/cache_planner_test.go +++ b/internal/engine/cache_planner_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func bigSys(n int) string { return strings.Repeat("s", n) } diff --git a/internal/engine/chat_provider.go b/internal/engine/chat_provider.go index 8e5af4b7..9e8e19c6 100644 --- a/internal/engine/chat_provider.go +++ b/internal/engine/chat_provider.go @@ -5,11 +5,11 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) -// BuildChatProvider adapts Hawk's engine-backed session client to the smaller +// BuildChatProvider adapts Graycode'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 Eyrie's engine facade. func BuildChatProvider(ctx context.Context, selection gateway.Selection, legacyProvider string) (types.ChatProvider, string, error) { @@ -28,7 +28,7 @@ func BuildChatProvider(ctx context.Context, selection gateway.Selection, legacyP }, provider, nil } -// engineChatProvider keeps compatibility-only provider behavior at Hawk's +// engineChatProvider keeps compatibility-only provider behavior at Graycode's // integration edge while all generation goes through the engine ChatClient. type engineChatProvider struct { client ChatClient diff --git a/internal/engine/chat_provider_test.go b/internal/engine/chat_provider_test.go index 2fcfc67c..427d34fd 100644 --- a/internal/engine/chat_provider_test.go +++ b/internal/engine/chat_provider_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/GrayCodeAI/eyrie/llm" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) type recordingChatClient struct { diff --git a/internal/engine/chat_replay.go b/internal/engine/chat_replay.go index c0331a6c..ad7fe678 100644 --- a/internal/engine/chat_replay.go +++ b/internal/engine/chat_replay.go @@ -4,19 +4,19 @@ import ( "context" "os" - "github.com/GrayCodeAI/hawk/internal/replaycache" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/replaycache" + "github.com/GrayCodeAI/graycode-cli/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 = "HAWK_REPLAY_CACHE_DIR" +const replayCacheDirEnv = "GRAYCODE_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 = "HAWK_REPLAY_FINGERPRINT" +const replayFingerprintEnv = "GRAYCODE_REPLAY_FINGERPRINT" // replayKey builds the cache key for one completion request. func replayKey(opts types.ChatOptions, messages []types.EyrieMessage) string { @@ -25,7 +25,7 @@ func replayKey(opts types.ChatOptions, messages []types.EyrieMessage) string { } // chatWithReplay wraps client.Chat with the replay cache when -// HAWK_REPLAY_CACHE_DIR is set; otherwise it calls straight through. +// GRAYCODE_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 == "" { diff --git a/internal/engine/chat_replay_test.go b/internal/engine/chat_replay_test.go index 308e6d36..6895f803 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func TestChatWithReplayDisabledPassesThrough(t *testing.T) { diff --git a/internal/engine/chat_service.go b/internal/engine/chat_service.go index 25392cee..2aac5360 100644 --- a/internal/engine/chat_service.go +++ b/internal/engine/chat_service.go @@ -8,10 +8,10 @@ import ( "time" "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" + "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" ) // ChatService is the Session's view of the LLM transport. It owns the @@ -207,7 +207,7 @@ 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 (HAWK_TOOL_SHRINK=1): fail-open, so the + // Opt-in tool-catalog compression (GRAYCODE_TOOL_SHRINK=1): fail-open, so the // returned tools equal the input whenever anything is off or drifts. opts.Tools = shrinkEyrieTools(opts.Tools) return opts @@ -224,7 +224,7 @@ func (c *ChatService) BuildOptions(systemPrompt, activeModel string, maxTokens i // // 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 Hawk's compatibility retry/rate layer. +// once; injected legacy clients retain Graycode'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 diff --git a/internal/engine/chat_service_test.go b/internal/engine/chat_service_test.go index e37de4fa..0a33a776 100644 --- a/internal/engine/chat_service_test.go +++ b/internal/engine/chat_service_test.go @@ -8,8 +8,8 @@ import ( "time" "github.com/GrayCodeAI/eyrie/llm" - "github.com/GrayCodeAI/hawk/internal/resilience/retry" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/resilience/retry" + "github.com/GrayCodeAI/graycode-cli/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 hawk. ", cacheMinPrefixBytes/7+64), "claude-opus-4", 4096, nil) - opts.System = "you are hawk" // restore exact assertion target + 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 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 hawk" { + if opts.System != "you are graycode" { t.Errorf("expected system prompt to be set, got %q", opts.System) } } diff --git a/internal/engine/client_interface.go b/internal/engine/client_interface.go index 981ff8d7..c3519835 100644 --- a/internal/engine/client_interface.go +++ b/internal/engine/client_interface.go @@ -4,7 +4,7 @@ import ( "context" "github.com/GrayCodeAI/eyrie/llm" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) // ChatClient abstracts the LLM client methods used by Session. diff --git a/internal/engine/code/code_actions.go b/internal/engine/code/code_actions.go index b39ff680..5320bd81 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) type CodeAction struct { diff --git a/internal/engine/code_index_adapter.go b/internal/engine/code_index_adapter.go index 4e9d781d..1151032b 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/hawk/internal/intelligence/memory" - "github.com/GrayCodeAI/hawk/internal/intelligence/repomap" + "github.com/GrayCodeAI/graycode-cli/internal/intelligence/memory" + "github.com/GrayCodeAI/graycode-cli/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 e11bc42f..60f04e09 100644 --- a/internal/engine/coding_soul.go +++ b/internal/engine/coding_soul.go @@ -5,11 +5,11 @@ import ( "path/filepath" "strings" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/storage" ) // CodingSoul defines the persistent coding personality and style preferences. -// Loaded from Hawk user state. +// Loaded from Graycode 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 Hawk user state based on my coding patterns. Analyze my recent code and infer: + return `Generate a coding soul profile for Graycode 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 fda3c884..5d514b56 100644 --- a/internal/engine/compact.go +++ b/internal/engine/compact.go @@ -7,11 +7,11 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/engine/compact" - "github.com/GrayCodeAI/hawk/internal/engine/token" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/engine/compact" + "github.com/GrayCodeAI/graycode-cli/internal/engine/token" + "github.com/GrayCodeAI/graycode-cli/internal/types" - modelPkg "github.com/GrayCodeAI/hawk/internal/provider/routing" + modelPkg "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" ) // ShouldAutoCompact returns true if the conversation is approaching context limits. @@ -104,7 +104,7 @@ 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("HAWK_COMPACTION_SEGMENT_DETAIL")) + detail, _ := compact.ParseCompactionDetail(os.Getenv("GRAYCODE_COMPACTION_SEGMENT_DETAIL")) if _, err := compact.WriteCompactionSegment(sessionID, compactedMsgs, detail); err != nil { slog.Debug("compaction segment persistence skipped", "error", err) } diff --git a/internal/engine/compact/aliases_extra_test.go b/internal/engine/compact/aliases_extra_test.go index 49c8724b..61bd0df8 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func TestDefaultConfig(t *testing.T) { diff --git a/internal/engine/compact/api.go b/internal/engine/compact/api.go index 1605a39c..1f1ed3bb 100644 --- a/internal/engine/compact/api.go +++ b/internal/engine/compact/api.go @@ -1,9 +1,9 @@ package compact import ( - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" - "github.com/GrayCodeAI/hawk/internal/engine/token" + "github.com/GrayCodeAI/graycode-cli/internal/engine/token" ) type APICompactConfig struct { diff --git a/internal/engine/compact/files.go b/internal/engine/compact/files.go index a5b5641c..0423945f 100644 --- a/internal/engine/compact/files.go +++ b/internal/engine/compact/files.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) type FileTracker struct { diff --git a/internal/engine/compact/files_test.go b/internal/engine/compact/files_test.go index 421560a2..1c29f6c4 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func TestFileTracker_NewFileTracker(t *testing.T) { diff --git a/internal/engine/compact/incremental_test.go b/internal/engine/compact/incremental_test.go index ffb89fe2..08b87ffb 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func TestBuildIncrementalCompactPromptIncludesPriorSummary(t *testing.T) { diff --git a/internal/engine/compact/micro.go b/internal/engine/compact/micro.go index 4b6cec73..c1b89493 100644 --- a/internal/engine/compact/micro.go +++ b/internal/engine/compact/micro.go @@ -3,7 +3,7 @@ package compact import ( "time" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) type MicroCompactConfig struct { diff --git a/internal/engine/compact/prompt.go b/internal/engine/compact/prompt.go index 6a5eefcf..bdbea298 100644 --- a/internal/engine/compact/prompt.go +++ b/internal/engine/compact/prompt.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) const noToolsPreamble = `CRITICAL: Respond with TEXT ONLY. Do NOT call any tools. @@ -121,7 +121,7 @@ func BuildIncrementalCompactPrompt(priorSummary string) string { fmt.Sprintf(incrementalUpdateTemplate, priorSummary) } -// PriorSummaryPrefix is the marker prefix hawk prepends to a persisted +// PriorSummaryPrefix is the marker prefix graycode prepends to a persisted // conversation summary message. const PriorSummaryPrefix = "[Conversation summary]" diff --git a/internal/engine/compact/session_memory.go b/internal/engine/compact/session_memory.go index d3adafb4..36b1a93f 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" - "github.com/GrayCodeAI/hawk/internal/engine/token" - "github.com/GrayCodeAI/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/engine/token" + "github.com/GrayCodeAI/graycode-cli/internal/storage" ) type SessionMemoryConfig struct { diff --git a/internal/engine/compact/split_test.go b/internal/engine/compact/split_test.go index a95dfcc3..a9516079 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) // --------------------------------------------------------------------------- diff --git a/internal/engine/compact/strategy.go b/internal/engine/compact/strategy.go index 0e95a8ad..652eadb6 100644 --- a/internal/engine/compact/strategy.go +++ b/internal/engine/compact/strategy.go @@ -4,7 +4,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) type CompactResult struct { diff --git a/internal/engine/compact/strategy_test.go b/internal/engine/compact/strategy_test.go index 77d998ec..3349141b 100644 --- a/internal/engine/compact/strategy_test.go +++ b/internal/engine/compact/strategy_test.go @@ -4,9 +4,9 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" - "github.com/GrayCodeAI/hawk/internal/engine/token" + "github.com/GrayCodeAI/graycode-cli/internal/engine/token" ) func TestCompactEstimateTokens(t *testing.T) { diff --git a/internal/engine/compact/transcript_segments.go b/internal/engine/compact/transcript_segments.go index db6ea471..c26590d1 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/hawk/internal/storage" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) // Compaction transcript segments, adopted from grok-build's diff --git a/internal/engine/compact/transcript_segments_test.go b/internal/engine/compact/transcript_segments_test.go index c558a9df..18c2836d 100644 --- a/internal/engine/compact/transcript_segments_test.go +++ b/internal/engine/compact/transcript_segments_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func segTestMessages() []types.EyrieMessage { @@ -74,7 +74,7 @@ func TestParseCompactionDetail(t *testing.T) { func TestWriteCompactionSegmentAndIndex(t *testing.T) { stateDir := t.TempDir() - t.Setenv("HAWK_STATE_DIR", stateDir) + t.Setenv("GRAYCODE_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 aea5e67f..17d8d541 100644 --- a/internal/engine/compact_auto.go +++ b/internal/engine/compact_auto.go @@ -6,8 +6,8 @@ import ( "sync" "time" - "github.com/GrayCodeAI/hawk/internal/circuitbreaker" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/circuitbreaker" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) // AutoCompactor orchestrates compaction with circuit breaker protection. diff --git a/internal/engine/compact_clear.go b/internal/engine/compact_clear.go index 7d4fee3f..0e4cc6ae 100644 --- a/internal/engine/compact_clear.go +++ b/internal/engine/compact_clear.go @@ -4,7 +4,7 @@ import ( "context" "sort" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) // Two-tier context management (adopted from herm): before compacting, "clear" diff --git a/internal/engine/compact_clear_test.go b/internal/engine/compact_clear_test.go index 86b91cc3..e5245e2e 100644 --- a/internal/engine/compact_clear_test.go +++ b/internal/engine/compact_clear_test.go @@ -3,7 +3,7 @@ package engine import ( "testing" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func mkMsg(role string, trs []types.ToolResult) types.EyrieMessage { diff --git a/internal/engine/compact_micro_engine.go b/internal/engine/compact_micro_engine.go index d5719056..cce6f3f4 100644 --- a/internal/engine/compact_micro_engine.go +++ b/internal/engine/compact_micro_engine.go @@ -4,9 +4,9 @@ import ( "context" "time" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" - "github.com/GrayCodeAI/hawk/internal/engine/compact" + "github.com/GrayCodeAI/graycode-cli/internal/engine/compact" ) type MicroCompactStrategy struct{} diff --git a/internal/engine/compact_provider_native.go b/internal/engine/compact_provider_native.go index bcdad611..e9ea2341 100644 --- a/internal/engine/compact_provider_native.go +++ b/internal/engine/compact_provider_native.go @@ -5,11 +5,11 @@ import ( "fmt" "github.com/GrayCodeAI/eyrie/llm" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) // ProviderNativeCompactStrategy delegates provider-specific compaction to -// Eyrie. Hawk owns when conversation state is compacted and how the resulting +// Eyrie. Graycode owns when conversation state is compacted and how the resulting // summary is inserted; Eyrie owns credentials and provider transport details. type ProviderNativeCompactStrategy struct{} diff --git a/internal/engine/compact_reexports.go b/internal/engine/compact_reexports.go index e964ba1b..91f1c318 100644 --- a/internal/engine/compact_reexports.go +++ b/internal/engine/compact_reexports.go @@ -4,9 +4,9 @@ package engine import ( - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" - "github.com/GrayCodeAI/hawk/internal/engine/compact" + "github.com/GrayCodeAI/graycode-cli/internal/engine/compact" ) type CompactVariant = compact.CompactVariant @@ -46,7 +46,7 @@ func ExtractPriorSummary(msgs []types.EyrieMessage) string { return compact.ExtractPriorSummary(msgs) } -// PriorSummaryPrefix is the marker prefix hawk prepends to a persisted +// PriorSummaryPrefix is the marker prefix graycode prepends to a persisted // conversation summary message. const PriorSummaryPrefix = compact.PriorSummaryPrefix diff --git a/internal/engine/compact_regression_test.go b/internal/engine/compact_regression_test.go index 3c274372..51e9bbe7 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) // Regression tests for compaction fixes. Each test pins a specific bug so it diff --git a/internal/engine/compact_relevance.go b/internal/engine/compact_relevance.go index 6e7ff84b..b86fec71 100644 --- a/internal/engine/compact_relevance.go +++ b/internal/engine/compact_relevance.go @@ -4,8 +4,8 @@ import ( "context" "time" - "github.com/GrayCodeAI/hawk/internal/relevanceprune" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/relevanceprune" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) // RelevancePruneStrategy prunes context by relevance: it scores older messages diff --git a/internal/engine/compact_relevance_test.go b/internal/engine/compact_relevance_test.go index 24295ba2..187015fd 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func TestRelevancePruneStrategy_ShouldTrigger(t *testing.T) { diff --git a/internal/engine/compact_session_memory_engine.go b/internal/engine/compact_session_memory_engine.go index 2c48a58f..a3f56f25 100644 --- a/internal/engine/compact_session_memory_engine.go +++ b/internal/engine/compact_session_memory_engine.go @@ -6,9 +6,9 @@ import ( "os" "strings" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" - "github.com/GrayCodeAI/hawk/internal/engine/compact" + "github.com/GrayCodeAI/graycode-cli/internal/engine/compact" ) type SessionMemoryStrategy struct{} diff --git a/internal/engine/compact_split.go b/internal/engine/compact_split.go index 2c24bc23..6ee1fb1f 100644 --- a/internal/engine/compact_split.go +++ b/internal/engine/compact_split.go @@ -5,7 +5,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) // splitTurnCompact handles the edge case where a single turn's messages diff --git a/internal/engine/compact_strategy_engine.go b/internal/engine/compact_strategy_engine.go index ace86f8d..77400d10 100644 --- a/internal/engine/compact_strategy_engine.go +++ b/internal/engine/compact_strategy_engine.go @@ -3,7 +3,7 @@ package engine import ( "context" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) type CompactStrategy interface { diff --git a/internal/engine/compact_strategy_test.go b/internal/engine/compact_strategy_test.go index 7b5c6f50..a32943af 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func TestSessionMemoryStrategy_ShouldTrigger(t *testing.T) { diff --git a/internal/engine/compression/cross_session.go b/internal/engine/compression/cross_session.go index 0180fc63..5b3f8a99 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/hawk/internal/mathutil" - "github.com/GrayCodeAI/hawk/internal/safewrite" + "github.com/GrayCodeAI/graycode-cli/internal/mathutil" + "github.com/GrayCodeAI/graycode-cli/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 bedea576..70ba85ce 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 5500cede..e1db2653 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) func TestNewTimeline(t *testing.T) { diff --git a/internal/engine/context_compaction.go b/internal/engine/context_compaction.go index 97e51092..645fa88d 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/hawk/internal/eventlog" - "github.com/GrayCodeAI/hawk/internal/session" - "github.com/GrayCodeAI/hawk/internal/storage" - "github.com/GrayCodeAI/hawk/internal/types" + "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" ) // 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 Hawk's user state sessions dir. +// PersistID is the on-disk session id under Graycode'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 { diff --git a/internal/engine/context_compaction_test.go b/internal/engine/context_compaction_test.go index bdb89ea5..e658b3b4 100644 --- a/internal/engine/context_compaction_test.go +++ b/internal/engine/context_compaction_test.go @@ -6,7 +6,7 @@ import ( "github.com/GrayCodeAI/eyrie/credentials" eyrieengine "github.com/GrayCodeAI/eyrie/engine" - "github.com/GrayCodeAI/hawk/internal/provider/gateway" + "github.com/GrayCodeAI/graycode-cli/internal/provider/gateway" ) func TestContextUsedTokens_PrefersAPI(t *testing.T) { diff --git a/internal/engine/context_governor.go b/internal/engine/context_governor.go index 017a9546..348a77eb 100644 --- a/internal/engine/context_governor.go +++ b/internal/engine/context_governor.go @@ -4,8 +4,8 @@ import ( "context" "fmt" - "github.com/GrayCodeAI/hawk/internal/engine/ctxmgr" - modelPkg "github.com/GrayCodeAI/hawk/internal/provider/routing" + "github.com/GrayCodeAI/graycode-cli/internal/engine/ctxmgr" + modelPkg "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" ) const ( diff --git a/internal/engine/context_governor_test.go b/internal/engine/context_governor_test.go index 596f4df4..f6052b39 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/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("HAWK_CACHE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_CACHE_DIR", t.TempDir()) in := strings.Repeat("x", toolOutputSpillMinChars+100) got := maybeSpillToolOutput(in, "Bash", "call-1") if !strings.Contains(got, "/scratch/") { diff --git a/internal/engine/control/backtrack.go b/internal/engine/control/backtrack.go index 4fa78b0d..eb193b75 100644 --- a/internal/engine/control/backtrack.go +++ b/internal/engine/control/backtrack.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) // DecisionPoint captures a point in the conversation where the agent made a diff --git a/internal/engine/control/backtrack_test.go b/internal/engine/control/backtrack_test.go index f262363c..c840d15f 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func TestNewBacktrackEngine(t *testing.T) { diff --git a/internal/engine/control/stall_detector.go b/internal/engine/control/stall_detector.go index 11ee2996..4224ae87 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 b05c6285..e75bed22 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 4b5a0617..d2a2f4fb 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/hawk/internal/sandbox" - "github.com/GrayCodeAI/hawk/internal/tool" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/sandbox" + "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func TestParseIsolationProfile(t *testing.T) { diff --git a/internal/engine/control_reexports.go b/internal/engine/control_reexports.go index 9fdd8249..0614917a 100644 --- a/internal/engine/control_reexports.go +++ b/internal/engine/control_reexports.go @@ -1,6 +1,6 @@ package engine -import "github.com/GrayCodeAI/hawk/internal/engine/control" +import "github.com/GrayCodeAI/graycode-cli/internal/engine/control" type ( LoopDetector = control.LoopDetector diff --git a/internal/engine/conversation_adapter.go b/internal/engine/conversation_adapter.go index 2046361a..c01c097d 100644 --- a/internal/engine/conversation_adapter.go +++ b/internal/engine/conversation_adapter.go @@ -29,7 +29,7 @@ type ConversationState struct { } // ConversationManager manages a conversation lifecycle, bridging eyrie's -// conversation management into hawk's chat session flow. It is safe for +// conversation management into graycode'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 ca0f62bb..ec8e849e 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 hawk engine. See ../../docs/plans/engine-refactor-plan.md. +// for the graycode 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 f14714bc..c39a2604 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/hawk/internal/provider/routing" + "github.com/GrayCodeAI/graycode-cli/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 260107c1..a75a8312 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/hawk/internal/provider/routing" + "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" ) const testProvider = "anthropic" diff --git a/internal/engine/cost/cost_table.go b/internal/engine/cost/cost_table.go index 9ca6791c..408d4408 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/hawk/internal/provider/routing" + "github.com/GrayCodeAI/graycode-cli/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 36fd4548..bffcf245 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/hawk/internal/observability" - "github.com/GrayCodeAI/hawk/internal/storage" + analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" + "github.com/GrayCodeAI/graycode-cli/internal/storage" ) type CostTracker struct { diff --git a/internal/engine/cost/cost_tracker_test.go b/internal/engine/cost/cost_tracker_test.go index aca7ffde..a9d3f510 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/hawk/internal/observability" - "github.com/GrayCodeAI/hawk/internal/testutil" + analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" + "github.com/GrayCodeAI/graycode-cli/internal/testutil" ) func TestCostTracker_NewAndRecord(t *testing.T) { diff --git a/internal/engine/cost_reexports.go b/internal/engine/cost_reexports.go index e0a1bab5..f395491a 100644 --- a/internal/engine/cost_reexports.go +++ b/internal/engine/cost_reexports.go @@ -1,8 +1,8 @@ package engine import ( - "github.com/GrayCodeAI/hawk/internal/engine/cost" - analytics "github.com/GrayCodeAI/hawk/internal/observability" + "github.com/GrayCodeAI/graycode-cli/internal/engine/cost" + analytics "github.com/GrayCodeAI/graycode-cli/internal/observability" ) type ( diff --git a/internal/engine/council.go b/internal/engine/council.go index 9fc3052a..0f5399dc 100644 --- a/internal/engine/council.go +++ b/internal/engine/council.go @@ -6,7 +6,7 @@ import ( "strings" "sync" - routing "github.com/GrayCodeAI/hawk/internal/provider/routing" + routing "github.com/GrayCodeAI/graycode-cli/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 b434d195..9e587a46 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/hawk/internal/mathutil" + "github.com/GrayCodeAI/graycode-cli/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 b7c13902..ecd995b8 100644 --- a/internal/engine/ctxmgr/context_collapse.go +++ b/internal/engine/ctxmgr/context_collapse.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) // CollapseRepeatedMessages finds and collapses similar consecutive messages to diff --git a/internal/engine/ctxmgr/context_collapse_test.go b/internal/engine/ctxmgr/context_collapse_test.go index 812b21c9..ce712407 100644 --- a/internal/engine/ctxmgr/context_collapse_test.go +++ b/internal/engine/ctxmgr/context_collapse_test.go @@ -3,7 +3,7 @@ package ctxmgr import ( "testing" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func TestCollapseRepeatedMessages_NoCollapse(t *testing.T) { diff --git a/internal/engine/ctxmgr/context_decay.go b/internal/engine/ctxmgr/context_decay.go index 34eb60a5..5a3528d9 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 099554c6..212385ce 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 c2ba1945..9f3f37ba 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 040de014..3ee68935 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/internal/ui/icons" ) func TestNewContextVisualizer(t *testing.T) { diff --git a/internal/engine/ctxmgr/incremental.go b/internal/engine/ctxmgr/incremental.go index e6a0834c..a66bd4e1 100644 --- a/internal/engine/ctxmgr/incremental.go +++ b/internal/engine/ctxmgr/incremental.go @@ -4,11 +4,11 @@ import ( "fmt" "time" - "github.com/GrayCodeAI/hawk/internal/systemcontext" + "github.com/GrayCodeAI/graycode-cli/internal/systemcontext" ) // Section defines a dynamic, incrementally-rendered system-prompt section. It -// is the hawk-host integration of the systemcontext package: each section is a +// is the graycode-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 "hawk" + return "graycode" } 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 hawk already uses for each. It is a +// a session, wired to the same loaders graycode 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 99de5b74..5401e3d8 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/eagle/policy" - "github.com/GrayCodeAI/hawk/internal/eventlog" - "github.com/GrayCodeAI/hawk/internal/sandbox" - "github.com/GrayCodeAI/hawk/internal/tool" + 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" ) func TestSubAgentDelegatedPolicy_InheritsParentExplicitMode(t *testing.T) { diff --git a/internal/engine/diff/diff_staging.go b/internal/engine/diff/diff_staging.go index a477322f..d4cd5d88 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 d1bd3706..c1879292 100644 --- a/internal/engine/diff_reexports.go +++ b/internal/engine/diff_reexports.go @@ -1,6 +1,6 @@ package engine -import "github.com/GrayCodeAI/hawk/internal/engine/diff" +import "github.com/GrayCodeAI/graycode-cli/internal/engine/diff" // Types from diff sub-package. diff --git a/internal/engine/directive_scanner.go b/internal/engine/directive_scanner.go index 703e5104..b8d241ef 100644 --- a/internal/engine/directive_scanner.go +++ b/internal/engine/directive_scanner.go @@ -9,7 +9,7 @@ import ( "strings" ) -// Directive is a parsed hawk: comment from source code. +// Directive is a parsed graycode: comment from source code. type Directive struct { File string Line int @@ -17,9 +17,9 @@ type Directive struct { Context string } -var hawkDirectivePattern = regexp.MustCompile(`(?i)(?://|#|--|/\*)\s*hawk:\s*(.+?)(?:\s*\*/)?$`) +var graycodeDirectivePattern = regexp.MustCompile(`(?i)(?://|#|--|/\*)\s*graycode:\s*(.+?)(?:\s*\*/)?$`) -// ScanDirectives finds all `// hawk: ` comments in source files. +// ScanDirectives finds all `// graycode: ` 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 == ".hawk" { + if name == ".git" || name == "node_modules" || name == "vendor" || name == ".graycode" { 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 := hawkDirectivePattern.FindStringSubmatch(line) + matches := graycodeDirectivePattern.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 hawk: comment asks for. Remove the hawk: comment after implementing." + "Implement what the graycode: comment asks for. Remove the graycode: comment after implementing." } diff --git a/internal/engine/elision.go b/internal/engine/elision.go index 11470af4..665a67fd 100644 --- a/internal/engine/elision.go +++ b/internal/engine/elision.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/GrayCodeAI/hawk/internal/engine/token" + "github.com/GrayCodeAI/graycode-cli/internal/engine/token" ) // elisionNotice computes a verified-facts suffix for a truncation marker from diff --git a/internal/engine/engine.go b/internal/engine/engine.go index bd699aff..0d76f7c8 100644 --- a/internal/engine/engine.go +++ b/internal/engine/engine.go @@ -4,7 +4,7 @@ import ( "context" "os" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) const ( diff --git a/internal/engine/engine_full_loop_test.go b/internal/engine/engine_full_loop_test.go index e83d0b25..37d8f02b 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func TestEngine_FullLoop_TextOnly(t *testing.T) { diff --git a/internal/engine/engine_integration_test.go b/internal/engine/engine_integration_test.go index dbdfe0df..d3d9b433 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/eagle/policy" - "github.com/GrayCodeAI/hawk/internal/types" + contracts "github.com/GrayCodeAI/graycode-cli/internal/contracts/policy" + "github.com/GrayCodeAI/graycode-cli/internal/types" - "github.com/GrayCodeAI/hawk/internal/session" - "github.com/GrayCodeAI/hawk/internal/tool" + "github.com/GrayCodeAI/graycode-cli/internal/session" + "github.com/GrayCodeAI/graycode-cli/internal/tool" ) // ────────────────────────────────────────────────────────────────────────────── diff --git a/internal/engine/engine_stage2_test_helpers.go b/internal/engine/engine_stage2_test_helpers.go index 00ed9280..95b4c554 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/hawk/internal/provider/routing" + "github.com/GrayCodeAI/graycode-cli/internal/provider/routing" ) const testProvider = "anthropic" diff --git a/internal/engine/engine_token_helpers.go b/internal/engine/engine_token_helpers.go index 5602c027..e5b63f7e 100644 --- a/internal/engine/engine_token_helpers.go +++ b/internal/engine/engine_token_helpers.go @@ -3,7 +3,7 @@ package engine import ( "strings" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/types" ) func isRecentToolHeavy(messages []types.EyrieMessage) bool { diff --git a/internal/engine/errs/aliases.go b/internal/engine/errs/aliases.go index 286f8874..791745b8 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 hawk engine. +// patterns, and recovery for the graycode 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 3e8ab0fb..25273e8f 100644 --- a/internal/engine/errs/error_context.go +++ b/internal/engine/errs/error_context.go @@ -410,7 +410,7 @@ func NewErrorContext() *ErrorContext { }, } - ec.Patterns["hawk_old_str_not_found"] = &ErrorHelp{ + ec.Patterns["graycode_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["hawk_file_too_large"] = &ErrorHelp{ + ec.Patterns["graycode_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["hawk_budget_exceeded"] = &ErrorHelp{ + ec.Patterns["graycode_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{ - "hawk --budget 10.00 # set a higher budget", + "graycode --budget 10.00 # set a higher budget", "/compact # reduce context size", }, } - ec.Patterns["hawk_tool_not_found"] = &ErrorHelp{ + ec.Patterns["graycode_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["hawk_sandbox_violation"] = &ErrorHelp{ + ec.Patterns["graycode_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 Hawk user settings", + "Check sandbox settings in Graycode 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 1aeafee9..145fdfa6 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_HawkOldStrNotFound(t *testing.T) { +func TestEnrich_GraycodeOldStrNotFound(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_HawkOldStrNotFound(t *testing.T) { } } -func TestEnrich_HawkFileTooLarge(t *testing.T) { +func TestEnrich_GraycodeFileTooLarge(t *testing.T) { ec := NewErrorContext() enriched := ec.Enrich("cannot read: file too large (50MB)") if enriched == nil { @@ -302,7 +302,7 @@ func TestEnrich_HawkFileTooLarge(t *testing.T) { } } -func TestEnrich_HawkBudgetExceeded(t *testing.T) { +func TestEnrich_GraycodeBudgetExceeded(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 8b7a9bab..5a1a96e3 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/hawk/internal/storage" + "github.com/GrayCodeAI/graycode-cli/internal/storage" ) type ErrorPattern struct { diff --git a/internal/engine/errs_reexports.go b/internal/engine/errs_reexports.go index 6ae27a91..d45e8fc2 100644 --- a/internal/engine/errs_reexports.go +++ b/internal/engine/errs_reexports.go @@ -1,6 +1,6 @@ package engine -import "github.com/GrayCodeAI/hawk/internal/engine/errs" +import "github.com/GrayCodeAI/graycode-cli/internal/engine/errs" type ( ErrorContext = errs.ErrorContext diff --git a/internal/engine/event_bus.go b/internal/engine/event_bus.go index 260eef33..1ae9b34e 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 hawk components. +// EventBus is a lightweight pub/sub system for decoupling graycode 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 b0b4026f..fa99b14d 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/eagle/graph" - policycontracts "github.com/GrayCodeAI/eagle/policy" eyrieengine "github.com/GrayCodeAI/eyrie/engine" eyriegraph "github.com/GrayCodeAI/eyrie/graph" - "github.com/GrayCodeAI/hawk/internal/engine/token" - "github.com/GrayCodeAI/hawk/internal/graphjournal" - "github.com/GrayCodeAI/hawk/internal/types" + 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" 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 = "hawk." + strings.TrimSpace(stage) + verdict.Source = "graycode." + strings.TrimSpace(stage) if !allowed { verdict = policycontracts.Deny(reason, strings.TrimSpace(stage)) - verdict.Source = "hawk." + strings.TrimSpace(stage) + verdict.Source = "graycode." + 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 Hawk session. It is safe to call before either side is configured. +// persisted Graycode 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 @@ -162,7 +162,7 @@ func (s *Session) recordShrikeCompressionObservation(source, stage string, stats if err == nil { err = graphjournal.AppendRuntimeGraph( sessionID, "", stage, "shrike", - shrikeToEagleNodes(export.Nodes), shrikeToEagleEdges(export.Edges), shrikeToEagleEvents(export.Events), observedAt, + shrikeToContractNodes(export.Nodes), shrikeToContractEdges(export.Edges), shrikeToContractEvents(export.Events), observedAt, ) } if err != nil { @@ -200,7 +200,7 @@ func (s *Session) recordShrikeRedactionObservation(source string, matchCount int if err == nil { err = graphjournal.AppendRuntimeGraph( sessionID, "", "response-redaction", "shrike", - shrikeToEagleNodes(export.Nodes), shrikeToEagleEdges(export.Edges), shrikeToEagleEvents(export.Events), observedAt, + shrikeToContractNodes(export.Nodes), shrikeToContractEdges(export.Edges), shrikeToContractEvents(export.Events), observedAt, ) } if err != nil { @@ -258,7 +258,7 @@ func (s *Session) recordShrikeUsageBudgetObservation( if err == nil { err = graphjournal.AppendRuntimeGraph( sessionID, "", "usage-budget", "shrike", - shrikeToEagleNodes(export.Nodes), shrikeToEagleEdges(export.Edges), shrikeToEagleEvents(export.Events), observedAt, + shrikeToContractNodes(export.Nodes), shrikeToContractEdges(export.Edges), shrikeToContractEvents(export.Events), observedAt, ) } if err != nil { @@ -323,7 +323,7 @@ func (s *Session) recordEyrieOperationObservation( if err == nil { err = graphjournal.AppendRuntimeGraph( sessionID, "", "model-generation", "eyrie", - toEagleNodes(export.Nodes), toEagleEdges(export.Edges), toEagleEvents(export.Events), observedAt, + toContractNodes(export.Nodes), toContractEdges(export.Edges), toContractEvents(export.Events), observedAt, ) } if err != nil { @@ -335,82 +335,82 @@ func (s *Session) recordEyrieOperationObservation( } // The following helpers convert Eyrie's vendored graph contract types into -// Hawk's eagle/graph contract types. The definitions are byte-identical, so +// Graycode's contracts/graph contract types. The definitions are byte-identical, so // conversion is a field-by-field copy at the sibling boundary. -func toEagleNodes(nodes []eyriegraph.Node) []graphcontracts.Node { +func toContractNodes(nodes []eyriegraph.Node) []graphcontracts.Node { out := make([]graphcontracts.Node, len(nodes)) for i, n := range nodes { - out[i] = toEagleNode(n) + out[i] = toContractNode(n) } return out } -func toEagleNode(n eyriegraph.Node) graphcontracts.Node { +func toContractNode(n eyriegraph.Node) graphcontracts.Node { return graphcontracts.Node{ ID: n.ID, Kind: graphcontracts.NodeKind(n.Kind), - Scope: toEagleScope(n.Scope), + Scope: toContractScope(n.Scope), CreatedAt: n.CreatedAt, EffectiveAt: n.EffectiveAt, - Provenance: toEagleProvenance(n.Provenance), + Provenance: toContractProvenance(n.Provenance), Attributes: n.Attributes, } } -func toEagleEdges(edges []eyriegraph.Edge) []graphcontracts.Edge { +func toContractEdges(edges []eyriegraph.Edge) []graphcontracts.Edge { out := make([]graphcontracts.Edge, len(edges)) for i, e := range edges { - out[i] = toEagleEdge(e) + out[i] = toContractEdge(e) } return out } -func toEagleEdge(e eyriegraph.Edge) graphcontracts.Edge { +func toContractEdge(e eyriegraph.Edge) graphcontracts.Edge { return graphcontracts.Edge{ ID: e.ID, Kind: graphcontracts.EdgeKind(e.Kind), - From: toEagleRef(e.From), - To: toEagleRef(e.To), - Scope: toEagleScope(e.Scope), + From: toContractRef(e.From), + To: toContractRef(e.To), + Scope: toContractScope(e.Scope), CreatedAt: e.CreatedAt, EffectiveAt: e.EffectiveAt, - Provenance: toEagleProvenance(e.Provenance), + Provenance: toContractProvenance(e.Provenance), Attributes: e.Attributes, } } -func toEagleEvents(events []eyriegraph.Event) []graphcontracts.Event { +func toContractEvents(events []eyriegraph.Event) []graphcontracts.Event { out := make([]graphcontracts.Event, len(events)) for i, ev := range events { - out[i] = toEagleEvent(ev) + out[i] = toContractEvent(ev) } return out } -func toEagleEvent(ev eyriegraph.Event) graphcontracts.Event { +func toContractEvent(ev eyriegraph.Event) graphcontracts.Event { return graphcontracts.Event{ ID: ev.ID, Type: graphcontracts.EventType(ev.Type), - Subject: toEagleRef(ev.Subject), - Scope: toEagleScope(ev.Scope), + Subject: toContractRef(ev.Subject), + Scope: toContractScope(ev.Scope), OccurredAt: ev.OccurredAt, CorrelationID: ev.CorrelationID, CausationID: ev.CausationID, IdempotencyKey: ev.IdempotencyKey, - Provenance: toEagleProvenance(ev.Provenance), + Provenance: toContractProvenance(ev.Provenance), } } -func toEagleRef(r eyriegraph.Ref) graphcontracts.Ref { +func toContractRef(r eyriegraph.Ref) graphcontracts.Ref { return graphcontracts.Ref{Kind: graphcontracts.NodeKind(r.Kind), ID: r.ID} } -func toEagleScope(s eyriegraph.Scope) graphcontracts.Scope { +func toContractScope(s eyriegraph.Scope) graphcontracts.Scope { return graphcontracts.Scope{TenantID: s.TenantID, ProjectID: s.ProjectID, RepositoryID: s.RepositoryID} } -func toEagleProvenance(p eyriegraph.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} @@ -418,82 +418,82 @@ func toEagleProvenance(p eyriegraph.Provenance) graphcontracts.Provenance { return graphcontracts.Provenance{Producer: p.Producer, Version: p.Version, SourceID: p.SourceID, Evidence: evidence} } -// Shrike's vendored graph contract types are byte-identical to eagle/graph, so +// Shrike's vendored graph contract types are byte-identical to contracts/graph, so // conversion is a field-by-field copy at the sibling boundary. -func shrikeToEagleNodes(nodes []shrikegraph.Node) []graphcontracts.Node { +func shrikeToContractNodes(nodes []shrikegraph.Node) []graphcontracts.Node { out := make([]graphcontracts.Node, len(nodes)) for i, n := range nodes { - out[i] = shrikeToEagleNode(n) + out[i] = shrikeToContractNode(n) } return out } -func shrikeToEagleNode(n shrikegraph.Node) graphcontracts.Node { +func shrikeToContractNode(n shrikegraph.Node) graphcontracts.Node { return graphcontracts.Node{ ID: n.ID, Kind: graphcontracts.NodeKind(n.Kind), - Scope: shrikeToEagleScope(n.Scope), + Scope: shrikeToContractScope(n.Scope), CreatedAt: n.CreatedAt, EffectiveAt: n.EffectiveAt, - Provenance: shrikeToEagleProvenance(n.Provenance), + Provenance: shrikeToContractProvenance(n.Provenance), Attributes: n.Attributes, } } -func shrikeToEagleEdges(edges []shrikegraph.Edge) []graphcontracts.Edge { +func shrikeToContractEdges(edges []shrikegraph.Edge) []graphcontracts.Edge { out := make([]graphcontracts.Edge, len(edges)) for i, e := range edges { - out[i] = shrikeToEagleEdge(e) + out[i] = shrikeToContractEdge(e) } return out } -func shrikeToEagleEdge(e shrikegraph.Edge) graphcontracts.Edge { +func shrikeToContractEdge(e shrikegraph.Edge) graphcontracts.Edge { return graphcontracts.Edge{ ID: e.ID, Kind: graphcontracts.EdgeKind(e.Kind), - From: shrikeToEagleRef(e.From), - To: shrikeToEagleRef(e.To), - Scope: shrikeToEagleScope(e.Scope), + From: shrikeToContractRef(e.From), + To: shrikeToContractRef(e.To), + Scope: shrikeToContractScope(e.Scope), CreatedAt: e.CreatedAt, EffectiveAt: e.EffectiveAt, - Provenance: shrikeToEagleProvenance(e.Provenance), + Provenance: shrikeToContractProvenance(e.Provenance), Attributes: e.Attributes, } } -func shrikeToEagleEvents(events []shrikegraph.Event) []graphcontracts.Event { +func shrikeToContractEvents(events []shrikegraph.Event) []graphcontracts.Event { out := make([]graphcontracts.Event, len(events)) for i, ev := range events { - out[i] = shrikeToEagleEvent(ev) + out[i] = shrikeToContractEvent(ev) } return out } -func shrikeToEagleEvent(ev shrikegraph.Event) graphcontracts.Event { +func shrikeToContractEvent(ev shrikegraph.Event) graphcontracts.Event { return graphcontracts.Event{ ID: ev.ID, Type: graphcontracts.EventType(ev.Type), - Subject: shrikeToEagleRef(ev.Subject), - Scope: shrikeToEagleScope(ev.Scope), + Subject: shrikeToContractRef(ev.Subject), + Scope: shrikeToContractScope(ev.Scope), OccurredAt: ev.OccurredAt, CorrelationID: ev.CorrelationID, CausationID: ev.CausationID, IdempotencyKey: ev.IdempotencyKey, - Provenance: shrikeToEagleProvenance(ev.Provenance), + Provenance: shrikeToContractProvenance(ev.Provenance), } } -func shrikeToEagleRef(r shrikegraph.Ref) graphcontracts.Ref { +func shrikeToContractRef(r shrikegraph.Ref) graphcontracts.Ref { return graphcontracts.Ref{Kind: graphcontracts.NodeKind(r.Kind), ID: r.ID} } -func shrikeToEagleScope(s shrikegraph.Scope) graphcontracts.Scope { +func shrikeToContractScope(s shrikegraph.Scope) graphcontracts.Scope { return graphcontracts.Scope{TenantID: s.TenantID, ProjectID: s.ProjectID, RepositoryID: s.RepositoryID} } -func shrikeToEagleProvenance(p shrikegraph.Provenance) graphcontracts.Provenance { +func shrikeToContractProvenance(p shrikegraph.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 72f56645..f2be5932 100644 --- a/internal/engine/execution_graph_observations_test.go +++ b/internal/engine/execution_graph_observations_test.go @@ -6,9 +6,9 @@ import ( "strings" "testing" - "github.com/GrayCodeAI/hawk/internal/graphjournal" - "github.com/GrayCodeAI/hawk/internal/tool" - "github.com/GrayCodeAI/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/internal/graphjournal" + "github.com/GrayCodeAI/graycode-cli/internal/tool" + "github.com/GrayCodeAI/graycode-cli/internal/types" shrike "github.com/GrayCodeAI/shrike" ) @@ -25,7 +25,7 @@ func (graphVerifyTool) Execute(context.Context, json.RawMessage) (string, error) } func TestToolExecutionAutomaticallyRecordsPolicyAndVerification(t *testing.T) { - t.Setenv("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) sess := NewSession("test", "test", "system", tool.NewRegistry(graphVerifyTool{})) sess.SetPersistID("graph-runtime-session") @@ -62,7 +62,7 @@ func TestToolExecutionAutomaticallyRecordsPolicyAndVerification(t *testing.T) { } func TestShrikeCompressionObservationIsPrivacySafe(t *testing.T) { - t.Setenv("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) sess := NewSession("test", "test", "system", tool.NewRegistry()) sess.SetPersistID("shrike-runtime-session") sess.recordShrikeCompressionObservation( @@ -89,7 +89,7 @@ func TestShrikeCompressionObservationIsPrivacySafe(t *testing.T) { } func TestShrikeRedactionObservationIsPrivacySafe(t *testing.T) { - t.Setenv("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) sess := NewSession("test", "test", "system", tool.NewRegistry()) sess.SetPersistID("shrike-redaction-session") sess.recordShrikeRedactionObservation( @@ -116,7 +116,7 @@ func TestShrikeRedactionObservationIsPrivacySafe(t *testing.T) { } func TestShrikeUsageBudgetObservationTracksAndProjectsAuthoritativeUsage(t *testing.T) { - t.Setenv("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) sess := NewSession("test", "test", "system", tool.NewRegistry()) sess.SetPersistID("shrike-usage-session") if err := sess.SetMaxBudgetUSD(1); err != nil { @@ -216,7 +216,7 @@ func TestDrainAlertsSurfacesHourlyWarning(t *testing.T) { } func TestEyrieOperationObservationIsPrivacySafe(t *testing.T) { - t.Setenv("HAWK_STATE_DIR", t.TempDir()) + t.Setenv("GRAYCODE_STATE_DIR", t.TempDir()) sess := NewSession("test", "test", "system", tool.NewRegistry()) sess.SetPersistID("eyrie-runtime-session") sess.recordEyrieOperationObservation( diff --git a/internal/engine/experiment_loop.go b/internal/engine/experiment_loop.go index 651bb69e..f611b1eb 100644 --- a/internal/engine/experiment_loop.go +++ b/internal/engine/experiment_loop.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/GrayCodeAI/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 b0df1d25..2e807fb5 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/hawk/internal/types" + "github.com/GrayCodeAI/graycode-cli/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 29ee0b79..c211d773 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 0d132ca7..b60ba0b9 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/hawk/internal/ui/icons" + "github.com/GrayCodeAI/graycode-cli/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 e36fb660..ed207622 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":"hawk","url":"https://github.com/GrayCodeAI/hawk"}` + jsonStr := `{"owner":{"login":"GrayCodeAI"},"name":"graycode-cli","url":"https://github.com/GrayCodeAI/graycode-cli"}` owner, repo, provider := parseGHRepoView(jsonStr) if owner != "GrayCodeAI" { t.Errorf("owner = %q, want %q", owner, "GrayCodeAI") } - if repo != "hawk" { - t.Errorf("repo = %q, want %q", repo, "hawk") + if repo != "graycode-cli" { + t.Errorf("repo = %q, want %q", repo, "graycode-cli") } 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/hawk.git + url = git@github.com:GrayCodeAI/graycode-cli.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 != "hawk" { - t.Errorf("repo = %q, want %q", repo, "hawk") + if repo != "graycode-cli" { + t.Errorf("repo = %q, want %q", repo, "graycode-cli") } 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 7df4780e..16165acd 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 // hawk/agent- when OnDefault + Suggested string // graycode/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("hawk/agent-%s", time.Now().Format("20060102-150405")) + info.Suggested = fmt.Sprintf("graycode/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 hawk/agent-* branch when currently +// EnsureAgentBranch creates and checks out a graycode/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("hawk/agent-%d", time.Now().Unix()) + name = fmt.Sprintf("graycode/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 (hawk/agent-*) + // #nosec G204 -- fixed git subcommand; branch name is generated internally (graycode/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 8913cc69..e722ee32 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, "hawk/agent-") { + if !strings.HasPrefix(name, "graycode/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 68fe6557..59ff2e1f 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, ".hawkhints"), []byte("Use Go idioms\nPrefer table-driven tests"), 0o644) + os.WriteFile(filepath.Join(dir, ".graycodehints"), []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, ".hawkhints"), []byte("hint"), 0o644) + os.WriteFile(filepath.Join(dir, ".graycodehints"), []byte("hint"), 0o644) h := NewHintsLoader() h.LoadHints(dir) diff --git a/internal/engine/hints_loader.go b/internal/engine/hints_loader.go index 5974f58b..361a5188 100644 --- a/internal/engine/hints_loader.go +++ b/internal/engine/hints_loader.go @@ -6,8 +6,8 @@ import ( "strings" ) -// HintsFilenames are the files hawk auto-loads for project context. -var HintsFilenames = []string{".hawkhints", "AGENTS.md"} +// HintsFilenames are the files graycode auto-loads for project context. +var HintsFilenames = []string{".graycodehints", "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 d8d39725..6003e779 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 [hawk:%s] %s", commentPrefix, a.Type, a.Content) + commentLine := fmt.Sprintf("%s [graycode:%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 { } } -// hawkAnnotationRe matches hawk annotation comment lines. -var hawkAnnotationRe = regexp.MustCompile(`^\s*(//|#|/\*|