Skip to content

Capstone Project: Build a CLI Bookmark Manager ​

Forget todo apps. You're going to build something you'll actually use: a command-line bookmark manager with fuzzy search, tags, and Markdown export.

Why Not a Todo App? ​

Every tutorial ends with a todo app. They're boring, you'll never use them, and they don't stretch your skills. Instead, you're building bm -- a CLI bookmark manager that:

  • Saves URLs with titles, tags, and notes
  • Searches bookmarks with fuzzy matching
  • Exports your collection to Markdown
  • Stores everything in a local JSON file
  • Actually replaces that mess of browser bookmarks you never organize

This project exercises everything from Ch1-4: tool-calling, CLAUDE.md, context management, and Git workflows.

Prerequisites ​

  • [x] Ch1: Claude Code installed, understand tool-calling and permissions
  • [x] Ch2: Can write CLAUDE.md and understand the memory system
  • [x] Ch3: Know how to monitor context and compact when needed
  • [x] Ch4: Can run Git workflows through Claude

Project Spec ​

Commands ​

bm add https://example.com --title "Example" --tags web,reference
bm add https://github.com/anthropics/claude-code --tags tools,ai --note "The CLI itself"
bm list
bm list --tag ai
bm search "claude"          # fuzzy search across title, URL, notes, tags
bm open 3                   # open bookmark #3 in default browser
bm delete 5
bm export                   # exports all bookmarks as Markdown
bm export --tag ai          # exports filtered bookmarks
bm stats                    # show total count, top tags, recently added

Technical Requirements ​

  • Language: Python 3.10+
  • Storage: JSON file (~/.bm/bookmarks.json)
  • CLI parsing: argparse or click
  • Fuzzy search: simple substring matching is fine, bonus for fuzzywuzzy/thefuzz
  • Testing: pytest
  • Code standards: whatever you define in CLAUDE.md

Export Format ​

markdown
# Bookmarks

