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
30 changes: 30 additions & 0 deletions .github/instructions/terminal.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
applyTo: "src/**/*.ts,github-code-search.ts"
---

# Terminal controls and redraw safety

- Read `AGENTS.md` and search `src/terminal-control.ts` before adding terminal controls.
Reuse named constants instead of duplicating encoded ANSI, keyboard or mouse bytes.
Add a missing control once in that module; use `cursorPosition()` for coordinates.
- In terminal output and new tests, compose `LINE_FEED`, `CARRIAGE_RETURN` and `TAB`
rather than embedding encoded line breaks or long protocol strings.
Preserve existing key-binding names when extracting them to shared vocabulary.
- Use `src/style.ts` for standard text styling. Named custom-colour/reset constants
are for the existing protocol-level rendering, not a second styling framework.
- Normalize carriage returns and tabs before clipping or comparing differential
frames. Measure visible columns through `render/terminal.ts`, ignoring SGR bytes.
A tab must overwrite skipped cells with spaces, not retain old text/highlights.
- Do not append a tail erase to an exactly full-width line: it erases the final
cell, including the edge of a wide glyph. Preserve unchanged rows and submit
changed frames in one write.
- Add failing regressions before fixes. Assert resulting terminal cells for CRLF,
tab-indented scrolling and full-width ASCII/wide-glyph lines; string-sequence
assertions alone do not prove terminal correctness.
- Keep the read-only architecture guard in `src/terminal-control.test.ts` passing
through the normal `bun test` suite. New render modules are covered automatically;
add other new terminal emitters to its surface set. Do not exempt production code.
Independent vocabulary wire-format tests, existing legacy protocol fixtures and
syntax-parsing regexes may contain literal encodings; new frame tests may not.
- Run `bun test --dots`, `bun run typecheck`, `bun run format:check`,
`bun run knip` and `bun run build.ts` before publishing changes.
56 changes: 31 additions & 25 deletions .github/skills/bug-fixing.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,32 @@ This skill complements `.github/instructions/bug-fixing.instructions.md`.

## Symptom → module diagnostic table

