|
1 | | -# AGENTS.md — Inspect |
| 1 | +--- |
| 2 | +description: Extending hawk-eco — how to write AGENTS.md files, custom specialists, skills, hooks, MCP servers, and plugins. |
| 3 | +globs: "*.go, *.js, *.md, *.json, *.toml, *.yaml, *.yml" |
| 4 | +alwaysApply: false |
| 5 | +--- |
2 | 6 |
|
3 | | -Website security auditing and crawling library for Go. Crawls sites concurrently, runs checks and declarative rules, generates findings with severity and CWE references. |
| 7 | +# Extending hawk-eco |
4 | 8 |
|
5 | | -## Design Principles |
| 9 | +hawk-eco is an open-source code intelligence platform. This document describes how to extend it with custom tools, skills, hooks, and integrations. |
6 | 10 |
|
7 | | -- **Library** — importable Go library + embeddable MCP server (no CLI binary) |
8 | | -- **No LLM dependency** — pure static analysis on crawled pages |
9 | | -- **Extensible** — custom checks (Go code) + declarative rules (no code required) |
| 11 | +## 1. Drop a project `AGENTS.md` |
10 | 12 |
|
11 | | -## Build & Test |
| 13 | +When hawk-eco 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)`). |
| 14 | + |
| 15 | +Accepted file names, in priority order at each level: |
| 16 | + |
| 17 | +| Path | Notes | |
| 18 | +| --- | --- | |
| 19 | +| `./AGENTS.md` | The classic spot — committed to your repo, shared with the team. | |
| 20 | +| `./ZERO.md` | Brand-specific alias. Same format, lower priority. | |
| 21 | +| `./.zero/AGENTS.md` | Project-local, hidden, gitignored. Personal notes that stay out of git. | |
| 22 | + |
| 23 | +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. |
| 24 | + |
| 25 | +Both files use the same format. YAML frontmatter is optional; the markdown body is loaded as instructions for the agent. hawk-eco reads the file once at session start, so changes take effect on the next launch — not mid-session. |
| 26 | + |
| 27 | +```markdown |
| 28 | +# Project conventions for <your project> |
| 29 | + |
| 30 | +- Build with `make`, not `go build` directly. |
| 31 | +- Tests live next to the source file (`foo_test.go` next to `foo.go`). |
| 32 | +- Run `make lint` before opening a PR. |
| 33 | +- Never edit files under `third_party/` — those are vendored. |
| 34 | +``` |
| 35 | + |
| 36 | +Tips: |
| 37 | + |
| 38 | +- Keep each file under ~8 KiB. hawk-eco caps the **total** across all matched files at 32 KiB; everything past the cap is dropped. |
| 39 | +- Re-state rules in the imperative voice: "Run `make lint`", not "you should consider running the linter". |
| 40 | +- Don't put secrets, model IDs, or environment-specific paths in `AGENTS.md`. Use config files for those. |
| 41 | +- In a monorepo, drop a narrower `AGENTS.md` in each sub-tree (e.g. `services/api/AGENTS.md`). hawk-eco picks those up automatically when you launch from inside the sub-tree. |
| 42 | +- 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. |
| 43 | + |
| 44 | +### Personal guidelines, across every project |
| 45 | + |
| 46 | +For preferences that follow *you*, not a specific repo (tone, tooling habits, workflow), drop a `ZERO.md` in your user config directory: `~/.config/hawk-eco/ZERO.md` on Linux/macOS, `%AppData%\Roaming\hawk-eco\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. |
| 47 | + |
| 48 | +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. |
| 49 | + |
| 50 | +## 2. Custom specialists |
| 51 | + |
| 52 | +Specialists are hawk-eco's sub-agents. Three scopes, in priority order: |
| 53 | + |
| 54 | +| Scope | Path | Shared? | |
| 55 | +| --- | --- | --- | |
| 56 | +| Built-in | compiled into hawk-eco | yes | |
| 57 | +| User | `~/.config/hawk-eco/specialists/*.md` | no — your machine only | |
| 58 | +| Project | `./.zero/specialists/*.md` | yes — the repo team | |
| 59 | + |
| 60 | +Project overrides user overrides built-in when names collide. |
| 61 | + |
| 62 | +A specialist is a markdown manifest with frontmatter and a system prompt: |
| 63 | + |
| 64 | +```markdown |
| 65 | +--- |
| 66 | +description: Reviews API changes for breaking-change risk and missing tests. |
| 67 | +tools: read-only,plan |
| 68 | +--- |
| 69 | + |
| 70 | +You review API changes. For every changed hunk in `internal/api/` or any file |
| 71 | +that ends in `_api.go`: |
| 72 | + |
| 73 | +1. Confirm the public signature is backward-compatible, or note the breaking |
| 74 | + change explicitly with the migration path. |
| 75 | +2. Confirm a corresponding test exists in `internal/api/*_test.go` and that |
| 76 | + the new behaviour is exercised. |
| 77 | +3. Flag any new exported symbol without a doc comment. |
| 78 | + |
| 79 | +Reply with one JSON object per finding: `{"file", "line", "severity", "message", "fix"}`. |
| 80 | +``` |
| 81 | + |
| 82 | +CLI management: |
12 | 83 |
|
13 | 84 | ```bash |
14 | | -go test ./... # Run all tests |
15 | | -go test -race ./... # Race detector |
16 | | -go test -coverprofile=c.out ./... # Coverage |
17 | | -go vet ./... # Static analysis |
18 | | -gofumpt -w . # Format |
| 85 | +hawk-eco specialist list |
| 86 | +hawk-eco specialist show api-reviewer |
| 87 | +hawk-eco specialist create api-reviewer \ |
| 88 | + --project \ |
| 89 | + --description "Reviews API changes" \ |
| 90 | + --tools read-only,plan \ |
| 91 | + --prompt "$(cat api-reviewer.md)" |
| 92 | +hawk-eco specialist edit api-reviewer --project |
| 93 | +hawk-eco specialist delete api-reviewer --project |
| 94 | +hawk-eco specialist path # prints the resolved specialists directory |
19 | 95 | ``` |
20 | 96 |
|
21 | | -## Architecture |
22 | | - |
23 | | -- `crawler.go` — Concurrent website crawler with depth control |
24 | | -- `check.go` — Check interface and built-in security checks |
25 | | -- `rule.go` — Declarative rule engine (YAML-based) |
26 | | -- `finding.go` — Findings with severity, CWE, and evidence |
27 | | -- `report.go` — Report generation (JSON, SARIF, HTML) |
28 | | - |
29 | | -## Conventions |
30 | | - |
31 | | -- Go 1.26+, pure Go, no CGO |
32 | | -- Table-driven tests |
33 | | -- Conventional Commits: `feat:`, `fix:`, `docs:`, `refactor:`, `test:` |
34 | | -- No `Co-authored-by:` trailers (auto-stripped by githook) |
35 | | -- `gofumpt` formatting enforced in CI |
36 | | -- CWE references required for all security findings |
37 | | - |
38 | | -## Common Pitfalls |
39 | | - |
40 | | -- Crawler tests need HTTP test servers — use `httptest.NewServer` |
41 | | -- Rule YAML must be validated before execution |
42 | | -- Session cookie matching uses substring, not exact match |
43 | | - |
44 | | -## Naming Conventions |
45 | | - |
46 | | -- **Types are domain nouns**: `Finding`, `Report`, `Stats`, `Page`, `PageLink`, `Checker`, `RuleCheck` |
47 | | -- **Option functions use `With` prefix**: `WithChecks()`, `WithDepth()`, `WithConcurrency()`, `WithAllowPrivateIPs()` |
48 | | -- **Preset options are bare vars**: `Quick`, `Standard`, `Deep`, `SecurityOnly`, `CI` — exported `var Option` values |
49 | | -- **Severity is a type alias**: `type Severity = types.Severity` from `hawk-core-contracts/types` — shared across hawk-eco |
50 | | -- **Internal adapters use `Adapter` suffix**: `ruleCheckAdapter`, `customCheckAdapter` — bridge public to internal interfaces |
51 | | -- **Check names are lowercase strings**: `"security"`, `"links"`, `"forms"`, `"a11y"`, `"performance"` — used in `WithChecks()` |
52 | | -- **Error handling**: `Scan()` returns `(*Report, error)` — validation errors for empty URL, nil errors for success |
53 | | - |
54 | | -## API Patterns |
55 | | - |
56 | | -- **Functional options pattern**: same as sight — `Option` interface with `optFunc` adapter, `buildConfig()` merge |
57 | | -- **One-shot + reusable**: `Scan(ctx, target, opts...)` creates a `Scanner` internally; `NewScanner(opts...)` for reuse |
58 | | -- **Checker interface for extensibility**: `Name() string` + `Run(ctx, pages) []Finding` — register via `RegisterCheck()` |
59 | | -- **RuleCheck for declarative rules**: `HeaderMatch`, `HeaderMissing`, `BodyMatch`, `BodyMissing`, `URLMatch` patterns |
60 | | -- **Global + per-scanner custom checks**: `RegisterCheck()`/`RegisterRule()` for global; pass slices to `Scanner` for scoped |
61 | | -- **Report.Failed()**: checks if any finding meets `FailOn` severity threshold — same pattern as sight |
62 | | -- **ReDoS protection**: all user-supplied regex patterns go through `compileWithTimeout()` and `matchWithTimeout()` with 1s/100ms limits |
63 | | -- **Regex complexity check**: `checkRegexComplexity()` rejects nested quantifiers and deep group nesting before compilation |
64 | | - |
65 | | -## Testing Patterns |
66 | | - |
67 | | -- **External test package**: `package inspect_test` — tests import `inspect` as a consumer would |
68 | | -- **httptest.NewServer for all tests**: each test spins up a mock HTTP server with specific HTML/headers/responses |
69 | | -- **Test patterns by concern**: `TestScan_BasicSite` (links), `TestScan_SecurityHeaders`, `TestScan_FormCSRF`, `TestScan_Accessibility` |
70 | | -- **Always pass `WithAllowPrivateIPs()`**: tests run against `127.0.0.1` — without this flag, localhost is blocked |
71 | | -- **Always pass `WithDepth(1)`**: keeps tests fast by limiting crawl depth |
72 | | -- **Finding assertions**: iterate `report.Findings` and check specific `Check`, `Severity`, `Message` fields |
73 | | -- **Preset smoke test**: `TestScan_Presets` runs all presets against a simple server — catches config panics |
74 | | -- **ClearCustomChecks() in tests**: call before registering test-specific checks to avoid global state leaks |
75 | | -- **Report method tests**: `TestReport_Failed`, `TestReport_MaxSeverity` — test on struct literals, no HTTP needed |
76 | | - |
77 | | -## Refactoring Guidelines |
78 | | - |
79 | | -- **Safe to refactor**: `checkRegexComplexity()`, `compileWithTimeout()`, `matchWithTimeout()` — internal helpers |
80 | | -- **Safe to refactor**: `truncateEvidence()`, `intIn()` — pure utility functions |
81 | | -- **Safe to refactor**: `parseInspectTOML()`, `parseInspectKeyValue()`, `applyFileConfig()` — config parsing internals |
82 | | -- **Do not touch**: `Checker` interface (`Name()`, `Run()`) — breaking change for all custom check implementations |
83 | | -- **Do not touch**: `RuleCheck` struct field names — used by consumers to define declarative rules |
84 | | -- **Do not touch**: `Finding`, `Report`, `Stats` struct field names/tags — JSON serialization contract |
85 | | -- **Safe to extend**: add new `Option` functions, new presets, new built-in checks in `checks/` package |
86 | | -- **When adding checks**: create a new file in `checks/`, implement `Checker` interface, register in `init()` |
87 | | - |
88 | | -## Key File Locations |
89 | | - |
90 | | -| What | Where | |
91 | | -|---|---| |
92 | | -| Public API entry point | `inspect.go` (types, `Scan()`, `Finding`, `Report`, `Stats`) | |
93 | | -| Check interface & adapters | `check.go` (`Checker`, `RuleCheck`, `RegisterCheck()`, `RegisterRule()`, ReDoS protection) | |
94 | | -| Scanner implementation | `scanner.go` (crawler orchestration, check execution) | |
95 | | -| Configuration & presets | `options.go` (`config` struct, `With*` functions, presets) | |
96 | | -| Config file loading | `config.go` (`.inspect.toml` parsing, `LoadConfig()`) | |
97 | | -| Severity type alias | `severity.go` (re-exports from `hawk-core-contracts/types`) | |
98 | | -| SARIF output | `sarif.go` | |
99 | | -| CI output formatting | `ci_output.go` | |
100 | | -| Built-in checks | `checks/` directory | |
101 | | -| Internal crawler | `internal/crawler/` | |
102 | | -| Internal check runner | `internal/check/` | |
103 | | -| Browser-based crawling | `browser.go`, `browser/` | |
104 | | -| LLM scanner integration | `llm_scanner.go` | |
105 | | -| API security checks | `api_security.go` | |
106 | | -| Dependency checking | `dependency_check.go` | |
107 | | -| SBOM generation | `sbom.go` | |
108 | | -| Main test file | `inspect_test.go` (httptest servers, per-concern scenarios) | |
109 | | -| Linter config | `.golangci.yml` (errcheck, govet, staticcheck, gocritic, bodyclose, noctx) | |
| 97 | +## 3. Skills |
| 98 | + |
| 99 | +Skills are markdown instruction files that extend agent capabilities. They can be: |
| 100 | +- Project-scoped: dropped in `./.zero/skills/` or `./skills/` |
| 101 | +- User-scoped: dropped in `~/.config/hawk-eco/skills/` |
| 102 | + |
| 103 | +A skill manifest: |
| 104 | + |
| 105 | +```markdown |
| 106 | +--- |
| 107 | +description: How to review Go code for security issues |
| 108 | +globs: "*.go" |
| 109 | +alwaysApply: true |
| 110 | +--- |
| 111 | + |
| 112 | +When reviewing Go code for security: |
| 113 | + |
| 114 | +1. Check for SQL injection patterns |
| 115 | +2. Verify error handling doesn't expose sensitive data |
| 116 | +3. Confirm secrets are not hardcoded |
| 117 | +4. Validate input sanitization |
| 118 | +``` |
| 119 | + |
| 120 | +## 4. Hooks |
| 121 | + |
| 122 | +Hooks allow custom commands to run at specific lifecycle points: |
| 123 | +- `beforeReview` — runs before code review starts |
| 124 | +- `afterReview` — runs after code review completes |
| 125 | +- `sessionStart` — runs at session initialization |
| 126 | +- `sessionEnd` — runs at session teardown |
| 127 | + |
| 128 | +```bash |
| 129 | +hawk-eco hook add beforeReview --command "lint-check" |
| 130 | +hawk-eco hook remove beforeReview |
| 131 | +hawk-eco hook list |
| 132 | +``` |
| 133 | + |
| 134 | +## 5. MCP integration |
| 135 | + |
| 136 | +MCP (Model Context Protocol) servers can expose tools to hawk-eco: |
| 137 | + |
| 138 | +```bash |
| 139 | +hawk-eco mcp add --name server --url http://localhost:8080 |
| 140 | +hawk-eco mcp remove server |
| 141 | +hawk-eco mcp list |
| 142 | +``` |
| 143 | + |
| 144 | +## 6. Plugins |
| 145 | + |
| 146 | +Plugins extend hawk-eco with custom tools and capabilities: |
| 147 | + |
| 148 | +```bash |
| 149 | +hawk-eco plugin add --name my-plugin --path ./my-plugin |
| 150 | +hawk-eco plugin remove my-plugin |
| 151 | +hawk-eco plugin list |
| 152 | +``` |
| 153 | + |
| 154 | +## 7. Verification |
| 155 | + |
| 156 | +hawk-eco includes a self-verification system to validate local changes before contributing: |
| 157 | + |
| 158 | +```bash |
| 159 | +hawk-eco verify |
| 160 | +hawk-eco verify --fix |
| 161 | +``` |
| 162 | + |
| 163 | +## Development |
| 164 | + |
| 165 | +```bash |
| 166 | +make lint |
| 167 | +hawk-eco verify |
| 168 | +``` |
0 commit comments