Skip to content

Commit 4e0da8b

Browse files
committed
Merge branch 'main' of github.com:s3bc40/devbrief
2 parents 19ea781 + 53b995a commit 4e0da8b

6 files changed

Lines changed: 218 additions & 139 deletions

File tree

.claude/rules/git.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
# Git & PR Discipline
3+
4+
## Branch Naming
5+
- `feat/<subcommand-or-description>`
6+
- `fix/<short-description>`
7+
- `chore/<short-description>`
8+
- `docs/<short-description>`
9+
- `test/<short-description>`
10+
11+
Never work directly on `main`.
12+
13+
## Commit Format — Conventional Commits
14+
15+
```
16+
type(scope): description
17+
```
18+
19+
Types: `feat`, `fix`, `chore`, `docs`, `test`, `refactor`
20+
21+
- Atomic commits — one intention per commit.
22+
- Never squash unless explicitly instructed.
23+
- Never amend a commit that has already been pushed.
24+
25+
## PR Workflow
26+
1. Create feature branch.
27+
2. Work and commit atomically.
28+
3. Push: `git push -u origin <branch>`
29+
4. Open PR: `gh pr create` — title follows conventional commits.
30+
5. **Stop. Do not merge. Sebastien reviews.**
31+
32+
## Versioning
33+
- Semver. Python and Rust share the same version number.
34+
- Update `pyproject.toml` and `rust/Cargo.toml` version + git tag simultaneously.
35+
- Tag format: `v<major>.<minor>.<patch>`
36+
37+
## Hard Stops — Create BLOCKED.md and Halt
38+
- Ambiguous spec on anything touching architecture.
39+
- Test suite failing without an understood cause.
40+
- Non-trivial merge conflict.
41+
- Any operation touching credentials or `.env` files.
42+
- Uncertainty about which branch to work on.

.claude/rules/python.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
# Python Conventions
3+
4+
## Runtime
5+
- Python 3.11+ only. `tomllib` is stdlib — import directly (no third-party `tomli`).
6+
- `pyproject.toml` sets `>=3.12` — do not lower this constraint.
7+
8+
## HTTP
9+
- `httpx` async for all HTTP. No `requests`.
10+
- FastAPI handlers must be `async def`.
11+
- Existing `requests` usage is tech debt — migrate if touching those files.
12+
13+
## Typing
14+
- Type hints on every function — no untyped functions.
15+
- No `Any` without an inline comment explaining why.
16+
17+
## Linting & Formatting
18+
- `ruff` only — no black, no flake8, no isort.
19+
- Run: `uv run ruff check src/ tests/` and `uv run ruff format src/ tests/`
20+
21+
## Output
22+
- All terminal output via `Rich` — no raw `print()` in command handlers.
23+
24+
## Model Resolution
25+
- Default: `claude-sonnet-4-6`.
26+
- Always resolved via `resolve_model()` in `devbrief.core.credentials`.
27+
- Chain: `DEVBRIEF_MODEL` env → `config.toml [anthropic] default_model` → `"claude-sonnet-4-6"`.
28+
- Never hardcode a model string in any command file.
29+
30+
## Credentials
31+
- Never log or print credentials (even partially).
32+
- Never commit `.env` or `config.toml` to the repo.
33+
- Config file permissions: `600` on write.
34+
35+
## Rust Extension
36+
- If `devbrief_core` is unavailable at import time, fall back to Python implementation.
37+
- Never hard-crash on a missing native extension.

