#155 by GitHub stars

backnotprop/plannotator

The workspace to stay in the loop and on top of agents - shape, decide, plan, annotate, review code, share with team, and send all feedback to agents with one click.

History

Review weekly changes to SKILL.md before updating. Dates mark the weeks in which we observed each version; checks apply to the recorded commits.

View weekly changes

apps/kiro-cli/skills/plannotator-annotate/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/kiro-cli/skills/plannotator-review/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/qwen-code/skills/plannotator-annotate/SKILL.md

Week of Nov 2, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 748c27a7de08

No earlier version was observed for comparison.

Compare on GitHub

apps/qwen-code/skills/plannotator-last/SKILL.md

Week of Nov 2, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 748c27a7de08

No earlier version was observed for comparison.

Compare on GitHub

apps/qwen-code/skills/plannotator-review/SKILL.md

Week of Nov 2, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 748c27a7de08

No earlier version was observed for comparison.

Compare on GitHub

apps/skills/claude/plannotator-annotate/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/skills/claude/plannotator-inbox/SKILL.md

Week of Nov 2, 2026 · +18 / −2 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

- 3 description: Open the Plannotator Inbox in the browser (starting it if needed) and say its address.
+ 3 description: Open the Plannotator Inbox in the browser (starting it if needed), say its address, and learn how to use the Inbox for the rest of the session.
+ 14 ## Using the Inbox
+ 15 
+ 16 The Plannotator Inbox is one window on this computer where you leave the person messages, `:::question` blocks they answer with a click, and files. They answer when they can. Nothing blocks: you send, then keep working or end your turn.
+ 17 
+ 18 - Use it for a decision only they can make (a :::question block; go on with other work), finished long work (a short summary with its pictures inline, files in `attachments`), or when they ask.
+ 19 - One thread per work stream, unless the person says otherwise.
+ 20 - Send with a `plannotator_inbox` tool when you have one (it can be listed with a prefix and may need loading through tool search first): `action` `send_message`, with a markdown `body`. Otherwise call `send_message` on the Inbox's MCP server, `plannotator inbox mcp`.
+ 21 - send_message's result says whether the person's reply arrives in this session by itself. When it does, do not wait or poll for it: end your turn or go on with other work. When it does not, go on with your work and read the thread later with read_thread.
+ 22 - A decision in the Inbox stays there: when the person writes in the chat, say what waits and link its thread, but never also ask it in the chat with a blocking question tool (AskUserQuestion or similar). Ask in the chat only about something new they raised there that needs no pictures or files, or when they ask.
+ 23 - Be proactive about Inbox updates and about replying to the person through the Inbox when you think they would want you to. Don't withhold important updates or decisions, and don't batch updates to send later: proactivity is better than procrastination.
+ 24 - Never run `plannotator inbox` just to send: send_message starts a stopped Inbox itself, and bare `plannotator inbox` opens the window for the person.
+ 25 - Before your first send, run `plannotator inbox --help` and read it: every field, the full question syntax, and how to connect the MCP server.
+ 26 - The `plannotator` skill, where you have it, has more.
+ 27 
- 16 The output above is the Plannotator Inbox's address; it opened in the person's browser unless the output says why not. Tell them the address in one sentence, with that reason when there is one, and stop. Do not begin any work.
+ 30 The output above is the Plannotator Inbox's address; it opened in the person's browser unless the output says why not. Tell them the address in one sentence, with that reason when there is one. If the output is an error, tell them what it says; `Unknown inbox option: --open` means their Plannotator is older than this command (update with `plannotator update`, or run `plannotator inbox` in a terminal).
+ 31 
+ 32 This command starts no new work and resumes none: do anything else the person asked in the same message, otherwise stop there. For the rest of this session, use the Inbox as above: for a decision the person is not here to make, and to hand over long work you finished.

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/skills/claude/plannotator-last/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/skills/claude/plannotator-review/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/skills/core/plannotator/SKILL.md

Week of Nov 2, 2026 · +25 / −15 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

