Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,14 @@ Per-origin choices are persisted in cookies, so a user can override the automati
## Annotate Flow

```
User runs /plannotator-annotate <file.md | file.html | https://... | folder/>
User runs /plannotator-annotate <file.md | file.adoc | file.html | https://... | folder/>
Claude Code: plannotator annotate subcommand runs
OpenCode/Pi: event handler intercepts command
Input type detected:
.md/.mdx → file read from disk
.adoc/.asciidoc → file read from disk, parsed natively by the AsciiDoc block parser
.html/.htm → file read, rendered as raw HTML by default (or converted to markdown with --markdown)
https:// → fetched via Jina Reader (default) or fetch+Turndown (--no-jina)
folder/ → file browser opened, files converted on demand
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ Review local changes or remote PRs. Comment on diffs, suggest code. Your comment

```
/plannotator-annotate README.md # Local markdown file
/plannotator-annotate docs/guide.adoc # Local AsciiDoc file (parsed natively)
/plannotator-annotate src/ # Browse and annotate files in a folder
/plannotator-annotate https://docs.rs/… # Fetch and annotate any URL
/plannotator-annotate report.html --render-html # Render HTML as-is instead of converting
Expand Down
2 changes: 1 addition & 1 deletion apps/hook/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ Plannotator's slash commands are installed as Claude Code skills in `~/.claude/s
| Command | Description |
|---------|-------------|
| `/plannotator-review [--git]` | Open code review UI for current changes or a GitHub PR; `--git` forces Git in JJ workspaces |
| `/plannotator-annotate <file.md \| file.html \| https://... \| folder/>` | Annotate a file, URL, or folder |
| `/plannotator-annotate <file.md \| file.adoc \| file.html \| https://... \| folder/>` | Annotate a file, URL, or folder |
| `/plannotator-last` | Annotate the agent's last message |

