Skip to content

Commit 5626eef

Browse files
francisfuzzCopilot
andcommitted
Add AGENTS.md and copilot-instructions.md for AI agent onboarding
Add two complementary instruction files so AI coding agents can work effectively in this repository without re-discovering conventions: - AGENTS.md (7K chars): agent-agnostic open standard with full project structure, build/test commands, coding patterns, testing conventions, error handling, key interfaces, and non-obvious gotchas. - .github/copilot-instructions.md (2K chars): concise Copilot-specific instructions under the 4K code review limit, covering the essentials and referencing AGENTS.md for full details. Closes #132 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 84d160b commit 5626eef

2 files changed

Lines changed: 167 additions & 0 deletions

File tree

.github/copilot-instructions.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# gh-stack — Copilot Instructions
2+
3+
A Go CLI extension (`gh stack`) for managing stacked branches and pull requests. Uses Cobra for commands, bubbletea/lipgloss for TUI, and `stretchr/testify` for tests.
4+
5+
## Build and validate
6+
7+
```sh
8+
go mod download # install deps
9+
go build ./... # build
10+
go vet ./... # static analysis — always run before tests
11+
go test -race -count=1 ./... # tests with race detection
12+
```
13+
14+
No Makefile, no code generation, no external linter config. Standard Go toolchain only.
15+
16+
## Project layout
17+
18+
- `cmd/` — one Cobra command per file; each exports `<Name>Cmd(cfg *config.Config)` with logic in `run<Name>()`.
19+
- `internal/git/``Ops` interface (50+ methods) wrapping git CLI. `MockOps` for tests. Package-level functions delegate to swappable `ops` variable.
20+
- `internal/github/``ClientOps` interface (12 methods) for GitHub API. `MockClient` for tests.
21+
- `internal/config/``Config` struct passed to all commands. Holds I/O, colors, and test hooks (`SelectFn`, `ConfirmFn`, `InputFn`, `GitHubClientOverride`).
22+
- `internal/stack/` — stack file (`.git/gh-stack`, JSON) management with file locking.
23+
- `internal/tui/` — bubbletea views (`stackview`, `modifyview`).
24+
25+
## Coding conventions
26+
27+
- Return typed `ExitError` sentinels (codes 1–10 in `cmd/utils.go`) from `RunE` — never call `os.Exit()` directly.
28+
- Check errors with `errors.As(err, &ExitError{})`.
29+
- Table-driven tests with `t.Run()` subtests.
30+
- Use `config.NewTestConfig()` for test configs with captured I/O.
31+
- Mock git: `restore := git.SetOps(&git.MockOps{...}); defer restore()` — always defer restore.
32+
- Mock GitHub: `cfg.GitHubClientOverride = &github.MockClient{...}`.
33+
- Mock prompts: set `cfg.SelectFn`, `cfg.ConfirmFn`, or `cfg.InputFn`.
34+
- Load stack files with `stack.Load(dir)` after writing to get correct checksums.
35+
36+
For full architecture details, see [AGENTS.md](../AGENTS.md) in the repository root.

