Interactive Markdown reviewer in the terminal (TUI) or a web browser. Add review comments at section level or line level using Conventional Comments and output structured feedback.
Formerly ccplan — see Migration from ccplan for upgrade instructions.
mise use -g github:koh-sh/commdgo install github.com/koh-sh/commd@latestDownload the latest release from the Releases page.
Display a Markdown file in a 2-pane TUI and add review comments to each section.
review is the default command, so the subcommand name can be omitted.
commd review path/to/document.md
# Same as above (review is the default command)
commd path/to/document.md
# Output review to a file
commd review --output file --output-path ./review.md document.md
# Output to stdout
commd review --output stdout document.md
# Review local git changes in diff view (working tree vs HEAD)
commd review --diff document.md
# Pick from all changed/untracked .md files, comparing against a branch
commd review --diff --base main
# Review in a web browser instead of the TUI
commd review --web document.md
commd review --web --diff| Flag | Description |
|---|---|
--output |
Output method: clipboard (default), stdout, file |
--output-path |
File path for --output file |
--theme |
Color theme: dark (default), light |
--track-viewed |
Persist viewed state to sidecar file (.reviewed.json) for change detection across sessions |
--diff |
Review local git changes in diff view. Without a file, pick from changed .md files |
--base |
Git ref to diff against (default: HEAD; requires --diff) |
--web |
Review in a web browser instead of the TUI (see Web review) |
--port |
Port for --web (default: a free port) |
--no-open |
With --web, print the URL instead of opening a browser |
When --track-viewed is enabled, commd saves which sections you've marked as viewed in a .reviewed.json sidecar file. On subsequent runs, viewed marks are restored automatically. If a section's content has changed, its viewed mark is cleared (detected via content hash).
--diff reviews what changed in the working tree instead of the whole file — for example, docs that Claude Code edited, before you commit them. It uses the same diff view as commd pr:
- The right pane opens in raw view showing the unified diff (
+/-lines); pressrfor the rendered working-tree content - Press
Tabto focus the diff pane, then comment on added, removed, or context lines withc/V; section comments work in the rendered view - Without a file argument, all changed and untracked
.mdfiles under the current directory are offered in a file picker and reviewed one by one; comments are combined into a single output - Untracked files are shown as entirely added; unchanged files are skipped
--track-viewedis not available in diff mode
--web opens the same review in your browser. commd serves the page on 127.0.0.1, prints the URL, opens it, and waits for the review to finish; the result is output exactly as from the TUI (--output applies).
- The page works like the TUI: the same modes, dialogs, status bar, and key bindings, including the raw view line cursor,
Vselection, theCcomment list, and/search.--themesets the initial colors - Unlike the TUI there is no pane focus (
Tabdoes nothing):j/k/↑/↓and the page keys scroll the content (in the raw view, move the line cursor), and pressing on at the end or start of a section moves to the next or previous one;gg/Ggo to the top or end of the whole document. In the full view the document reads as one page - The mouse works too: click a section, a line number (drag or shift+click for a range), a label chip, or a button; drag the pane border to resize. Scrolling runs on across sections: past the end or start of a section it continues into the next or previous one. On narrow windows the ☰ button shows the section list
- With
--diffand no file argument, the changed files are offered in a picker first and then reviewed one by one, as in the TUI (sfinishes a file,qorCtrl+Cskips it) Ctrl+Ssaves a comment (Ctrl+Enter/⌘+Enteralso work);Ctrl+Ccopies instead of quitting while text is selected- Pressing
Ctrl+Cin the terminal abandons the whole review without output - Reloading the page is safe: the review lives in commd, so comments and progress are kept (only a comment being typed is lost). Like
R, it also reads the file again (see Reloading), noting it only when the file changed - The URL carries a random token that the page needs to read or change the review, so other web pages cannot access it and other local users need the token. Treat the URL like a password: it is printed to the terminal, passed to the browser launcher (briefly visible in the process list), and kept in the browser history. Besides the page, only images referenced by relative paths in the document are served, and only from the document's directory
- To review on a remote machine, run with
--portand forward the port (e.g.ssh -L 8080:127.0.0.1:8080 host), then open the printed URL locally
Review Markdown files changed in a GitHub pull request. Comments are submitted as a GitHub PR Review with inline file comments.
# Interactive file picker for changed .md files
commd pr https://github.com/owner/repo/pull/123
# Review a specific file directly
commd pr https://github.com/owner/repo/pull/123 --file docs/README.md| Flag | Description |
|---|---|
--file |
Review a specific file instead of showing the file picker |
--theme |
Color theme: dark (default), light |
Authentication: Requires a GitHub token via GITHUB_TOKEN environment variable or gh auth login.
File size: The GitHub Contents API only decodes files up to 1 MB. Larger files (up to GitHub's 100 MB limit) are fetched automatically via their raw download URL.
File picker: When --file is not specified, an interactive file picker shows all changed .md files. All files are selected by default. Use space to toggle, a to select/deselect all, enter to confirm, q or esc to cancel.
Review flow: After selecting files, you review them one by one. For each file you can add comments, then press s to finish or q to skip. After all files, a summary dialog lets you choose to approve, comment, or cancel the review.
Submit behavior: Comments are posted as a GitHub PR Review with inline comments on each file. If no comments are added, you can optionally approve the PR. Note: Overview (file-level) comments are not posted to GitHub due to API limitations — only section-level and line-level comments are submitted.
Show the current version.
commd version| Key | Action |
|---|---|
j / k / ↑ / ↓ |
Navigate sections (left pane) or lines (right pane, raw view) |
gg / G |
Jump to first / last |
Ctrl+D / Ctrl+U |
Half page down / up |
Ctrl+F / Ctrl+B |
Full page down / up |
l / h / → / ← |
Scroll the detail pane right / left |
H / L |
Scroll to start / end (right pane) |
> / < |
Resize left pane wider / narrower |
Enter |
Toggle expand/collapse |
Tab |
Switch focus between panes |
f |
Toggle full view / section view |
r |
Toggle raw source view (with line numbers) / rendered view |
c |
Add comment (section-level in rendered view, line-level in raw view) |
C |
Manage comments (edit/delete) |
V |
Start visual line selection (raw view, right pane) |
v |
Toggle viewed mark |
/ |
Search sections |
s |
Submit review and exit |
R |
Reload the file from disk (see Reloading) |
q / Ctrl+C |
Quit |
? |
Show help |
R reads the file under review again, so edits made while you review show up (in --diff, the diff against the base is taken again). The review carries over:
- Comments follow their heading, and line comments follow the lines they quote. A comment whose target was edited away becomes a comment on its section (or the first one listed) and keeps its quote; the status bar says how many were moved
- Viewed marks stay on sections whose content is unchanged
- The view stays on the selected section
- An unchanged file, or one that cannot be read, leaves the review as it was
Files from a PR (commd pr) cannot be reloaded.
| Key | Action |
|---|---|
Tab |
Cycle comment label (forward) |
Shift+Tab |
Cycle comment label (reverse) |
Ctrl+D |
Cycle decoration (none, non-blocking, blocking, if-minor) |
Ctrl+S |
Save comment |
Esc |
Cancel |
| Key | Action |
|---|---|
j / k |
Navigate comments |
e |
Edit selected comment |
d |
Delete selected comment |
Esc |
Back to normal mode |
The status bar shows key hints and a progress indicator: [X/Y viewed] for sections marked as viewed, and [N comments] when comments have been added.
| Key | Action |
|---|---|
| Type text | Incremental filter (searches ID, title, and body) |
↑ / ↓ |
Navigate results |
Enter |
Confirm search |
Esc |
Cancel search |
Fenced ```mermaid code blocks are automatically converted to ASCII art in the detail pane. If rendering fails (e.g. unsupported diagram type), the original source is shown as-is.
Press r to switch the right pane to raw source view with line numbers. In this mode:
- Line-level commenting: Press
cto comment on the cursor line - Visual selection: Press
V, move withj/kto select a range, thencto comment. In diff mode (commd prand--diff), a selection spanning both sides is automatically restricted to the cursor's side (old or new file lines) to satisfy GitHub's single-side comment requirement - Section navigation:
j/kat the edge of a section automatically moves to the adjacent section - Press
fto toggle between section view (only selected section's lines) and full file view - Press
ragain to return to rendered view
Both section-level comments (from rendered view) and line-level comments (from raw view) can coexist in the same session.
The review output generated on submit uses Conventional Comments labels:
# Review
Please review and address the following comments on: /path/to/document.md
## Overview
[note] Add a performance metrics section.
## S1.1: JWT verification
[suggestion (non-blocking)] Switch to HS256. Load the key from an environment variable.
## S2: Update routing
[issue (blocking)] Not needed; the existing implementation covers this.
---
`L15` [question] Is this variable used?
> const token = parseToken(header)
`L20-L25` [suggestion] Extract this block into a helper function.
> if err := validate(input); err != nil {
> return err
> }Section-level comments (including Overview for the preamble) are grouped under section headings. Line-level comments appear below a --- divider with inline code line references, followed by a quote of the commented source (up to 5 lines) so the target can still be found after the file changes. In diff mode, comments on removed lines are numbered by the old file and marked (removed). When several files are reviewed with --diff, each file gets its own ## path heading with section headings nested below it.
Labels: suggestion, issue, question (default), nitpick, todo, thought, note, praise, chore
Decorations: non-blocking, blocking, if-minor — cycle with Ctrl+D in comment mode
Deprecated:
commd cchookandcommd cclocateare deprecated and will be removed in a future release. They still work but are hidden fromcommd --help, andcclocateprints a deprecation notice. To review a plan or other Markdown file, runcommd reviewon it directly.
commd can be used as a Claude Code hook to review plan files interactively during plan mode.
Run as a Claude Code hook. Launches the review TUI for the plan file to enable a feedback loop. Two trigger points are supported:
PreToolUseonExitPlanMode(recommended): fires once, when Claude has finished the plan and asks to leave plan mode. Claude Code injects the plan file path (tool_input.planFilePath), so noplansDirectorylookup is involved. Intermediate edits to the plan do not open a review.PostToolUseonWrite|Edit: fires on every write to a file underplansDirectory, including edits made before the plan is complete.
# Called automatically by Claude Code hook (no manual invocation needed)
commd cchook| Flag | Description |
|---|---|
--spawner |
Terminal multiplexer: auto (default), wezterm, tmux |
--theme |
Color theme: dark (default), light |
Note: Currently only WezTerm is supported as a terminal multiplexer spawner. tmux support is not yet implemented.
autowill try WezTerm first, then fall back to running in the same terminal.
Locate plan file paths from a Claude Code transcript JSONL. Useful for debugging the PostToolUse trigger's plansDirectory resolution; the PreToolUse/ExitPlanMode trigger receives the plan path directly and does not need it.
commd cclocate --transcript ~/.claude/projects/.../session.jsonl
# List all plan files found in a transcript
commd cclocate --transcript session.jsonl --all
# Read hook JSON input from stdin to resolve the plan file
commd cclocate --stdinAdd the following to .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "ExitPlanMode",
"hooks": [
{
"type": "command",
"command": "commd cchook",
"timeout": 600
}
]
}
]
}
}The hook only activates in plan mode and automatically enables --track-viewed. The review opens before Claude Code's own plan approval dialog:
- Claude writes the plan and calls
ExitPlanMode - commd opens the plan for review
- submitted (exit 2): the review is sent to Claude as the denial reason; Claude revises the plan in plan mode and calls
ExitPlanModeagain (back to step 2) - approved / cancelled (exit 0): Claude Code shows its usual plan approval dialog
To review on every plan file write instead (including intermediate edits), use the PostToolUse trigger. It requires the file to be under plansDirectory:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "commd cchook",
"timeout": 600
}
]
}
]
}
}Set CC_PLAN_REVIEW_SKIP=1 to temporarily disable the hook.
Dev tools are managed by mise. Run mise install to set up the toolchain (includes Go linters, formatters, and bun).
mise run ci # Run full CI pipeline (fmt, fix, lint, build, cov, e2e-basic)
mise run e2e # Run all E2E tests (full suite)
mise run e2e-basic # Run basic E2E tests (critical path only, included in ci)E2E tests use tuistory to drive the TUI in a virtual terminal and Playwright to drive the --web page in headless Chromium (installed by the e2e tasks).
commd was formerly known as ccplan. If you are upgrading:
| Item | Before | After |
|---|---|---|
| Binary | ccplan |
commd |
| Subcommand | ccplan review |
commd review |
| Subcommand | ccplan hook |
commd cchook |
| Subcommand | ccplan locate |
commd cclocate |
| Hook config | "command": "ccplan hook" |
"command": "commd cchook" |
| Environment variable | PLAN_REVIEW_SKIP=1 |
CC_PLAN_REVIEW_SKIP=1 |
| go install | github.com/koh-sh/ccplan |
github.com/koh-sh/commd |
| mise | github:koh-sh/ccplan |
github:koh-sh/commd |
For mise users upgrading:
mise uninstall github:koh-sh/ccplan
mise use -g github:koh-sh/commd