## Obsidian Integration
Expand Down
2 changes: 1 addition & 1 deletion apps/hook/server/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe("CLI top-level help", () => {
expect(output).toContain("plannotator --version, -v");
expect(output).toContain("plannotator [--browser <name>]");
expect(output).toContain("plannotator review [--git] [PR_URL]");
expect(output).toContain("plannotator annotate <file.md | file.txt | file.html | https://... | folder/>");
expect(output).toContain("plannotator annotate <file.md | file.txt | file.adoc | file.html | https://... | folder/>");
expect(output).toContain("[--markdown] [--no-jina]");
expect(output).toContain("plannotator annotate-last [--stdin]");
expect(output).toContain("plannotator setup-goal <interview|facts>");
Expand Down
6 changes: 3 additions & 3 deletions apps/hook/server/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function formatTopLevelHelp(): string {
" plannotator --version, -v",
" plannotator [--browser <name>]",
" plannotator review [--git] [PR_URL]",
" plannotator annotate <file.md | file.txt | file.html | https://... | folder/> [--markdown] [--no-jina] [--gate] [--json] [--hook]",
" plannotator annotate <file.md | file.txt | file.adoc | file.html | https://... | folder/> [--markdown] [--no-jina] [--gate] [--json] [--hook]",
" plannotator annotate-last [--stdin] [--gate] [--json] [--hook]",
" plannotator setup-goal <interview|facts> <bundle.json | -> [--json]",
" plannotator last",
Expand Down Expand Up @@ -74,7 +74,7 @@ const SUBCOMMAND_HELP: Record<string, string> = {
].join("\n"),
annotate: [
"Usage:",
" plannotator annotate <file.md | file.txt | file.html | https://... | folder/> [--markdown] [--no-jina] [--gate] [--json] [--hook]",
" plannotator annotate <file.md | file.txt | file.adoc | file.html | https://... | folder/> [--markdown] [--no-jina] [--gate] [--json] [--hook]",
"",
"Open a markdown/text/HTML file, a URL, or a folder of documents in the annotation UI.",
"",
Expand Down Expand Up @@ -165,7 +165,7 @@ export function formatInteractiveNoArgClarification(): string {
"",
"For interactive use, try:",
" plannotator review",
" plannotator annotate <file.md | file.txt | file.html | https://...>",
" plannotator annotate <file.md | file.txt | file.adoc | file.html | https://...>",
" plannotator setup-goal interview bundle.json --json",
" plannotator last",
" plannotator archive",
Expand Down
15 changes: 9 additions & 6 deletions apps/hook/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ import { createWorktreePool, type WorktreePool, type PoolEntry } from "@plannota
import { parsePRUrl, checkPRAuth, fetchPR, getCliName, getCliInstallUrl, getMRLabel, getMRNumberLabel, getDisplayRepo } from "@plannotator/server/pr";
import { writeRemoteShareLink } from "@plannotator/server/share-url";
import { resolveMarkdownFile, resolveUserPath, hasMarkdownFiles } from "@plannotator/shared/resolve-file";
import { BROWSABLE_DOC_EXTENSIONS, isAsciidocPath } from "@plannotator/shared/document-format";
import { FILE_BROWSER_EXCLUDED } from "@plannotator/shared/reference-common";
import { statSync, rmSync, realpathSync, existsSync } from "fs";
import { parseRemoteUrl } from "@plannotator/shared/repo";
Expand Down Expand Up @@ -894,7 +895,7 @@ if (args[0] === "sessions") {

const rawFilePath = args[1];
if (!rawFilePath) {
console.error("Usage: plannotator annotate <file.md | file.txt | file.html | https://... | folder/> [--markdown] [--no-jina] [--gate] [--json] [--hook]");
console.error("Usage: plannotator annotate <file.md | file.txt | file.adoc | file.html | https://... | folder/> [--markdown] [--no-jina] [--gate] [--json] [--hook]");
process.exit(1);
}

Expand Down Expand Up @@ -947,9 +948,9 @@ if (args[0] === "sessions") {

if (folderCandidate !== null) {
const resolvedArg = resolveUserPath(folderCandidate, projectRoot);
// Folder annotation mode (markdown/plain text + HTML files)
if (!hasMarkdownFiles(resolvedArg, FILE_BROWSER_EXCLUDED, /\.(mdx?|txt|html?)$/i)) {
console.error(`No markdown, text, or HTML files found in ${resolvedArg}`);
// Folder annotation mode (markdown/plain text/AsciiDoc + HTML files)
if (!hasMarkdownFiles(resolvedArg, FILE_BROWSER_EXCLUDED, BROWSABLE_DOC_EXTENSIONS)) {
console.error(`No markdown, text, AsciiDoc, or HTML files found in ${resolvedArg}`);
process.exit(1);
}
folderPath = resolvedArg;
Expand Down Expand Up @@ -1003,7 +1004,7 @@ if (args[0] === "sessions") {
const ext = path.extname(resolvedPath).toLowerCase();
console.error(
`File type not supported: ${ext}\n` +
`Only .md, .mdx, .txt, .html, .htm files are supported.\n` +
`Only .md, .mdx, .txt, .adoc, .asciidoc, .html, .htm files are supported.\n` +
`For code review, use: plannotator review [file]`
);
} else {
Expand Down Expand Up @@ -1050,7 +1051,9 @@ if (args[0] === "sessions") {
pasteApiUrl,
}).catch(() => {});
} else if (markdown) {
await writeRemoteShareLink(markdown, shareBaseUrl, "annotate", "document only").catch(() => {});
await writeRemoteShareLink(markdown, shareBaseUrl, "annotate", "document only", {
docFormat: !isUrl && isAsciidocPath(absolutePath) ? "asciidoc" : "markdown",
}).catch(() => {});
}
}
},
Expand Down
11 changes: 8 additions & 3 deletions apps/marketing/src/content/docs/commands/annotate.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Annotate"
description: "The /plannotator-annotate slash command for annotating markdown files, HTML files, URLs, and folders."
description: "The /plannotator-annotate slash command for annotating markdown files, AsciiDoc files, HTML files, URLs, and folders."
sidebar:
order: 12
section: "Commands"
Expand All @@ -13,9 +13,10 @@ The `/plannotator-annotate` command opens files, URLs, or folders in the Plannot
| Input | Command | What happens |
|-------|---------|--------------|
| Markdown file | `plannotator annotate README.md` | Opens the file directly |
| AsciiDoc file | `plannotator annotate docs/guide.adoc` | Opens the file directly, parsed natively |
| HTML file | `plannotator annotate docs/guide.html` | Renders the HTML directly |
| URL | `plannotator annotate https://docs.stripe.com/api` | Fetches the page, converts to markdown, then opens |
| Folder | `plannotator annotate ./docs/` | Opens a file browser showing all `.md`, `.mdx`, `.html`, and `.htm` files |
| Folder | `plannotator annotate ./docs/` | Opens a file browser showing all `.md`, `.mdx`, `.adoc`, `.asciidoc`, `.html`, and `.htm` files |

### Slash command (inside an agent session)

Expand All @@ -40,7 +41,7 @@ Starts a local server, opens the browser, and blocks until you submit. Formatted

## Folders

When you pass a folder, Plannotator opens a file browser showing all markdown and HTML files in the directory tree. Click any file to open it in the annotation UI. This is useful for annotating a set of specs, documentation, or your Obsidian vault.
When you pass a folder, Plannotator opens a file browser showing all markdown, AsciiDoc, and HTML files in the directory tree. Click any file to open it in the annotation UI. This is useful for annotating a set of specs, documentation, or your Obsidian vault.

Build output directories like `_site/`, `public/`, `.docusaurus/`, and `node_modules/` are automatically excluded from the file browser.

Expand Down Expand Up @@ -80,6 +81,10 @@ Three ways to disable Jina Reader, in priority order:

If none of these are set, Jina is enabled by default.

## AsciiDoc files

Local `.adoc` and `.asciidoc` files are parsed natively — sections, listings (`[source,lang]` blocks get syntax highlighting), admonitions (`NOTE:`, `[WARNING]`, …), tables, quotes, and lists map onto the same annotatable blocks as markdown, and inline AsciiDoc formatting is converted for display. The file itself is never converted, so direct edits and source-save write the original AsciiDoc back to disk, and feedback line numbers refer to the real source lines.

## HTML files

Local `.html` and `.htm` files are read from disk and rendered as HTML by default. If you want Plannotator to convert the file to markdown first, pass `--markdown`.
Expand Down
2 changes: 1 addition & 1 deletion apps/opencode-plugin/cli-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ export async function handleCliCommand(input: {
if (input.command === "plannotator-annotate") {
const parsed = parseAnnotateArgs(input.rawArgs);
if (!parsed.filePath) {
log(input.client, "error", "Usage: /plannotator-annotate <file.md | file.txt | file.html | https://... | folder/> [--markdown] [--no-jina] [--gate] [--json]");
log(input.client, "error", "Usage: /plannotator-annotate <file.md | file.txt | file.adoc | file.html | https://... | folder/> [--markdown] [--no-jina] [--gate] [--json]");
return;
}

Expand Down
7 changes: 4 additions & 3 deletions apps/opencode-plugin/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
getAnnotateFileFeedbackPrompt,
} from "@plannotator/shared/prompts";
import { resolveMarkdownFile, resolveUserPath, hasMarkdownFiles } from "@plannotator/shared/resolve-file";
import { BROWSABLE_DOC_EXTENSIONS } from "@plannotator/shared/document-format";
import { FILE_BROWSER_EXCLUDED } from "@plannotator/shared/reference-common";
import { htmlToMarkdown } from "@plannotator/shared/html-to-markdown";
import { parseAnnotateArgs } from "@plannotator/shared/annotate-args";
Expand Down Expand Up @@ -211,7 +212,7 @@ export async function handleAnnotateCommand(
const { filePath, rawFilePath, gate, renderMarkdown: renderMarkdownFlag, noJina } = parseAnnotateArgs(rawArgs);

if (!filePath) {
client.app.log({ level: "error", message: "Usage: /plannotator-annotate <file.md | file.txt | file.html | https://... | folder/> [--markdown] [--no-jina] [--gate] [--json]" });
client.app.log({ level: "error", message: "Usage: /plannotator-annotate <file.md | file.txt | file.adoc | file.html | https://... | folder/> [--markdown] [--no-jina] [--gate] [--json]" });
return;
}

Expand Down Expand Up @@ -252,8 +253,8 @@ export async function handleAnnotateCommand(
}

if (isFolder) {
if (!hasMarkdownFiles(resolvedArg, FILE_BROWSER_EXCLUDED, /\.(mdx?|txt|html?)$/i)) {
client.app.log({ level: "error", message: `No markdown, text, or HTML files found in ${resolvedArg}` });
if (!hasMarkdownFiles(resolvedArg, FILE_BROWSER_EXCLUDED, BROWSABLE_DOC_EXTENSIONS)) {
client.app.log({ level: "error", message: `No markdown, text, AsciiDoc, or HTML files found in ${resolvedArg}` });
return;
}
folderPath = resolvedArg;
Expand Down
11 changes: 6 additions & 5 deletions apps/pi-extension/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
parseChecklist,
} from "./generated/checklist.js";
import { hasMarkdownFiles, resolveUserPath } from "./generated/resolve-file.js";
import { ANNOTATABLE_DOC_EXTENSIONS, BROWSABLE_DOC_EXTENSIONS } from "./generated/document-format.js";
import { FILE_BROWSER_EXCLUDED } from "./generated/reference-common.js";
import { htmlToMarkdown } from "./generated/html-to-markdown.js";
import { urlToMarkdown, isConvertedSource } from "./generated/url-to-markdown.js";
Expand Down Expand Up @@ -502,7 +503,7 @@ export default function plannotator(pi: ExtensionAPI): void {
// (scoped-package-style names).
const { filePath, rawFilePath, gate, renderMarkdown: renderMarkdownFlag, noJina } = parseAnnotateArgs(args ?? "");
if (!filePath) {
ctx.ui.notify("Usage: /plannotator-annotate <file.md | file.txt | file.html | https://... | folder/> [--markdown] [--no-jina] [--gate] [--json]", "error");
ctx.ui.notify("Usage: /plannotator-annotate <file.md | file.txt | file.adoc | file.html | https://... | folder/> [--markdown] [--no-jina] [--gate] [--json]", "error");
return;
}
if (!hasPlanBrowserHtml()) {
Expand Down Expand Up @@ -562,8 +563,8 @@ export default function plannotator(pi: ExtensionAPI): void {
}

if (isFolder) {
if (!hasMarkdownFiles(absolutePath, FILE_BROWSER_EXCLUDED, /\.(mdx?|txt|html?)$/i)) {
ctx.ui.notify(`No markdown, text, or HTML files found in ${absolutePath}`, "error");
if (!hasMarkdownFiles(absolutePath, FILE_BROWSER_EXCLUDED, BROWSABLE_DOC_EXTENSIONS)) {
ctx.ui.notify(`No markdown, text, AsciiDoc, or HTML files found in ${absolutePath}`, "error");
return;
}
markdown = "";
Expand All @@ -583,8 +584,8 @@ export default function plannotator(pi: ExtensionAPI): void {
sourceInfo = basename(absolutePath);
ctx.ui.notify(`Opening annotation UI for ${filePath}...`, "info");
} else {
if (!/\.(mdx?|txt)$/i.test(absolutePath)) {
ctx.ui.notify("Only .md, .mdx, .txt, .html, .htm files are supported.", "error");
if (!ANNOTATABLE_DOC_EXTENSIONS.test(absolutePath)) {
ctx.ui.notify("Only .md, .mdx, .txt, .adoc, .asciidoc, .html, .htm files are supported.", "error");
return;
}
markdown = readFileSync(absolutePath, "utf-8");
Expand Down
5 changes: 3 additions & 2 deletions apps/pi-extension/server/reference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
} from "../generated/resolve-file.js";
import { parseCodePath } from "../generated/code-file.js";
import { htmlToMarkdown } from "../generated/html-to-markdown.js";
import { BROWSABLE_DOC_EXTENSIONS } from "../generated/document-format.js";
import { disabledSourceSave, type SourceFileSnapshot, type SourceSaveCapability } from "../generated/source-save.js";
import {
createSourceSaveCapability,
Expand Down Expand Up @@ -213,7 +214,7 @@ function jsonDoc(
}

/** Recursively walk a directory collecting files by extension, skipping ignored dirs. */
const FILE_BROWSER_EXTENSIONS = /\.(mdx?|txt|html?)$/i;
const FILE_BROWSER_EXTENSIONS = BROWSABLE_DOC_EXTENSIONS;

function walkMarkdownFiles(dir: string, root: string, results: string[], extensions: RegExp = FILE_BROWSER_EXTENSIONS): void {
let entries: Dirent[];
Expand Down Expand Up @@ -261,7 +262,7 @@ export async function handleDocRequest(res: Res, url: URL, options: HandleDocOpt
if (
resolvedBase &&
!isAbsoluteUserPath(requestedPath) &&
/\.(mdx?|txt|html?)$/i.test(requestedPath)
BROWSABLE_DOC_EXTENSIONS.test(requestedPath)
) {
const fromBase = resolveUserPath(requestedPath, resolvedBase);
if (!isWithinAllowedRoots(fromBase, allowedRoots)) {
Expand Down
2 changes: 1 addition & 1 deletion apps/pi-extension/vendor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ rm -rf generated
mkdir -p generated generated/ai/providers

# Modules that MOVED to @plannotator/core — vendor the real impl from core.
for f in feedback-templates project favicon code-file external-annotation agent-jobs agent-terminal source-save open-in-apps; do
for f in feedback-templates project favicon code-file external-annotation agent-jobs agent-terminal source-save open-in-apps document-format; do
src="../../packages/core/$f.ts"
printf '// @generated — DO NOT EDIT. Source: packages/core/%s.ts\n' "$f" | cat - "$src" > "generated/$f.ts"
done
Expand Down
2 changes: 1 addition & 1 deletion apps/skills/claude/plannotator-annotate/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: plannotator-annotate
description: Open Plannotator's annotation UI for a markdown file, HTML file, URL, or folder and then respond to the returned annotations.
description: Open Plannotator's annotation UI for a markdown file, AsciiDoc file, HTML file, URL, or folder and then respond to the returned annotations.
allowed-tools: Bash(plannotator:*)
disable-model-invocation: true
---
Expand Down
2 changes: 1 addition & 1 deletion apps/skills/core/plannotator-annotate/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: plannotator-annotate
description: Open Plannotator's annotation UI for a markdown file, HTML file, URL, or folder and then respond to the returned annotations.
description: Open Plannotator's annotation UI for a markdown file, AsciiDoc file, HTML file, URL, or folder and then respond to the returned annotations.
disable-model-invocation: true
---

Expand Down
Loading