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
2 changes: 2 additions & 0 deletions .github/instructions/bug-fixing.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ excludeAgent: "code-review"

# Bug fixing — instructions for Copilot coding agent

> **Skill reference:** for the extended symptom → module diagnostic table, test-first patterns and minimal fix principles read `.github/skills/bug-fixing.md` first.

Follow these steps when fixing a bug in this repository.

## 1. Reproduce the bug before writing any fix
Expand Down
17 changes: 15 additions & 2 deletions .github/instructions/documentation.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ applyTo: "docs/**"

# Documentation — instructions for Copilot coding agent

> **Skill reference:** for deep domain knowledge (VitePress theme architecture, CSS variables, accessibility patterns, responsive patterns, full validation commands) read `.github/skills/documentation.md` first.

Follow these conventions when writing or editing pages in the `docs/` directory.

## 1. Tool & rendering pipeline
Expand Down Expand Up @@ -93,9 +95,20 @@ docs/
Before opening a PR for any docs change:

```bash
bun run docs:build # must complete without errors
bun run format:check # oxfmt — no formatting diff
bun run docs:build # must complete without errors or dead-link warnings
bun run format:check # oxfmt — no formatting diff
```

- All internal links must resolve (VitePress reports dead links on build).
- No new `bun run knip` violations (docs/\*\* is excluded but `package.json` changes are not).

If the PR modifies any Vue component, CSS, or page layout, also run the accessibility and responsive suites:

```bash
bun run docs:build:a11y
bun run docs:preview -- --port 4173 &
bun run docs:a11y # pa11y-ci WCAG 2.1 AA — must report 0 errors
bun run docs:test:responsive # Playwright — 20/20 tests green (4 viewports × 5 pages)
```

See `.github/skills/documentation.md` for pa11y configuration details, WCAG contrast rules, accessible component patterns, and responsive breakpoint guidance.
2 changes: 2 additions & 0 deletions .github/instructions/implement-feature.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ excludeAgent: "code-review"

# Implement feature — instructions for Copilot coding agent

> **Skill reference:** for the full architectural layer map, type-first design patterns, CLI option conventions, render sub-module extension playbook and test strategies read `.github/skills/feature.md` first.

Follow these steps when implementing a new feature in this repository.

## 1. Understand the task scope before writing code
Expand Down
2 changes: 2 additions & 0 deletions .github/instructions/refactoring.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ excludeAgent: "code-review"

# Refactoring — instructions for Copilot coding agent

> **Skill reference:** for architectural invariants, safe rename playbook, module extraction patterns, characterisation test strategy and `knip` output interpretation read `.github/skills/refactoring.md` first.

Follow these steps when refactoring existing code in this repository.

## 1. Define the goal and scope
Expand Down
2 changes: 2 additions & 0 deletions .github/instructions/release.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ excludeAgent: "code-review"

# Release — instructions for Copilot coding agent

> **Skill reference:** for the semver decision guide, CD pipeline mechanics, binary targets, blog post format reference and versioned docs snapshot details read `.github/skills/release.md` first.

Follow these steps when cutting a new release of `github-code-search`.

## 1. Determine the version bump
Expand Down
125 changes: 125 additions & 0 deletions .github/skills/bug-fixing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Bug fixing skill — github-code-search

Deep reference for diagnosing and fixing bugs in this codebase.
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` |
| 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`) |

---

## Reproducing a bug

A complete bug report must have:

1. **Exact command** (redact `GITHUB_TOKEN` with `***`):
```
GITHUB_TOKEN=*** github-code-search query "pattern" --org acme
```
2. **Observed output** vs **expected output**.
3. **Version string**: `github-code-search --version` → e.g. `1.8.0 (a1b2c3d · darwin/arm64)`.
4. **Bun version** (when running from source): `bun --version`.

If the report is missing items 1 or 3, read the relevant module(s) to hypothesise the root cause before asking for more info.

---

## Test-first patterns for bugs

### Pure function bug (aggregate, group, output, render/\*)

```typescript
// src/aggregate.test.ts
describe("applyFiltersAndExclusions — bug #N", () => {
it("excludes org-prefixed repo names correctly", () => {
const result = applyFiltersAndExclusions(matches, {
excludeRepositories: ["acme/my-repo"], // the previously broken form
});
expect(result).not.toContainEqual(expect.objectContaining({ repo: "acme/my-repo" }));
});
});
```

