# Development Guide

This guide covers setting up Zorac for development, testing, and contributing.

## Development Setup

### Prerequisites

- Python 3.13+
- `uv` package manager
- Git

### Quick Setup

```bash
# Clone the repository
git clone https://github.com/chris-colinsky/zorac.git
cd zorac

# Complete development setup (installs deps + pre-commit hooks)
make dev-setup

# Or manually:
uv sync --extra dev
cp .env.example .env
# Edit .env to set your vLLM server URL
```

## Project Structure

```
zorac/
├── zorac/                  # Main package
│   ├── __init__.py        # Package exports
│   ├── __main__.py        # Module entry point
│   ├── commands.py        # Command registry
│   ├── config.py          # Configuration management
│   ├── console.py         # Rich console singleton
│   ├── llm.py             # LLM operations
│   ├── main.py            # Main event loop
│   ├── markdown_custom.py # Custom markdown renderer
│   ├── session.py         # Session persistence
│   └── utils.py           # Utility functions
├── tests/                  # Test suite
│   ├── __init__.py
│   ├── test_commands.py   # Command registry tests (NEW)
│   └── test_zorac.py      # Comprehensive tests
├── requirements/           # Requirements documents (NEW)
│   └── help_feature.md    # Help feature requirements
├── docs/                   # Documentation
│   ├── INSTALLATION.md
│   ├── CONFIGURATION.md
│   ├── USAGE.md
│   ├── DEVELOPMENT.md     # This file
│   ├── SERVER_SETUP.md    # vLLM server guide
│   └── TEST_TRAINING.md   # Multi-GPU training guide (NEW)
├── .github/workflows/      # CI/CD
│   └── release.yml        # Release automation
├── pyproject.toml          # Dependencies and metadata
├── uv.lock                 # Dependency lock file
├── .env.example            # Configuration template
├── README.md               # Project overview
├── CLAUDE.md               # AI assistant guide
├── CHANGELOG.md            # Version history
├── CONTRIBUTING.md         # Contribution guidelines
├── Makefile                # Development commands
└── LICENSE                 # MIT License
```

## Running Tests

### Basic Testing

```bash
# Run all tests
make test

# Run with coverage report (generates HTML report)
make test-coverage

# View coverage report
open htmlcov/index.html
```

### Test Coverage

Current coverage: **42%** with **34 passing tests**

The test suite covers:
- **Token counting** with various message types
- **Session management** (save/load, error handling)
- **UI header rendering**
- **Message summarization logic**
- **Configuration validation**
- **Command registry** structure and validation
- **Help system** (display and LLM awareness)
- **Integration workflows** (save/load roundtrips, help feature)

**Module coverage:**
- `zorac/commands.py`: 100% coverage
- `zorac/session.py`: 100% coverage
- `zorac/console.py`: 100% coverage
- `zorac/llm.py`: 96% coverage
- `zorac/markdown_custom.py`: 45% coverage (UI rendering, mostly tested via integration)

**Coverage targets:**
- Minimum 80% code coverage
- All core functions tested
- Edge cases and error paths covered

## Code Quality

### Linting

```bash
# Run linter
make lint

# Auto-fix linting issues
make lint-fix
```

### Formatting

```bash
# Format code
make format
```

### Type Checking

```bash
# Run mypy type checking
make type-check
```

### All Checks

```bash
# Run all checks (lint + type + test)
make all-checks
```

## Pre-commit Hooks

Zorac uses pre-commit hooks for automated quality checks:

```bash
# Install pre-commit hooks
make pre-commit-install

# Run pre-commit on all files
make pre-commit

# Pre-commit will automatically run on git commit
```

## Dependencies

### Production Dependencies

- **openai** (>=2.8.1): Client library for vLLM's OpenAI-compatible API
- **tiktoken** (>=0.8.0): Token counting for accurate usage tracking
- **python-dotenv** (>=1.0.0): Environment variable management from .env files
- **rich** (>=14.2.0): Rich terminal formatting, markdown rendering, and live updates
- **textual** (>=1.0.0): Modern TUI framework for the full terminal user interface

### Development Dependencies

- **pytest** (>=8.0.0): Testing framework
- **pytest-cov** (>=4.1.0): Coverage reporting for pytest
- **pytest-mock** (>=3.12.0): Mock fixtures for pytest
- **ruff** (>=0.8.0): Fast Python linter and formatter
- **mypy** (>=1.8.0): Static type checker
- **pre-commit** (>=3.6.0): Git hook management

