Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
126 changes: 126 additions & 0 deletions .github/skills/bug-fixing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# 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
```
154 changes: 154 additions & 0 deletions .github/skills/documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# 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