The test must **fail** with the current code before the fix. Commit the test, then fix.

### api-utils bug (retry / pagination)

```typescript
// src/api-utils.test.ts
it("retries on 429 with Retry-After header", async () => {
let callCount = 0;
globalThis.fetch = async () => {
callCount++;
if (callCount === 1) {
return new Response(null, {
status: 429,
headers: { "Retry-After": "0" },
});
}
return new Response(JSON.stringify({ ok: true }), { status: 200 });
};
await fetchWithRetry("https://api.github.com/test");
expect(callCount).toBe(2);
});
```

### Side-effectful bug (tui, api) — no unit test possible

Document manual repro steps in the PR description:

```markdown
## Steps to reproduce (before fix)

1. `GITHUB_TOKEN=... github-code-search query "foo" --org acme`
2. Press `↓` past the last result
3. Expected: cursor stays on last row / Expected: wraps to first row
4. Observed: cursor jumps to blank row
```

---

## Minimal fix principles

- **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.
- **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
// Fix: short names ("repo") and qualified names ("org/repo") must both match — see issue #N
```

---

## Validation after fix

```bash
bun test # the previously failing test now passes; full suite still green
bun run lint # oxlint — zero errors
bun run format:check # oxfmt — no formatting diff
bun run knip # no unused exports or imports
bun run build.ts # binary compiles without errors
```
162 changes: 162 additions & 0 deletions .github/skills/documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Documentation skill — github-code-search

Deep reference for writing and maintaining VitePress documentation in this project.
This skill complements `.github/instructions/documentation.instructions.md` (the step-by-step workflow).

---

## VitePress theme architecture

Custom components live in `docs/.vitepress/theme/`:

| File / folder | Role |
| ------------------------- | ------------------------------------------------------------------ |
| `index.ts` | Theme entry point — imports components and global CSS |
| `custom.css` | All CSS overrides — brand colours, typography, responsive, a11y |
| `Layout.vue` | Root layout wrapper (adds `RichFooter`, manages slot injection) |
| `TerminalDemo.vue` | Animated terminal on the hero — `aria-hidden="true"` (decorative) |
| `ComparisonTable.vue` | Feature comparison table with responsive 3-column layout |
| `UseCaseTabs.vue` | WAI-ARIA Tabs pattern (roving tabindex, ArrowLeft/Right/Home/End) |
| `InstallSection.vue` | Install command snippets with OS tabs |
| `HowItWorks.vue` | 3-step explainer with responsive card layout |
| `TestimonialsSection.vue` | Community testimonials carousel |
| `ProductionCta.vue` | "Used in production?" CTA banner (`<section aria-labelledby="…">`) |
| `VersionBadge.vue` | Release badge — reads `version` from `package.json` at build time |
| `RichFooter.vue` | Custom footer replacing VitePress default |

**Extending a component:**

1. Locate the `.vue` file above.
2. Follow the existing scoped `<style>` conventions (no global selectors inside `<style scoped>`).
3. Add responsive styles inside the component's `<style>` or in `custom.css` if the rule is global.
4. Never import additional NPM packages for styling — only `picocolors` (CLI) and VitePress built-ins.

---

## CSS variable system

Always use VitePress CSS variables — never hard-code colours in component `<style>` blocks:

| Variable | Meaning |
| ----------------------- | -------------------------------------------------------- |
| `--vp-c-brand-1` | Violet `#9933FF` / `#cc88ff` (dark) |
| `--vp-c-brand-2` | Hover darkening |
| `--vp-c-brand-soft` | Soft tint for backgrounds |
| `--vp-c-text-1` | Primary text (≥ WCAG AA on bg) |
| `--vp-c-text-2` | Secondary text (≥ WCAG AA on bg) |
| `--vp-c-text-3` | **Do not use for text** — 2.87:1 contrast, fails WCAG AA |
| `--vp-c-divider` | Border / separator |
| `--vp-c-bg-soft` | Card / inset background |
| `--vp-font-family-mono` | Monospace (code blocks) |

Dark mode: VitePress applies a `.dark` class on `<html>`. Use `.dark .selector { … }` — never `@media (prefers-color-scheme: dark)`.