| Symptom | Primary suspect | Secondary suspect |
| ----------------------------------------------------------------- | ------------------------------------------------------ | --------------------------------------------- |
| Results missing or duplicated | `src/aggregate.ts` | `src/api.ts` (pagination) |
| Wrong repository grouping | `src/group.ts` | `src/aggregate.ts` |
| `--exclude-repositories` / `--exclude-extracts` not working | `src/aggregate.ts` | `github-code-search.ts` (parsing) |
| Markdown output malformed | `src/output.ts` | — |
| JSON output missing fields or wrong shape | `src/output.ts` | `src/types.ts` (interface) |
| Syntax highlighting wrong colour / wrong language | `src/render/highlight.ts` | — |
| Row navigation skips or wraps incorrectly | `src/render/rows.ts` | `src/tui.ts` (key handler) |
| Select-all / select-none inconsistent | `src/render/selection.ts` | `src/tui.ts` |
| Filter count / stats incorrect | `src/render/filter.ts`, `src/render/summary.ts` | — |
| Path filter (`/regex/`) doesn't match expected | `src/render/filter-match.ts` | `src/tui.ts` (filter state) |
| API returns 0 results or stops paginating | `src/api.ts` | `src/api-utils.ts` |
| Rate limit hit / 429 not retried | `src/api-utils.ts` (`fetchWithRetry`) | — |
| TUI shows blank screen or wrong row | `src/tui.ts` | `src/render/rows.ts` |
| Mouse clicks land on the wrong row/zone | `src/render/mouse-hit.ts` | `src/tui.ts` (click dispatch) |
| Mouse escape sequences not parsed / terminal stuck in mouse mode | `src/render/mouse.ts` | `src/tui.ts` (enable/disable mouse reporting) |
| Wheel scroll wrong step size or accidental clicks after scrolling | `src/render/layout-constants.ts` (`MOUSE_SCROLL_STEP`) | `src/scroll-cooldown.ts` |
| Help overlay doesn't appear / has wrong keys | `src/render.ts` (`renderHelpOverlay`) | `src/tui.ts` |
| Upgrade fails or replaces wrong binary | `src/upgrade.ts` | — |
| Completion script wrong content | `src/completions.ts` | — |
| Completion file written to wrong path | `src/completions.ts` (`getCompletionFilePath`) | env vars (`XDG_*`, `ZDOTDIR`) |
| Completion not refreshed after upgrade | `src/upgrade.ts` (`refreshCompletions`) | — |
| `--version` shows wrong info | `build.ts` (SHA injection) | — |
| CLI option ignored or parsed wrong | `github-code-search.ts` | `src/types.ts` (`OutputType`) |
| Symptom | Primary suspect | Secondary suspect |
| -------------------------------------------------------------------------- | ------------------------------------------------------ | --------------------------------------------------- |
| Results missing or duplicated | `src/aggregate.ts` | `src/api.ts` (pagination) |
| Wrong repository grouping | `src/group.ts` | `src/aggregate.ts` |
| `--exclude-repositories` / `--exclude-extracts` not working | `src/aggregate.ts` | `github-code-search.ts` (parsing) |
| Markdown output malformed | `src/output.ts` | — |
| JSON output missing fields or wrong shape | `src/output.ts` | `src/types.ts` (interface) |
| Syntax highlighting wrong colour / wrong language | `src/render/highlight.ts` | — |
| Row navigation skips or wraps incorrectly | `src/render/rows.ts` | `src/tui.ts` (key handler) |
| Select-all / select-none inconsistent | `src/render/selection.ts` | `src/tui.ts` |
| Filter count / stats incorrect | `src/render/filter.ts`, `src/render/summary.ts` | — |
| Path filter (`/regex/`) doesn't match expected | `src/render/filter-match.ts` | `src/tui.ts` (filter state) |
| API returns 0 results or stops paginating | `src/api.ts` | `src/api-utils.ts` |
| Rate limit hit / 429 not retried | `src/api-utils.ts` (`fetchWithRetry`) | — |
| TUI shows blank screen or wrong row | `src/tui.ts` | `src/render/rows.ts` |
| Flicker, erased CRLF snippets, stale tab indentation or missing edge cells | `src/render/frame.ts` | `src/terminal-control.ts`, `src/render/terminal.ts` |
| Mouse clicks land on the wrong row/zone | `src/render/mouse-hit.ts` | `src/tui.ts` (click dispatch) |
| Mouse escape sequences not parsed / terminal stuck in mouse mode | `src/render/mouse.ts` | `src/tui.ts` (enable/disable mouse reporting) |
| Wheel scroll wrong step size or accidental clicks after scrolling | `src/render/layout-constants.ts` (`MOUSE_SCROLL_STEP`) | `src/scroll-cooldown.ts` |
| Help overlay doesn't appear / has wrong keys | `src/render.ts` (`renderHelpOverlay`) | `src/tui.ts` |
| Upgrade fails or replaces wrong binary | `src/upgrade.ts` | — |
| Completion script wrong content | `src/completions.ts` | — |
| Completion file written to wrong path | `src/completions.ts` (`getCompletionFilePath`) | env vars (`XDG_*`, `ZDOTDIR`) |
| Completion not refreshed after upgrade | `src/upgrade.ts` (`refreshCompletions`) | — |
| `--version` shows wrong info | `build.ts` (SHA injection) | — |
| CLI option ignored or parsed wrong | `github-code-search.ts` | `src/types.ts` (`OutputType`) |

---

Expand Down Expand Up @@ -109,6 +110,11 @@ Document manual repro steps in the PR description:

