# cmdop-claude

Self-maintaining `.claude/` runtime. Turns a static config folder into living project memory — LLM-powered documentation review, project map generation, task queue, auto-fix, and project initialization.

**$0.001 per full cycle** (scan → review → fix → map) using DeepSeek V3.2 via SDKRouter.

## What it does

| Feature | Description |
|---------|-------------|
| **Review** | Detects stale docs, contradictions, and gaps in `.claude/` files |
| **Fix** | Generates targeted fixes for review issues (dry-run + apply) |
| **Init** | Bootstraps `CLAUDE.md` + rules for bare projects |
| **Map** | Auto-generates `project-map.md` with LLM-annotated directory descriptions |
| **Tasks** | Structured task queue from review items or manual creation |

## Install

```bash
pip install cmdop-claude

# With Streamlit dashboard
pip install cmdop-claude[ui]

# Dev
pip install -e ".[dev]"
```

## Quick Start

### MCP Server (12 tools for Claude Code)

```bash
pip install cmdop-claude
python -m cmdop_claude.sidecar.hook register    # adds to ~/.claude.json
```

Registered globally — available to all Claude Code projects. To unregister:

```bash
python -m cmdop_claude.sidecar.hook unregister
```

| Tool | LLM? | Description |
|------|------|-------------|
| `sidecar_scan` | yes | Run documentation review |
| `sidecar_review` | no | Read current review |
| `sidecar_status` | no | Last run, pending items, token usage |
| `sidecar_acknowledge` | no | Suppress item for N days |
| `sidecar_map` | yes | Generate/update project map |
| `sidecar_map_view` | no | Read current map |
| `sidecar_tasks` | no | List tasks by status |
| `sidecar_task_update` | no | Update task status |
| `sidecar_task_create` | no | Create manual task |
| `sidecar_fix` | yes | Generate fix for a task (dry-run or apply) |
| `sidecar_init` | yes | Bootstrap `.claude/` for bare projects |
| `sidecar_activity` | no | View recent action log (init, review, fix, map) |

### CLI Hooks

```bash
python -m cmdop_claude.sidecar.hook register             # add to ~/.claude.json
python -m cmdop_claude.sidecar.hook unregister           # remove from ~/.claude.json
python -m cmdop_claude.sidecar.hook scan
python -m cmdop_claude.sidecar.hook status
python -m cmdop_claude.sidecar.hook map-update           # debounced
python -m cmdop_claude.sidecar.hook inject-tasks         # for UserPromptSubmit
python -m cmdop_claude.sidecar.hook fix <task_id> [--apply]
python -m cmdop_claude.sidecar.hook init
python -m cmdop_claude.sidecar.hook acknowledge <id> [days]
python -m cmdop_claude.sidecar.hook activity [limit]
```

Integration with Claude Code hooks:

```json
{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write|Edit",
      "command": "python -m cmdop_claude.sidecar.hook map-update"
    }],
    "UserPromptSubmit": [{
      "command": "python -m cmdop_claude.sidecar.hook inject-tasks"
    }]
  }
}
```

### Python API

```python
from cmdop_claude import Client

client = Client()

# Review → find issues
result = client.sidecar.generate_review()

# Fix a specific task
fix = client.sidecar.fix_task("T-001", apply=True)

# Init bare project
init = client.sidecar.init_project()

# Generate project map
project_map = client.sidecar.generate_map()
```

### Streamlit Dashboard

```bash
make run   # http://localhost:8501
```

## Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `CMDOP_CLAUDE_DIR_PATH` | `.claude` | Path to .claude directory |
| `SDKROUTER_API_KEY` | — | API key for SDKRouter |
| `CLAUDE_CP_SIDECAR_MODEL` | `deepseek/deepseek-v3.2` | LLM model for sidecar |
| `CMDOP_DEBUG_MODE` | `false` | Debug logging |

## File Layout

```
.claude/
├── CLAUDE.md                # project instructions
├── project-map.md           # auto-generated structure map
├── rules/*.md               # coding rules
└── .sidecar/                # runtime state (git-ignored)
    ├── review.md            # latest review
    ├── history/*.md         # past reviews
    ├── tasks/T-001.md       # task queue (YAML frontmatter + md)
    ├── map_cache.json       # annotation cache (SHA256)
    ├── activity.jsonl       # action log (init, review, fix, map)
    ├── usage.json           # daily token tracking
    └── suppressed.json      # acknowledged items
```

## Architecture

```
src/cmdop_claude/
├── _config.py                  # Pydantic Settings
├── models/
│   ├── sidecar.py              # Review, Fix, Init models
│   ├── project_map.py          # Map models
│   └── task.py                 # Task models
├── services/
│   └── sidecar_service/        # Decomposed into domain mixins
│       ├── _base.py            # State, lock, scan, usage
│       ├── _review.py          # LLM review + review.md
│       ├── _fix.py             # LLM fix for tasks
│       ├── _init.py            # LLM project init
│       ├── _tasks.py           # Task CRUD
│       ├── _mcp.py             # MCP registration
│       └── _status.py          # Status + map access
├── sidecar/
│   ├── server.py               # FastMCP server (11 tools)
│   ├── hook.py                 # CLI (7 commands)
│   ├── scanner.py              # .claude/ filesystem scanner
│   ├── mapper.py               # Project map generator
│   ├── exclusions.py           # Junk filter + .gitignore
│   ├── activity.py             # Activity logger (JSONL append)
│   ├── cache.py                # SHA256 annotation cache
│   ├── tasks.py                # Task queue manager
│   └── prompts.py              # LLM prompt templates
└── ui/app.py                   # Streamlit dashboard
```

## Cost

| Operation | Tokens | Cost |
|-----------|--------|------|
| Documentation review | ~1800 | ~$0.0005 |
| Fix a task | ~500 | ~$0.0001 |
| Project init | ~2300 | ~$0.0006 |
| Map generation (7 dirs) | ~1400 | ~$0.0004 |
| Map incremental (cached) | 0 | $0.00 |
| Tasks / status / read | 0 | $0.00 |

Full cycle: **~$0.001**. Daily estimate: **~$0.003/day**.

## Testing

```bash
make test   # 281 tests
```

## Development

```bash
make patch          # 0.1.0 → 0.1.1
make minor          # 0.1.0 → 0.2.0
make build          # sdist + wheel
make publish        # upload to PyPI
make publish-test   # upload to TestPyPI
```

## License

MIT
