pi-annotate-anything1.4.0
Pi extension: annotate arbitrary line/character ranges of any local file, injected as a quote block plus comment
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-annotate-anything
A Pi coding agent extension for annotating arbitrary line/character ranges of any local file, injected into the conversation as a quote block plus comment.
Highlights
- Vim-like modal interaction. The annotation card is a modal viewer; if your fingers know vim, they already know this card:
- normal / visual / comment modes, char-wise and line-wise selection with
oto swap endpoints - motions:
h j k l,0$,ggG, half/full page jumps,{count}prefixes /search: regex + smartcase, incremental jump as you type, persistent highlight,n/Nto hop between matchesTabcycling when annotations stack at the cursor
- normal / visual / comment modes, char-wise and line-wise selection with
- Annotate anything. Any local file, not just code: source, logs, configs, docs, data. Any range, down to character precision. Each annotation becomes a pill in the composer and ships to the agent as an exact annotate-time snapshot plus your comment.
Demo
Install
pi install npm:pi-annotate-anything
Requirements: a terminal font with Nerd Font Mono glyphs (used for the pending-annotation gutter icon). Without one the icon degrades to a placeholder box; everything else works the same.
Features
- Overlay card with line numbers, syntax highlighting, a live pending-count title bar, and a vim-style mode badge footer.
- Vim-style
/search in the viewer: regex + smartcase (case-insensitive unless the pattern has an uppercase letter), jumps to the nearest match as you type, all matches stay highlighted after submit,n/Nhop with wrap messages,{count}prefixes work on motions and hops. - Fuzzy path completion in the
/annotateargument: session-relative paths,~expansion, directories first, and workspace-wide fuzzy matching backed by an async file index. Recommended host: pi >= 1.0 (older hosts report an explicit error and get no completion). - Composer pills like
[src/a.ts:10-20]that expand into full quote-block-plus-comment payloads when the message is sent. - Annotate-time snapshots: the payload carries the text you saw when annotating, never a silent re-read of the file.
- Floating comment editor next to the selection with soft wrap and multiline support (
ctrl+j/shift+enter). - Pending ranges marked with underlines plus a gutter marker (Nerd Font icon + count on each draft's start line, continuation lines get a dim bar).
- Stacked annotations: when several pending drafts cover the cursor,
Tabcycles the target; preview, edit (c), and delete (x) act on the target. - Hover preview of the comment under the cursor (
ptoggles it). - The card stays open after each annotation: mark many ranges in one pass, then send them all in one message.
Usage
/annotate <path>
<path> supports ~ expansion, Tab-completion, and is resolved relative to the session working directory. It must be an existing regular file. The viewer refuses directories, binary files (files containing NUL bytes), and files larger than 1 MB. CRLF line endings are normalized to LF.
Keys
Every key is listed per mode; - means the key does nothing in that mode. The search / column applies while the search prompt is open, the comment box column while the floating comment editor is open.
| Key | Normal | Visual | Search / |
Comment box |
|---|---|---|---|---|
h j k l / arrows |
move the cursor | expand / shrink the selection | - | - |
0 / $ |
start / end of the line | same, keeping the anchor | - | - |
gg / G |
first / last line; {count}G jumps to line N |
same, keeping the anchor | - | - |
ctrl+d ctrl+u / PgDn PgUp |
half / full page | same | - | - |
1-9 (then digits) |
count prefix for j k h l G n N; never displayed |
- | - | - |
/ |
enter search mode | - | - | - |
n / N |
next / previous match (wraps, with a message) | same | - | - |
| printable text | - | - | append to the pattern; the match jumps as you type | insert |
Backspace |
- | - | delete the last pattern char | delete char |
Enter |
- | - | submit (empty pattern repeats the last search) | submit the annotation |
ctrl+j / shift+enter |
- | - | - | insert a newline |
v / V |
enter visual char / line mode | switch mode, or exit to normal | - | - |
o |
- | swap the anchor and cursor ends | - | - |
Tab |
cycle the target among stacked pending annotations | - | - | - |
c |
edit the targeted annotation's comment (pre-filled) | comment the selection | - | - |
x |
delete the targeted annotation (and its pill) | - | - | - |
p |
toggle the hover preview of pending comments | - | - | - |
Esc |
close the card | exit to normal mode | cancel; the cursor returns to where / started |
cancel the edit |
q |
close the card | - | - | - |
The comment box is a floating panel that opens next to the selection (below it, or above when space runs out).
Differences from vim
The viewer borrows vim's grammar, not its job: it annotates instead of editing. Where behavior diverges:
- Keys are repurposed for annotation, not editing:
ccomments (not change),xdeletes the annotation at the cursor (not a character),ppreviews the annotation (not paste),oswaps selection endpoints in visual mode (not open-line),qcloses the card (not macro recording),Tabcycles stacked annotation targets (not a vim binding). Escbacks out one level wherever you are: comment box -> cancel the edit, search -> cancel and restore the cursor, visual -> normal, normal -> close the card.- Selections start empty at the cursor and expand/shrink per keypress; vim grows the selection from the entry cell instead.
{count}is accepted only in normal mode, only beforejkhlGnN, and is never displayed.- Search uses JavaScript regex + smartcase (case-insensitive unless the pattern contains an uppercase letter), not vim's regex dialect; no
\c\vmagic, no*#ft%motions, no operators, no:noh; the highlight clears on the next search (see Roadmap).
Stacked annotations and the gutter
Pending annotations render as underlined ranges. The gutter before each line shows a marker field of fixed width:
- On a draft's start line: the comment icon plus the number of drafts starting there (warning color,
+above 9). - On lines a draft merely passes through: a dim vertical bar.
- The sum of all gutter counts always equals the pending count in the title bar.
When the cursor sits on a position covered by several pending drafts, exactly one is the target: its range renders bold and warning-colored, overlapping non-targets go dim, and the preview box shows its comment with a (i/n) position indicator. Press Tab to cycle the target (newest first, wrapping); moving the cursor resets the cycle.
Payload format
A pill in the composer looks like [src/a.ts:10-20]. When the message is sent, it is expanded to:
In `src/a.ts` (lines 10-20):
```ts
export function greet(name: string) { return `Hello, ${name}`; }
```
Comment: this name is misleading, rename it?
Label variants:
[src/a.ts:10-20]- line-wise range[src/a.ts:10]- single line[src/a.ts:10:5-12:20]- char-wise range (1-based line:column)[src/a.ts:10-20 ~2]- suffix added when the identical label is already pending in the composer
Behavior notes
- The card stays open after submitting an annotation so you can mark several ranges in one pass. Close it with
Escorq, then compose prose around the pills in the composer. Sending always happens from the composer. - An empty comment is allowed: the annotation is sent without a
Comment:line. - Deleting a pill from the composer deletes that annotation; its markers disappear from the card.
- Sending the message expands every remaining pill into its payload and clears them.
- Multiple annotations of the same range are allowed; each gets its own disambiguated token.
- The custom composer editor is installed unconditionally and does not coexist with other extensions that install their own editor.
Roadmap
- Buffers: annotate and navigate multiple sources in a single card session.
- More vim: a limited, curated set of additional vim commands (motions and operators that fit the annotation workflow).
- Basic Markdown rendering: render
.mdfiles with basic formatting (headings, emphasis, lists, code blocks) in the viewer instead of raw text.
Development
No build step: Pi bundles jiti and runs the TypeScript source directly.
npm install
npm test
npm run typecheck
Run the extension from source:
pi --extension ./index.ts
Release
Versions, changelogs, and npm publishing are automated: release-please opens a release PR from Conventional Commits, and merging it tags the release and publishes to npm via trusted publishing (OIDC). Never bump package.json or edit CHANGELOG.md by hand.
License
MIT. See LICENSE.