Set Up Claude Code: Bookmark Manager
Build a Python CLI bookmark manager using Claude Code and natural language prompts.
Introduction
30 Second Summary
Imagine describing an app you want in plain English and watching it appear in your terminal, file by file, fully working. That's what agentic coding makes possible.
In this project, you will install Claude Code on your Mac and use it to build a complete Python bookmark manager from scratch. You'll go from an empty folder to a tested, committed project without writing a single line of code by hand.
What You'll Build
You'll type a command like python bookmark_manager.py search "python" and instantly find bookmarks across your collection, knowing every line of code was written by Claude Code from your natural language prompts.
By the end of this project, you'll have:
- A working Python CLI where you save, search, and delete bookmarks, scaffolded from a single sentence you typed into Claude Code.
- Firsthand experience watching multi-file editing in action as Claude Code adds a search feature across two files simultaneously.
- A test-driven debugging loop where Claude Code writes tests, uncovers bugs, and fixes them on its own.
- Secret Mission: Add a Markdown export feature with a single prompt, letting Claude Code handle everything autonomously.
Do I need anything before starting?
You'll need a Mac with Python 3 and Git installed, plus an active Claude Pro (or Max) subscription. No prior experience with Claude Code is required.
Step 1 walks you through installing Claude Code itself, so you don't need to set that up beforehand.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters.
Install and Launch Claude Code
Before you can build anything with Claude Code, you need it running inside a project directory. An empty folder and a working session are the starting line for everything that follows.
In this step, you'll install Claude Code with a single terminal command, create a fresh project folder, and authenticate with your Claude Pro account.
In this step, get ready to:
- Install Claude Code using the native installer.
- Create a project directory with Git initialized.
- Launch Claude Code and authenticate with your Claude Pro account.
Install Claude Code
Claude Code's native installer handles the full setup and keeps itself updated in the background. You never need to manually upgrade.
- Press Cmd+Space to open Spotlight search.
- Type Terminal and press Enter to open it.
- Install Claude Code by running this command:
curl -fsSL https://claude.ai/install.sh | bash
What does this command do?
This downloads and runs Claude Code's official installer script. It installs the claude command on your system and sets up auto-updates so you always have the latest version.
- Wait for the installer to finish. You should see a success message confirming Claude Code is installed.
Seeing a curl error or 403?
Make sure you have an active internet connection and try the command again. If the error persists, check that your terminal is not behind a corporate proxy or firewall blocking the download.
help me troubleshoot the Claude Code installation
Create your project directory
Claude Code works best when launched inside a dedicated project folder with version control. This gives it a clean workspace.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder, move into it, and initialize a Git repository by running these commands:
mkdir bookmark-manager && cd bookmark-manager
git init
- You should see a message like Initialized empty Git repository in /Users/.../bookmark-manager/.git/ confirming the repository was created.
Seeing 'mkdir: cannot create directory'?
This means a folder called bookmark-manager already exists on your Desktop. Either delete it first or choose a different name.
help me fix my project directory setup
Launch Claude Code and authenticate
With your project folder ready, you can now start a Claude Code session. On first launch, it will ask you to log in through your browser.
- Launch Claude Code by running:
claude
What happens on first launch?
Claude Code opens a browser window for authentication. Log in with the same account that has your Claude Pro subscription. Once authenticated, your terminal session connects and you'll see the Claude Code prompt.
- When the browser window opens, log in with your Claude Pro account credentials.
- Return to your terminal after logging in. You should see the > input cursor, meaning Claude Code is ready to accept commands.
Before you start building, let's confirm everything is healthy. What do you think a diagnostic command will report for a fresh install?
- Run this inside Claude Code to verify your setup:
/doctor
- The /doctor command runs an automated check of your installation, settings, and environment. It should confirm a healthy setup with no errors.
What does /doctor check?
This built-in diagnostic verifies your installation health, checks for settings-file validation errors, and confirms your authentication is working. If anything is misconfigured, it proposes fixes you can apply directly.
Seeing warnings or errors from /doctor?
If /doctor reports an authentication issue, run /login inside Claude Code to re-authenticate. For other warnings, follow the suggested fixes that /doctor proposes.
help me fix my Claude Code setup
You're now inside Claude Code, ready to build. Next up, you'll give it a single sentence and watch it generate an entire multi-file project from scratch.
Scaffold the Bookmark Manager
Claude Code is running and waiting for its first instruction. The real test of an agentic coding tool is whether a plain-English description can produce working, multi-file code without you touching a single line.
In this step, you'll give Claude Code a one-sentence specification for a bookmark manager. Then you'll test the generated app by actually using it to save and list bookmarks.
In this step, get ready to:
- Give Claude Code a natural language prompt describing the bookmark manager.
- Test the generated CLI by adding and listing bookmarks.
Give Claude Code your first prompt
Claude Code reads natural language and translates it into working code. Your prompt needs to describe three things: what commands the CLI should have, how it stores data, and how to split the code across files.
- Type this prompt into the Claude Code > cursor:
Create a Python CLI bookmark manager with these commands: add (takes a URL, --title flag, and optional --tags), list (shows all bookmarks), and delete (by ID). Store bookmarks in a JSON file. Split the code into bookmark_manager.py for the CLI and storage.py for the data layer.
What does this prompt ask for?
- Three CLI commands: add, list, and delete.
- JSON file storage for persistence between runs.
- Two-file structure: bookmark_manager.py handles the CLI interface, storage.py handles reading and writing data.
Claude Code will generate both files and show you the code it wrote. It will ask for your permission before writing the files to disk.
- Review the generated code that Claude Code displays.
- Accept the changes when Claude Code asks for confirmation.
Claude Code may generate slightly different code
Since Claude Code works from natural language, your generated code may not be identical to a reference implementation. That's fine. The important thing is that the commands work correctly when you test them.
Test the bookmark manager
The generated files are real Python scripts on your disk. To prove they work independently of Claude Code, you'll test them in a separate terminal.
- Open a new terminal tab by pressing Cmd+T in your Terminal app.
- Navigate to your project folder by running this command:
cd ~/Desktop/bookmark-manager
- Confirm the files Claude Code generated exist by running:
ls
You should see bookmark_manager.py and storage.py in the output.
Don't see both files?
If you only see one file or no Python files at all, switch back to your Claude Code terminal tab and check whether you accepted the changes. Claude Code will not write files until you confirm.
If Claude Code only generated one file, prompt it again: "You only created one file. Please also create storage.py for the data layer."
help me troubleshoot why Claude Code didn't generate both files
- Add your first bookmark by running:
python3 bookmark_manager.py add "https://docs.python.org" --title "Python Docs" --tags python docs
You should see a confirmation message like Added bookmark #1: Python Docs.
- Add a second bookmark by running:
python3 bookmark_manager.py add "https://github.com" --title "GitHub" --tags code git
You should see Added bookmark #2: GitHub.
Before you run the next command, what do you think list will show? How many bookmarks, and what information about each one?
- List all saved bookmarks by running:
python3 bookmark_manager.py list
You should see both bookmarks displayed with their IDs, titles, URLs, and tags.
What should I see?
The output shows each bookmark with a numeric ID in square brackets, the title, the full URL, and the tags. Both bookmarks you just added appear in the list.
A bookmarks.json file was also created in your project folder. This is where the data layer persists your bookmarks between runs.
Seeing a ModuleNotFoundError?
If you see an error mentioning storage not being found, make sure you're running the command from inside the bookmark-manager directory where both .py files live.
If you see a syntax error, the generated code may have a small issue. Switch back to your Claude Code tab and tell it: "Running the CLI gives this error:" followed by the full error message. Claude Code will fix it.
help me fix errors when running my bookmark manager
From a single English prompt, Claude Code generated a complete multi-file Python app with a CLI, a data layer, and JSON persistence. Next up, you'll hit the first limitation of this app and use Claude Code to fix it.
Add Search with Multi-File Editing
Your bookmark manager can save and list bookmarks. But what happens when you have dozens saved and need to find a specific one?
Right now, your only option is list, which dumps everything. In this step, you'll feel that limitation firsthand, then ask Claude Code to add a search feature. Watch how it coordinates changes across both files in a single pass.
In this step, get ready to:
- Experience the limitation of having no search functionality.
- Prompt Claude Code to add a search command with case-insensitive matching.
- Verify multi-file editing by testing the new search command.
Try to find a bookmark manually
Imagine you want to find just your Python Docs bookmark. You have two saved right now, but picture having 50.
- In a separate terminal (outside Claude Code), try to find your Python bookmark by running:
python bookmark_manager.py list
You see every bookmark. Both of them appear. There is no way to filter or narrow the results.
Why this matters
With just two bookmarks, scanning the full list is manageable. With 50 bookmarks, you'd be scrolling and reading every entry to find the one you want. This is the gap you'll ask Claude Code to fill.
Prompt Claude Code to add search
You know the problem. Now describe the solution in plain English and let Claude Code figure out the implementation across both files.
- Switch back to your Claude Code session in the terminal.
- Type this prompt into Claude Code:
Add a search command that finds bookmarks matching a query against titles, URLs, and tags. Make the search case-insensitive.
What is multi-file editing?
Claude Code understands how your files depend on each other. Adding search requires a new method in storage.py (the data layer) AND a new subcommand in bookmark_manager.py (the CLI). Claude Code edits both files in a single pass without you specifying which files to touch.
- Watch Claude Code work. It will show you the edits it plans to make across both files.
- Accept the changes when Claude Code prompts you for permission.
What did Claude Code change?
In storage.py, Claude Code added a search method that lowercases both the query and each field before comparing. It uses the bm.get("tags") or [] pattern to safely handle bookmarks that might have missing or empty tags.
In bookmark_manager.py, it added a search subparser with a query argument, plus the matching elif block that calls storage.search(args.query) and prints formatted results.
Verify the search command works
Time to test whether Claude Code's implementation actually works. You'll search by title and confirm case-insensitive matching.
- Before you run this, what do you expect to see when you search for "python" in lowercase?
- In your terminal (outside Claude Code), test the search by running:
python bookmark_manager.py search "python"
You should see only the Python Docs bookmark returned. The GitHub bookmark does not appear because it has no match for "python" in its title, URL, or tags.
- Now confirm case-insensitive matching by running:
python bookmark_manager.py search "GITHUB"
You should see the GitHub bookmark returned, even though you searched in all caps. The search lowercases both the query and the bookmark fields before comparing.
Search returning no results?
Make sure you accepted Claude Code's changes when it prompted you. If you declined, return to Claude Code and re-enter the search prompt.
Check that you have bookmarks saved by running python bookmark_manager.py list first. If the list is empty, re-add bookmarks from Step 2.
Help me debug why search returns no results
✔️ Awesome, I see search results!
Your search works across titles, URLs, and tags. Claude Code coordinated the changes across both files without you specifying which files to edit.
ⓧ I'd like to double check the full code
Since Claude Code generates the code, your output may look slightly different. The key things to check in each file:
- In storage.py, look for a search method that lowercases the query and checks it against title, URL, and tags.
- In bookmark_manager.py, look for a search_parser that takes a query argument, and an elif args.command == "search" block that calls storage.search(args.query).
Here is the expected search method in storage.py:
def search(self, query):
query_lower = query.lower()
results = []
for bm in self.bookmarks:
tags = bm.get("tags") or []
if (
query_lower in bm["title"].lower()
or query_lower in bm["url"].lower()
or any(query_lower in tag.lower() for tag in tags)
):
results.append(bm)
return results
Here is the expected search subparser in bookmark_manager.py:
search_parser = subparsers.add_parser("search", help="Search bookmarks")
search_parser.add_argument("query", help="Search query")
And the expected elif block for handling the search command:
elif args.command == "search":
results = storage.search(args.query)
if not results:
print(f"No bookmarks matching '{args.query}'.")
else:
print(f"Found {len(results)} result(s):")
for bm in results:
tags = ", ".join(bm["tags"]) if bm["tags"] else "none"
print(f" [{bm['id']}] {bm['title']}")
print(f" {bm['url']}")
print(f" Tags: {tags}")
print()
You just described a feature in one sentence and watched Claude Code implement it across two files simultaneously. Next up, you'll push this further by asking Claude Code to write tests that expose hidden bugs in your code.
Write Tests and Fix What They Find
Your search command works for the queries you've tried so far. But how do you know it handles every edge case?
Untested code hides bugs that only surface when someone enters unexpected input, like special characters or missing fields. In this step, you'll ask Claude Code to write a comprehensive test suite. The tests will expose real bugs, and Claude Code will fix them without you touching the code.
In this step, get ready to:
- Have Claude Code generate a pytest test suite covering all edge cases.
- Let Claude Code run the tests, discover failures, and fix the bugs it finds.
- Verify all tests pass independently outside of Claude Code.
Ask Claude Code to write a test suite
Manual testing catches obvious problems, but it misses edge cases. A test suite automates the checking by trying dozens of scenarios in seconds.
- Switch back to your Claude Code session in the terminal.
- Type this prompt into Claude Code to generate a complete test file:
Write a comprehensive pytest test suite for the bookmark manager. Include tests for: adding bookmarks, listing, searching by title/URL/tag, case-insensitive search, searching with special characters like "c++", searching bookmarks that have no tags, deleting bookmarks, and deleting nonexistent IDs. Put tests in test_bookmark_manager.py.
What does this prompt do?
You're giving Claude Code a detailed spec of what to test. Each scenario you listed targets a potential edge case.
Claude Code will create a new file called test_bookmark_manager.py with test functions for every scenario you named. It uses pytest fixtures to create a temporary data file for each test so they don't interfere with your real bookmarks.
- Review the test file Claude Code generates. You should see individual test functions like test_add_bookmark, test_search_case_insensitive, and test_search_special_characters.
- Accept the changes when Claude Code prompts you.
Claude Code may install pytest
If pytest is not already installed, Claude Code may run pip install pytest automatically as part of this process. Allow it if prompted.
Run tests and let Claude Code fix failures
Having tests is only half the battle. Running them against your code is where the real bugs surface.
- Ask Claude Code to run the tests and fix any issues it finds by typing this prompt:
Run pytest test_bookmark_manager.py -v and fix any failures
Watch what happens next. Claude Code runs the full test suite, and any failing tests show up in red. Common failures include special characters like c++ crashing the search, or bookmarks without tags causing errors.
Claude Code reads the failure output, identifies the root cause in storage.py, and applies the fix. It then reruns the tests to confirm they pass.
What is test-driven debugging?
This loop of write tests, run them, discover bugs, and fix is called test-driven debugging. You never had to read the traceback or locate the bug yourself.
Claude Code handled the entire cycle: interpreting the error, finding the faulty code, applying a fix, and verifying the fix worked. This is one of its most powerful capabilities.
- Accept all the changes Claude Code makes to storage.py (and any other files it touches).
Verify tests pass independently
Claude Code told you the tests pass. Now you confirm it yourself by running pytest outside of Claude Code.
- Open a separate terminal tab or exit Claude Code temporarily by typing /exit.
- Before you run the tests, predict: do you think all tests will pass, or might some still fail?
- Run the test suite by running this command:
pytest test_bookmark_manager.py -v
You should see every test listed with a green PASSED status. The -v flag shows each test function name so you can confirm all edge cases were covered.
Seeing failures or command not found?
- If you see command not found: pytest, install it by running pip install pytest, then try again.
- If tests fail, go back into Claude Code (run claude in your terminal) and paste the failing test output. Ask Claude Code to fix the remaining issues.
Having trouble getting tests to pass? Help me debug my failing pytest tests in the bookmark manager
✔️ All tests pass with green output
Your bookmark manager is now fully tested. Every edge case from special characters to missing tags has been validated.
ⓧ I'd like to double check my test file
Your test_bookmark_manager.py should contain these test functions. Confirm each one is present:
- test_add_bookmark - verifies a bookmark is created with correct fields.
- test_list_bookmarks - verifies multiple bookmarks appear in the list.
- test_search_by_title - finds a bookmark by its title.
- test_search_case_insensitive - confirms uppercase queries match lowercase titles.
- test_search_by_tag - finds a bookmark by one of its tags.
- test_search_by_url - finds a bookmark by part of its URL.
- test_search_special_characters - handles queries like c++ without crashing.
- test_search_no_results - returns empty when nothing matches.
- test_search_bookmark_without_tags - handles bookmarks that have no tags.
- test_delete_bookmark - removes a bookmark and confirms it is gone.
- test_delete_nonexistent - returns False for an ID that does not exist.
If any are missing, go back into Claude Code and ask it to add the missing test cases.
Your code is now backed by a full test suite that proves every feature works, even with tricky inputs. Next up, you'll give Claude Code a memory so it remembers your project between sessions, and commit everything to git.
Add Project Memory and Commit
Your bookmark manager has tests, handles edge cases, and works reliably. But there is a gap. If you close Claude Code right now and reopen it tomorrow, it will not remember anything about your project.
A CLAUDE.md file solves this by giving Claude Code persistent context it reads at the start of every session. You will also commit your finished project using Claude Code's built-in Git integration.
In this step, get ready to:
- Create a CLAUDE.md file that documents your project for future Claude Code sessions.
- Commit all project files to git with a descriptive message generated by Claude Code.
Give Claude Code persistent memory
Claude Code loads CLAUDE.md files from your project root at the start of every session. This gives it context about your file structure, conventions, and design decisions without you needing to re-explain anything.
- In your Claude Code session, type this prompt:
Create a CLAUDE.md file at the project root that documents: the project purpose, file structure, available commands, how to run tests, and key design decisions (JSON storage, case-insensitive string matching for search).
- Claude Code generates the file and shows you its contents. Review what it produced.
- Accept the changes when prompted.
What is CLAUDE.md?
CLAUDE.md is a project memory file. Claude Code reads it automatically at the start of every session, so it immediately knows your project's structure, how to run tests, and what design choices you made.
Without it, every new Claude Code session starts from scratch. With it, Claude Code picks up right where you left off.
- Verify the file was created by running this command in your regular terminal (outside Claude Code):
cat CLAUDE.md
You should see a Markdown document describing your project purpose, file structure, commands, test instructions, and design decisions.
✔️ I see the project documentation
Your CLAUDE.md is ready. Double check it covers these key sections:
- Project purpose (command-line bookmark manager).
- File structure (bookmark_manager.py, storage.py, test_bookmark_manager.py, bookmarks.json).
- Available commands (add, list, search, delete).
- How to run tests (pytest test_bookmark_manager.py -v).
- Design decisions (JSON storage, case-insensitive search).
ⓧ The file is missing or incomplete
If cat CLAUDE.md shows "No such file or directory", Claude Code may not have saved it. Switch back to your Claude Code session and re-run the prompt. Make sure you accept the changes when Claude Code asks.
If the file exists but is missing sections, you can ask Claude Code to add them: "Update CLAUDE.md to also include the available commands and design decisions."
Commit your project with Claude Code
Claude Code can stage files and generate commit messages based on what it built. This saves you from writing commit messages manually for code you described in natural language.
- Back in your Claude Code session, type this prompt:
Stage all files and commit with a descriptive message summarizing what was built.
- Claude Code stages all project files and creates a commit with a message it writes based on the work it did. Accept when prompted.
What just happened?
Claude Code used its git integration to run git add and git commit for you. It wrote a commit message that summarizes the entire bookmark manager project because it has full context of everything it built across your session.
Before you check the result, what do you think git log --oneline will show?
- Verify the commit by running this in your regular terminal:
git log --oneline
You should see one commit with a descriptive message summarizing what was built (something like "Add bookmark manager CLI with add, list, search, delete commands").
Not seeing a commit?
- If git log says "does not have any commits yet", Claude Code may not have completed the commit. Return to your Claude Code session and re-run the commit prompt.
- If you see the commit but the message is generic, that is fine. The important thing is that your files are tracked.
- Make sure you accepted the changes when Claude Code prompted you. Declining blocks the git operation.
Still stuck? Why isn't my git commit showing up after using Claude Code?
Secret mission
Export Bookmarks to Markdown
You've been guiding Claude Code step by step. Now it's time to let go of the reins. Give Claude Code a single prompt and let it handle everything: figuring out the file structure changes, writing the feature, and updating CLAUDE.md, all without guidance.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources to continue experimenting, pause them to come back later, or delete them entirely.
Resources you used:
- The bookmark-manager project directory on your Desktop (contains all Python files, tests, and the local git repository).
- Claude Code (installed via the native installer to ~/.local/bin/claude).
- pytest (installed via pip).
Keep everything running
No action needed. Choose this if you plan to keep experimenting with Claude Code or extending your bookmark manager.
- Your bookmark-manager folder stays on your Desktop, ready to open whenever you want to add features or practice Claude Code workflows.
- Claude Code stays installed and will auto-update in the background.
- pytest remains available for any Python testing.
Pause - I'll come back to this later
Free up terminal clutter while keeping your files intact. You can resume anytime by navigating back to the project folder and running claude.
- If Claude Code is still running in your terminal, type /exit to quit the session.
- Close any terminal windows you opened for this project.
- Your files, git history, and Claude Code installation all remain in place for next time.
Delete - I don't want to use this again
Remove all project files and optionally uninstall the tools you set up. This is a clean slate.
- If Claude Code is still running, type /exit to quit.
- Delete the project folder by running:
cd ~/Desktop
rm -rf bookmark-manager
- Uninstall Claude Code by removing the binary and version files:
rm -f ~/.local/bin/claude
rm -rf ~/.local/share/claude
- Remove Claude Code configuration data (this deletes all settings, session history, and MCP server configs):
rm -rf ~/.claude
rm ~/.claude.json
- Uninstall pytest:
pip uninstall pytest -y
Nice Work!
Nice Work!
You just went from an empty directory to a fully tested, committed Python CLI app without writing a single line of code yourself. Every feature, every fix, and every commit came from a natural language conversation with Claude Code.
You've learned how to:
- Build a complete multi-file Python CLI tool from a single natural language prompt, and extend it with new features by describing what you need.
- Use test-driven debugging with Claude Code to write tests, surface edge-case bugs, and fix them automatically.
- Give Claude Code persistent project memory with a CLAUDE.md file and use its git integration to commit your work with descriptive messages.
- Secret Mission: Added a Markdown export feature with a single prompt, letting Claude Code handle the file structure changes, implementation, and documentation update autonomously.
Ready to quiz yourself?