Thank you for your interest in contributing to Deadend CLI! This document provides guidelines and instructions for contributing.
- Getting Started
- Development Setup
- Project Structure
- Code Style & Conventions
- Making Contributions
- Testing
- Submitting Changes
- Python 3.11+ required
- Docker - Required for running the pgvector database and sandbox execution
- uv >= 0.5.30 - Package manager for dependency management
- Playwright - For browser automation
-
Fork and clone the repository:
git clone https://github.com/<your-username>/deadend-cli.git cd deadend-cli
-
Install dependencies:
uv sync
-
Install Playwright browsers:
pipx install pytest-playwright playwright install
-
Initialize the CLI:
deadend-cli init
uv sync
uv buildpytestFormat your code before committing:
# Format with black
black .
# Sort imports
isort .
# Lint with flake8
flake8 .deadend-cli/
├── deadend_cli/ # CLI entry point and workflow orchestration
│ └── src/deadend_cli/
├── deadend_agent/ # Core agent framework
│ └── src/deadend_agent/
│ ├── agents/ # Specialized AI agents
│ ├── tools/ # Agent tools (shell, browser, RAG, etc.)
│ ├── rag/ # RAG database and retrieval
│ ├── sandbox/ # Docker sandbox management
│ ├── embedders/ # Code indexing and embeddings
│ └── context/ # Context engine and memory
├── deadend_prompts/ # Jinja2 prompt templates
├── deadend_eval/ # Evaluation framework
└── tests/ # Test suite
- Use black for code formatting
- Use isort for import sorting
- Follow flake8 linting rules
- Use type hints throughout
This project uses Pydantic v2. Key conventions:
from pydantic import BaseModel, Field
class MyModel(BaseModel):
# Use Field(default_factory=list) for mutable defaults
items: list[str] = Field(default_factory=list)
# Use .model_dump(), not .dict()
def to_dict(self):
return self.model_dump()All I/O operations should be async:
async def my_function():
result = await some_async_operation()
return resultAgent outputs should extend AgentOutput:
class AgentOutput(BaseModel):
confidence_score: float # 0.0 to 1.0
notes: str | None = None
updated_state: dict[str, Any] | None = None- Confidence scores: Always 0.0 to 1.0 (float), not percentages
- Task status: Use
"pending","expanding","completed","failed","validated" - Forward references: Use
from __future__ import annotations - Type constraints: Use
Literal["value1", "value2"]for constrained strings
- Bug fixes: Fix issues and improve stability
- New features: Add new capabilities aligned with the project vision
- Documentation: Improve docs, examples, and guides
- Tests: Expand test coverage
- Performance: Optimize code and reduce API calls
- Check existing issues for related work
- For significant changes, open an issue first to discuss your approach
- Ensure your contribution aligns with the project's security research focus
# Run all tests
pytest
# Run with coverage
pytest --cov
# Run specific test file
pytest tests/test_specific.py
# Run async tests
pytest -v # pytest-asyncio handles async tests- Use
pytestfor all tests - Use
@pytest.mark.asynciofor async tests - Use
pytest-mockfor mocking
Example:
import pytest
@pytest.mark.asyncio
async def test_my_async_function():
result = await my_async_function()
assert result is not None-
Create a branch:
git checkout -b feature/your-feature-name
-
Make your changes following the code style guidelines
-
Run tests and formatting:
black . isort . flake8 . pytest
-
Commit your changes:
git add . git commit -m "Add: brief description of changes"
-
Push and create a PR:
git push origin feature/your-feature-name
-
Open a Pull Request against the
mainbranch
- Use clear, descriptive commit messages
- Start with a verb:
Add,Fix,Update,Remove,Refactor - Keep the first line under 72 characters
- All tests must pass
- Code must be formatted with black and isort
- No flake8 errors
- Update documentation if needed
- Add tests for new functionality
- Open an issue on GitHub
- Check the README for usage documentation
By contributing, you agree that your contributions will be licensed under the same license as the project.
Thank you for contributing to Deadend CLI!