Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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..."
Expand All @@ -191,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 ==="
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,15 @@ 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).

## Quick Start (contributors — from source)

```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

Expand All @@ -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
Expand Down
110 changes: 110 additions & 0 deletions docs/DEVELOPER-PATH.md
Original file line number Diff line number Diff line change
@@ -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.
42 changes: 31 additions & 11 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
2 changes: 1 addition & 1 deletion internal/config/developer_path.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}

Expand Down
10 changes: 8 additions & 2 deletions scripts/fresh-setup-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand All @@ -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 <provider> (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 · <Gateway> → paste → one probe → Models."
echo "In Setup: Keys → Add key · <Gateway> → paste → one probe → Models."
echo "After setup: ./hawk path and ./hawk preflight"
Loading