## Architecture Overview

### Module Breakdown

**zorac/commands.py** - Command Registry (NEW)
- Centralized registry of all interactive commands
- `COMMANDS`: List of command definitions with descriptions
- `get_help_text()`: Generates formatted help for `/help` display
- `get_system_prompt_commands()`: Provides command info to LLM via system prompt
- Single source of truth for command documentation
- Enables LLM awareness of Zorac's capabilities

**zorac/config.py** - Configuration Management
- Three-tier priority: Environment Variables > Config File > Defaults
- Type-safe getters: `get_int_setting()`, `get_float_setting()`, `get_bool_setting()`
- Runtime configuration via `/config` command
- Persistent storage in `~/.zorac/config.json`

**zorac/console.py** - Rich Console Singleton
- Single Rich console instance for consistent formatting
- Used across all modules for output

**zorac/llm.py** - LLM Operations
- `summarize_old_messages()`: Auto-summarization with status spinner
- Handles API calls to vLLM server
- Token limit management

**zorac/markdown_custom.py** - Custom Markdown Renderer
- `LeftAlignedMarkdown`: Custom markdown renderer with left-aligned headings
- Monkey-patches Rich's `Heading.__rich_console__` to remove centered heading panels
- Provides cleaner, more readable terminal output
- Maintains Rich styling (bold, colored) while forcing left justification

**zorac/main.py** - Textual TUI application with chat log, stats bar, and input widgets
- `get_initial_system_message()`: Generates enhanced system prompt with command info
- Textual Input widget with SuggestFromList for slash command autocomplete
- Real-time streaming via `Markdown.get_stream()` with persistent stats bar
- Streaming and non-streaming response modes
- Session auto-save after each response
- Stats bar widget (`Static#stats-bar`) docked to bottom for always-visible metrics

**zorac/session.py** - Session Persistence
- `save_session()`: Saves conversation to JSON
- `load_session()`: Restores conversation from disk
- Error handling for invalid sessions

**zorac/utils.py** - Utility Functions
- `count_tokens()`: Uses tiktoken to count tokens in conversation
- `print_header()`: Displays formatted welcome header
- `check_connection()`: Verifies vLLM server connectivity

## Adding Features

### New Commands

When adding a new command, follow these steps to ensure consistency:

1. **Add to Command Registry** (`zorac/commands.py`):

```python
{
    "command": "/mycommand",
    "description": "Short one-line description for /help",
    "detailed": "Detailed explanation for the LLM system prompt. Describe what the command does and when to use it."
}
```

2. **Implement Command Handler** in `zorac/main.py`:

```python
if user_input.lower() == "/mycommand":
    # Your command logic here
    console.print("[green]Command executed![/green]")
    continue
```

3. **Add Tests** in `tests/test_commands.py` or `tests/test_zorac.py`

4. **Update Documentation**:
   - Add to command list in `docs/USAGE.md`
   - Update `CLAUDE.md` if architecture changes
   - Update `README.md` if user-facing

**Why use the command registry?**
- Single source of truth for all commands
- `/help` command automatically displays your new command
- LLM automatically knows about your command and can suggest it to users
- Ensures consistency across documentation

### New Configuration Settings

1. Add default to `DEFAULT_CONFIG` in `zorac/config.py`
2. Load it in `main()` using appropriate getter
3. Use it in your code
4. Update documentation

### Custom Token Limits

Modify via `.env` file or `/config` command:

```python
MAX_INPUT_TOKENS = get_int_setting("MAX_INPUT_TOKENS", 12000)
```

### UI Customization

**Panel Formatting:**

Modify panel styling in `zorac/main.py`:

```python
console.print(Panel(
    content,
    box=box.ROUNDED,
    style="cyan",
    expand=True  # Full width for left-aligned content
))
```

**Custom Markdown Rendering:**

Modify heading styles in `zorac/markdown_custom.py`:

```python
def _left_aligned_heading_rich_console(self, console, options):
    if self.tag == "h1":
        yield Text(text.plain, style="bold #ffffff")  # Customize H1 style
    elif self.tag == "h2":
        yield Text(text.plain, style="bold #cccccc")  # Customize H2 style
```