## ai
- [Claude Code CLI](https://github.com/anthropics/claude-code) - The CLI itself
- [Anthropic Docs](https://docs.anthropic.com) - Official API documentation

## web
- [Example](https://example.com)

## reference
- [MDN Web Docs](https://developer.mozilla.org)

---
*Exported on 2026-04-10 | 42 bookmarks | 8 tags*

Step-by-Step Guide ​

Step 1: Project Setup (Ch1 + Ch2) ​

bash
mkdir -p ~/claude-demos/capstone-bm && cd ~/claude-demos/capstone-bm
git init

Create your CLAUDE.md:

bash
cat > CLAUDE.md << 'EOF'
# bm -- CLI Bookmark Manager

## Key Commands
- Run: python -m bm <command>
- Test: pytest
- Test single: pytest tests/test_service.py -v

## Tech Stack
- Python 3.10+
- Testing: pytest
- CLI: argparse
- Storage: JSON file at ~/.bm/bookmarks.json

## Code Standards
- Type hints on all functions
- Docstrings in Google style
- File names: snake_case
- Constants: UPPER_SNAKE_CASE
- No bare except clauses
- Return types on all public functions

## Project Structure
- src/bm/ -- main source
- tests/ -- test files, mirror src/ structure
- Data: ~/.bm/bookmarks.json

## Commit Convention
- Conventional Commits (feat:, fix:, test:, refactor:)
- One logical change per commit
- English commit messages

## Voice
- Be direct. Skip the preamble.
- No filler. When done, say "Done."
EOF

Start Claude:

bash
claude
Set up the project structure:
- src/bm/ directory with __init__.py and __main__.py
- tests/ directory
- pyproject.toml with pytest config and a console_scripts entry point for "bm"
- A .gitignore for Python projects

Then commit to main.

Step 2: Data Model & Storage (Ch1 + Ch4) ​

Create feature/core branch.

Implement src/bm/models.py:
- Bookmark dataclass: id (auto-increment), url, title, tags (list[str]), note (optional), created_at (ISO datetime)

Implement src/bm/storage.py:
- BookmarkStore class that loads/saves from ~/.bm/bookmarks.json
- Methods: add, get_by_id, delete, list_all, search, filter_by_tag
- search should do case-insensitive substring matching across url, title, note, and tags
- Handle the case where ~/.bm/ doesn't exist yet

Commit.

Step 3: Business Logic (Ch1) ​

Implement src/bm/service.py:
- BookmarkService wrapping BookmarkStore
- add_bookmark(url, title=None, tags=None, note=None) -- auto-fetch title from URL if not provided (use urllib)
- delete_bookmark(id)
- search_bookmarks(query)
- get_stats() -- total count, tag frequency, last 5 added
- export_markdown(tag_filter=None) -- generate the markdown export format

Commit.

Step 4: CLI Interface (Ch1) ​

Implement src/bm/cli.py with argparse:
- bm add <url> [--title] [--tags comma,separated] [--note "text"]
- bm list [--tag filter]
- bm search <query>
- bm open <id>  (use webbrowser.open)
- bm delete <id>
- bm export [--tag filter]
- bm stats

Wire it up in __main__.py so "python -m bm" works.
Commit.

Step 5: Tests (Ch1 + Ch3) ​

Write comprehensive tests:
- test_models.py: Bookmark creation, serialization
- test_storage.py: CRUD operations, file handling, empty state
- test_service.py: search, stats, export format
- test_cli.py: CLI argument parsing (mock the service layer)

Use a tmp_path fixture for storage tests so we don't touch real ~/.bm/

Run all tests. Fix anything that fails.
Commit.

Check /context here. If you're above 50%, compact: /compact keep the project structure, all file paths, and test results

Step 6: Merge & Polish (Ch4) ​

Switch to main, merge feature/core.
Show the full git log.
Then run the tool manually:
  python -m bm add "https://github.com/anthropics/claude-code" --title "Claude Code" --tags tools,ai --note "CLI for Claude"
  python -m bm add "https://docs.python.org" --title "Python Docs" --tags python,reference
  python -m bm list
  python -m bm search "claude"
  python -m bm stats
  python -m bm export

Step 7: Verify ​

Exit Claude and test manually:

bash
python -m bm list
python -m bm search "python"
python -m bm export --tag reference

Acceptance Criteria ​

ItemRequirementHow to Check
CLAUDE.mdComplete, project-specificRead it
Structuresrc/bm/ + tests/ls -R
All commands workadd, list, search, delete, export, statsRun each one
Data persistsBookmarks survive process exitAdd, exit, list
Search worksFinds matches across all fieldsbm search "python"
Export worksGenerates valid Markdown grouped by tagbm export
Tests passAll greenpytest -v
Git history4+ meaningful, atomic commitsgit log --oneline
Feature branchUsed and mergedgit log --graph --oneline
Code qualityType hints, docstrings, no bare exceptsRead the source

Bonus Challenges ​

Once the basics work, try these in new feature branches:

  1. Import from browser: Parse a Chrome/Firefox bookmark export (HTML) and import into bm
  2. Duplicate detection: Warn when adding a URL that already exists
  3. Interactive mode: bm interactive opens a TUI with arrow key navigation
  4. Link checking: bm check verifies all bookmarked URLs still return 200
  5. Colored output: Use ANSI colors for tags, status indicators, and search highlights

Each one is a good exercise in feature branching and atomic commits.

Alternative Project: Git Stats Analyzer ​

If bookmarks aren't your thing, build a git stats analyzer instead:

gitstats ~/my-project

Analyzing 1,247 commits across 18 months...

Top Contributors:
  alice    587 commits (47%)
  bob      412 commits (33%)
  charlie  248 commits (20%)

Most Productive Hours:
  10:00-11:00  ████████████████ 23%
  14:00-15:00  ████████████ 18%
  16:00-17:00  ██████████ 15%

Language Distribution:
  TypeScript   ████████████████████ 62%
  Python       ████████ 24%
  Shell        ████ 14%

Commit Patterns:
  Mon ████████████ 18%
  Tue ████████████████ 22%
  Wed ████████████████ 21%
  Thu ██████████████ 19%
  Fri ████████████ 18%
  Sat ██ 2%
  Sun ██ 0%

Hot Files (most changed):
  src/api/routes.ts         142 changes
  src/utils/helpers.ts       98 changes
  tests/api.test.ts          87 changes

Same project structure, same CLAUDE.md approach, same Git workflow. Different output.

What You've Built ​

This isn't a tutorial exercise. It's a real tool. People actually use bookmark managers like this. If you went through the full capstone:

  • You used CLAUDE.md to configure project standards and Claude followed them
  • You managed context across a multi-step implementation
  • You used feature branches with atomic commits
  • You wrote tests and had Claude fix failures
  • You built something you might actually keep using

That's the Beginner Stage. Everything from here builds on these foundations.

Ready for more? Stage 2: Intermediate unlocks Hooks, MCP integrations, sub-agents, and the tools that turn Claude Code from an assistant into an autonomous development system.

Released under MIT License