From 4aac0479e58b73d2fee9bcff9b169fec29f14487 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Wed, 24 Jun 2026 20:27:34 +0530 Subject: [PATCH 1/4] docs: clarify hawk developer path --- README.md | 5 +- docs/DEVELOPER-PATH.md | 110 ++++++++++++++++++++++++++++++ internal/config/developer_path.go | 2 +- 3 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 docs/DEVELOPER-PATH.md diff --git a/README.md b/README.md index 7ead9d9f..f09e6e77 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ hawk is an AI-powered coding agent that lives in your terminal. It reads your co ## Status -**Hawk is in active development.** There is no public install script or release channel yet. We are building features, tests, and hardening in the open. +**Hawk is in active development.** Contributor source builds are the primary path today while we keep hardening the product in the open. Tagged releases and install assets may exist for validation, but they are not the recommended first path yet. Follow [GrayCode](https://github.com/GrayCodeAI) for progress. When Hawk is ready to try, we will announce it on [graycodeai.gateandtech.in](https://graycodeai.gateandtech.in/changelog). @@ -45,6 +45,7 @@ Follow [GrayCode](https://github.com/GrayCodeAI) for progress. When Hawk is read ```bash git clone https://github.com/GrayCodeAI/hawk && cd hawk +make setup # clones required support repos into external/ and syncs go.work go build -o hawk ./cmd/hawk ./hawk @@ -58,7 +59,7 @@ See [docs/SECURITY-DEVELOPER.md](docs/SECURITY-DEVELOPER.md) for the credential Optional for contributors: ```bash -go install github.com/GrayCodeAI/hawk@latest # requires a published tag +go install github.com/GrayCodeAI/hawk@latest # only after Hawk and support repo tags are published ``` ## Features diff --git a/docs/DEVELOPER-PATH.md b/docs/DEVELOPER-PATH.md new file mode 100644 index 00000000..cc01af57 --- /dev/null +++ b/docs/DEVELOPER-PATH.md @@ -0,0 +1,110 @@ +# Hawk Developer Path + +This guide explains what `hawk path` checks and how to get a fresh developer machine ready to use Hawk safely. + +## What "developer path" means + +For Hawk, the developer path is the minimum local setup required to chat, edit code, and keep credentials off disk: + +- A provider credential stored in the OS secret store +- A model selected in Hawk settings +- A local model catalog available through eyrie +- No plaintext API keys left in `provider.json` or legacy env files +- Safe defaults for Bash execution and filesystem access +- Optional but healthy ecosystem integrations like yaad memory + +Run the report at any time: + +```bash +hawk path +hawk path --strict +hawk doctor +hawk preflight +``` + +## Setup checklist + +### 1. Build and workspace setup + +If you are contributing from source, clone Hawk and fetch the support repos first: + +```bash +git clone https://github.com/GrayCodeAI/hawk && cd hawk +make setup +go build -o hawk ./cmd/hawk +``` + +`make setup` populates `external/` and syncs `go.work`, which Hawk expects in contributor builds. + +### 2. Configure credentials + +Start Hawk 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. + +Useful checks: + +```bash +hawk credentials status +hawk preflight +``` + +### 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. + +If the catalog is missing or empty: + +```bash +hawk models refresh +``` + +## Security checks + +`hawk path` treats these as important security conditions: + +- `provider.json` must not contain secret fields +- legacy `~/.hawk/env` or `~/.hawk/.env` files should be migrated away +- sensitive files like provider config and SSH paths should be blocked from agent reads + +If Hawk detects old plaintext secrets, run Hawk once and complete `/config`, or remove the secret fields manually after backing up the file. + +Read the full credential and isolation model in [SECURITY-DEVELOPER.md](./SECURITY-DEVELOPER.md). + +## Sandbox checks + +When Docker is available, Hawk prefers containerized Bash execution for stronger isolation. If Docker is unavailable, `hawk path` warns but does not block normal local development. + +Use strict mode if you want Docker to be required: + +```bash +hawk path --strict +``` + +## Ecosystem checks + +`hawk path` also verifies the core support layer behind Hawk: + +- `eyrie` for provider routing and preflight readiness +- `tok` for token estimation and compression +- `yaad` for optional persistent memory + +If you want the broader status summary: + +```bash +hawk ecosystem +hawk doctor +``` + +## Typical recovery path + +If `hawk path` says you are not ready, this is the intended order: + +1. Run `hawk` +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` + +If security items still fail, fix those before treating the machine as ready. diff --git a/internal/config/developer_path.go b/internal/config/developer_path.go index 9c23e6a4..dcc06ae6 100644 --- a/internal/config/developer_path.go +++ b/internal/config/developer_path.go @@ -294,7 +294,7 @@ func FormatDeveloperPathReport(ctx context.Context) string { } b.WriteString("Next: " + r.NextStep + "\n") - b.WriteString("\nDocs: docs/SECURITY-DEVELOPER.md · hawk doctor · hawk preflight\n") + b.WriteString("\nDocs: docs/DEVELOPER-PATH.md · docs/SECURITY-DEVELOPER.md · hawk doctor · hawk preflight\n") return strings.TrimRight(b.String(), "\n") } From 8cabbcf18afe3f8a0f600953e17a9a0076b4064b Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Wed, 24 Jun 2026 20:41:18 +0530 Subject: [PATCH 2/4] build: align contributor setup with ci --- Makefile | 23 ++++++++++++++++++----- scripts/fresh-setup-test.sh | 10 ++++++++-- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index bf15fc2e..aec0e045 100644 --- a/Makefile +++ b/Makefile @@ -162,12 +162,25 @@ setup: ## Set up local development environment (go.work + external repos). @echo "=== Setting up hawk development environment ===" @mkdir -p external @for repo in $(WORKSPACE_REPOS); do \ - if [ ! -d "external/$$repo" ]; then \ - echo "Cloning $$repo..."; \ - git clone --depth=1 "https://github.com/GrayCodeAI/$$repo.git" "external/$$repo" 2>/dev/null || \ - echo " ⚠ Could not clone $$repo (may not exist yet or no access)"; \ + dest="external/$$repo"; \ + if [ -d "$$dest/.git" ]; then \ + echo "✓ $$dest already exists"; \ else \ - echo "✓ external/$$repo already exists"; \ + commit=$$(git ls-tree HEAD "$$dest" 2>/dev/null | awk '{print $$3}' || true); \ + if [ -n "$$commit" ]; then \ + echo "Cloning $$repo at pinned commit $$commit..."; \ + git clone "https://github.com/GrayCodeAI/$$repo.git" "$$dest" 2>/dev/null && \ + (cd "$$dest" && git checkout --quiet "$$commit") || \ + echo " ⚠ Could not clone $$repo at pinned commit $$commit"; \ + else \ + ref=$$(git branch --show-current 2>/dev/null || echo main); \ + if ! git ls-remote --heads "https://github.com/GrayCodeAI/$$repo.git" "$$ref" | grep -q .; then \ + ref=main; \ + fi; \ + echo "Cloning $$repo at branch $$ref..."; \ + git clone --depth=1 --branch "$$ref" "https://github.com/GrayCodeAI/$$repo.git" "$$dest" 2>/dev/null || \ + echo " ⚠ Could not clone $$repo (may not exist yet or no access)"; \ + fi; \ fi; \ done @echo "Generating go.work..." diff --git a/scripts/fresh-setup-test.sh b/scripts/fresh-setup-test.sh index 421d5faa..aed3c2a3 100755 --- a/scripts/fresh-setup-test.sh +++ b/scripts/fresh-setup-test.sh @@ -5,6 +5,10 @@ set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT" +echo "==> Ensuring ecosystem repos are present (make setup)..." +make setup + +echo "" echo "==> Building hawk..." go build -o hawk ./cmd/hawk/ echo " $(./hawk --version 2>/dev/null || echo built)" @@ -18,15 +22,17 @@ echo "==> Optional: isolated config dir (sessions/settings separate from ~/.hawk ISOLATED="${HAWK_FRESH_CONFIG_DIR:-$(mktemp -d)/hawk-fresh}" mkdir -p "$ISOLATED" rm -f "$ISOLATED/learned_credential_prefixes.json" +rm -f "$ISOLATED/settings.json" echo " HAWK_CONFIG_DIR=$ISOLATED" echo "" echo "To match first-run Setup (no API key in Keys tab):" echo " - Remove stored keys: ./hawk credentials remove (e.g. anthropic, openai)" -echo " - Or clear model in settings so /config opens on start" +echo " - This script already clears the isolated settings.json so model selection starts fresh" echo "" echo "Run TUI (from $ROOT):" echo " export HAWK_CONFIG_DIR=\"$ISOLATED\"" echo " ./hawk" echo "" -echo "In Setup: Keys → Add key · → paste → one probe → Models." \ No newline at end of file +echo "In Setup: Keys → Add key · → paste → one probe → Models." +echo "After setup: ./hawk path and ./hawk preflight" From 435e785648bb23691030a64f02aebdb52ff87d0e Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Wed, 24 Jun 2026 20:53:59 +0530 Subject: [PATCH 3/4] build: handle windows release archives in installer --- install.sh | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/install.sh b/install.sh index 8fad76a6..f173dcb4 100755 --- a/install.sh +++ b/install.sh @@ -11,36 +11,50 @@ case "$ARCH" in aarch64|arm64) ARCH="arm64" ;; esac +ARCHIVE_EXT="tar.gz" +EXTRACT_CMD="tar xz -C \"$TMP\" -f \"$ARCHIVE\"" +BIN_NAME="$BINARY" + +case "$OS" in + mingw*|msys*|cygwin*) + OS="windows" + ARCHIVE_EXT="zip" + EXTRACT_CMD="unzip -q \"$ARCHIVE\" -d \"$TMP\"" + BIN_NAME="${BINARY}.exe" + ;; +esac + LATEST=$(curl -sL "https://api.github.com/repos/$REPO/releases/latest" | grep '"tag_name"' | sed -E 's/.*"v([^"]+)".*/\1/') if [ -z "$LATEST" ]; then echo "Error: could not determine latest version" exit 1 fi -URL="https://github.com/$REPO/releases/download/v${LATEST}/${BINARY}_${LATEST}_${OS}_${ARCH}.tar.gz" +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}..." TMP=$(mktemp -d) -TARBALL="$TMP/${BINARY}_${LATEST}_${OS}_${ARCH}.tar.gz" -curl -sL "$URL" -o "$TARBALL" +ARCHIVE="$TMP/${ARCHIVE_NAME}" +curl -sL "$URL" -o "$ARCHIVE" CHECKSUMS_URL="https://github.com/$REPO/releases/download/v${LATEST}/checksums.txt" CHECKSUMS="$TMP/checksums.txt" curl -sL "$CHECKSUMS_URL" -o "$CHECKSUMS" if command -v sha256sum >/dev/null 2>&1; then - ACTUAL=$(sha256sum "$TARBALL" | awk '{print $1}') + ACTUAL=$(sha256sum "$ARCHIVE" | awk '{print $1}') elif command -v shasum >/dev/null 2>&1; then - ACTUAL=$(shasum -a 256 "$TARBALL" | awk '{print $1}') + ACTUAL=$(shasum -a 256 "$ARCHIVE" | awk '{print $1}') else echo "Error: no sha256sum or shasum found; cannot verify checksum" rm -rf "$TMP" exit 1 fi -EXPECTED=$(grep "${BINARY}_${LATEST}_${OS}_${ARCH}.tar.gz" "$CHECKSUMS" | awk '{print $1}') +EXPECTED=$(grep "${ARCHIVE_NAME}" "$CHECKSUMS" | awk '{print $1}') if [ -z "$EXPECTED" ]; then - echo "Error: checksum not found for ${BINARY}_${LATEST}_${OS}_${ARCH}.tar.gz in checksums.txt" + echo "Error: checksum not found for ${ARCHIVE_NAME} in checksums.txt" rm -rf "$TMP" exit 1 fi @@ -54,15 +68,21 @@ if [ "$ACTUAL" != "$EXPECTED" ]; then fi echo "Checksum verified." -tar xz -C "$TMP" -f "$TARBALL" +if [ "$OS" = "windows" ] && ! command -v unzip >/dev/null 2>&1; then + echo "Error: unzip is required to install Windows release archives" + rm -rf "$TMP" + exit 1 +fi + +eval "$EXTRACT_CMD" INSTALL_DIR="/usr/local/bin" if [ ! -w "$INSTALL_DIR" ]; then echo "Installing to $INSTALL_DIR (requires sudo)..." - sudo mv "$TMP/$BINARY" "$INSTALL_DIR/" + sudo mv "$TMP/$BIN_NAME" "$INSTALL_DIR/" else - mv "$TMP/$BINARY" "$INSTALL_DIR/" + mv "$TMP/$BIN_NAME" "$INSTALL_DIR/" fi rm -rf "$TMP" -echo "hawk v${LATEST} installed to $INSTALL_DIR/$BINARY" +echo "hawk v${LATEST} installed to $INSTALL_DIR/$BIN_NAME" From b31317edaa25d6ea15b91637956b77818d44dd86 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Wed, 24 Jun 2026 21:10:10 +0530 Subject: [PATCH 4/4] build: tolerate dev tool bootstrap failures --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index aec0e045..cec29acf 100644 --- a/Makefile +++ b/Makefile @@ -204,11 +204,11 @@ setup: ## Set up local development environment (go.work + external repos). @echo "GOBIN: $$(go env GOPATH)/bin" @echo "" @echo "=== Installing development tools ===" - @command -v $(GOFUMPT) >/dev/null 2>&1 || go install mvdan.cc/gofumpt@latest - @command -v $(GOIMPORTS) >/dev/null 2>&1 || go install golang.org/x/tools/cmd/goimports@latest - @command -v $(GOLANGCI) >/dev/null 2>&1 || go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest - @command -v $(GOVULNCHECK) >/dev/null 2>&1 || go install golang.org/x/vuln/cmd/govulncheck@latest - @command -v lefthook >/dev/null 2>&1 || go install github.com/evilmartians/lefthook@latest + @command -v $(GOFUMPT) >/dev/null 2>&1 || go install mvdan.cc/gofumpt@latest || echo " ⚠ Could not install gofumpt" + @command -v $(GOIMPORTS) >/dev/null 2>&1 || go install golang.org/x/tools/cmd/goimports@latest || echo " ⚠ Could not install goimports" + @command -v $(GOLANGCI) >/dev/null 2>&1 || go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest || echo " ⚠ Could not install golangci-lint" + @command -v $(GOVULNCHECK) >/dev/null 2>&1 || go install golang.org/x/vuln/cmd/govulncheck@latest || echo " ⚠ Could not install govulncheck" + @command -v lefthook >/dev/null 2>&1 || go install github.com/evilmartians/lefthook@latest || echo " ⚠ Could not install lefthook" @echo "✓ All tools installed" @echo "" @echo "=== Installing git hooks ==="