**Design Principles:**
- Full-width output for assistant responses
- Left-align all content to match "Assistant:" label
- Use `Markdown.get_stream()` during streaming for real-time content updates
- `Static#stats-bar` displays real-time metrics during streaming
- Stats bar updates in real-time during streaming, persists after response
- Stats persist in bottom toolbar between interactions

## Feature Requirements

The `requirements/` directory contains detailed specifications for features. When adding significant new functionality:

1. **Create a requirements document**: `requirements/feature_name.md`
2. **Include these sections**:
   - Overview and user stories
   - Functional requirements
   - Technical design
   - Implementation plan
   - Testing strategy
   - Documentation updates
   - Acceptance criteria

**Example:** See `requirements/help_feature.md` for a comprehensive example of a feature specification.

**Benefits:**
- Clear specification before implementation
- Easier code review
- Better documentation
- Prevents scope creep

## Publishing Releases

Zorac is distributed via PyPI and Homebrew. Releases are automated via GitHub Actions.

### Distribution Channels

- **PyPI**: `pip install zorac` / `pipx install zorac`
- **Homebrew**: `brew tap chris-colinsky/zorac && brew install zorac`

### Semantic Versioning

- **MAJOR** (X.0.0): Breaking changes, incompatible API changes
- **MINOR** (x.Y.0): New features, backward compatible
- **PATCH** (x.y.Z): Bug fixes, documentation updates only

### Release Process

#### 1. Prepare the Release

```bash
# Create a release branch from main
git checkout main
git pull origin main
git checkout -b release/vX.Y.Z

# Update version in pyproject.toml
# Update CHANGELOG.md with release notes

# Run all quality checks
make all-checks

# Commit changes
git add .
git commit -m "Prepare release vX.Y.Z"
git push origin release/vX.Y.Z
```

#### 2. Create PR and Merge

```bash
# Create a pull request to main
gh pr create --base main --head release/vX.Y.Z --title "Release vX.Y.Z"

# After PR is approved and CI passes, merge via GitHub UI
```

#### 3. Tag and Release

```bash
# Switch to main and pull the merged changes
git switch main
git pull

# Create and push an annotated tag
git tag -a vX.Y.Z -m "Release version X.Y.Z"
git push origin vX.Y.Z

# Clean up release branch
git branch -d release/vX.Y.Z
```

#### 4. Automated Pipeline

When you push the tag, GitHub Actions automatically:

1. **Runs tests** - Ensures all tests pass
2. **Builds package** - Creates wheel and sdist
3. **Publishes to PyPI** - Via Trusted Publisher (OIDC)
4. **Creates GitHub Release** - With package artifacts
5. **Updates Homebrew formula** - Automatically updates `chris-colinsky/homebrew-zorac` with the new version, URL, and SHA256

**Monitor the release:**
- Visit: https://github.com/chris-colinsky/zorac/actions
- Look for "Release" workflow
- Verify PyPI publish and Homebrew update succeed

**Note:** The Homebrew update requires a `HOMEBREW_TAP_TOKEN` secret (a GitHub PAT with write access to `chris-colinsky/homebrew-zorac`).

#### 5. Verify Release

- Check PyPI: https://pypi.org/project/zorac/
- Check GitHub Release: https://github.com/chris-colinsky/zorac/releases/tag/vX.Y.Z
- Test installation:
  ```bash
  pip install zorac==X.Y.Z
  zorac --help
  ```
- Test Homebrew (after formula update):
  ```bash
  brew upgrade zorac
  zorac --help
  ```

### Release Checklist

Before creating a release:

- [ ] All tests pass: `make test`
- [ ] Code is linted: `make lint`
- [ ] Type checking passes: `make type-check`
- [ ] Version updated in `pyproject.toml`
- [ ] CHANGELOG.md updated with release notes
- [ ] All PRs for this release are merged
- [ ] Documentation is up to date

After release:

- [ ] PyPI page shows correct version
- [ ] `pip install zorac` works
- [ ] GitHub Release created with artifacts
- [ ] Homebrew formula updated (automatic)

### Quick Reference

```bash
# Complete release workflow
git checkout main && git pull
git checkout -b release/vX.Y.Z
# Update pyproject.toml version and CHANGELOG.md
make all-checks
git add . && git commit -m "Prepare release vX.Y.Z"
git push origin release/vX.Y.Z
gh pr create --base main --head release/vX.Y.Z --title "Release vX.Y.Z"
# Merge PR via GitHub UI
git switch main && git pull
git tag -a vX.Y.Z -m "Release version X.Y.Z"
git push origin vX.Y.Z
git branch -d release/vX.Y.Z
# Homebrew formula updates automatically after PyPI publish
```