---

## Accessibility (WCAG 2.1 AA)

This project maintains **zero pa11y-ci violations** at WCAG 2.1 AA level.

### Tool

```bash
bun run docs:build:a11y # build with VITEPRESS_HOSTNAME=http://localhost:4173
bun run docs:preview -- --port 4173 &
bun run docs:a11y # pa11y-ci via sitemap — must report 0 errors
```

Config: `.pa11yci.json`. F77 (Mermaid duplicate SVG IDs) is ignored — not blocking for AT users.

### Common patterns

| Pattern | Correct implementation |
| -------------------------------- | ------------------------------------------------------------------------------------------------------ |
| Landmark regions | `<section aria-labelledby="id">` + matching `id` on heading |
| Icon-only buttons / links | `aria-label="Descriptive text"` |
| Decorative images / SVG | `aria-hidden="true"` and no `alt` (or `alt=""`) |
| External links (open in new tab) | `aria-label="Label (opens in a new tab)"` or `.sr-only` suffix |
| Check / cross icons in tables | `aria-label="Yes"` / `aria-label="No"` |
| Table column headers | `<th scope="col">` + `<caption class="sr-only">` on `<table>` |
| Interactive tabs | Full WAI-ARIA Tabs: `role="tablist"`, `role="tab"`, `role="tabpanel"`, roving `tabindex`, keyboard nav |
| Screen-reader-only text | `.sr-only` utility class defined in `custom.css` |
| Focus visibility | `:focus-visible` ring defined globally in `custom.css` |

### Contrast minimums (WCAG AA)

- Normal text (< 18pt): **4.5:1**
- Large text (≥ 18pt bold or ≥ 24pt): **3:1**
- UI components and icons: **3:1**

Avoid `var(--vp-c-text-3)` for any visible text — it is ~2.87:1 against default VitePress backgrounds.

---

## Responsive (mobile-first)

This project maintains **zero horizontal overflow** across 4 tested viewports via Playwright.

### Tested viewports

| Label | Width | Height |
| --------------- | ----- | ------ |
| Galaxy S21 | 360px | 800px |
| iPhone SE | 375px | 667px |
| iPhone 14 | 390px | 844px |
| Tablet portrait | 768px | 1024px |

### Tool

```bash
bun run docs:build
bun run docs:preview -- --port 4173 &
bun run docs:test:responsive # 20 tests (4 viewports × 5 pages) — must all pass
```

Config: `playwright.config.ts`. Spec: `scripts/responsive.pw.ts`. Screenshots on failure: `test-results/screenshots/` (gitignored).

### VitePress quirks at 768px

VitePress hides its hamburger at `≥768px` and shows desktop nav links — which overflow the viewport at exactly 768px on this project's config. The fix in `custom.css`:

```css
@media (max-width: 960px) {
.VPNavBarMenu,
.VPNavBarExtra {
display: none !important;
}
.VPNavBarHamburger {
display: flex !important;
}
}
@media (min-width: 768px) and (max-width: 960px) {
.VPNavScreen {
display: block !important;
}
}
```

### Responsive patterns for components

| Problem | Solution |
| ----------------------------------------- | ---------------------------------------------------------------------------------------- |
| Table columns too wide on mobile | `table-layout: fixed`, abbreviated headers via `.ct-name-short` / `.ct-name-long` toggle |
| Long feature descriptions push rows wider | `display: none` on `.ct-feature-desc` at `≤640px` |
| Terminal/code blocks cause page scroll | `overflow-x: auto` on the scroll container, `max-width: 100%` on the block |
| Long strings in `<pre>` overflow | `pre { max-width: 100%; overflow-x: auto; }` in `custom.css` |

Never use `overflow-x: hidden` on the page root — it silently clips content. Apply it only to specific containers where clipping is intentional.

---

## Validation checklist

Before opening a PR for any docs change:

```bash
bun run docs:build # must complete without errors or dead-link warnings
bun run docs:build:a11y # build for a11y audit
bun run docs:preview -- --port 4173 &
bun run docs:a11y # 0 pa11y violations
bun run docs:test:responsive # 20/20 Playwright tests green
bun run format:check # oxfmt — no formatting diff
```
Loading