.claude/rules/rust.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
# Rust Conventions
3+
4+
## Quality
5+
- `clippy` clean — zero warnings allowed. CI enforces this.
6+
- No `unwrap()` in library code — use `?` or explicit error handling.
7+
8+
## Build Setup (PyO3 / maturin)
9+
- `Cargo.toml` must declare `crate-type = ["cdylib", "rlib"]`:
10+
- `cdylib` — produces the Python extension module.
11+
- `rlib` — lets the linker produce a test binary.
12+
- `maturin develop` to build and install locally.
13+
14+
## Running Tests
15+
16+
```
17+
PYO3_BUILD_EXTENSION_MODULE=1 cargo test --manifest-path rust/Cargo.toml
18+
```
19+
20+
- `PYO3_BUILD_EXTENSION_MODULE=1` tells PyO3 not to link against libpython
21+
(may not be available as a shared lib on the host).
22+
- Tests only call pure Rust functions — no live Python interpreter needed.
23+
- The `rust-check` CI job sets this env var automatically.
24+
25+
## Versioning
26+
- Python and Rust share the same version number (semver).
27+
- Update both `pyproject.toml` and `rust/Cargo.toml` versions simultaneously.
28+
- Tag format: `v<major>.<minor>.<patch>`
29+
30+
## Scope
31+
- Rust is used only for `devbrief env` (gitignore audit, .env drift, secret scan).
32+
- Published as `devbrief-core` on crates.io.
33+
- If unavailable at runtime, Python falls back gracefully — never hard-crash.

.claude/rules/testing.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
# Testing Conventions
3+
4+
## Python — pytest
5+
6+
- **Required:** tests for every new command and every credential resolution path.
7+
- Current count: ~122 Python tests across 5 test files.
8+
- Test files mirror `src/devbrief/` structure:
9+
- `test_cache.py` — cache module + repo cache integration
10+
- `test_credentials.py` — credential resolution + auth command
11+
- `test_logs.py` — log parser, ring buffer, polling endpoints
12+
- `test_github.py` — GitHub fetchers
13+
- `test_display.py` — Rich display functions
14+
15+
## Credential Mocking
16+
- **Always** mock credential reads in tests — never use real API keys.
17+
- Mock at the `devbrief.core.credentials` boundary, not deeper.
18+
19+
## Running Python Tests
20+
21+
```
22+
uv run pytest
23+
```
24+
25+
## Rust — cargo test
26+
27+
- Current count: 12 `#[cfg(test)]` tests in `rust/`.
28+
- `dev-dependencies` must include `tempfile` for filesystem tests.
29+
30+
## Running Rust Tests
31+
32+
```
33+
PYO3_BUILD_EXTENSION_MODULE=1 cargo test --manifest-path rust/Cargo.toml
34+
```
35+
36+
See `rust.md` for why this env var is required.
37+
38+
## CI
39+
- `ci.yml` runs lint + type-check + Python tests on every PR and push to `main`.
40+
- `rust-check` CI job runs Rust tests with `PYO3_BUILD_EXTENSION_MODULE=1` set automatically.

.claude/settings.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"$schema": "https://claude.ai/claude-code/settings.json",
3+
"permissions": {
4+
"allow": [
5+
"Bash(uv:*)",
6+
"Bash(cargo:*)",
7+
"Bash(maturin:*)",
8+
"Bash(pytest:*)",
9+
"Bash(ruff:*)",
10+
"Bash(git:*)",
11+
"Bash(gh:*)",
12+
"Bash(python3 -m json.tool *)"
13+
],
14+
"deny": []
15+
}
16+
}

CLAUDE.md

Lines changed: 50 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1,167 +1,78 @@
1-
# CLAUDE.md — DevBrief Agent Memory
1+
# CLAUDE.md — DevBrief
22

3-
## 1. Project Identity
3+
## Project
44

5-
DevBrief is a developer CLI tool for **project situational awareness**: given a GitHub repository URL (and later: log streams, API endpoints, infra configs, and PRs), it fetches structured data and generates a human-readable brief via Claude AI. The tool is designed for developers who need rapid context on any project without reading every file manually.
5+
DevBrief: developer CLI for **project situational awareness** — fetches structured data from a GitHub repo and generates a human-readable brief via Claude AI.
66

7-
**Tagline:** Project situational awareness
87
**Distribution:** PyPI (`devbrief`) + crates.io (`devbrief-core`)
8+
**Stack:** Python (uv) + Rust (maturin/PyO3). No React, no Docker, no webpack.
99

1010
---
1111

12-
## 2. Tech Stack
12+
## Commands
1313