AGENTS.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# gh-stack — Agent Instructions
2+
3+
A GitHub CLI (`gh`) extension for managing stacked branches and pull requests. Written in Go, it automates creating branches, keeping them rebased, setting PR base branches, and navigating between stack layers.
4+
5+
## Build, test, and validate
6+
7+
```sh
8+
go mod download # install dependencies
9+
go build ./... # build (produces ./gh-stack binary)
10+
go vet ./... # static analysis — run before tests
11+
go test -race -count=1 ./... # all tests with race detection
12+
```
13+
14+
Always run `go vet` before `go test`. CI runs both on every push/PR across ubuntu, windows, and macOS (`test.yml`).
15+
16+
There is no Makefile, linter config, or code generation step. The standard Go toolchain is all that's needed.
17+
18+
### Install locally as a `gh` extension
19+
20+
```sh
21+
go build -o gh-stack .
22+
gh extension remove stack 2>/dev/null
23+
gh extension install .
24+
```
25+
26+
## Project structure
27+
28+
```
29+
main.go # entrypoint — calls cmd.Execute()
30+
cmd/ # Cobra commands (one file per command + tests)
31+
root.go # registers all subcommands in four groups
32+
utils.go # shared helpers, ExitError types, exit codes
33+
internal/
34+
git/ # git.Ops interface + defaultOps (exec-based)
35+
gitops.go # Ops interface (50+ methods)
36+
mock_ops.go # MockOps — each method has a corresponding *Fn field
37+
github/ # github.ClientOps interface + real Client
38+
client_interface.go # ClientOps interface (12 methods)
39+
mock_client.go # MockClient — function-pointer fields for testing
40+
stack/ # stack file (.git/gh-stack) management, JSON schema, locking
41+
schema.json # JSON Schema for the stack file format
42+
config/ # Config struct (I/O, colors, test overrides)
43+
testing.go # NewTestConfig() — returns *Config + stdout/stderr pipes
44+
branch/ # branch naming (Slugify, DateSlug, NextNumberedName)
45+
modify/ # interactive stack modification state machine
46+
pr/ # PR template discovery
47+
tui/ # bubbletea/bubbles/lipgloss terminal UI
48+
stackview/ # interactive stack visualization
49+
modifyview/ # interactive modify session UI
50+
shared/ # shared TUI types
51+
docs/ # Astro + Starlight documentation site
52+
skills/ # AI agent skill definition (SKILL.md)
53+
```
54+
55+
### Command groups (registered in `cmd/root.go`)
56+
57+
| Group | Commands |
58+
|-------|----------|
59+
| Stack management | `init`, `add`, `view`, `checkout`, `modify`, `unstack` |
60+
| Remote operations | `submit`, `sync`, `rebase`, `push`, `link` |
61+
| Navigation | `switch`, `up`, `down`, `top`, `bottom`, `trunk` |
62+
| Utilities | `alias`, `feedback` |
63+
64+
## Coding patterns
65+
66+
### Command structure
67+
68+
Each command lives in its own file (`cmd/<name>.go`) and follows this pattern:
69+
70+
1. Define an `<name>Options` struct for flags/args.
71+
2. Export a `<Name>Cmd(cfg *config.Config) *cobra.Command` constructor.
72+
3. Implement logic in a private `run<Name>(cfg, opts, args)` function.
73+
4. The `RunE` field on the command calls `run<Name>`.
74+
75+
### Error handling
76+
77+
Use typed exit codes defined in `cmd/utils.go`:
78+
79+
| Code | Sentinel | Meaning |
80+
|------|----------|---------|
81+
| 1 | `ErrSilent` | Error already printed |
82+
| 2 | `ErrNotInStack` | Branch/stack not found |
83+
| 3 | `ErrConflict` | Rebase conflict |
84+
| 4 | `ErrAPIFailure` | GitHub API error |
85+
| 5 | `ErrInvalidArgs` | Invalid arguments or flags |
86+
| 6 | `ErrDisambiguate` | Multiple stacks/remotes, can't auto-select |
87+
| 7 | `ErrRebaseActive` | Rebase already in progress |
88+
| 8 | `ErrLockFailed` | Stack file lock contention |
89+
| 9 | `ErrStacksUnavailable` | Stacked PRs not enabled for repository |
90+
| 10 | `ErrModifyRecovery` | Modify session interrupted |
91+
92+
Return these from `RunE` — never call `os.Exit()` directly from commands. Check with `errors.As(err, &ExitError{})`.
93+
94+
### Testing patterns
95+
96+
- **Framework:** `stretchr/testify` (`assert`, `require`) for assertions.
97+
- **Table-driven tests** are the norm — see `cmd/utils_test.go` for examples.
98+
- **Config:** Use `config.NewTestConfig()` which returns `(*Config, stdoutReader, stderrReader)` with captured I/O and no-op color functions.
99+
- **Git mocking:** Call `git.SetOps(&git.MockOps{...})` — it returns a restore function: always `defer restore()` to prevent test pollution.
100+
- **GitHub mocking:** Set `cfg.GitHubClientOverride = &github.MockClient{...}`.
101+
- **Prompt mocking:** Set `cfg.SelectFn`, `cfg.ConfirmFn`, or `cfg.InputFn` on the config to simulate interactive input.
102+
- **Stack file setup:** Use `stack.Load(dir)` after writing a stack file to get correct checksums for `Save`.
103+
104+
### Key interfaces
105+
106+
- **`git.Ops`** (`internal/git/gitops.go`) — 50+ methods wrapping git CLI calls. Production implementation uses `exec.Command("git", ...)`. Package-level functions (e.g., `git.CurrentBranch()`) delegate to a swappable package-level `ops` variable.
107+
- **`github.ClientOps`** (`internal/github/client_interface.go`) — 12 methods for GitHub API (PRs, stacks). Injected via `cfg.GitHubClientOverride` in tests.
108+
- **`config.Config`** (`internal/config/config.go`) — central configuration passed to all commands. Holds I/O streams, color functions, and test hook fields (`SelectFn`, `ConfirmFn`, `InputFn`, `TokenForHostFn`, `RepoOverride`).
109+
110+
### Stack file
111+
112+
- **Location:** `.git/gh-stack` (JSON format, schema version 1).
113+
- **Schema:** `internal/stack/schema.json`.
114+
- **Locking:** Exclusive file lock at `.git/gh-stack.lock` with 5-second timeout. Errors surface as `LockError`.
115+
- **Staleness:** Concurrent modifications detected via `StaleError`.
116+
117+
## CI workflows (`.github/workflows/`)
118+
119+
| Workflow | Trigger | What it does |
120+
|----------|---------|-------------|
121+
| `test.yml` | push to main, PRs | `go vet` + `go test -race -count=1 ./...` on 3 OS matrix |
122+
| `release.yml` | `v*` tags | Cross-platform precompiled binaries via `cli/gh-extension-precompile` |
123+
| `docs.yml` | push to main (docs/**) | Builds Astro/Starlight docs, deploys to GitHub Pages |
124+
125+
## Non-obvious things
126+
127+
- The `Queued` field on `BranchRef` is transient (populated from GitHub API, never persisted to the stack JSON file).
128+
- `git.SetOps()` replaces the **package-level** ops variable — forgetting `defer restore()` in a test will break every subsequent test in the package.
129+
- Interrupt detection: Ctrl+C is caught as `terminal.InterruptErr`, wrapped into an `errInterrupt` sentinel, and printed with a friendly message before a silent exit.
130+
- Rerere: on first rebase conflict, the user is prompted to enable `git rerere`. If declined, a flag file prevents future prompts. `tryAutoResolveRebase()` loops up to 1000 times auto-continuing when rerere resolves conflicts.
131+
- The `.gitignore` ignores `/gh-stack` and `/gh-stack.exe` — the built binary.

0 commit comments

Comments
 (0)