- **Touch only the root cause.** Do not opportunistically refactor neighbouring code in the same PR — it makes the fix harder to review and risks introducing new bugs.
- **Respect pure/IO layering**: a fix in `aggregate.ts` must not add a `console.log` call.
- **Terminal regressions need cell assertions.** Reuse `src/terminal-control.ts`
constants (including named line breaks) in fixes and new tests. Follow
`.github/instructions/terminal.instructions.md`; the normal Bun suite enforces
the architecture guard. For redraw changes reproduce CRLF, tab indentation,
full-width cells and scrolling over old content, not just emitted string shapes.
- **Type changes cascade**: if `src/types.ts` must change, run `bun run knip` to find all affected consumers and update them all in the same PR.
- **Regression comment**: if the root cause is non-obvious, add one line above the fix:
```typescript
Expand Down
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,12 @@ src/
style.ts # Sole call site for node:util's styleText — dim/bold/italic/
# underline/colors + style(names[], text) composer for combined
# styles — no I/O
terminal-control.ts # Shared named terminal controls, key sequences, line breaks,
# tab stops and cursorPosition() — no I/O

render/
frame.ts # Pure differential terminal updates — repaints changed lines,
# erases stale content and clips frames to the viewport
terminal.ts # Bun 1.4+ native API wrappers (stringWidth, stripANSI, sliceAnsi)
# sole authorized call site for these APIs
highlight.ts # Syntax highlighting (language detection + token rules)
Expand All @@ -168,6 +172,8 @@ src/
- **`render.ts` is a façade.** It re-exports everything from `render/` and adds two top-level rendering functions. Consumers import from `render.ts`, not directly from sub-modules. Exceptions: `render/team-pick.ts`, `render/mouse.ts` and `render/mouse-hit.ts` are pure modules imported **directly** by their sole consumer (`render.ts` for `team-pick.ts`, `tui.ts` for the mouse modules) and are not re-exported publicly (knip would flag unused re-exports otherwise).
- **`render/terminal.ts` is the sole Bun API call site.** All calls to `Bun.stringWidth()`, `Bun.stripANSI()`, and `Bun.sliceAnsi()` must go through the `terminal.ts` wrapper functions (`visibleWidth()`, `stripAnsi()`, `clipToWidth()`, `hasAnsi()`). This centralizes terminal handling logic and makes it easy to verify correct Unicode handling (graphemes, emoji, CJK, ZWJ sequences).
- **`src/style.ts` is the sole call site for `node:util`'s `styleText`.** All ANSI styling across the codebase (`render.ts`, `render/highlight.ts`, `render/summary.ts`, `render/team-pick.ts`, `tui.ts`, `upgrade.ts`, `api.ts`, `github-code-search.ts`) goes through this facade instead of importing `styleText` directly or reintroducing a styling npm package. Multi-style compositions (e.g. bold+yellow) must use the `style(names[], text)` array form, not chained/nested calls — `bold` and `dim` share the same SGR reset code, so naive chaining can produce incorrect output when there is trailing content after a nested style.
- **`src/terminal-control.ts` owns encoded terminal controls.** Reuse its named cursor, erase, SGR, keyboard and mouse constants; use `cursorPosition()` for parameterized movement. Terminal surfaces and their new tests must compose named controls, including `LINE_FEED`, `CARRIAGE_RETURN` and `TAB`, instead of embedding escape strings. Standard text styling still uses `src/style.ts`; the shared constants cover protocol resets and the existing custom 256-colour styles. Raw bytes are allowed in the vocabulary's independent wire-format tests, existing legacy protocol fixtures and syntax-parsing regular expressions, not in new terminal output code.
- **Differential redraws must preserve terminal cells, not merely strings.** Remove carriage returns and expand tabs to visible-column-aware spaces before clipping/comparing frames. Ignore ANSI styles when measuring tab stops. Never erase a line tail when the line fills the terminal width: the terminal cursor still occupies its final cell. Test CRLF snippets, styled/Unicode tab indentation, full-width ASCII/wide-glyph lines and scrolling over occupied cells.
- **`types.ts` is the single source of truth** for all shared interfaces. Any new shared type must go there.
- **No classes** — the codebase uses plain TypeScript interfaces and functions throughout.

Expand All @@ -182,6 +188,7 @@ src/
- When adding a function to an existing module, add the corresponding test case in the existing `<module>.test.ts`.
- When creating a new module that contains pure functions, create a companion `<module>.test.ts`.
- Tests must be self-contained: no network calls, no filesystem side effects.
- `src/terminal-control.test.ts` includes a read-only architecture guard in the normal `bun test`/CI suite. It rejects encoded control bytes outside the shared vocabulary and raw line/tab escapes in terminal surfaces and the frame regression tests. New render modules are covered automatically; extend the terminal-surface set for new emitters outside `render/`. Do not bypass the guard by adding exceptions for production emitters. Existing text parsers/fixtures are not a mandate to replace every data-format newline in the repository.

## Git conventions

Expand Down
Loading
Loading