14-
**Python layer:**
15-
- `typer` — CLI framework (migration target from current `click`)
16-
- `fastapi` + `jinja2` + HTMX — web UI for future `devbrief serve`
17-
- SSE (Server-Sent Events) — streaming output in web UI
18-
- `httpx` (async) — HTTP client (migration target from current `requests`)
19-
- `boto3` — AWS integration for `devbrief logs`
20-
- `anthropic` SDK — Claude AI integration
21-
- `tomllib` (stdlib, Python 3.11+) — config file parsing
22-
- `uv` — package manager and virtual env
23-
24-
**Rust layer:**
25-
- `maturin` + `PyO3` — Rust extensions callable from Python
26-
- Enters via `devbrief env` subcommand
27-
- Published as `devbrief-core` on crates.io
28-
29-
**Not used:** React, Node, webpack, Docker (end users install via pip/cargo only)
30-
31-
---
32-
33-
## 3. Project Structure
34-
35-
```
36-
devbrief/
37-
├── src/
38-
│ └── devbrief/
39-
│ ├── __init__.py # Package init
40-
│ ├── cli.py # Typer app — registers all subcommands
41-
│ ├── commands/
42-
│ │ ├── repo.py # devbrief repo (cache-aware)
43-
│ │ ├── auth.py # devbrief auth
44-
│ │ └── logs.py # devbrief logs — FastAPI server, log parser, ring buffer
45-
│ ├── core/
46-
│ │ ├── credentials.py # API key + model resolution chain
47-
│ │ ├── config.py # Config file read/write (~/.config/devbrief/config.toml)
48-
│ │ └── cache.py # Brief cache keyed by sha256(url+commit_sha) → ~/.cache/devbrief/
49-
│ ├── github.py # GitHub REST API fetchers (+ fetch_latest_commit_sha)
50-
│ ├── brief.py # Claude prompt builder and generate_brief()
51-
│ └── display.py # Rich terminal display functions
52-
├── tests/
53-
│ ├── __init__.py
54-
│ ├── test_cache.py # Cache module + repo cache integration tests
55-
│ ├── test_credentials.py # Credential resolution + auth command tests
56-
│ ├── test_logs.py # Log parser, ring buffer, polling endpoints
57-
│ ├── test_github.py # Unit tests for GitHub fetchers
58-
│ └── test_display.py # Unit tests for Rich display functions
59-
├── dist/ # Built distributions (gitignored except .gitignore)
60-
├── .github/
61-
│ └── workflows/
62-
│ ├── ci.yml # CI: lint + test on every PR and push to main
63-
│ └── release.yml # Release: build + publish to PyPI on git tag v*
64-
├── pyproject.toml # Project metadata, deps, build config (maturin)
65-
├── uv.lock # Locked dependency tree
66-
├── README.md # PyPI-ready README
67-
├── assets/
68-
│ ├── devbrief-cache.gif # Demo GIF for devbrief repo (excluded from wheel)
69-
│ ├── devbrief-env.gif # Demo GIF for devbrief env (excluded from wheel)
70-
│ └── vhs/
71-
│ ├── devbrief-cache.tape # VHS tape source for devbrief-cache.gif
72-
│ └── devbrief-env.tape # VHS tape source for devbrief-env.gif
73-
├── LICENSE # MIT
74-
├── CLAUDE.md # This file — agent persistent memory
75-
└── .gitignore
76-
```
77-
78-
**Assets policy:** `assets/` is excluded from the PyPI wheel via `[tool.maturin] exclude`. GIFs and tapes are repo-only. To regenerate: `vhs assets/vhs/devbrief-cache.tape` or `vhs assets/vhs/devbrief-env.tape`.
14+
| Task | Command |
15+
|------|---------|
16+
| Install deps | `uv sync` |
17+
| Run tests | `uv run pytest` |
18+
| Lint | `uv run ruff check src/ tests/` |
19+
| Format | `uv run ruff format src/ tests/` |
20+
| Type check | `uv run mypy src/` |
21+
| Rust tests | `PYO3_BUILD_EXTENSION_MODULE=1 cargo test --manifest-path rust/Cargo.toml` |
22+
| Build wheel | `maturin develop` |
7923

8024
---
8125