### Hotfix Releases

For urgent bug fixes:

```bash
git checkout main && git pull
git checkout -b hotfix/vX.Y.Z
# Fix the bug, then follow the same release process
# Use PATCH version bump (e.g., 1.1.0 -> 1.1.1)
```

### Manual Publishing (Emergency)

If automated publishing fails:

```bash
# Build
python -m build

# Upload (requires PyPI token)
pip install twine
twine upload dist/*
```

## LLM Command Awareness

Zorac includes an innovative feature where the LLM assistant running inside the chat client is aware of Zorac's own commands and capabilities.

### How It Works

1. **Command Registry** (`zorac/commands.py`):
   - Centralized list of all commands with detailed descriptions
   - Single source of truth for command information

2. **Enhanced System Prompt** (`zorac/main.py`):
   - `get_initial_system_message()` injects command information into the system prompt
   - LLM receives command details at session start

3. **Natural Language Queries**:
   - Users can ask: "How do I save my session?"
   - LLM responds with relevant command information
   - LLM suggests appropriate commands based on user needs

### Token Overhead

The enhanced system prompt adds ~400-450 tokens to each session. This is acceptable because:
- Default context limit is 12,000 tokens
- Overhead is <4% of available context
- Users benefit from having an assistant that knows how to use the tool

### Testing Command Awareness

See `tests/test_zorac.py::TestHelpFeatureIntegration` for tests that verify:
- System message includes all commands
- Token overhead is reasonable
- Help text is properly formatted

## AI-Friendly Documentation

This project includes `CLAUDE.md`, a comprehensive development guide designed to help AI coding assistants understand the project.

**What it contains:**
- Detailed project structure and file purposes
- Architecture overview and design decisions
- Configuration documentation
- Development workflows and commands
- Dependencies and their usage

**Why it's useful:**
AI-powered development tools can use this file to provide better suggestions and write code that matches project conventions.

## Contribution Guidelines

See [CONTRIBUTING.md](../CONTRIBUTING.md) for detailed contribution guidelines.

### Quick Checklist

Before submitting a PR:

- [ ] All tests pass: `make test`
- [ ] Code is linted: `make lint`
- [ ] Code is formatted: `make format`
- [ ] Type checking passes: `make type-check`
- [ ] Documentation is updated (README, USAGE, CLAUDE.md as needed)
- [ ] New commands added to command registry (`zorac/commands.py`)
- [ ] Requirements document created for significant features (`requirements/`)
- [ ] CHANGELOG.md is updated
- [ ] Commit messages are clear and descriptive

## Troubleshooting Development

### Virtual Environment Issues

```bash
# Remove and recreate virtual environment
rm -rf .venv
uv sync
```

### Test Failures

```bash
# Run tests with verbose output
uv run pytest -vv

# Run specific test
uv run pytest tests/test_zorac.py::TestCountTokens -vv
```

### Import Errors

```bash
# Ensure package is installed in development mode
uv sync --extra dev
```

### Command Registry Issues

If commands aren't showing up in `/help` or the LLM doesn't know about them:

```bash
# Verify command is in the registry
python -c "from zorac.commands import COMMANDS; print([c['command'] for c in COMMANDS])"

# Test help text generation
python -c "from zorac.commands import get_help_text; print(get_help_text())"

# Test system prompt generation
python -c "from zorac.commands import get_system_prompt_commands; print(get_system_prompt_commands())"
```

## Resources

- [vLLM Documentation](https://docs.vllm.ai/en/stable/) - vLLM inference server
- [Rich Documentation](https://rich.readthedocs.io/) - Terminal UI library
- [OpenAI API Reference](https://platform.openai.com/docs/api-reference) - API compatibility
- [tiktoken](https://github.com/openai/tiktoken) - Token counting library
- [Textual](https://textual.textualize.io/) - Modern TUI framework

## Support

For questions or issues:
1. Check existing [GitHub Issues](https://github.com/chris-colinsky/Zorac/issues)
2. Review documentation in `docs/` directory
3. Open a new issue with details

## License

MIT License - see [LICENSE](../LICENSE) for details.
