Skip to content

Commit 3efd793

Browse files
committed
Add claude.md for better understanding of this project and convensions.
1 parent 800bd3c commit 3efd793

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed

CLAUDE.md

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# CLAUDE.md — Agent Instructions for talk-python-cli
2+
3+
## Project Overview
4+
5+
CLI client for the Talk Python to Me podcast and Talk Python Training courses.
6+
Wraps a remote MCP server (`https://talkpython.fm/api/mcp`) using JSON-RPC 2.0
7+
over HTTP. The CLI is a **thin client** — all business logic lives on the server.
8+
The CLI handles argument parsing, HTTP transport, and output formatting.
9+
10+
Published on PyPI as `talk-python-cli`. Entry point: `talkpython`.
11+
12+
## Critical Rules
13+
14+
- **Use `uv pip install`, never `pip install`.**
15+
- **Virtual environment is `./venv`, NOT `./.venv`.**
16+
- **After every code edit, run: `ruff format && ruff check --fix`**
17+
- **Use `pyrefly check` to validate type information after changes.**
18+
- Do not add unnecessary abstractions, comments, or docstrings to unchanged code.
19+
20+
## Build & Run
21+
22+
```bash
23+
# Activate venv
24+
source venv/bin/activate
25+
26+
# Install in editable mode
27+
uv pip install -e ".[dev]"
28+
29+
# Run CLI
30+
talkpython --help
31+
talkpython episodes search "fastapi"
32+
talkpython status
33+
34+
# Run tests
35+
pytest
36+
37+
# Lint & format (ALWAYS after edits)
38+
ruff format && ruff check --fix
39+
40+
# Type check (ALWAYS after edits)
41+
pyrefly check
42+
```
43+
44+
## Project Structure
45+
46+
```
47+
src/talk_python_cli/
48+
__init__.py # Version from importlib.metadata
49+
__main__.py # python -m entry point
50+
app.py # Root Cyclopts app, global options, meta-handler, status cmd
51+
client.py # MCPClient: httpx-based JSON-RPC 2.0 client
52+
formatting.py # Rich output: Markdown panels (text) or JSON
53+
episodes.py # Episode commands (search, get, list, recent, transcript)
54+
guests.py # Guest commands (search, get, list)
55+
courses.py # Course commands (search, get, list)
56+
57+
tests/
58+
conftest.py # Shared fixtures, JSON-RPC response builders
59+
test_client.py # MCPClient tests
60+
test_episodes.py # Episode command tests
61+
test_guests.py # Guest command tests
62+
test_courses.py # Course command tests
63+
```
64+
65+
## Architecture & Key Patterns
66+
67+
### CLI Framework: Cyclopts (not Click, not Typer)
68+
69+
- Root app in `app.py` with sub-apps for episodes, guests, courses.
70+
- **Meta-app launcher** (`@app.meta.default`): intercepts all invocations to
71+
process global options (`--format`, `--url`) before dispatching to subcommands.
72+
- Parameters use `Annotated[type, cyclopts.Parameter(...)]` for docs/defaults.
73+
- Cyclopts auto-converts snake_case commands to kebab-case (e.g. `transcript_vtt``transcript-vtt`).
74+
75+
### Client Pattern
76+
77+
- `MCPClient` in `client.py` wraps httpx for JSON-RPC 2.0 over HTTP.
78+
- Lazy initialization: `_ensure_initialized()` runs MCP handshake on first call.
79+
- Session ID tracked via `Mcp-Session-Id` response header.
80+
- `call_tool(tool_name, arguments)` is the only public API for MCP tool calls.
81+
- Output format sent as URL query param: `?format=json` when JSON mode.
82+
- No authentication required (public API).
83+
84+
### Lazy Client Access (avoids circular imports)
85+
86+
Each command module retrieves the client via a local helper:
87+
```python
88+
def _client():
89+
from talk_python_cli.app import get_client
90+
return get_client()
91+
```
92+
This deferred import avoids circular dependency since `app.py` imports the command modules.
93+
94+
### Output Formatting
95+
96+
- `display(content, format)` in `formatting.py` routes to markdown or JSON renderer.
97+
- Text mode: Rich Markdown panel with "Talk Python" theme (cyan border, monokai code).
98+
- JSON mode on TTY: pretty-printed with syntax highlighting.
99+
- JSON mode piped: compact single-line JSON for scripting.
100+
101+
### Adding a New Command
102+
103+
1. Add function in the appropriate module (`episodes.py`, `guests.py`, `courses.py`).
104+
2. Decorate with `@sub_app.default` or just define as a regular function in the sub-app.
105+
3. Call `_client().call_tool('tool_name', {'arg': value})` to invoke the MCP tool.
106+
4. Pass result to `display(result, _client().output_format)`.
107+
5. Add tests in the corresponding test file using `pytest-httpx` mocks.
108+
109+
### Adding a New Command Group
110+
111+
1. Create `src/talk_python_cli/newgroup.py` with a `cyclopts.App(name='newgroup')`.
112+
2. Register in `app.py`: `app.command(newgroup.sub_app)`.
113+
3. Create `tests/test_newgroup.py`.
114+
115+
## Testing
116+
117+
- Framework: **pytest** with **pytest-httpx** for HTTP mocking.
118+
- `conftest.py` provides helpers: `jsonrpc_result()`, `tool_result()`, `add_init_responses()`.
119+
- Every test must call `add_init_responses(httpx_mock)` before making MCP client calls.
120+
- Tests verify JSON-RPC request structure, argument passing, and response handling.
121+
122+
## Dependencies
123+
124+
| Package | Purpose |
125+
|-------------|--------------------------------|
126+
| cyclopts | CLI framework (commands, args) |
127+
| httpx | HTTP client for MCP calls |
128+
| rich | Terminal output formatting |
129+
| pytest | Testing (dev) |
130+
| pytest-httpx| HTTP mocking in tests (dev) |
131+
132+
Build system: **hatchling**. Package manager: **uv**.
133+
134+
## Config Files
135+
136+
- `pyproject.toml` — Package metadata, dependencies, entry points, build config
137+
- `ruff.toml` — Line length 120, single quotes, target Python 3.14
138+
- `pyrefly.toml` — Type checker config, search path includes `src/`
139+
- `uv.lock` — Locked dependencies (committed)
140+
141+
## Style Conventions
142+
143+
- Line length: 120
144+
- Quotes: single quotes
145+
- Modern Python type syntax: `dict | None` not `Optional[dict]`
146+
- `from __future__ import annotations` in all modules
147+
- Private helpers prefixed with `_`
148+
- Minimal docstrings: only on public functions/classes
149+
- Python target: 3.12+ (currently targeting 3.14 in tooling)

0 commit comments

Comments
 (0)