82-
## 4. Subcommand Status
83-
84-
| Subcommand | Status | Notes |
85-
|-----------------|-------------|------------------------------------------------|
86-
| devbrief repo | LIVE | v0.3.2, cache layer (SHA-keyed, ~/.cache/devbrief/), --no-cache/--refresh |
87-
| devbrief auth | LIVE | v0.2.0, key validation, config write/read/clear, 600 perms |
88-
| devbrief logs | LIVE | v0.3.0, FastAPI+HTMX polling dashboard, ring buffer, file (1s tail)/stdin |
89-
| devbrief env | LIVE | v0.4.2, Rust active (maturin/PyO3), gitignore audit + .env drift + secret scan |
90-
| devbrief api | PLANNED | |
91-
| devbrief infra | PLANNED | |
92-
| devbrief pr | PLANNED | |
93-
26+
## Architecture — Do Not Change Without a Spec Card
9427

28+
- **Credential resolution:** env var → `.env` file → `~/.config/devbrief/config.toml` → keychain (future). Implemented in `devbrief.core.credentials`.
29+
- **Cache key:** `sha256(url + commit_sha)``~/.cache/devbrief/`
30+
- **Config file:** `~/.config/devbrief/config.toml`, permissions `600` on write.
31+
- **Model:** always resolved via `resolve_model()` — never hardcoded in command files.
32+
- **Rust extension:** `devbrief env` only. If unavailable at runtime, fall back to Python.
33+
- **Assets:** `assets/` excluded from PyPI wheel via `[tool.maturin] exclude`.
9534

9635
---
9736

98-
## 5. Credential System
99-
100-
**Layered resolution chain (highest priority first):**
101-
1. Environment variable (e.g. `ANTHROPIC_API_KEY`, `GITHUB_TOKEN`)
102-
2. `.env` file in working directory (loaded via `python-dotenv`)
103-
3. `~/.config/devbrief/config.toml` — user-level config file
104-
4. System keychain (future, not yet implemented)
37+
## Subcommand Status
10538

106-
**Config file:** `~/.config/devbrief/config.toml`
107-
**File permissions:** `600` (user read/write only — enforce on write)
108-
**Rules:**
109-
- Never log credentials
110-
- Never print credentials (even partially) in normal output
111-
- Never commit `.env` files or `config.toml` to the repo
112-
- Tests must mock credential reads — never use real keys in tests
39+
| Subcommand | Status | Notes |
40+
|-----------------|----------|----------------------------------------------------------------|
41+
| devbrief repo | LIVE | v0.3.2, SHA-keyed cache, --no-cache/--refresh |
42+
| devbrief auth | LIVE | v0.2.0, key validation, config write/read/clear, 600 perms |
43+
| devbrief logs | LIVE | v0.3.0, FastAPI+HTMX polling dashboard, ring buffer, file/stdin|
44+
| devbrief env | LIVE | v0.4.2, gitignore audit + .env drift + secret scan (Rust) |
45+
| devbrief api | PLANNED | |
46+
| devbrief infra | PLANNED | |
47+
| devbrief pr | PLANNED | |
11348

11449
---
11550

116-
## 6. CI/CD Rules
51+
## Current Sprint
11752

118-
- **`ci.yml`**: Runs on every PR and push to `main`. Steps: lint (ruff), type-check, test (pytest).
119-
- **`release.yml`**: Runs on git tag push matching `v*`. Steps: build wheels only (no sdist — Rust extension requires Rust to build from source), publish to PyPI via trusted publishing (OIDC).
120-
- **Branch strategy:** `main` is protected. Feature branches: `feat/<subcommand-name>` or `feat/<short-description>`.
121-
- **Conventional commits:** `feat:`, `fix:`, `chore:`, `docs:`, `test:`, `refactor:`
122-
- **Versioning:** semver. Python and Rust share the same version number. Update `pyproject.toml` version and tag simultaneously.
53+
All subcommands through v0.4.2 are LIVE. **Next action:** await spec card before touching any subcommand.
12354

