|
| 1 | +# Agent Guide for `why` Database Entries |
| 2 | + |
| 3 | +This file helps coding agents (Claude, Copilot, Cursor, etc.) create correct `why` database entries. Read this before generating YAML files. |
| 4 | + |
| 5 | +## Project context |
| 6 | + |
| 7 | +`why` is a CLI tool that captures stderr from failed commands and explains the error in plain English. The error database lives in `db/` as YAML files — one file per error, organized by tool. |
| 8 | + |
| 9 | +## Directory structure |
| 10 | + |
| 11 | +``` |
| 12 | +db/ |
| 13 | + TEMPLATE.yaml # universal template — copy this |
| 14 | + rust/ # rustc compiler errors (E0499.yaml) |
| 15 | + python/ # Python exceptions (TypeError.yaml) |
| 16 | + c_cpp/ # gcc/clang errors (undefined-reference.yaml) |
| 17 | + go/ # Go compiler errors (undefined.yaml) |
| 18 | + git/ # git CLI errors (merge-conflict.yaml) |
| 19 | + docker/ # Docker CLI errors (daemon-not-running.yaml) |
| 20 | + npm/ # npm CLI errors (eresolve.yaml) |
| 21 | + cargo/ # Cargo build errors (could-not-compile.yaml) |
| 22 | + why/ # errors from the why tool itself |
| 23 | +``` |
| 24 | + |
| 25 | +To add a new tool, just create the directory. No code changes needed. |
| 26 | + |
| 27 | +## Creating an entry |
| 28 | + |
| 29 | +### Filename |
| 30 | + |
| 31 | +- Must match the `id` field exactly: `id: merge-conflict` → `merge-conflict.yaml` |
| 32 | +- Rust errors use the error code: `E0499.yaml` |
| 33 | +- Python errors use the exception name: `TypeError.yaml` |
| 34 | +- Everything else uses lowercase slugs: `daemon-not-running.yaml` |
| 35 | + |
| 36 | +### Required fields |
| 37 | + |
| 38 | +```yaml |
| 39 | +id: merge-conflict # matches filename without .yaml |
| 40 | +tool: git # the CLI/compiler that produces this error |
| 41 | +language: git # matches the parent directory name |
| 42 | +title: Merge conflict # under 60 characters |
| 43 | +explain: | # plain English, 3-6 sentences |
| 44 | + ... |
| 45 | +fix: | # concrete steps, 2-4 actions |
| 46 | + ... |
| 47 | +``` |
| 48 | +
|
| 49 | +### Patterns (required for auto-detection) |
| 50 | +
|
| 51 | +The `patterns` field is how `why` matches stderr output to this entry. Each pattern is a list of substrings that must ALL appear in the same stderr line (AND logic). Multiple patterns give OR logic. |
| 52 | + |
| 53 | +```yaml |
| 54 | +patterns: |
| 55 | + - ["Automatic merge failed"] # matches this exact substring |
| 56 | + - ["CONFLICT", "Merge conflict"] # both must appear in same line |
| 57 | + - ["fix conflicts and then commit"] # OR this matches |
| 58 | +``` |
| 59 | + |
| 60 | +Rules: |
| 61 | + |
| 62 | +- Use the most specific substring possible — avoid single common words |
| 63 | +- Copy-paste from real error output when possible |
| 64 | +- No regex — plain substring matching only |
| 65 | +- Patterns are case-sensitive |
| 66 | +- Each pattern group is checked against each line independently |
| 67 | +- First match wins across the entire database, so be specific enough to avoid collisions |
| 68 | + |
| 69 | +When two tools produce similar errors, use `exclude`: |
| 70 | + |
| 71 | +```yaml |
| 72 | +# C/C++ "too many arguments" would also match Go errors |
| 73 | +patterns: |
| 74 | + - ["too many arguments"] |
| 75 | +exclude: |
| 76 | + - "go" |
| 77 | +``` |
| 78 | + |
| 79 | +Tools with structured error codes (`rustc`, `python`) don't need patterns — detection works via regex on the error code / exception name. |
| 80 | + |
| 81 | +### Optional fields |
| 82 | + |
| 83 | +```yaml |
| 84 | +tags: [merge, conflict, branches] # for search/filtering |
| 85 | +exclude: ["text that must NOT appear"] # avoid false matches |
| 86 | +example_error: | # real error output |
| 87 | + Auto-merging src/main.rs |
| 88 | + CONFLICT (content): Merge conflict in src/main.rs |
| 89 | +example_code: | # minimal reproduction |
| 90 | + // code that triggers the error |
| 91 | +links: # official docs |
| 92 | + - https://git-scm.com/docs/git-merge |
| 93 | +``` |
| 94 | + |
| 95 | +## Style rules |
| 96 | + |
| 97 | +These are strict — entries that violate them need revision: |
| 98 | + |
| 99 | +1. **`explain` uses second person.** "You tried to..." not "The user tried to..." |
| 100 | +2. **`explain` is plain English.** If you use a technical term, define it briefly. |
| 101 | +3. **`fix` gives concrete commands/steps.** "Run `git pull --rebase`" not "update your branch." |
| 102 | +4. **No filler.** Don't start with "This error occurs when..." — just explain what happened. |
| 103 | +5. **No emoji.** Anywhere. |
| 104 | +6. **`language` must match the directory name exactly.** `db/c_cpp/` → `language: c_cpp`, not `language: cpp`. |
| 105 | +7. **`id` must match the filename exactly.** No exceptions. |
| 106 | + |
| 107 | +## Validation |
| 108 | + |
| 109 | +After creating entries, run: |
| 110 | + |
| 111 | +```sh |
| 112 | +python scripts/validate.py |
| 113 | +``` |
| 114 | + |
| 115 | +This checks: required fields, id/filename match, language/directory match, patterns structure, duplicate IDs, unknown fields, and empty values. |
| 116 | + |
| 117 | +Also run the Rust tests to make sure detection still works: |
| 118 | + |
| 119 | +```sh |
| 120 | +cargo test |
| 121 | +``` |
| 122 | + |
| 123 | +## Common mistakes to avoid |
| 124 | + |
| 125 | +- Setting `language: cpp` when the directory is `c_cpp` — must match exactly |
| 126 | +- Writing patterns that are too generic (e.g., `["error"]`) — will match everything |
| 127 | +- Forgetting the `|` after `explain:` and `fix:` — these must be YAML block scalars |
| 128 | +- Adding fields not in the schema — the validator rejects unknown fields |
| 129 | +- Putting patterns on entries in `db/rust/` or `db/python/` — not needed, detection uses regex for those |
| 130 | + |
| 131 | +## Full example |
| 132 | + |
| 133 | +```yaml |
| 134 | +id: port-already-in-use |
| 135 | +tool: docker |
| 136 | +language: docker |
| 137 | +title: Port is already allocated |
| 138 | +tags: [port, networking, bind] |
| 139 | +patterns: |
| 140 | + - ["port is already allocated"] |
| 141 | + - ["address already in use"] |
| 142 | + - ["Bind for", "failed"] |
| 143 | +
|
| 144 | +explain: | |
| 145 | + Docker tried to bind a container port to a host port that is already in use by |
| 146 | + another container or a process running on your machine. |
| 147 | +
|
| 148 | +fix: | |
| 149 | + 1. Find what is using the port: lsof -i :<port> or ss -tlnp | grep <port> |
| 150 | + 2. Stop the conflicting container: docker stop <container> |
| 151 | + 3. Or use a different host port: docker run -p 8081:80 instead of -p 8080:80 |
| 152 | + 4. To see all running containers: docker ps |
| 153 | +
|
| 154 | +example_error: | |
| 155 | + docker: Error response from daemon: driver failed programming external connectivity |
| 156 | + on endpoint mycontainer: Bind for 0.0.0.0:8080 failed: port is already allocated. |
| 157 | +
|
| 158 | +links: |
| 159 | + - https://docs.docker.com/engine/reference/run/#expose-incoming-ports |
| 160 | +``` |
0 commit comments