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
8 changes: 7 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ src/
aggregate.ts # Result grouping & filtering (applyFiltersAndExclusions)
completions.ts # Pure shell-completion generators: generateCompletion(),
# detectShell(), getCompletionFilePath() — no I/O
group.ts # groupByTeamPrefix — team-prefix grouping logic
group.ts # groupByTeamPrefix, applyTeamPick, rebuildTeamSections,
# flattenTeamSections — team-prefix grouping + pick logic
regex.ts # Pure query parser: isRegexQuery(), buildApiQuery()
# Detects /pattern/ syntax, derives safe API term,
# returns RegExp for local client-side filtering — no I/O
Expand All @@ -94,9 +95,11 @@ src/
render/
highlight.ts # Syntax highlighting (language detection + token rules)
filter.ts # FilterStats + buildFilterStats
filter-match.ts # Pure pattern matchers — makeExtractMatcher, makeRepoMatcher
rows.ts # buildRows, rowTerminalLines, isCursorVisible
summary.ts # buildSummary, buildSummaryFull, buildSelectionSummary
selection.ts # applySelectAll, applySelectNone
team-pick.ts # renderTeamPickHeader — pick-mode candidate bar (pure, no I/O)

*.test.ts # Unit tests co-located with source files
test-setup.ts # Global test setup (Bun preload)
Expand Down Expand Up @@ -254,3 +257,6 @@ For minor/major releases update `docs/blog/index.md` to add a row in the version
- Shell-integration tests for `install.sh` live in `install.test.bats` and require `bats-core`. Run them with `bun run test:bats`. The CI runs them in a dedicated `test-bats` job using `bats-core/bats-action`.
- `picocolors` is the only styling dependency; do not add `chalk` or similar.
- Keep `knip` clean: every exported symbol must be used; every import must resolve.
- The `--pick-team` option is repeatable (Commander collect function); each assignment resolves one combined section label to a single team. A warning is emitted on stderr when a label is not found.
- `src/render/team-pick.ts` is a pure module (no I/O) and must be consumed only via the `src/render.ts` façade — it is imported **directly** inside `render.ts` for internal use but is not re-exported publicly (knip would flag it).
- `RepoGroup.pickedFrom` (optional field in `src/types.ts`) tracks the combined label a repo was moved from; future split-mode features will use this to offer re-assignment.
6 changes: 5 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ src/
aggregate.test.ts # Unit tests for aggregate.ts
completions.ts # Pure shell-completion generators (generateCompletion, detectShell, getCompletionFilePath)
completions.test.ts # Unit tests for completions.ts
group.ts # groupByTeamPrefix, applyTeamPick, rebuildTeamSections, flattenTeamSections
group.test.ts # Unit tests for group.ts
render.ts # Façade: re-exports sub-modules + TUI renderGroups/renderHelpOverlay
render.test.ts # Unit tests for render.ts (rows, filter, selection, rendering)
render/
Expand All @@ -43,6 +45,8 @@ src/
rows.ts # Row builder (buildRows, rowTerminalLines, isCursorVisible)
summary.ts # Stats labels (buildSummary, buildSummaryFull, buildSelectionSummary)
selection.ts # Selection mutations (applySelectAll, applySelectNone)
team-pick.ts # Pick-mode candidate bar renderer (renderTeamPickHeader) — pure, no I/O
team-pick.test.ts # Unit tests for team-pick.ts
output.ts # Text (markdown) and JSON output formatters
output.test.ts # Unit tests for output.ts
tui.ts # Interactive keyboard-driven UI (navigation, filter mode, help overlay)
Expand All @@ -60,7 +64,7 @@ bun test # TypeScript unit tests (co-located *.test.ts files)
bun run test:bats # Shell-integration tests for install.sh (requires bats-core)
```

TypeScript tests are co-located with their source files and cover the pure functions in `aggregate.ts`, `completions.ts`, `output.ts`, `render.ts`, `render/highlight.ts`, and `upgrade.ts`.
TypeScript tests are co-located with their source files and cover the pure functions in `aggregate.ts`, `completions.ts`, `group.ts`, `output.ts`, `render.ts`, `render/highlight.ts`, `render/team-pick.ts`, and `upgrade.ts`.

Shell-integration tests use [bats-core](https://github.com/bats-core/bats-core). Install it once with:

Expand Down
2 changes: 1 addition & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 19 additions & 14 deletions docs/architecture/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ provides shared pattern-matching helpers used by several render modules.
C4Component
title Level 3b: TUI render layer

UpdateLayoutConfig($c4ShapeInRow="5", $c4BoundaryInRow="1")
UpdateLayoutConfig($c4ShapeInRow="6", $c4BoundaryInRow="1")

Container(tui, "TUI", "src/tui.ts", "Calls render functions<br/>on every redraw;<br/>formats output on Enter")

Expand All @@ -65,6 +65,7 @@ C4Component
Component(filter, "Filter stats", "src/render/filter.ts", "buildFilterStats()<br/>FilterStats — visible/hidden counts")
Component(selection, "Selection helpers", "src/render/selection.ts", "applySelectAll()<br/>applySelectNone()")
Component(highlight, "Syntax highlighter", "src/render/highlight.ts", "highlightFragment()<br/>ANSI token colouring")
Component(teamPick, "Team pick bar", "src/render/team-pick.ts", "renderTeamPickHeader()<br/>ANSI candidate bar")
Component(outputFn, "Output formatter", "src/output.ts", "buildOutput()<br/>markdown or JSON")
Component(filterMatch, "Pattern matchers", "src/render/filter-match.ts", "makeExtractMatcher()<br/>makeRepoMatcher()")
}
Expand All @@ -84,6 +85,9 @@ C4Component
Rel(tui, highlight, "Highlight<br/>extracts")
UpdateRelStyle(tui, highlight, $offsetX="-150", $offsetY="-16")

Rel(tui, teamPick, "Render pick<br/>mode bar")
UpdateRelStyle(tui, teamPick, $offsetX="-180", $offsetY="-16")

Rel(tui, outputFn, "Format<br/>on Enter")
UpdateRelStyle(tui, outputFn, $offsetX="17", $offsetY="160")

Expand All @@ -100,22 +104,23 @@ C4Component

## Component descriptions

| Component | Source file | Key exports |
| ------------------------ | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Filter & aggregation** | `src/aggregate.ts` | `aggregate()` — filters `CodeMatch[]` by repository and extract exclusion lists; normalises both `repoName` and `org/repoName` forms. |
| **Team grouping** | `src/group.ts` | `groupByTeamPrefix()` — groups `RepoGroup[]` into `TeamSection[]` keyed by team slug; `flattenTeamSections()` — converts back to a flat list for the TUI row builder. |
| **Shell completions** | `src/completions.ts` | `generateCompletion(shell)` — returns the full bash/zsh/fish completion script; `detectShell()` — reads `$SHELL`; `getCompletionFilePath(shell, opts)` — resolves the XDG-aware installation path. |
| **Row builder** | `src/render/rows.ts` | `buildRows()` — converts `RepoGroup[]` into `Row[]` filtered by the active target (path / content / repo); `rowTerminalLines()` — measures wrapped height; `isCursorVisible()` — viewport clipping. |
| **Summary builder** | `src/render/summary.ts` | `buildSummary()` — compact header line; `buildSummaryFull()` — detailed counts; `buildSelectionSummary()` — "N files selected" footer. |
| **Filter stats** | `src/render/filter.ts` | `buildFilterStats()` — produces the `FilterStats` object (visible repos, files, matches) used by the TUI filter bar live counter. |
| **Pattern matchers** | `src/render/filter-match.ts` | `makeExtractMatcher()` — builds a case-insensitive substring or RegExp test function for path or content targets; `makeRepoMatcher()` — wraps the same logic for repo-name matching. |
| **Selection helpers** | `src/render/selection.ts` | `applySelectAll()` — marks all visible rows as selected (respects filter target); `applySelectNone()` — deselects all visible rows. |
| **Syntax highlighter** | `src/render/highlight.ts` | `highlightFragment()` — maps file extension to a language token ruleset and applies ANSI escape sequences. Falls back to plain text for unknown extensions. |
| **Output formatter** | `src/output.ts` | `buildOutput()` — entry point for both `--format markdown` and `--format json` serialisation of the confirmed selection. |
| Component | Source file | Key exports |
| ------------------------ | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Filter & aggregation** | `src/aggregate.ts` | `aggregate()` — filters `CodeMatch[]` by repository and extract exclusion lists; normalises both `repoName` and `org/repoName` forms. |
| **Team grouping** | `src/group.ts` | `groupByTeamPrefix()` — groups `RepoGroup[]` into `TeamSection[]` keyed by team slug; `flattenTeamSections()` — converts back to a flat list for the TUI row builder; `applyTeamPick()` — moves repos from a combined section to a chosen team section; `rebuildTeamSections()` — reconstructs `TeamSection[]` from a flat list (used by TUI pick mode). |
| **Shell completions** | `src/completions.ts` | `generateCompletion(shell)` — returns the full bash/zsh/fish completion script; `detectShell()` — reads `$SHELL`; `getCompletionFilePath(shell, opts)` — resolves the XDG-aware installation path. |
| **Row builder** | `src/render/rows.ts` | `buildRows()` — converts `RepoGroup[]` into `Row[]` filtered by the active target (path / content / repo); `rowTerminalLines()` — measures wrapped height; `isCursorVisible()` — viewport clipping. |
| **Summary builder** | `src/render/summary.ts` | `buildSummary()` — compact header line; `buildSummaryFull()` — detailed counts; `buildSelectionSummary()` — "N files selected" footer. |
| **Filter stats** | `src/render/filter.ts` | `buildFilterStats()` — produces the `FilterStats` object (visible repos, files, matches) used by the TUI filter bar live counter. |
| **Pattern matchers** | `src/render/filter-match.ts` | `makeExtractMatcher()` — builds a case-insensitive substring or RegExp test function for path or content targets; `makeRepoMatcher()` — wraps the same logic for repo-name matching. |
| **Selection helpers** | `src/render/selection.ts` | `applySelectAll()` — marks all visible rows as selected (respects filter target); `applySelectNone()` — deselects all visible rows. |
| **Syntax highlighter** | `src/render/highlight.ts` | `highlightFragment()` — maps file extension to a language token ruleset and applies ANSI escape sequences. Falls back to plain text for unknown extensions. |
| **Team pick bar** | `src/render/team-pick.ts` | `renderTeamPickHeader()` — renders the ANSI pick-mode candidate bar shown when the user presses `p` on a multi-team section header. Focused candidate is highlighted in bold magenta; others are dimmed. |
| **Output formatter** | `src/output.ts` | `buildOutput()` — entry point for both `--format markdown` and `--format json` serialisation of the confirmed selection. |

## Design principles

- **No I/O.** Every component in this layer is a pure function: given the same inputs it always returns the same outputs. This makes them straightforward to test with Bun's built-in test runner.
- **Single responsibility.** Each component owns exactly one concern (rows, summary, selection, …). The TUI composes them at render time rather than duplicating logic.
- **`types.ts` as the contract.** All components share the interfaces defined in `src/types.ts` (`TextMatchSegment`, `TextMatch`, `CodeMatch`, `RepoGroup`, `Row`, `TeamSection`, `OutputFormat`, `OutputType`, `FilterTarget`). Changes to these types require updating all components.
- **`render.ts` as façade.** External consumers import from `src/render.ts`, which re-exports all symbols from the `src/render/` sub-modules plus the top-level `renderGroups()` and `renderHelpOverlay()` functions.
- **`render.ts` as façade.** External consumers import from `src/render.ts`, which re-exports all symbols from the `src/render/` sub-modules plus the top-level `renderGroups()` and `renderHelpOverlay()` functions. `renderTeamPickHeader` is consumed internally by `render.ts` and is not re-exported (it is not part of the public façade).
Loading
Loading