- 69 - `--no-git-remote-check` stops the session contacting the git remote at all: no `git ls-remote` for the default branch or the "behind GitHub" baseline check. The compare target then comes from local refs only and the staleness banner never shows, which also takes away its one-click Fetch button (the `/api/fetch-base` endpoint stays available); fetching from a terminal is unaffected. Use it when a remote probe is expensive or intrusive — most sharply when SSH authentication is backed by a hardware token, where each probe is a physical touch prompt. The same opt-out is available session-wide as `PLANNOTATOR_GIT_REMOTE_CHECK=0` or `{ "gitRemoteCheck": false }` in `~/.plannotator/config.json` (the flag beats the env var, which beats the config key). Without it the remote is queried when the review opens, on diff load, on a diff-type/base switch, and on Fetch — never on a timer.
+ 69 - `--no-git-remote-check` stops the session contacting the git remote at all: no `git ls-remote` for the default branch or the "behind GitHub" baseline check. The compare target then comes from local refs only and the staleness banner never shows, which also takes away its one-click Fetch button (the `/api/fetch-base` endpoint stays available); fetching from a terminal is unaffected. Use it when a remote probe is expensive or intrusive — most sharply when SSH authentication is backed by a hardware token, where each probe is a physical touch prompt. The same opt-out is available session-wide as `PLANNOTATOR_GIT_REMOTE_CHECK=0` or `{ "gitRemoteCheck": false }` in `config.json` in the data directory (`plannotator data-dir` prints it) (the flag beats the env var, which beats the config key). Without it the remote is queried when the review opens, on diff load, on a diff-type/base switch, and on Fetch — never on a timer.
+ 88 - AsciiDoc files: `.adoc`, `.asciidoc`.
+ 179 plannotator inbox call [--session <id>] <action> [<json> | -]
+ 180 plannotator inbox status [--json] | stop | restart
- 182 - Use it for a decision only they can make (a `:::question` block, then go on with work that does not depend on it), long work you finished (one short summary, with the files to look at in `attachments`), or when they ask. A decision you sent to the Inbox stays there: when the person writes in the chat, say there what waits (a short summary and the thread's link), but never also ask it in the chat with a blocking question tool (AskUserQuestion or similar). Ask in the chat only about something new they raised there that needs no pictures or files, or when they ask. Do not send routine progress. Use a review (plan review, `annotate`, `review`) when you need a decision on this document or diff before you go on.
- 183 - If you have a `plannotator_inbox` tool, use it; otherwise use the stdio MCP server `plannotator inbox mcp`. With the tool, the person's reply arrives in your session by itself, so end your turn instead of waiting.
+ 185 - Use it for a decision only they can make (a `:::question` block, then go on with work that does not depend on it), long work you finished (one short summary with its pictures in place, `![alt](path)` in the body where you write about each, and the files to look at in `attachments`), or when they ask. One thread per work stream, unless the person says otherwise. A decision you sent to the Inbox stays there: when the person writes in the chat, say there what waits (a short summary and the thread's link), but never also ask it in the chat with a blocking question tool (AskUserQuestion or similar). Ask in the chat only about something new they raised there that needs no pictures or files, or when they ask. Be proactive about Inbox updates and about replying to the person through the Inbox when you think they would want you to. Don't withhold important updates or decisions, and don't batch updates to send later: proactivity is better than procrastination. Use a review (plan review, `annotate`, `review`) when you need a decision on this document or diff before you go on.
+ 186 - If you have a `plannotator_inbox` tool, use it; otherwise use the stdio MCP server `plannotator inbox mcp`. When send_message says the person's reply arrives in this session by itself, end your turn instead of waiting; otherwise go on with your work and read the thread later with read_thread.
+ 187 - For many Inbox calls in a row (a tidy above all), `plannotator inbox call` runs any action from a script and prints its result as JSON; `plannotator inbox --help` says how.
- 185 - Never run `plannotator inbox` just to send: calls start a stopped Inbox in the background, and bare `plannotator inbox` opens the window for the person. `--tailscale` is the person's choice; never add it.
+ 189 - Never run `plannotator inbox` just to send: calls start a stopped Inbox in the background, and bare `plannotator inbox` opens the window for the person. `--tailscale` is the person's choice; never add it. `plannotator inbox status` only reads; never run `plannotator inbox stop` or `restart` unless the person asks.
- 194 Only while the person has turned the Inbox's Board on (Settings → Capabilities); it is off by default, and then every call answers `board_off` and writes nothing, so use it only when your inbox tool lists `card` or the person asks. The Board holds one card per piece of work that outlasts a reply, in the column of its stage (backlog, designing, your_look, building, your_review, shipped). Use the `card` action of a `plannotator_inbox` tool when you have one; this CLI is the same call from a shell. `plannotator card --help` gives the arguments, and `plannotator inbox --help` its card section while the Board is on. The icebox is the person's only. A title is 2–5 words naming the work ("Pi update fix"), never a sentence; where it stands goes in `say`. When a card waits on the person, link the Inbox thread where they make the call (`thread_id`), so "You: …" opens it.
+ 198 Only while the person has turned the Inbox's Board on (Settings → Capabilities); it is off by default, and then every call answers `board_off` and writes nothing, so use it only when your inbox tool lists `card` or the person asks. The Board holds one card per piece of work that outlasts a reply, in the column of where the work stands (todo, in_progress, in_review, done; backlog for what nobody asked for yet). Use the `card` action of a `plannotator_inbox` tool when you have one; this CLI is the same call from a shell. `plannotator card --help` gives the arguments, and `plannotator inbox --help` its card section while the Board is on. A card the person put in backlog is theirs to move. A title is 2–5 words naming the work ("Pi update fix"), never a sentence; where it stands goes in `say`. Move a card to in_review yourself when you hand your part over, with `you` and the Inbox thread where the person makes the call (`thread_id`), so "You: …" opens it. Ship a card when you see its PR merge (the Board may also move it to done once every PR linked with `--pr` merged).
+ 204 plannotator data-dir
+ 205 plannotator feedback --stdin [--category <bug|idea|other>] [--contact '<text>'] <<'PLANNOTATOR_FEEDBACK_END'
+ 206 <the message, exactly as the person approved it>
+ 207 PLANNOTATOR_FEEDBACK_END
+ 214 - `data-dir` prints the absolute path of Plannotator's data directory (plans, history, drafts, `config.json`, `hooks/`) and creates nothing. Run it whenever you need a file in that folder, and use the path it prints. Never write a path such as `~/.plannotator/...` from memory: an install can keep its data under `$XDG_DATA_HOME/plannotator`, and a new `~/.plannotator` can hide that data from Plannotator. If it prints nothing or fails (an older Plannotator, before 0.28.41), use `$PLANNOTATOR_DATA_DIR` when it is set; otherwise the home folder's `.plannotator` if it exists, else `$XDG_DATA_HOME/plannotator` when `XDG_DATA_HOME` is set, else the home folder's `.plannotator`.
+ 215 - `feedback` sends the person's feedback about Plannotator to its maintainers and prints `Sent` (the message, the category and contact if given, the version, the OS name and that it came from the CLI; nothing else). Only when the person asks you to send feedback: show them the exact text first and run it only after they say yes. Pass the message on stdin with a quoted heredoc (`<<'PLANNOTATOR_FEEDBACK_END'`, quotes included; pick another end word if a line of the message is exactly that), never inside double quotes in any shell, where `$VAR`, backticks and `$(...)` are expanded and change the approved text. Fill in or leave out the bracketed options (`--category idea`), and put a contact in single quotes. In PowerShell, set `$OutputEncoding = [Text.UTF8Encoding]::new($false)` and pipe a single-quoted here-string (`@'` … `'@ | plannotator feedback --stdin`). Exit 0 sent, 1 not sent, 2 usage.
- 209 - Additional host-internal subcommands (the `opencode-*` and `copilot-plan` family) are invoked by their plugins, not by you.
+ 219 - Additional host-internal subcommands (the `opencode-*` family, `copilot-plan` and `dsh-plan`) are invoked by their plugins, not by you.
- 217 | `PLANNOTATOR_ORIGIN` | Override agent-origin detection (`claude-code`, `codex`, `opencode`, `pi`, `oh-my-pi`, `amp`, `droid`, `copilot-cli`, `gemini-cli`, `kiro-cli`, `mistral-vibe`). Set it when launching Plannotator from a wrapper the detection cannot see through. |
+ 227 | `PLANNOTATOR_ORIGIN` | Override agent-origin detection (`claude-code`, `codex`, `opencode`, `pi`, `oh-my-pi`, `amp`, `droid`, `copilot-cli`, `gemini-cli`, `kiro-cli`, `mistral-vibe`, `qwen-code`, `dsh`). Set it when launching Plannotator from a wrapper the detection cannot see through. |
- 220 | `PLANNOTATOR_DATA_DIR` | Move the data directory (default `~/.plannotator`): plans, history, drafts, config. |
+ 230 | `PLANNOTATOR_DATA_DIR` | Move the data directory: plans, history, drafts, config. Unset, the default is `~/.plannotator` or `$XDG_DATA_HOME/plannotator`; `plannotator data-dir` prints which one this install uses. |
- 236 When two devices edit one note offline, the later save wins and the other edit is lost. Local storage keeps it with no server work.
- 237 
- 238 - [ ] Local only, purged after 30 days — cheap, no server change
+ 246 When two devices edit one note offline, the later save wins and the other is lost. Local storage keeps it with no server work.
+ 247 
+ 248 - [ ] Nowhere — silent loss, as today
+ 249 - [ ] Local only, purged after 30 days — cheap
- 240 - [ ] Nowhere — accept silent loss for v1
- 252 - Choices: `- [ ] label`, optionally `- [ ] label — why`. The reviewer can also answer "Other", add a note, or skip.
- 253 - `Recommended: <label>` marks yours (`A, B and C` on `:::question-multi`); other text shows as one more answer, tagged Recommended.
- 254 - Ask only what you cannot decide alone, about 8 a round at most. Do not ask rhetorical questions or questions the codebase answers.
+ 262 - Choices: `- [ ] label`, optionally `- [ ] label — why`, in natural order. The reviewer can also answer "Other", add a note, or skip.
+ 263 - `Recommended: <label>` marks yours (`A, B and C` on `:::question-multi`); other text is one more answer, tagged Recommended. Never move yours first.
+ 264 - Ask only what you cannot decide alone, about 8 a round. Do not ask rhetorical questions or questions the codebase answers.

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/skills/core/plannotator-annotate/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/skills/core/plannotator-inbox/SKILL.md

Week of Nov 2, 2026 · +21 / −3 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

- 3 description: Open the Plannotator Inbox in the browser (starting it if needed) and tell the person its address.
+ 3 description: Open the Plannotator Inbox in the browser (starting it if needed), tell the person its address, and learn how to use the Inbox for the rest of the session.
- 9 Use this skill when the person wants their Plannotator Inbox: they closed its tab, or ask where it is.
+ 9 Use this skill when the person wants their Plannotator Inbox (they closed its tab, or ask where it is), or wants you to use it.
- 20 2. Tell the person the address in one sentence and stop. Do not begin any work. When it also says no browser opened (a remote session), pass on how it says to reach the Inbox.
+ 20 2. Tell the person the address in one sentence. When it also says no browser opened (a remote session), pass on how it says to reach the Inbox.
+ 24 
+ 25 ## Using the Inbox
+ 26 
+ 27 The Plannotator Inbox is one window on this computer where you leave the person messages, `:::question` blocks they answer with a click, and files. They answer when they can. Nothing blocks: you send, then keep working or end your turn.
+ 28 
+ 29 - Use it for a decision only they can make (a :::question block; go on with other work), finished long work (a short summary with its pictures inline, files in `attachments`), or when they ask.
+ 30 - One thread per work stream, unless the person says otherwise.
+ 31 - Send with a `plannotator_inbox` tool when you have one (it can be listed with a prefix and may need loading through tool search first): `action` `send_message`, with a markdown `body`. Otherwise call `send_message` on the Inbox's MCP server, `plannotator inbox mcp`.
+ 32 - send_message's result says whether the person's reply arrives in this session by itself. When it does, do not wait or poll for it: end your turn or go on with other work. When it does not, go on with your work and read the thread later with read_thread.
+ 33 - A decision in the Inbox stays there: when the person writes in the chat, say what waits and link its thread, but never also ask it in the chat with a blocking question tool (AskUserQuestion or similar). Ask in the chat only about something new they raised there that needs no pictures or files, or when they ask.
+ 34 - Be proactive about Inbox updates and about replying to the person through the Inbox when you think they would want you to. Don't withhold important updates or decisions, and don't batch updates to send later: proactivity is better than procrastination.
+ 35 - Never run `plannotator inbox` just to send: send_message starts a stopped Inbox itself, and bare `plannotator inbox` opens the window for the person.
+ 36 - Before your first send, run `plannotator inbox --help` and read it: every field, the full question syntax, and how to connect the MCP server.
+ 37 - The `plannotator` skill, where you have it, has more.
+ 38 
+ 39 ## After the address
+ 40 
+ 41 This command starts no new work and resumes none: do anything else the person asked in the same message, otherwise stop there. For the rest of this session, use the Inbox as above: for a decision the person is not here to make, and to hand over long work you finished.

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/skills/core/plannotator-last/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/skills/core/plannotator-review/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/skills/extra/plannotator-compound/SKILL.md

Week of Nov 2, 2026 · +35 / −11 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

- 24 1. **Plannotator mode (first-class)** — Determine the Plannotator data directory:
- 25    use `$PLANNOTATOR_DATA_DIR` if set, otherwise `~/.plannotator`. Check the
- 26    `plans/` subdirectory there. If it exists and contains `*-denied.md` files,
- 27    use this mode. The entire workflow below is written for Plannotator data.
+ 24 1. **Plannotator mode (first-class)** — Find the Plannotator data directory,
+ 25    called `<data-dir>` from here on. Run:
+ 26 
+ 27    ```
+ 28    plannotator data-dir
+ 29    ```
+ 30 
+ 31    It prints the folder's absolute path and creates nothing; use what it
+ 32    prints. If it prints nothing or fails (an older Plannotator), use `$PLANNOTATOR_DATA_DIR` if
+ 33    set, else whichever of `$XDG_DATA_HOME/plannotator` and the home folder's
+ 34    `.plannotator` holds `history/` or `sessions/` (the home one if both do),
+ 35    and ask the user if neither does.
+ 36 
+ 37    Never write into any other folder than `<data-dir>`. In particular, do not
+ 38    create a `.plannotator` folder in the home folder unless `<data-dir>` is
+ 39    that folder: on an install that keeps its data under `$XDG_DATA_HOME`, a
+ 40    new one can make Plannotator lose sight of the person's plans and history.
+ 41    Leave any other Plannotator folder you find alone, and do not suggest
+ 42    deleting it: it may hold older data.
+ 43 
+ 44    Check the `plans/` subdirectory of `<data-dir>`. If it exists and contains
+ 45    `*-denied.md` files, use this mode. The entire workflow below is written for
+ 46    Plannotator data.
- 58 ls ${PLANNOTATOR_DATA_DIR:-~/.plannotator}/plans/compound-planning-report*.html
- 59 ```
+ 77 ls <data-dir>/plans/compound-planning-report*.html
+ 78 ```
+ 79 
+ 80 (`<data-dir>` is the folder from Source Selection; put its real path in the command.)
- 364 the user's plans directory with a versioned filename:
+ 385 `<data-dir>/plans/` (the folder from Source Selection) with a versioned filename:
- 522 ${PLANNOTATOR_DATA_DIR:-~/.plannotator}/hooks/compound/enterplanmode-improve-hook.txt
- 523 ```
- 524 
- 525 Create the `hooks/compound/` directory inside the data directory if it doesn't exist.
+ 543 <data-dir>/hooks/compound/enterplanmode-improve-hook.txt
+ 544 ```
+ 545 
+ 546 `<data-dir>` is the folder from Source Selection (`plannotator data-dir`
+ 547 prints it). If you have not found it yet, find it now the same way; never
+ 548 guess it. Create the `hooks/compound/` directory inside `<data-dir>` if it
+ 549 doesn't exist.

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/skills/extra/plannotator-setup-goal/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/skills/extra/plannotator-visual-explainer/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/vibe/skills/plannotator-annotate/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/vibe/skills/plannotator-last/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

apps/vibe/skills/plannotator-review/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

6a6263ddaa89 → 748c27a7de08

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 6a6263ddaa89

No earlier version was observed for comparison.

Compare on GitHub

View on GitHubAdd to my skills

Verified commit 748c27a7de085444de509ad8c1f8a9cd0859e3a2

git clone --filter=blob:none --no-checkout https://github.com/backnotprop/plannotator.git .hotskills/backnotprop-plannotator && git -C .hotskills/backnotprop-plannotator checkout --detach 748c27a7de085444de509ad8c1f8a9cd0859e3a2 # Copy each selected skill directory from .hotskills/backnotprop-plannotator into .claude/skills/<skill-name>/
git clone --filter=blob:none --no-checkout https://github.com/backnotprop/plannotator.git .hotskills/backnotprop-plannotator && git -C .hotskills/backnotprop-plannotator checkout --detach 748c27a7de085444de509ad8c1f8a9cd0859e3a2 # Copy each selected skill directory from .hotskills/backnotprop-plannotator into .agents/skills/<skill-name>/
9.9kGitHub stars
87stars in 7 days
20skills in this repository
4.3kcommits in 30 days

Heat

7.0
  • Star growth, last 7 daysAhead of 81% of tracked repositories
  • Star growth, last 24 hoursAhead of 74% of tracked repositories
  • Rank movement, last 7 daysAhead of 69% of tracked repositories
  • Commits, last 30 daysAhead of 100% of tracked repositories
  • Accounts asking about it through MCP, last 7 daysNot available
  • Hot votes this weekAhead of 0% of tracked repositories

Heat never changes the order by stars, and sponsorship never touches it. How heat works

Stars over time

Aug 2, 2026Nov 7, 20269,879

Individual skills in this repository

How it works

plannotator-annotate

When your agent uses it

Open Plannotator's annotation UI for a file, folder, or URL, then address the returned annotations.

What it loads into context

Frontmatter

41 tokens

Always available to the agent

SKILL.md

217 tokens

When the skill is activated

Reference documents

0 tokens

Loaded on request

Token counts are estimates. Scripts run separately; they are not counted as reference context.

The playbook

    Scripts executed separately

    • ../../../hook/hooks/mod/bridge.test.ts
    • ../../../hook/hooks/mod/bridge.ts
    • ../../../hook/hooks/mod/controller.test.ts
    • ../../../hook/hooks/mod/controller.ts
    • ../../../hook/hooks/mod/delivery.test.ts
    • ../../../hook/hooks/mod/delivery.ts
    • ../../../hook/hooks/mod/enabled.test.ts
    • ../../../hook/hooks/mod/enabled.ts
    • ../../../hook/hooks/mod/globals.d.ts
    • ../../../hook/hooks/mod/host.ts
    • ../../../hook/hooks/mod/inbox-ask.test.ts
    • ../../../hook/hooks/mod/inbox-contract.test.ts
    • ../../../hook/hooks/mod/inbox-contract.ts
    • ../../../hook/hooks/mod/inbox-default-tools.test.ts
    • ../../../hook/hooks/mod/inbox-default-tools.ts
    • ../../../hook/hooks/mod/inbox-loaded.test.ts
    • ../../../hook/hooks/mod/inbox-reminder-custom.test.ts
    • ../../../hook/hooks/mod/inbox-reminder.test.ts
    • ../../../hook/hooks/mod/inbox-session-label.test.ts
    • ../../../hook/hooks/mod/inbox-session-project.test.ts
    • ../../../hook/hooks/mod/inbox-session-threads.test.ts
    • ../../../hook/hooks/mod/inbox.test.ts
    • ../../../hook/hooks/mod/inbox.ts
    • ../../../hook/hooks/mod/launch.test.ts
    • ../../../hook/hooks/mod/launch.ts
    • ../../../hook/hooks/mod/plan.ts
    • ../../../hook/hooks/mod/register.test.ts
    • ../../../hook/hooks/mod/register.ts
    • ../../../hook/hooks/mod/send-now-engine.test.ts
    • ../../../hook/hooks/mod/shell-words.ts
    • ../../../hook/hooks/mod/tailscale.test.ts
    • ../../../hook/hooks/mod/take-over.test.ts
    • ../../../hook/hooks/mod/take-over.ts
    • ../../../hook/hooks/mod/targets.test.ts
    • ../../../hook/hooks/mod/testing/claude-session.ts
    • ../../../hook/hooks/mod/testing/fake-host.ts
    • ../../../hook/hooks/mod/tool.test.ts
    • ../../../hook/hooks/mod/tool.ts
    • ../../../hook/hooks/mod/turns.test.ts
    • ../../../hook/hooks/mod/turns.ts

    Files

    • ../../../../.claude-plugin/marketplace.jsonasset338 bytes
    • ../../../copilot/hooks.jsonasset366 bytes
    • ../../../hook/.claude-plugin/plugin.jsonasset410 bytes
    • ../../../hook/hooks/hooks.jsonasset1,474 bytes
    • ../../../hook/hooks/mod/bridge.test.tsruns17,001 bytes
    • ../../../hook/hooks/mod/bridge.tsruns15,839 bytes
    • ../../../hook/hooks/mod/controller.test.tsruns75,234 bytes
    • ../../../hook/hooks/mod/controller.tsruns82,743 bytes
    • ../../../hook/hooks/mod/delivery.test.tsruns10,260 bytes
    • ../../../hook/hooks/mod/delivery.tsruns13,019 bytes
    • ../../../hook/hooks/mod/enabled.test.tsruns6,832 bytes
    • ../../../hook/hooks/mod/enabled.tsruns7,691 bytes
    • ../../../hook/hooks/mod/globals.d.tsruns782 bytes
    • ../../../hook/hooks/mod/host.tsruns4,597 bytes
    • ../../../hook/hooks/mod/inbox-ask.test.tsruns23,204 bytes
    • ../../../hook/hooks/mod/inbox-contract.test.tsruns988 bytes
    • ../../../hook/hooks/mod/inbox-contract.tsruns71,460 bytes
    • ../../../hook/hooks/mod/inbox-default-tools.test.tsruns2,043 bytes
    • ../../../hook/hooks/mod/inbox-default-tools.tsruns26,512 bytes
    • ../../../hook/hooks/mod/inbox-loaded.test.tsruns4,608 bytes
    • ../../../hook/hooks/mod/inbox-reminder-custom.test.tsruns3,895 bytes
    • ../../../hook/hooks/mod/inbox-reminder.test.tsruns7,332 bytes
    • ../../../hook/hooks/mod/inbox-session-label.test.tsruns21,193 bytes
    • ../../../hook/hooks/mod/inbox-session-project.test.tsruns8,663 bytes
    • ../../../hook/hooks/mod/inbox-session-threads.test.tsruns3,403 bytes
    • ../../../hook/hooks/mod/inbox.test.tsruns67,484 bytes
    • ../../../hook/hooks/mod/inbox.tsruns59,605 bytes
    • ../../../hook/hooks/mod/launch.test.tsruns10,003 bytes
    • ../../../hook/hooks/mod/launch.tsruns30,610 bytes
    • ../../../hook/hooks/mod/plan.tsruns6,741 bytes
    • ../../../hook/hooks/mod/register.test.tsruns3,306 bytes
    • ../../../hook/hooks/mod/register.tsruns48,203 bytes
    • ../../../hook/hooks/mod/send-now-engine.test.tsruns1,817 bytes
    • ../../../hook/hooks/mod/shell-words.tsruns1,687 bytes
    • ../../../hook/hooks/mod/tailscale.test.tsruns9,823 bytes
    • ../../../hook/hooks/mod/take-over.test.tsruns6,254 bytes
    • ../../../hook/hooks/mod/take-over.tsruns2,092 bytes
    • ../../../hook/hooks/mod/targets.test.tsruns11,296 bytes
    • ../../../hook/hooks/mod/testing/claude-session.tsruns11,393 bytes
    • ../../../hook/hooks/mod/testing/fake-host.tsruns4,782 bytes
    • ../../../hook/hooks/mod/tool.test.tsruns946 bytes
    • ../../../hook/hooks/mod/tool.tsruns41,561 bytes
    • ../../../hook/hooks/mod/tsconfig.jsonasset308 bytes
    • ../../../hook/hooks/mod/turns.test.tsruns21,384 bytes
    • ../../../hook/hooks/mod/turns.tsruns21,219 bytes
    • SKILL.mdcore855 bytes
    • SKILL.mdcore855 bytes
    • ../../../qwen-code/extension/hooks/hooks.jsonasset1,101 bytes
    Read SKILL.md
    
    # Plannotator Annotate (Kiro)
    
    Run:
    
    ```bash
    PLANNOTATOR_ORIGIN=kiro-cli plannotator annotate $ARGUMENTS
    ```
    
    `$ARGUMENTS` should be a markdown or plain-text config file path (.md, .txt, .yaml, .json, .toml, .ini, .csv, .log, …), folder path, html file path, or URL.
    
    If the command reports that the arguments could not be resolved to a file, URL, or folder, work out which target the user means and re-run the command yourself with that concrete path or URL.
    
    If the session closes without feedback (the output is empty, or a JSON object with `"decision": "dismissed"`), acknowledge it in one sentence and stop. Do not begin any work: closing the session is not approval.
    View the complete source on GitHub

    Skills included

    • plannotator-annotate
    • plannotator-review
    • plannotator-annotate
    • plannotator-last
    • plannotator-review
    • plannotator-annotate
    • plannotator-inbox
    • plannotator-last
    • plannotator-review
    • plannotator
    • plannotator-annotate
    • plannotator-inbox
    • plannotator-last
    • plannotator-review
    • plannotator-compound
    • plannotator-setup-goal
    • plannotator-visual-explainer
    • plannotator-annotate
    • plannotator-last
    • plannotator-review

    Repository facts

    License
    Apache-2.0
    Created
    Dec 28, 2025
    Last pushed
    Nov 7, 2026
    Forks
    745
    Open issues
    77
    Project activity
    Active

    Rank over the last 12 weeks

    Now #155 · best #145 · down 7 in 30 days

    1. #152 on Oct 30
    2. #153 from Oct 31 to Nov 1
    3. #154 on Nov 2
    4. #155 on Nov 3
    5. #154 from Nov 4 to Nov 6
    6. #155 since Nov 7
    49 earlier periods
    1. #143 on Aug 2
    2. #144 on Aug 8
    3. #143 on Aug 9
    4. #147 from Aug 10 to Aug 12
    5. #148 from Aug 13 to Aug 15
    6. #147 on Aug 16
    7. #148 from Aug 17 to Aug 20
    8. #147 on Aug 21
    9. #146 on Aug 22
    10. #148 from Aug 23 to Aug 26
    11. #147 from Aug 27 to Aug 29
    12. #146 from Aug 30 to Aug 31
    13. #147 on Sep 1
    14. #145 on Sep 2
    15. #146 on Sep 3
    16. #145 on Sep 4
    17. #146 on Sep 5
    18. #145 on Sep 6
    19. #146 from Sep 7 to Sep 10
    20. #145 on Sep 11
    21. #146 from Sep 12 to Sep 14
    22. #148 on Sep 15
    23. #147 on Sep 16
    24. #148 from Sep 17 to Sep 18
    25. #147 on Sep 19
    26. #148 from Sep 20 to Sep 21
    27. #149 from Sep 22 to Sep 23
    28. #148 on Sep 24
    29. #149 on Sep 25
    30. #148 from Sep 26 to Sep 27
    31. #149 on Sep 28
    32. #148 from Sep 29 to Sep 30
    33. #149 from Oct 1 to Oct 2
    34. #148 on Oct 3
    35. #147 on Oct 4
    36. #149 on Oct 5
    37. #147 on Oct 6
    38. #149 on Oct 7
    39. #148 from Oct 8 to Oct 11
    40. #149 on Oct 12
    41. #150 on Oct 13
    42. #151 on Oct 14
    43. #149 on Oct 15
    44. #148 on Oct 16
    45. #149 on Oct 17
    46. #150 from Oct 18 to Oct 23
    47. #151 from Oct 24 to Oct 25
    48. #150 on Oct 28
    49. #151 on Oct 29

    Before you install

    Network or broad shell

    Automated signals, not a guarantee

    Checked Nov 3, 2026

    Not fully analyzed: some files were too long, unreadable or missing. This never lowers the level.

    Show 41 notes
    • apps/kiro-cli/skills/plannotator-annotate/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/kiro-cli/skills/plannotator-review/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/kiro-cli/skills/plannotator-review/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/kiro-cli/skills/plannotator-review/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/qwen-code/skills/plannotator-annotate/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/qwen-code/skills/plannotator-annotate/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/qwen-code/skills/plannotator-last/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/qwen-code/skills/plannotator-review/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/skills/core/plannotator/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/skills/core/plannotator/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/skills/core/plannotator/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/skills/core/plannotator/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/skills/core/plannotator/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/skills/core/plannotator/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/skills/core/plannotator/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/skills/core/plannotator/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/skills/core/plannotator/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/skills/core/plannotator/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/skills/core/plannotator/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    • apps/skills/core/plannotator/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
    Show all 373 signals by file

    apps/hook/.claude-plugin/plugin.json

    • Network accessline 6
      "name": "backnotprop"   },   "repository": "https://github.com/backnotprop/plannotator",
    • Network accessline 7
      },   "repository": "https://github.com/backnotprop/plannotator",   "keywords": ["planning", "review", "hooks", "ExitPlanMode", "collaboration"]
    • Network accessline 8
      "repository": "https://github.com/backnotprop/plannotator",

    apps/hook/hooks/mod/bridge.test.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 74
      [Credential-related source omitted]
    • Network accessline 75
      [Credential-related source omitted]
    • Network accessline 76
      [Credential-related source omitted]
    • Network accessline 107
      [Credential-related source omitted]
    • Network accessline 108
      [Credential-related source omitted]
    • Network accessline 109
      [Credential-related source omitted]
    • Network accessline 135
      [Credential-related source omitted]
    • Network accessline 136
      [Credential-related source omitted]
    • Network accessline 137
      [Credential-related source omitted]
    • Network accessline 160
      turns.onPromptEntered({ text: 'Review PR 12.', fromUs: false, originKind: 'plugin' })     turns.onTurnStart('plugin-turn', 'The workspaces plugin sent a message: Review PR 12.')     const client = createBridge({ host, baseUrl: 'http://127.0
    • Network accessline 161
      [Credential-related source omitted]
    • Network accessline 162
      [Credential-related source omitted]
    • Network accessline 180
      [Credential-related source omitted]
    • Network accessline 181
      [Credential-related source omitted]
    • Network accessline 182
      [Credential-related source omitted]
    • Network accessline 210
      [Credential-related source omitted]
    • Network accessline 211
      [Credential-related source omitted]
    • Network accessline 212
      [Credential-related source omitted]
    • Network accessline 237
      [Credential-related source omitted]
    • Network accessline 238
      [Credential-related source omitted]
    • Network accessline 239
      [Credential-related source omitted]
    • Network accessline 277
      [Credential-related source omitted]
    • Network accessline 278
      [Credential-related source omitted]
    • Network accessline 279
      [Credential-related source omitted]
    • Network accessline 306
      [Credential-related source omitted]
    • Network accessline 307
      [Credential-related source omitted]
    • Network accessline 308
      [Credential-related source omitted]
    • Network accessline 316
      describe('how a bridge loop ends', () => {   const POLL = 'http://127.0.0.1:4321/api/ai/bridge/poll'
    • Network accessline 317
      const POLL = 'http://127.0.0.1:4321/api/ai/bridge/poll'
    • Network accessline 319
      [Credential-related source omitted]
    • Network accessline 320
      [Credential-related source omitted]

    apps/hook/hooks/mod/bridge.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 93
      /** e.g. `http://127.0.0.1:4321`; always the loopback literal. */
    • Network accessline 102
      export function bridgeBaseUrl(port: number): string {   return `http://127.0.0.1:${port}`
    • Network accessline 103
      return `http://127.0.0.1:${port}`
    • Network accessline 163
      if (events.length === 0) return     try {       const response = await host.fetch(`${base}${BRIDGE_EVENT_PATH}`, {
    • Network accessline 164
      try {       const response = await host.fetch(`${base}${BRIDGE_EVENT_PATH}`, {         method: 'POST',
    • Network accessline 165
      const response = await host.fetch(`${base}${BRIDGE_EVENT_PATH}`, {
    • Network accessline 312
      let response       try {         response = await host.fetch(`${base}${BRIDGE_POLL_PATH}`, {
    • Network accessline 313
      try {         response = await host.fetch(`${base}${BRIDGE_POLL_PATH}`, {           method: 'POST',
    • Network accessline 314
      response = await host.fetch(`${base}${BRIDGE_POLL_PATH}`, {

    apps/hook/hooks/mod/controller.test.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 21
      function serveOnLaunch(host: FakeHost, port = 4321) {   host.onRun = (call) => {     if (isLaunch(call)) host.files.set(`${launchDirOf(call)}/ready`, `${JSON.stringify({ url: `http://localhost:${port}`, isRemote: false, port })}\n`)
    • Network accessline 22
      host.onRun = (call) => {     if (isLaunch(call)) host.files.set(`${launchDirOf(call)}/ready`, `${JSON.stringify({ url: `http://localhost:${port}`, isRemote: false, port })}\n`)   }
    • Network accessline 23
      if (isLaunch(call)) host.files.set(`${launchDirOf(call)}/ready`, `${JSON.stringify({ url: `http://localhost:${port}`, isRemote: false, port })}\n`)
    • Access to secretsline 99
      [Credential-related source omitted]
    • Network accessline 406
      const text = await mod.runCommand('review', 'https://github.com/o/r/pull/412 --base "feature one"')
    • Network accessline 408
      expect(text).toContain('PR #412')     expect(text).toContain('http://localhost:5555')
    • Network accessline 409
      expect(text).toContain('http://localhost:5555')
    • Network accessline 410
      expect(launches(host)[0]?.argv.slice(5)).toEqual(['plannotator', 'review', 'https://github.com/o/r/pull/412', '--base', 'feature one'])
    • Network accessline 587
      expect(byTool?.argv.slice(5)).toEqual(byCommand?.argv.slice(5))     expect(Object.keys(byTool?.env ?? {}).sort()).toEqual(Object.keys(byCommand?.env ?? {}).sort())     expect('text' in answer && answer.text).toContain('http://localhost:6060
    • Network accessline 588
      expect(Object.keys(byTool?.env ?? {}).sort()).toEqual(Object.keys(byCommand?.env ?? {}).sort())     expect('text' in answer && answer.text).toContain('http://localhost:6060')     expect('text' in answer && answer.text).toContain('End your t
    • Network accessline 589
      expect('text' in answer && answer.text).toContain('http://localhost:6060')
    • Network accessline 674
      serveOnLaunch(host)     const mod = new PlannotatorMod(host, SESSION)     await mod.runTool({ action: 'review', target: 'https://github.com/o/r/pull/7', gate: true })
    • Network accessline 675
      const mod = new PlannotatorMod(host, SESSION)     await mod.runTool({ action: 'review', target: 'https://github.com/o/r/pull/7', gate: true })     await mod.runTool({ action: 'review', target: 'https://github.com/o/r/pull/8' })
    • Network accessline 676
      await mod.runTool({ action: 'review', target: 'https://github.com/o/r/pull/7', gate: true })
    • Network accessline 677
      await mod.runTool({ action: 'review', target: 'https://github.com/o/r/pull/8' })
    • Network accessline 678
      const [gated, plain] = launches(host)     const status = (n: number) => ({ surface: 'review', decision: 'annotated', noop: true, platform: true, message: `Pull request approved on GitHub: https://github.com/o/r/pull/${n}` })     decide(host
    • Network accessline 679
      const status = (n: number) => ({ surface: 'review', decision: 'annotated', noop: true, platform: true, message: `Pull request approved on GitHub: https://github.com/o/r/pull/${n}` })
    • Network accessline 684
      expect(host.submits).toHaveLength(1)     expect(host.submits[0]).toMatch(/^Plannotator: .+ \(pn-[0-9a-f]{6}\) — Review posted\./)     expect(host.submits[0]).toContain('Pull request approved on GitHub: https://github.com/o/r/pull/7')
    • Network accessline 685
      expect(host.submits[0]).toMatch(/^Plannotator: .+ \(pn-[0-9a-f]{6}\) — Review posted\./)     expect(host.submits[0]).toContain('Pull request approved on GitHub: https://github.com/o/r/pull/7')     expect(host.logs.some((line) => line.includ
    • Network accessline 686
      expect(host.submits[0]).toContain('Pull request approved on GitHub: https://github.com/o/r/pull/7')
    • Network accessline 1088
      '  a.md -> /r/a.md',       '  b.md -> /r/b.md',       'Re-run with exactly one target: plannotator annotate <file.md | file.txt | file.html | https://... | folder/>',
    • Network accessline 1089
      '  b.md -> /r/b.md',       'Re-run with exactly one target: plannotator annotate <file.md | file.txt | file.html | https://... | folder/>',     ].join('\n')
    • Network accessline 1090
      'Re-run with exactly one target: plannotator annotate <file.md | file.txt | file.html | https://... | folder/>',
    • Network accessline 1118
      }     const mod = new PlannotatorMod(host, SESSION)     const answer = await mod.runTool({ action: 'annotate', target: ['a.md', 'https://example.com'] })
    • Network accessline 1119
      const mod = new PlannotatorMod(host, SESSION)     const answer = await mod.runTool({ action: 'annotate', target: ['a.md', 'https://example.com'] })     expect('deny' in answer && answer.deny).toContain(PLANNOTATOR_BUNDLE_HINT_LINE)
    • Network accessline 1120
      const answer = await mod.runTool({ action: 'annotate', target: ['a.md', 'https://example.com'] })
    • Network accessline 1633
      const host = fakeHost()     host.onRun = (call) =>       call.argv[1] === 'inbox' ? { exitCode: 0, stdout: 'http://localhost:59812/\n', stderr: '' } : undefined
    • Network accessline 1634
      host.onRun = (call) =>       call.argv[1] === 'inbox' ? { exitCode: 0, stdout: 'http://localhost:59812/\n', stderr: '' } : undefined     const mod = new PlannotatorMod(host, SESSION)
    • Network accessline 1635
      call.argv[1] === 'inbox' ? { exitCode: 0, stdout: 'http://localhost:59812/\n', stderr: '' } : undefined
    • Network accessline 1643
      expect(call?.argv).toEqual(['plannotator', 'inbox', '--open'])     expect(call?.env?.PLANNOTATOR_DATA_DIR).toBe('/data')     expect(text).toContain('http://localhost:59812/')
    • Network accessline 1645
      expect(text).toContain('http://localhost:59812/')
    • Network accessline 1650
      test('over SSH the CLI opens no browser and says how to reach the Inbox: the answer passes that on', async () => {     const host = fakeHost()     host.onRun = () => ({ exitCode: 0, stdout: 'http://localhost:59812/\n', stderr: 'No browser o
    • Network accessline 1651
      const host = fakeHost()     host.onRun = () => ({ exitCode: 0, stdout: 'http://localhost:59812/\n', stderr: 'No browser opened: this is a remote session. Forward the port (ssh -L 59812:127.0.0.1:59812 <this host>) and open the URL, or run p
    • Network accessline 1652
      host.onRun = () => ({ exitCode: 0, stdout: 'http://localhost:59812/\n', stderr: 'No browser opened: this is a remote session. Forward the port (ssh -L 59812:127.0.0.1:59812 <this host>) and open the URL, or run plannotator inbox --tailscale
    • Network accessline 1653
      const { text } = await new PlannotatorMod(host, SESSION).openInbox()     expect(text).toContain('http://localhost:59812/')     expect(text).toContain('ssh -L 59812:127.0.0.1:59812')
    • Network accessline 1654
      expect(text).toContain('http://localhost:59812/')

    apps/hook/hooks/mod/controller.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 1016
      if (!launch.port || !launch.bridgeToken) return null     try {       const response = await this.host.fetch(`${bridgeBaseUrl(launch.port)}${HOST_STATUS_PATH}`, {
    • Network accessline 1017
      try {       const response = await this.host.fetch(`${bridgeBaseUrl(launch.port)}${HOST_STATUS_PATH}`, {         method: 'GET',
    • Network accessline 1018
      const response = await this.host.fetch(`${bridgeBaseUrl(launch.port)}${HOST_STATUS_PATH}`, {
    • Network accessline 1077
      }     const response = await this.host       .fetch(`${bridgeBaseUrl(launch.port)}${HOST_CLOSE_PATH}`, {
    • Network accessline 1078
      const response = await this.host       .fetch(`${bridgeBaseUrl(launch.port)}${HOST_CLOSE_PATH}`, {         method: 'POST',
    • Network accessline 1079
      .fetch(`${bridgeBaseUrl(launch.port)}${HOST_CLOSE_PATH}`, {

    apps/hook/hooks/mod/delivery.test.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 40
      test('a review posted to the PR platform logs and suggests the follow-up', () => {     const delivery = deliveryFor(       record({ surface: 'review', noop: true, platform: true, message: 'Pull request reviewed on GitHub: https://x/pull/412
    • Network accessline 41
      const delivery = deliveryFor(       record({ surface: 'review', noop: true, platform: true, message: 'Pull request reviewed on GitHub: https://x/pull/412' }),       { ...CONTEXT, subject: 'PR #412' },
    • Network accessline 42
      record({ surface: 'review', noop: true, platform: true, message: 'Pull request reviewed on GitHub: https://x/pull/412' }),
    • Network accessline 50
      test('a gated review posted to the PR platform is a turn with its status line; ungated it logs', () => {     const status = 'Pull request approved on GitHub: https://x/pull/412'
    • Network accessline 51
      const status = 'Pull request approved on GitHub: https://x/pull/412'
    • Network accessline 63
      const lines = [       ...['Pull request', 'Merge request'].flatMap((kind) =>         ['approved', 'reviewed'].map((verb) => `${kind} ${verb} on GitLab: https://x/-/merge_requests/9`),
    • Network accessline 64
      ...['Pull request', 'Merge request'].flatMap((kind) =>         ['approved', 'reviewed'].map((verb) => `${kind} ${verb} on GitLab: https://x/-/merge_requests/9`),       ),
    • Network accessline 65
      ['approved', 'reviewed'].map((verb) => `${kind} ${verb} on GitLab: https://x/-/merge_requests/9`),
    • Network accessline 66
      ),       'Changes requested on GitHub: https://x/pull/412',       'Pull request reviewed on Bitbucket',
    • Network accessline 67
      'Changes requested on GitHub: https://x/pull/412',

    apps/hook/hooks/mod/delivery.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/enabled.test.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/enabled.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/globals.d.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/host.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 105
      fetch(url: string, init: { method: string; headers: Record<string, string>; body?: string }): Promise<HttpResult>

    apps/hook/hooks/mod/inbox-ask.test.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 55
      servers.push(server)   const client = new Client({ name: 'mod-inbox-ask-test', version: '1.0.0' })   await client.connect(new StreamableHTTPClientTransport(new URL(`http://127.0.0.1:${server.port}/mcp`)))
    • Network accessline 56
      const client = new Client({ name: 'mod-inbox-ask-test', version: '1.0.0' })   await client.connect(new StreamableHTTPClientTransport(new URL(`http://127.0.0.1:${server.port}/mcp`)))   clients.push(client)
    • Network accessline 57
      await client.connect(new StreamableHTTPClientTransport(new URL(`http://127.0.0.1:${server.port}/mcp`)))
    • Network accessline 398
      [Credential-related source omitted]
    • Network accessline 399
      [Credential-related source omitted]
    • Network accessline 400
      const ASK_POLL = `http://127.0.0.1:4321${inboxAskBridgeBase(SESSION)}${BRIDGE_POLL_PATH}`
    • Network accessline 502
      [Credential-related source omitted]
    • Network accessline 504
      [Credential-related source omitted]

    apps/hook/hooks/mod/inbox-contract.test.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/inbox-contract.ts

    • Executable scriptline 1
      Executable file
    • Access to secretsline 437
      [Credential-related source omitted]

    apps/hook/hooks/mod/inbox-default-tools.test.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 29
      test("is exactly what a real Inbox's /mcp tools/list answers (run scripts/sync-mod-inbox-tools.ts when it is not)", async () => {     const response = await fetch(`http://127.0.0.1:${inbox.port}/mcp`, {
    • Network accessline 30
      const response = await fetch(`http://127.0.0.1:${inbox.port}/mcp`, {

    apps/hook/hooks/mod/inbox-default-tools.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 13
      "inputSchema": {       "type": "object",       "$schema": "https://json-schema.org/draft/2020-12/schema",
    • Network accessline 14
      "type": "object",       "$schema": "https://json-schema.org/draft/2020-12/schema",       "properties": {
    • Network accessline 15
      "$schema": "https://json-schema.org/draft/2020-12/schema",
    • Network accessline 18
      "type": "string",           "minLength": 1,           "description": "The message, markdown. Question blocks render as answerable cards. Blocks fenced as `mermaid` or `dot` draw as diagrams; one fenced as `html` draws as a live, sandboxed p
    • Network accessline 19
      "minLength": 1,           "description": "The message, markdown. Question blocks render as answerable cards. Blocks fenced as `mermaid` or `dot` draw as diagrams; one fenced as `html` draws as a live, sandboxed page in the message (chart, S
    • Network accessline 20
      "description": "The message, markdown. Question blocks render as answerable cards. Blocks fenced as `mermaid` or `dot` draw as diagrams; one fenced as `html` draws as a live, sandboxed page in the message (chart, SVG, mock; https libraries 
    • Access to secretsline 69
      [Credential-related source omitted]
    • Network accessline 85
      "inputSchema": {       "type": "object",       "$schema": "https://json-schema.org/draft/2020-12/schema",
    • Network accessline 86
      "type": "object",       "$schema": "https://json-schema.org/draft/2020-12/schema",       "properties": {
    • Network accessline 87
      "$schema": "https://json-schema.org/draft/2020-12/schema",
    • Network accessline 166
      "inputSchema": {       "type": "object",       "$schema": "https://json-schema.org/draft/2020-12/schema",
    • Network accessline 167
      "type": "object",       "$schema": "https://json-schema.org/draft/2020-12/schema",       "properties": {
    • Network accessline 168
      "$schema": "https://json-schema.org/draft/2020-12/schema",
    • Network accessline 199
      "inputSchema": {       "type": "object",       "$schema": "https://json-schema.org/draft/2020-12/schema",
    • Network accessline 200
      "type": "object",       "$schema": "https://json-schema.org/draft/2020-12/schema",       "properties": {
    • Network accessline 201
      "$schema": "https://json-schema.org/draft/2020-12/schema",
    • Network accessline 221
      "inputSchema": {       "type": "object",       "$schema": "https://json-schema.org/draft/2020-12/schema",
    • Network accessline 222
      "type": "object",       "$schema": "https://json-schema.org/draft/2020-12/schema",       "properties": {
    • Network accessline 223
      "$schema": "https://json-schema.org/draft/2020-12/schema",
    • Network accessline 316
      "inputSchema": {       "type": "object",       "$schema": "https://json-schema.org/draft/2020-12/schema",
    • Network accessline 317
      "type": "object",       "$schema": "https://json-schema.org/draft/2020-12/schema",       "properties": {},
    • Network accessline 318
      "$schema": "https://json-schema.org/draft/2020-12/schema",

    apps/hook/hooks/mod/inbox-loaded.test.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 11
      [Credential-related source omitted]
    • Network accessline 12
      [Credential-related source omitted]

    apps/hook/hooks/mod/inbox-reminder-custom.test.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/inbox-reminder.test.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/inbox-session-label.test.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 20
      [Credential-related source omitted]
    • Network accessline 21
      [Credential-related source omitted]

    apps/hook/hooks/mod/inbox-session-project.test.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/inbox-session-threads.test.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 12
      [Credential-related source omitted]
    • Network accessline 13
      [Credential-related source omitted]
    • Network accessline 49
      }     expect(await readSessionThreads(host, DATA, 'sess-1', '/work', TOOL)).toBeNull()     expect(urls).toEqual(['http://127.0.0.1:4100/api/inbox/health'])
    • Network accessline 50
      expect(await readSessionThreads(host, DATA, 'sess-1', '/work', TOOL)).toBeNull()     expect(urls).toEqual(['http://127.0.0.1:4100/api/inbox/health'])   })
    • Network accessline 51
      expect(urls).toEqual(['http://127.0.0.1:4100/api/inbox/health'])
    • Network accessline 61
      }     expect(await readSessionThreads(host, DATA, 'sess-1', '/work', TOOL)).toBeNull()     expect(urls).toEqual(['http://127.0.0.1:4100/api/inbox/health'])
    • Network accessline 62
      expect(await readSessionThreads(host, DATA, 'sess-1', '/work', TOOL)).toBeNull()     expect(urls).toEqual(['http://127.0.0.1:4100/api/inbox/health'])   })
    • Network accessline 63
      expect(urls).toEqual(['http://127.0.0.1:4100/api/inbox/health'])

    apps/hook/hooks/mod/inbox.test.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 137
      async function thread(w: World, threadId: string): Promise<{ subject: string; thread_name: string | null; project: { root: string }; messages: ThreadMessage[] }> {   const response = await fetch(`http://127.0.0.1:${registry(w).port}/api/inb
    • Network accessline 138
      const response = await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/threads/${threadId}`)
    • Network accessline 151
      async function personSends(w: World, messageId: string, words: string): Promise<string> {   const { port } = registry(w)   const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 152
      const { port } = registry(w)   const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }   const response = await fetch(`http://127.0.0.1:${port}/api/inbox/messages/${messageId}/re
    • Network accessline 153
      const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 154
      const response = await fetch(`http://127.0.0.1:${port}/api/inbox/messages/${messageId}/reply`, {
    • Network accessline 155
      method: 'POST',     headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },     body: JSON.stringify({ serverSession: health.serverSession, idempotency_key: `window-${messageId}-${words.length}`, words }),
    • Network accessline 156
      headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },
    • Network accessline 201
      const listed = await fetch(`http://127.0.0.1:${registry(w).port}/mcp`, {
    • Network accessline 294
      const delivered = await waitFor('the delivery record', async () => (await thread(w, asked.thread_id)).messages.find((m) => m.id === replyId)?.delivery)     expect(delivered).toMatchObject({ state: 'delivered', host: 'claude-code', session: 
    • Network accessline 295
      expect(delivered).toMatchObject({ state: 'delivered', host: 'claude-code', session: 'session-wake' })     const list = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/threads`)).json()) as { sections: { id: string; threa
    • Network accessline 296
      const list = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/threads`)).json()) as { sections: { id: string; threads: { thread_id: string; sent: { checked_at: string | null } | null }[] }[] }
    • Network accessline 313
      const sent = await send(session, w, 'Merged the refund fix. CI is green on main; nothing needs you here.')     const { port } = registry(w)     const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { se
    • Network accessline 314
      const { port } = registry(w)     const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }     const pressed = Date.now()
    • Network accessline 315
      const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 316
      const pressed = Date.now()     const response = await fetch(`http://127.0.0.1:${port}/api/inbox/messages/${sent.message_id}/ack`, {       method: 'POST',
    • Network accessline 317
      const response = await fetch(`http://127.0.0.1:${port}/api/inbox/messages/${sent.message_id}/ack`, {
    • Network accessline 318
      method: 'POST',       headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },       body: JSON.stringify({ serverSession: health.serverSession }),
    • Network accessline 319
      headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },
    • Network accessline 407
      const poll = await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/bridge/poll`, {
    • Network accessline 540
      [Credential-related source omitted]
    • Network accessline 541
      [Credential-related source omitted]
    • Network accessline 542
      [Credential-related source omitted]
    • Network accessline 566
      const { port } = registry(w)     const live = await waitFor('both sessions live', async () => {       const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/sessions`)).json()) as {
    • Network accessline 567
      const live = await waitFor('both sessions live', async () => {       const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/sessions`)).json()) as {         sessions: { session: string; host: string
    • Network accessline 568
      const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/sessions`)).json()) as {
    • Network accessline 579
      const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 580
      const post = (session: string, body: string, key: string) =>       fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/message`, {         method: 'POST',
    • Network accessline 581
      fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/message`, {
    • Network accessline 582
      method: 'POST',         headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },         body: JSON.stringify({ serverSession: health.serverSession, session, body, idempotency_key: key }),
    • Network accessline 583
      headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },
    • Network accessline 617
      const before = (await thread(w, asked.thread_id)).messages.length     await waitFor('session-other no longer live', async () => {       const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/session
    • Network accessline 618
      await waitFor('session-other no longer live', async () => {       const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/sessions`)).json()) as { sessions: { session: string }[] }       return model
    • Network accessline 619
      const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/sessions`)).json()) as { sessions: { session: string }[] }
    • Network accessline 636
      const { port } = registry(w)     await waitFor('the other session live', async () => {       const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/sessions`)).json()) as { sessions: { session: stri
    • Network accessline 637
      await waitFor('the other session live', async () => {       const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/sessions`)).json()) as { sessions: { session: string }[] }       return model.sessi
    • Network accessline 638
      const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/sessions`)).json()) as { sessions: { session: string }[] }
    • Network accessline 639
      return model.sessions.some((s) => s.session === 'session-taker') ? true : null     })     const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 640
      })     const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }     const words = 'Retry with the same key.'
    • Network accessline 641
      const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 642
      const words = 'Retry with the same key.'     const response = await fetch(`http://127.0.0.1:${port}/api/inbox/messages/${asked.message_id}/reply`, {       method: 'POST',
    • Network accessline 643
      const response = await fetch(`http://127.0.0.1:${port}/api/inbox/messages/${asked.message_id}/reply`, {
    • Network accessline 644
      method: 'POST',       headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },       body: JSON.stringify({ serverSession: health.serverSession, idempotency_key: 'ended-1', words, to: { host: 'claude-code', sessi
    • Network accessline 645
      headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },
    • Network accessline 676
      await waitFor('the tagged session taggable', async () => {       const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/taggable`)).json()) as { sessions: { session: string; taggable: boolean }[] }
    • Network accessline 677
      const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/taggable`)).json()) as { sessions: { session: string; taggable: boolean }[] }
    • Network accessline 681
      await owner.startTurn('Refactor the webhook handler.')     const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 682
      const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 683
      const response = await fetch(`http://127.0.0.1:${port}/api/inbox/messages/${asked.message_id}/reply`, {
    • Network accessline 684
      method: 'POST',       headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },       body: JSON.stringify({
    • Network accessline 685
      headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },
    • Network accessline 736
      const listed = await waitFor('the session in the live list', async () => {       const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/sessions`)).json()) as {
    • Network accessline 737
      const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/sessions`)).json()) as {
    • Network accessline 745
      const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 746
      const words = 'Idea: show which session wrote each thread.\n\nA small chip on each row would do.'     const response = await fetch(`http://127.0.0.1:${port}/api/inbox/messages`, {       method: 'POST',
    • Network accessline 747
      const response = await fetch(`http://127.0.0.1:${port}/api/inbox/messages`, {
    • Network accessline 748
      method: 'POST',       headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },       body: JSON.stringify({ serverSession: health.serverSession, to: { host: 'claude-code', session: 'session-compose' }, body: word
    • Network accessline 749
      headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },
    • Network accessline 790
      const connection = await waitFor('the busy connection', async () => {       const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/sessions?asker=session-send-now`)).json()) as {
    • Network accessline 791
      const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/sessions?asker=session-send-now`)).json()) as {
    • Network accessline 803
      const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 804
      const pressed = await fetch(`http://127.0.0.1:${port}/api/inbox/messages/${replyId}/send-now`, {
    • Network accessline 805
      method: 'POST',       headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },       body: JSON.stringify({ serverSession: health.serverSession }),
    • Network accessline 806
      headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },
    • Network accessline 837
      const replyId = await personSends(w, asked.message_id, 'Fail the job and alert.')     await waitFor('the busy connection', async () => {       const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/
    • Network accessline 838
      await waitFor('the busy connection', async () => {       const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/sessions?asker=session-late`)).json()) as {         asker: { connection: { busy: boole
    • Network accessline 839
      const model = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/threads/${asked.thread_id}/sessions?asker=session-late`)).json()) as {
    • Network accessline 842
      return model.asker.connection?.busy === true     })     const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 843
      })     const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }     expect(
    • Network accessline 844
      const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 845
      expect(       (         await fetch(`http://127.0.0.1:${port}/api/inbox/messages/${replyId}/send-now`, {
    • Network accessline 846
      (         await fetch(`http://127.0.0.1:${port}/api/inbox/messages/${replyId}/send-now`, {           method: 'POST',
    • Network accessline 847
      await fetch(`http://127.0.0.1:${port}/api/inbox/messages/${replyId}/send-now`, {
    • Network accessline 848
      method: 'POST',           headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },           body: JSON.stringify({ serverSession: health.serverSession }),
    • Network accessline 849
      headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },
    • Network accessline 865
      async function pressSendNow(w: World, replyId: string): Promise<void> {     const { port } = registry(w)     const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 866
      const { port } = registry(w)     const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }     const pressed = await fetch(`http://127.0.0.1:${port}/api/inbox/messages/${replyId}/s
    • Network accessline 867
      const health = (await (await fetch(`http://127.0.0.1:${port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 868
      const pressed = await fetch(`http://127.0.0.1:${port}/api/inbox/messages/${replyId}/send-now`, {
    • Network accessline 869
      method: 'POST',       headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },       body: JSON.stringify({ serverSession: health.serverSession }),
    • Network accessline 870
      headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${port}` },
    • Network accessline 877
      async function busyKnown(w: World, threadId: string, session: string): Promise<void> {     await waitFor('the busy connection', async () => {       const model = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/threads/${
    • Network accessline 878
      await waitFor('the busy connection', async () => {       const model = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/threads/${threadId}/sessions?asker=${session}`)).json()) as {         asker: { connection: { busy: bo
    • Network accessline 879
      const model = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/threads/${threadId}/sessions?asker=${session}`)).json()) as {
    • Network accessline 939
      await waitFor('the busy connection without Send now', async () => {       const model = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/threads/${asked.thread_id}/sessions?asker=session-ask`)).json()) as {
    • Network accessline 940
      const model = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/threads/${asked.thread_id}/sessions?asker=session-ask`)).json()) as {
    • Network accessline 943
      return model.asker.connection?.busy === true && model.asker.connection.send_now === false     })     const health = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 944
      })     const health = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/health`)).json()) as { serverSession: string }     const refused = await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/messages/${replyId}/sen
    • Network accessline 945
      const health = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 946
      const refused = await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/messages/${replyId}/send-now`, {
    • Network accessline 947
      method: 'POST',       headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${registry(w).port}` },       body: JSON.stringify({ serverSession: health.serverSession }),
    • Network accessline 948
      headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${registry(w).port}` },
    • Network accessline 976
      const replyId = await personSends(w, asked.message_id, 'Retry with the same key.')     await waitFor('the busy connection without Send now', async () => {       const model = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inb
    • Network accessline 977
      await waitFor('the busy connection without Send now', async () => {       const model = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/threads/${asked.thread_id}/sessions?asker=session-other-plugin`)).json()) as {      
    • Network accessline 978
      const model = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/threads/${asked.thread_id}/sessions?asker=session-other-plugin`)).json()) as {
    • Network accessline 981
      return model.asker.connection?.busy === true && model.asker.connection.send_now === false     })     const health = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 982
      })     const health = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/health`)).json()) as { serverSession: string }     const refused = await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/messages/${replyId}/sen
    • Network accessline 983
      const health = (await (await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/health`)).json()) as { serverSession: string }
    • Network accessline 984
      const refused = await fetch(`http://127.0.0.1:${registry(w).port}/api/inbox/messages/${replyId}/send-now`, {
    • Network accessline 985
      method: 'POST',       headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${registry(w).port}` },       body: JSON.stringify({ serverSession: health.serverSession }),
    • Network accessline 986
      headers: { 'Content-Type': 'application/json', Origin: `http://127.0.0.1:${registry(w).port}` },

    apps/hook/hooks/mod/inbox.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 275
      const health = await host.fetch(`http://127.0.0.1:${registry.port}/api/inbox/health`, { method: 'GET', headers: {} }).catch(() => null)
    • Dangerous shell operationline 426
      'rm -rf "$d"',
    • Network accessline 449
      async function mcpCall(host: Host, port: number, method: string, params: Record<string, unknown>): Promise<{ result?: unknown; error?: { message?: string } } | null> {   const response = await host.fetch(`http://127.0.0.1:${port}/mcp`, {
    • Network accessline 450
      const response = await host.fetch(`http://127.0.0.1:${port}/mcp`, {
    • Network accessline 757
      private async isRunning(registry: InboxRegistryView): Promise<boolean> {     const response = await this.host       .fetch(`http://127.0.0.1:${registry.port}/api/inbox/health`, { method: 'GET', headers: {} })
    • Network accessline 758
      const response = await this.host       .fetch(`http://127.0.0.1:${registry.port}/api/inbox/health`, { method: 'GET', headers: {} })       .catch(() => null)
    • Network accessline 759
      .fetch(`http://127.0.0.1:${registry.port}/api/inbox/health`, { method: 'GET', headers: {} })
    • Network accessline 927
      if (!registry) return null     return this.host       .fetch(`http://127.0.0.1:${registry.port}${path}`, {
    • Network accessline 928
      return this.host       .fetch(`http://127.0.0.1:${registry.port}${path}`, {         method: 'POST',
    • Network accessline 929
      .fetch(`http://127.0.0.1:${registry.port}${path}`, {

    apps/hook/hooks/mod/launch.test.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/launch.ts

    • Executable scriptline 1
      Executable file
    • Dangerous shell operationline 243
      'for f in "$@"; do rm -f "$dir/$f"; done',
    • Dangerous shell operationline 244
      `if [ ! -e "$dir/${LAUNCH_FILES.overflow}" ]; then rm -f "$dir/${SETTLED_BY}" "$dir/${SETTLED_DIR}/delivered" "$dir/${SETTLED_DIR}/reported"; rmdir "$dir/${SETTLED_DIR}" 2>/dev/null; rmdir "$dir" 2>/dev/null; fi`,
    • Dangerous shell operationline 412
      return ['/bin/sh', '-c', 'rm -f "$1"', 'plannotator-rm', path]

    apps/hook/hooks/mod/plan.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 141
      export const CLASSIC_PLAN_REVIEW_TEXT =   'Your plannotator CLI is older than the Plannotator plugin and has no non-blocking plan review, so plans open in the classic review, which holds this session until you decide. Update the CLI (curl -
    • Dangerous shell operationline 142
      'Your plannotator CLI is older than the Plannotator plugin and has no non-blocking plan review, so plans open in the classic review, which holds this session until you decide. Update the CLI (curl -fsSL https://plannotator.ai/install.sh | b
    • Network accessline 142
      'Your plannotator CLI is older than the Plannotator plugin and has no non-blocking plan review, so plans open in the classic review, which holds this session until you decide. Update the CLI (curl -fsSL https://plannotator.ai/install.sh | b

    apps/hook/hooks/mod/register.test.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/register.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 235
      }))     },     fetch: (url, init) => $.http.fetch(url, init),
    • Network accessline 236
      },     fetch: (url, init) => $.http.fetch(url, init),     abortTurn: (turnId) => $.turn.abort({ turnId }),
    • Network accessline 237
      fetch: (url, init) => $.http.fetch(url, init),

    apps/hook/hooks/mod/send-now-engine.test.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/shell-words.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/tailscale.test.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 39
      const TAILNET_URL = 'https://studio.tail0000.ts.net:6060/'
    • Network accessline 74
      for (const url of [TAILNET_URL, 'http://localhost:6060']) {
    • Network accessline 123
      expect('text' in answer && answer.text).toContain('Plannotator is starting')     const dir = launchDirOf(launches(host)[0]!)     host.files.set(`${dir}/ready`, `${JSON.stringify({ url: 'http://localhost:6060', isRemote: false, port: 6060 })
    • Network accessline 124
      const dir = launchDirOf(launches(host)[0]!)     host.files.set(`${dir}/ready`, `${JSON.stringify({ url: 'http://localhost:6060', isRemote: false, port: 6060 })}\n`)     await host.tick()
    • Network accessline 125
      host.files.set(`${dir}/ready`, `${JSON.stringify({ url: 'http://localhost:6060', isRemote: false, port: 6060 })}\n`)
    • Network accessline 133
      serveWith(host, { url: 'http://localhost:6060', isRemote: false, port: 6060 })
    • Network accessline 147
      test('the slash command passes --tailscale on, for last too, and checks nothing', async () => {     const host = fakeHost()     serveWith(host, { url: 'http://localhost:6060', isRemote: false, port: 6060 })
    • Network accessline 148
      const host = fakeHost()     serveWith(host, { url: 'http://localhost:6060', isRemote: false, port: 6060 })     host.transcript = [{ role: 'assistant', text: 'Hi.' }]
    • Network accessline 149
      serveWith(host, { url: 'http://localhost:6060', isRemote: false, port: 6060 })

    apps/hook/hooks/mod/take-over.test.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 12
      function serveOnLaunch(host: FakeHost, port = 4321) {   host.onRun = (call) => {     if (isLaunch(call)) host.files.set(`${call.argv[4]}/ready`, `${JSON.stringify({ url: `http://localhost:${port}`, isRemote: false, port })}\n`)
    • Network accessline 13
      host.onRun = (call) => {     if (isLaunch(call)) host.files.set(`${call.argv[4]}/ready`, `${JSON.stringify({ url: `http://localhost:${port}`, isRemote: false, port })}\n`)   }
    • Network accessline 14
      if (isLaunch(call)) host.files.set(`${call.argv[4]}/ready`, `${JSON.stringify({ url: `http://localhost:${port}`, isRemote: false, port })}\n`)
    • Access to secretsline 40
      [Credential-related source omitted]
    • Network accessline 46
      const bashText = 'result' in answer ? answer.result.stdout : ''     expect(withoutSession(bashText)).toBe(withoutSession('text' in viaTool ? viaTool.text : ''))     expect(JSON.stringify(answer)).toContain('http://localhost:7070')
    • Network accessline 47
      expect(withoutSession(bashText)).toBe(withoutSession('text' in viaTool ? viaTool.text : ''))     expect(JSON.stringify(answer)).toContain('http://localhost:7070')   })
    • Network accessline 48
      expect(JSON.stringify(answer)).toContain('http://localhost:7070')

    apps/hook/hooks/mod/take-over.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/targets.test.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 31
      host.files.set(       `${dirOf(call)}/ready`,       `${JSON.stringify({ url: `http://localhost:${port}`, isRemote: false, port, ...(target ? { target } : {}) })}\n`,
    • Network accessline 32
      `${dirOf(call)}/ready`,       `${JSON.stringify({ url: `http://localhost:${port}`, isRemote: false, port, ...(target ? { target } : {}) })}\n`,     )
    • Network accessline 33
      `${JSON.stringify({ url: `http://localhost:${port}`, isRemote: false, port, ...(target ? { target } : {}) })}\n`,
    • Network accessline 128
      test('a review switched in place to another PR is headed and targeted as that PR', async () => {     const host = fakeHost()     serve(host, ['https://github.com/o/r/pull/12'])
    • Network accessline 129
      const host = fakeHost()     serve(host, ['https://github.com/o/r/pull/12'])     const mod = new PlannotatorMod(host, SESSION)
    • Network accessline 130
      serve(host, ['https://github.com/o/r/pull/12'])
    • Network accessline 133
      await mod.runTool({ action: 'review', target: 'https://github.com/o/r/pull/12' })
    • Network accessline 137
      message: 'fix the handler',       noop: false,       target: 'https://github.com/o/r/pull/13',
    • Network accessline 138
      noop: false,       target: 'https://github.com/o/r/pull/13',     })
    • Network accessline 139
      target: 'https://github.com/o/r/pull/13',
    • Network accessline 143
      expect(host.submits[0]?.split('\n').slice(0, 2)).toEqual([       expect.stringContaining('Plannotator: PR #13 ('),       'Target: https://github.com/o/r/pull/13',
    • Network accessline 144
      expect.stringContaining('Plannotator: PR #13 ('),       'Target: https://github.com/o/r/pull/13',     ])
    • Network accessline 145
      'Target: https://github.com/o/r/pull/13',

    apps/hook/hooks/mod/testing/claude-session.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 135
      fetch: async (url, init) => {       host.fetches.push(`${init.method} ${url}`)       const response = await fetch(url, { method: init.method, headers: init.headers, ...(init.body !== undefined ? { body: init.body } : {}) })
    • Network accessline 136
      host.fetches.push(`${init.method} ${url}`)       const response = await fetch(url, { method: init.method, headers: init.headers, ...(init.body !== undefined ? { body: init.body } : {}) })       return { status: response.status, ok: response
    • Network accessline 137
      const response = await fetch(url, { method: init.method, headers: init.headers, ...(init.body !== undefined ? { body: init.body } : {}) })

    apps/hook/hooks/mod/testing/fake-host.ts

    • Executable scriptline 1
      Executable file
    • Network accessline 26
      /** Answer a fetch (the bridge). */

    apps/hook/hooks/mod/tool.test.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/tool.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/turns.test.ts

    • Executable scriptline 1
      Executable file

    apps/hook/hooks/mod/turns.ts

    • Executable scriptline 1
      Executable file

    apps/skills/core/plannotator/SKILL.md

    • Network accessline 28
      | Annotate a markdown, text, config, or HTML file | `plannotator annotate <file>` | | Annotate a web page | `plannotator annotate <https-url>` | | Annotate a running local app (dev server) | `plannotator annotate <http://localhost:PORT/>` |
    • Network accessline 29
      | Annotate a web page | `plannotator annotate <https-url>` | | Annotate a running local app (dev server) | `plannotator annotate <http://localhost:PORT/>` | | Pick a file to annotate from a folder | `plannotator annotate <folder/>` |
    • Network accessline 30
      | Annotate a running local app (dev server) | `plannotator annotate <http://localhost:PORT/>` |
    • Network accessline 66
      - `--patch-file <path>` reviews a static caller-supplied unified diff with no repository at all (use `-` to read it from stdin): the session serves the patch as-is with no file-system affordances that need a worktree. It cannot be combined 
    • Network accessline 67
      - Pass a directory to review another repo or worktree: `plannotator review ../feature-worktree` or `plannotator review ./backend --diff-type last-commit`. Paths resolve relative to the invoking directory; quote paths containing spaces. A di
    • Network accessline 68
      - PR review (`plannotator review https://github.com/owner/repo/pull/123`, GitLab MR and Bitbucket Cloud PR URLs too) needs an authenticated `gh` or `glab` CLI; Bitbucket Cloud (`https://bitbucket.org/<workspace>/<repo>/pull-requests/<id>`) 
    • Instruction override signalline 81
      Plain `annotate` is feedback-only: it shows **Close** but no **Approve** button. When the user asks to review, approve, accept, or gate a generated plan/spec/document saved as a file, call the `plannotator` tool with `"gate": true` if you h
    • Network accessline 88
      - AsciiDoc files: `.adoc`, `.asciidoc`. - HTML files (`.html`, `.htm`): rendered as the raw page by default; `--markdown` converts to markdown instead. `--render-html` is accepted for compatibility; raw rendering is already the default. - U
    • Network accessline 89
      - HTML files (`.html`, `.htm`): rendered as the raw page by default; `--markdown` converts to markdown instead. `--render-html` is accepted for compatibility; raw rendering is already the default. - URLs (`https://...`): fetched and convert
    • Network accessline 90
      - URLs (`https://...`): fetched and converted via Jina Reader by default; `--no-jina` uses plain fetch plus Turndown instead.
    • Network accessline 91
      - Running local apps: a loopback `http://localhost:PORT/` URL whose probe returns HTML opens in live-app mode (annotate the real running page). `--app` forces live mode and fails loudly when it cannot apply; `--static` forces the classic co

    apps/skills/extra/plannotator-compound/assets/report-template.html

    • Network accessline 5
      <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Compound Planning — What 370 Files Reveal</title> <link rel="preconnect" href="https://fonts.googleapis.com">
    • Network accessline 6
      <title>Compound Planning — What 370 Files Reveal</title> <link rel="preconnect" href="https://fonts.googleapis.com"> <link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;1,400&family=Inter:wght@300;400
    • Network accessline 7
      <link rel="preconnect" href="https://fonts.googleapis.com">
    • Network accessline 8
      <link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;1,400&family=Inter:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
    • Network accessline 675
      <div class="prompt-header">           <span class="prompt-header-label">             <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="roun
    • Network accessline 676
      <span class="prompt-header-label">             <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"><
    • Network accessline 677
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"
    • Network accessline 679
      </span>           <button class="copy-btn" onclick="copyPrompt(this)">             <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" 
    • Network accessline 680
      <button class="copy-btn" onclick="copyPrompt(this)">             <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="r
    • Network accessline 681
      <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></re
    • Network accessline 783
      const text = document.getElementById('prompt-content').textContent;   navigator.clipboard.writeText(text).then(() => {     btn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" strok
    • Network accessline 784
      navigator.clipboard.writeText(text).then(() => {     btn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejo
    • Network accessline 785
      btn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.1
    • Network accessline 786
      btn.classList.add('copied');     setTimeout(() => {       btn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-l
    • Network accessline 787
      setTimeout(() => {       btn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9"
    • Network accessline 788
      btn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" r

    apps/skills/extra/plannotator-compound/scripts/extract_exit_plan_mode_outcomes.py

    • Executable scriptline 1
      #!/usr/bin/env python3

    apps/skills/extra/plannotator-setup-goal/SKILL.md

    • Network accessline 29
      <!-- Grilling protocol below adapted verbatim from the /grill-me skill by Matt Pocock (MIT-licensed):      https://github.com/mattpocock/skills/blob/main/skills/productivity/grill-me/SKILL.md -->
    • Network accessline 30
      https://github.com/mattpocock/skills/blob/main/skills/productivity/grill-me/SKILL.md -->

    apps/skills/extra/plannotator-setup-goal/SKILL.test.ts

    • Executable scriptline 1
      Executable file

    apps/skills/extra/plannotator-visual-explainer/SKILL.test.ts

    • Executable scriptline 1
      Executable file

    apps/skills/extra/plannotator-visual-explainer/references/design-system.md

    • Network accessline 419
      <div class="diagram-panel">   <svg viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg" style="width:100%">
    • Network accessline 420
      <svg viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg" style="width:100%">

    apps/skills/extra/plannotator-visual-explainer/references/pr-components.md

    • Network accessline 91
      <script type="module">   import { getSingularPatch, registerDiffsComponent } from 'https://cdn.jsdelivr.net/npm/@pierre/diffs@1.1.21/+esm';
    • Network accessline 92
      import { getSingularPatch, registerDiffsComponent } from 'https://cdn.jsdelivr.net/npm/@pierre/diffs@1.1.21/+esm';

    apps/skills/extra/plannotator-visual-explainer/references/svg-patterns.md

    • Network accessline 113
      <svg viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg"
    • Network accessline 265
      <svg viewBox="0 0 400 180" xmlns="http://www.w3.org/2000/svg"
    • Network accessline 355
      <svg viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg">

    How the signals fit together

    Facts from a static read of the files at the pinned commit. They describe what the code does, not why.

    Some files could not be read; the facts below still hold.

    Declared and executed: 3 facts
    Signal confidence: 354 facts

    and 304 more

    Works with

    Agent compatibility, from the files in this repository
    AgentStatusEvidenceInstall to
    Claude CodeNativeManifest .claude-plugin/marketplace.json.claude/skills/<skill-name>/
    CodexNativeManifest AGENTS.md.agents/skills/<skill-name>/
    CursorCompatibleStandard SKILL.md apps/kiro-cli/skills/plannotator-annotate/SKILL.md.cursor/skills/<skill-name>/
    Gemini CLICompatibleStandard SKILL.md apps/kiro-cli/skills/plannotator-annotate/SKILL.md.gemini/skills/<skill-name>/
    GitHub CopilotCompatibleStandard SKILL.md apps/kiro-cli/skills/plannotator-annotate/SKILL.md.github/skills/<skill-name>/
    OpenCodeCompatibleStandard SKILL.md apps/kiro-cli/skills/plannotator-annotate/SKILL.md.opencode/skills/<skill-name>/
    • Native The repository ships a manifest, folder or frontmatter field for this agent.
    • Compatible A standard SKILL.md that this agent can read.
    • Unknown No evidence either way.

    Derived automatically from paths, manifests and frontmatter. Not tested on each agent.

    Add this badge to your README

    It updates every night and links to this page.

    backnotprop/plannotator: #155 on hotskills, 9.9k stars

    [![backnotprop/plannotator on hotskills](https://hotskills.dev/badge/backnotprop-plannotator.svg)](https://hotskills.dev/skill/backnotprop-plannotator/)