Collaborative annotation and review for Claude Code
Ano enables teams to annotate, review, and approve Claude-generated plans and markdown files before execution. It integrates with Claude Code via MCP and hooks, creating a human-in-the-loop workflow for AI-assisted development.
- Inline Annotations - Add comments to specific lines with types:
concern,question,suggestion,blocker - Approval Gates - Require N approvals before Claude can proceed (via hooks)
- Title & Role-Based Authorization - Require approval from specific roles (e.g., "Tech Lead", "Security")
- Team Management - Configure team members, roles, and approval requirements
- Web Viewer - Visual interface with real-time updates, keyboard shortcuts, and inline editing
- Activity Feed - Track all annotation and approval changes chronologically
- Version Diff - Compare changes between versions, see what was added/removed/resolved
- Shareable URLs - Deep links to specific annotations or lines
- Export - Export annotated files as standalone HTML or copy for Claude
- Multi-file Support - Review multiple files in a single session
- Claude Integration - Claude can read and respond to annotations via MCP
- Git-Friendly - All data stored in sidecar JSON files alongside your code
# Install globally
npm install -g @nakedved/ano
# Add an annotation
ano annotate plan.md:15 "Is this approach safe?" --type concern
# List annotations
ano list plan.md
# Approve a file
ano lgtm plan.md
# Check approval status (for hooks)
ano check plan.md
# Start web viewer
ano serve plan.md
# Start web viewer with multiple files
ano serve plan.md README.md design.mdnpm install -g @nakedved/anogit clone https://github.com/vedpawar2254/Ano.git
cd Ano
npm install
npm run build:all
npm link| Command | Description |
|---|---|
ano annotate <file>:<line> "msg" |
Add annotation to a specific line |
ano annotate <file>:<start>-<end> "msg" |
Add annotation spanning multiple lines |
ano list <file> |
List all annotations for a file |
ano resolve <file> <id> |
Mark annotation as resolved |
ano reply <file> <id> "msg" |
Add threaded reply |
ano delete <file> <id> |
Delete an annotation |
ano sync <file> |
Sync annotation positions after file changes |
| Command | Description |
|---|---|
ano lgtm <file> |
Quick approve ("Looks good to me") |
ano shipit <file> |
Strong approve ("Ship it!") |
ano nit <file>:<line> "msg" |
Minor nitpick (won't block) |
ano block <file>:<line> "msg" |
Add blocker (will block) |
ano q <file>:<line> "msg" |
Quick question |
| Command | Description |
|---|---|
ano approve <file> |
Add approval with optional title |
ano approve <file> --title "Tech Lead" |
Approve with a title (for role-based gates) |
ano check <file> |
Verify approval requirements (exit 0/1) |
ano check <file> --required 2 |
Require specific number of approvals |
ano check <file> --require-title "Tech Lead" |
Require approval from specific title |
ano check <file> --require-role lead |
Require approval from specific team role |
ano check <file> --soft |
Warn but don't block |
ano check <file> --override --reason "msg" |
Bypass with audit trail |
ano check <file> --json |
Output result as JSON |
| Command | Description |
|---|---|
ano diff <file> |
Show changes from previous version |
ano diff <file> --json |
Output diff as JSON |
| Command | Description |
|---|---|
ano team init [name] |
Initialize team configuration |
ano team add <email> |
Add team member |
ano team remove <email> |
Remove team member |
ano team list |
List team members and requirements |
ano team roles |
Show available roles |
| Command | Description |
|---|---|
ano serve <file> |
Start web viewer on localhost:3000 |
ano serve <files...> |
View multiple files with tab switching |
ano serve <file> --port 8080 |
Use custom port |
ano serve <file> --no-open |
Don't open browser automatically |
The web viewer provides a rich interface for reviewing annotated files:
| Key | Action |
|---|---|
j / ↓ |
Next annotation |
k / ↑ |
Previous annotation |
r |
Resolve selected annotation |
Shift+D |
Delete selected annotation |
a |
Add annotation at selected line |
/ |
Focus search |
? |
Show keyboard shortcuts help |
Esc |
Close modal / deselect |
- Annotations - List and filter annotations by status (all/open/blockers)
- Activity - Chronological feed of all annotation and approval changes
- Changes - Diff view showing what changed since page load (added, removed, resolved)
- Copy link to view - Deep link to current annotation/line selection
- Copy for Claude - Formatted markdown summary of open annotations
- Export HTML - Standalone HTML file with all annotations embedded
Double-click any line to edit it directly. Changes are saved automatically.
Select text across lines to add annotations to specific ranges.
The viewer uses Server-Sent Events (SSE) to automatically refresh when files or annotations change.
Add to your Claude Code settings (.claude/settings.json):
{
"mcpServers": {
"ano": {
"command": "node",
"args": ["/path/to/ano/dist/mcp/server.js"]
}
}
}Available MCP tools:
read_annotations- Read annotations for a fileadd_annotation- Add a new annotationresolve_annotation- Mark as resolvedapprove_file- Add approval
Block Claude from executing plans without approval:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"command": "ano check PLAN.md --quiet"
}
]
}
}Require Tech Lead approval:
{
"command": "ano check PLAN.md --require-title 'Tech Lead' --quiet"
}Require 2 approvals from team leads:
{
"command": "ano check PLAN.md --required 2 --require-role lead --quiet"
}Soft gate (warn but don't block):
{
"command": "ano check PLAN.md --soft --quiet"
}Exit codes:
0= Approved, Claude proceeds1= Not approved, Claude blocked
Override logs are written to .ano-overrides.log for audit trails.
Team config is stored in .ano/config.json:
{
"version": "1.0",
"projectName": "My Project",
"members": [
{ "name": "Alice", "email": "alice@example.com", "role": "lead" },
{ "name": "Bob", "email": "bob@example.com", "role": "reviewer" }
],
"roles": {
"lead": { "canOverride": true, "weight": 2 },
"reviewer": { "canOverride": false, "weight": 1 }
},
"requirements": {
"minApprovals": 2,
"requiredRoles": ["lead"],
"requiredTitles": ["Tech Lead"]
}
}| Field | Description |
|---|---|
minApprovals |
Minimum number of approvals required |
minWeight |
Minimum total weight of approvals (based on role weights) |
requiredRoles |
Roles that must approve (matches team member roles) |
requiredTitles |
Titles that must approve (matches approval titles) |
Team membership is advisory only - anyone can approve, but ano check shows who is/isn't in the team.
Annotations are stored in sidecar files alongside your source:
plan.md→plan.md.annotations.json
This keeps annotations git-tracked and version-controlled with your code.
When files change, annotations can become misaligned. Ano uses content anchoring to relocate annotations:
- Stores surrounding context (2 lines before/after)
- Stores content hash for change detection
- On file change, searches for matching context
- Uses fuzzy matching (Levenshtein distance) for tolerance
Run ano sync <file> to update positions after major edits.
URLs encode the current view state:
#annotation=<id>- Link to specific annotation#line=<number>- Link to specific line#file=<path>- Link to specific file (multi-file mode)
Example: http://localhost:3000/#annotation=abc123
No login required. Ano uses your git identity:
git config user.name # Your name
git config user.email # Your emailThis makes Ano trust-based (same as git commits).
┌─────────────────────────────────────────────────────────┐
│ 1. Claude generates plan.md │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 2. Team reviews (via CLI or web viewer) │
│ ano serve plan.md │
│ ano block plan.md:15 "Security concern" │
│ ano q plan.md:30 "Why this approach?" │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 3. Address feedback │
│ ano resolve plan.md <blocker-id> │
│ (Claude can read annotations via MCP and respond) │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 4. Approve │
│ ano approve plan.md --title "Tech Lead" │
│ ano lgtm plan.md │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 5. Execute │
│ User: "Execute the plan" │
│ Hook: ano check plan.md → exit 0 │
│ Claude: *proceeds with implementation* │
└─────────────────────────────────────────────────────────┘
ano/
├── src/
│ ├── core/ # Core data operations
│ │ ├── annotations.ts # CRUD for annotations
│ │ ├── anchoring.ts # Position tracking
│ │ ├── config.ts # User identity (git)
│ │ ├── team.ts # Team configuration
│ │ └── types.ts # TypeScript interfaces
│ ├── cli/ # CLI commands
│ │ ├── index.ts # Entry point
│ │ └── commands/ # Individual commands
│ │ ├── annotate.ts
│ │ ├── approve.ts
│ │ ├── check.ts
│ │ ├── diff.ts
│ │ ├── list.ts
│ │ ├── serve.ts
│ │ └── ...
│ └── mcp/ # MCP server for Claude
│ └── server.ts
├── web/ # Svelte 5 web viewer
│ ├── src/
│ │ ├── App.svelte
│ │ └── lib/
│ │ ├── FileViewer.svelte
│ │ ├── Sidebar.svelte
│ │ ├── AnnotationCard.svelte
│ │ ├── ActivityFeed.svelte
│ │ ├── DiffView.svelte
│ │ └── ...
│ └── dist/ # Built assets
├── hooks/ # Example hook configurations
│ └── README.md
└── .ano/ # Team config (per-project)
└── config.json
| Type | Purpose | Blocks Execution |
|---|---|---|
blocker |
Must resolve before proceeding | Yes |
concern |
Risk or issue identified | No |
question |
Clarification needed | No |
suggestion |
Improvement idea | No |
NPM: https://www.npmjs.com/package/@nakedved/ano
MIT
Contributions welcome! Please open an issue or PR.