124-
---
125-
126-
## 7. Coding Rules (Enforce Always)
127-
128-
- **Python 3.11+ only**`tomllib` is stdlib, import it directly. (`pyproject.toml` currently sets `>=3.12`.)
129-
- **Async-first:** Use `httpx` async for all HTTP. FastAPI handlers must be `async def`. (Current `requests` usage is tech debt to migrate.)
130-
- **Ruff** for linting and formatting — no other linters, no black, no flake8.
131-
- **Type hints everywhere** — no untyped functions, no `Any` without explanation.
132-
- **Rust:** `clippy` clean, zero warnings allowed.
133-
- **Rust testing:** `cargo test` requires two things: `crate-type = ["cdylib", "rlib"]` (rlib lets
134-
the linker produce a test binary) and `PYO3_BUILD_EXTENSION_MODULE=1` (tells PyO3 not to link
135-
against libpython, which may not be available as a shared lib on the host). Our tests only call
136-
pure Rust functions so they do not need a live Python interpreter. Run as:
137-
`PYO3_BUILD_EXTENSION_MODULE=1 cargo test --manifest-path rust/Cargo.toml`.
138-
The `rust-check` CI job sets this env var automatically.
139-
- **Tests required** for every new command and every credential resolution path.
140-
- **Default model:** `claude-sonnet-4-6`. Never hardcode a model string in any command file. Model is always resolved via `resolve_model()` in `devbrief.core.credentials` (env var `DEVBRIEF_MODEL``config.toml [anthropic] default_model``"claude-sonnet-4-6"`).
141-
- **Graceful degradation:** If Rust extension is unavailable, fall back to Python implementation. Never hard-crash on missing native extension.
142-
- **Rich** for all terminal output — no raw `print()` in command handlers.
55+
- **CI:** `ci.yml` runs lint + type-check + test on every PR and push to `main`.
56+
- **Release:** `release.yml` on git tag `v*` — wheels only (no sdist), PyPI OIDC.
57+
- **Versioning:** semver. Python and Rust share version. Update `pyproject.toml` + tag simultaneously.
14358

14459
---
14560

146-
## 8. Agent Boundaries
61+
## What NOT to Touch
14762

148-
- **You implement. You do not decide architecture.**
149-
- If a spec is ambiguous, stop and ask before writing code.
150-
- If a decision would be hard to reverse (schema changes, public API shape, breaking changes), flag it before proceeding.
151-
- When a task is complete, summarize: what was built, what files changed, what tests cover it.
152-
- Read this file at the start of every session. If a subcommand ships or status changes, update the table in section 4.
63+
- Do not push to `main` or merge PRs — Sebastien reviews.
64+
- Do not hardcode model strings — always use `resolve_model()`.
65+
- Do not use `print()` in command handlers — use Rich.
66+
- Do not commit `.env` or `config.toml`.
67+
- Do not build `sdist` — Rust extension requires Rust toolchain; wheels only.
68+
- Do not implement new subcommands without a spec card.
15369

15470
---
15571

156-
## 9. Current Task Queue
157-
158-
1. [x] Create CLAUDE.md
159-
2. [x] Set up CI/CD pipeline (`ci.yml` + `release.yml`) — Rust steps present as commented stubs
160-
3. [x] v0.2.0: CLI restructure (`devbrief repo`), `devbrief auth`, credential + model resolution
161-
4. [x] v0.3.0: `devbrief logs` — FastAPI+HTMX polling dashboard, ring buffer, file/stdin
162-
5. [x] v0.3.1: `devbrief repo` cache layer — SHA-keyed local cache, --no-cache/--refresh flags
163-
6. [x] v0.3.2: `github.py` migrated from `requests` to `httpx` — closes HTTP client tech debt
164-
7. [x] v0.4.0: `devbrief env` — gitignore audit, .env drift (Rust), secret scan (Rust), stub types
165-
8. [x] Rust unit tests: `["cdylib","rlib"]`, `tempfile` dev-dep, 12 `#[cfg(test)]` tests,
166-
`rust-check` CI job active, `PYO3_BUILD_EXTENSION_MODULE=1` for cargo test
167-
9. [ ] Await spec card before touching any subcommand
72+
## Conventions
73+
74+
See `.claude/rules/` for enforced coding conventions:
75+
- `python.md` — Python conventions, async, typing, Rich, model resolution
76+
- `rust.md` — PyO3/maturin, clippy, cargo test setup
77+
- `testing.md` — pytest structure, cargo test, credential mocking
78+
- `git.md` — branch naming, conventional commits, PR discipline, hard stops

0 commit comments

Comments
 (0)