diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6573a5f..72972e7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,7 +6,17 @@ ## Related Issues - + + +## Spec / ADR Reference + + + + + + + + ## Test Plan @@ -16,12 +26,15 @@ ## Checklist -- [ ] Tests pass (`pytest`) -- [ ] Linting passes (`ruff check src tests`) -- [ ] Type checking passes (`ty check`) +- [ ] Tests pass (`pytest` / `go test -race ./...`) +- [ ] Linting passes (`ruff check src tests` / `golangci-lint run`) +- [ ] `go vet ./...` passes (Go changes only, N/A otherwise) +- [ ] Type checking passes (`uv run ty check`) +- [ ] Coverage >80% on changed source files (N/A for docs/config) +- [ ] E2E BDD tests added for new CLI commands (N/A if none) +- [ ] Commit subjects ≤ 50 chars, body wrapped at 72 ## AI Disclosure - - - + + diff --git a/.gitignore b/.gitignore index 36df14e..9513a81 100644 --- a/.gitignore +++ b/.gitignore @@ -38,7 +38,8 @@ htmlcov/ .cursor/ .gemini/ GEMINI.md -.specify/ +.specify/* +!.specify/memory/ # MkDocs build output site/ @@ -80,3 +81,6 @@ bin/ ### SuperPowers ### docs/superpowers/ + +### SDD — optional contributor tooling (not committed) ### +.opencode/ diff --git a/.specify/memory/constitution.md b/.specify/memory/constitution.md new file mode 100644 index 0000000..307d439 --- /dev/null +++ b/.specify/memory/constitution.md @@ -0,0 +1,201 @@ +# Lola Constitution + +## Core Principles + +### I. Modular Design (NON-NEGOTIABLE) +Every feature must respect clear separation of concerns. Code is +organized into composable, interchangeable components: +- CLI commands live in `src/lola/cli/` +- Data models in `src/lola/models.py` +- Parsing logic in `src/lola/parsers.py` +- Target generation in `src/lola/targets/` +- No circular dependencies between modules +- Favor composition over inheritance + +### II. Flexible Configuration +Support multiple module structures and discovery patterns: +- Auto-discovery of skills from `skills//SKILL.md` +- Auto-discovery of commands from `commands/*.md` +- Auto-discovery of agents from `agents/*.md` +- Backward compatibility with legacy structures required +- No mandatory manifest files - prefer convention over config +- Extensible source handling via strategy pattern + +### III. Type Safety & Modern Python (NON-NEGOTIABLE) +Leverage Python 3.13+ features for code quality: +- Type hints required on all functions (params + returns) +- Use modern syntax: `list[str]` not `List[str]` +- Dataclasses for data models +- Immutable data structures preferred +- Pass type checking: `uv run ty check` (primary), + `uv run mypy src` (secondary) +- No `typing.Any` without justification + +### IV. Testing Philosophy (NON-NEGOTIABLE) +Comprehensive testing using pytest with fixture-based approach: +- Tests written BEFORE implementation (TDD when possible) +- Use `tests/conftest.py` fixtures for common setup +- Test multiple scenarios and edge cases +- Mock external dependencies (filesystem, git, etc) +- Isolate test environments (temp directories) +- CLI tests use Click's `CliRunner` +- Maintain >80% code coverage + +### V. Robust Error Handling +Validate inputs and provide clear, actionable error messages: +- Custom exceptions in `src/lola/exceptions.py` +- User-facing errors use Rich console formatting +- Prevent security issues (path traversal, etc) +- Fail fast with meaningful context +- No silent failures or generic exceptions + +### VI. Extensibility via Strategy Pattern +New capabilities added without modifying core logic: +- Source handlers implement common interface +- Target generators follow consistent pattern +- Easy to add new assistants in `src/lola/targets/` +- Plugin-like architecture for formats +- No hardcoded assistant names in core logic + +### VII. Line Length Limit (NON-NEGOTIABLE) +All files must respect 80-character line limit: +- Code lines: max 80 characters +- Markdown lines: max 80 characters (except URLs/code blocks) +- Comments: max 80 characters +- Properly break, fold, or escape long lines +- Use implicit string concatenation for long strings +- Use parentheses for multi-line expressions + +### VIII. Spec-Driven Development +Architectural and process changes require an ADR in `docs/adr/` +before implementation begins. For significant features, a +proposal is recommended in `openspec/changes/` or `specs/`. +No specific spec format or tooling is mandated — contributors +use whatever workflow fits; AI agents find specs by following +the topic guide in `AGENTS.md`. + +See `docs/adr/spec-driven-development.md` for the full decision. + +## Development Standards + +### Python Code Style +- Ruff linter must pass (configured in pyproject.toml) +- Consistent naming: snake_case for functions/vars, + PascalCase for classes +- Docstrings for public functions (Google style preferred) +- No magic numbers — use named constants +- Single responsibility per function + +### Go Code Style +- `gofmt` formatting required (no exceptions) +- `golangci-lint run` must pass +- Errors wrapped with context (`fmt.Errorf("x: %w", err)`) +- No global mutable state +- Tests use `testify` with `-race` flag enabled + +### Commit Message Standards +- Format: Conventional Commits (feat, fix, docs, chore, + test, refactor) +- Subject line: max 50 characters (tpope 50/72 rule) +- Body lines: wrap at 72 characters +- Blank line required between subject and body +- Subject in imperative mood: "fix bug" not "fixed bug" + +### Dependency Management +- Python: use `uv`, pin major versions in pyproject.toml, + dev deps in `[dependency-groups]` (PEP 735) +- Go: standard `go.mod` / `go.sum`, justify additions +- Minimal dependency footprint in both languages + +### File Organization +```text +src/lola/ # Python source +├── cli/ # Command implementations +├── targets/ # Assistant-specific generators +├── main.py # Entry point +├── models.py # Core data structures +├── parsers.py # Module parsing & fetching +├── config.py # Global paths & settings +├── frontmatter.py # YAML frontmatter handling +├── utils.py # Shared utilities +└── exceptions.py # Custom exceptions + +cmd/ # Go CLI entry points +internal/ # Go internal packages + +tests/ # Python tests +├── conftest.py # Shared fixtures +├── test_*.py # Test modules + +e2e/features/ # BDD Gherkin tests +├── steps/ # Step implementations +├── support/ # Test helpers +├── *.feature # Feature files +``` + +### Documentation Requirements +- README.md: User-facing, installation & quick start +- AGENTS.md: Navigation guide for AI tools (topic index) +- Code comments: Explain WHY, not WHAT +- Docstrings: Public API only, focus on usage + +## Quality Gates + +### Pre-Commit (Automated) +- Python: Ruff linting, ty check, mypy +- Go: golangci-lint, go vet +- 80-character line limit enforced +- No trailing whitespace +- YAML/Markdown valid + +### Pre-Merge (Required) +- All tests pass (`pytest` + `go test -race ./...`) +- Code coverage >80% for changed source files +- No new type errors introduced +- New CLI commands have e2e BDD tests +- Updated AGENTS.md if dev workflow changes + +## Complexity Budget + +### Maximum Complexity Thresholds +- **Cyclomatic complexity**: <10 per function +- **Source files**: <500 lines (refactor if exceeded) +- **Function parameters**: <5 (use dataclasses for more) +- **Nesting depth**: <4 levels +- **Test files**: No limit (clarity over brevity) + +### Violations Requiring Justification + +| Pattern | When Allowed | Justification Required | +|---------|--------------|------------------------| +| Circular imports | Never | Hard error | +| `typing.Any` | External API boundaries | Document why | +| >80 chars | URLs in markdown | N/A | +| Magic numbers | Test data | Use descriptive vars | +| God classes | Never | Split responsibilities | + +## Governance + +### Constitution Authority +- This constitution supersedes code review preferences +- All PRs must verify compliance via checklist +- Violations require justification in PR description +- Amendments require approval from at least two maintainers + +### Amendment Process +1. Propose change with rationale +2. Discuss impact on existing code +3. Document migration plan if breaking +4. Require approval from at least two maintainers +5. Update constitution and announce + +### Runtime Guidance +For implementation-specific guidance during development, see +`AGENTS.md` which provides: +- Development commands +- Architecture overview +- Testing patterns +- Common tasks + +**Version**: 2.0.0 | **Ratified**: 2025-12-19 | **Last Amended**: +2026-08-12 diff --git a/AGENTS.md b/AGENTS.md index d924eda..221a124 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,214 +1,136 @@ -# AGENTS.md - -This file provides guidance to coding agents when working with code in this repository. - -## What is Lola - -Lola is an AI Skills Package Manager that lets you write AI context/skills once and install them to multiple AI assistants (Claude Code, Cursor, Gemini CLI, OpenCode, etc.). Skills are portable modules with a SKILL.md file that get converted to each assistant's native format. +# Lola — Agent Instructions + +Lola is an AI skills package manager. Write AI context and skills +once, install them everywhere — Claude Code, Cursor, Gemini CLI, +OpenCode, and more. + +## Working in this repo + +- Before making changes: read the topic guide below and follow + the referenced file for your task +- Commit format: Conventional Commits (feat, fix, docs, chore, + test, refactor) +- Line limit: 80 characters — code, markdown, and comments +- Python: `ruff check src tests` and `uv run ty check` must pass +- Go: `golangci-lint run` and `go vet ./...` must pass +- Tests: `pytest` (Python), `go test -race ./...` (Go) +- Coverage: >80% on changed source files +- New CLI commands require e2e BDD tests in `e2e/features/` +- Use `uv` for Python deps, standard Go toolchain for Go code +- Never commit secrets, API keys, or internal hostnames ## Development Commands -Remember to source the virtual environment before running commands: ```bash +# Python source .venv/bin/activate -``` - -```bash -# Install in development mode with dev dependencies uv sync --group dev - -# Run tests pytest # All tests -pytest tests/test_cli_mod.py # Single test file -pytest -k test_add # Tests matching pattern -pytest --cov=src/lola # With coverage - -# Run linting and type checking -ruff check src tests -basedpyright src - -# Run the CLI +pytest tests/test_cli_mod.py # Single file +pytest -k test_add # Pattern match +pytest --cov=src/lola # Coverage +ruff check src tests # Linting +uv run ty check # Type checking (primary) +uv run mypy src # Type checking (secondary) + +# Go +go test -race ./... # All Go tests +golangci-lint run # Go linting +go vet ./... # Go vet + +# E2E +make e2e # BDD tests (behave) +make e2e-wip # @wip tagged only + +# CLI lola --help lola mod ls lola install -a claude-code ``` +## Topic Guide + +| Task | Read this | +|------|-----------| +| Project principles and standards | `.specify/memory/constitution.md` | +| SDD process | `docs/adr/spec-driven-development.md` | +| Contribution workflow | `CONTRIBUTING.md` | +| Architectural decisions | `docs/adr/` | +| Roles and decision-making | `GOVERNANCE.md` | +| Architecture overview | `docs/dev-guide/architecture.md` | +| E2E tests | `docs/dev-guide/design/e2e-bdd.md` | +| CLI reference | `docs/cli-reference/` | +| Proposing a change | `openspec/changes/` or `specs/` | +| PR template | `.github/PULL_REQUEST_TEMPLATE.md` | + ## Architecture ### Core Data Flow -1. **Module Registration**: `lola mod add ` fetches modules (from git, zip, tar, or folder) to `~/.lola/modules/` -2. **Installation**: `lola install ` copies modules to project's `.lola/modules/` and generates assistant-specific files -3. **Updates**: `lola update` regenerates assistant files from source modules -4. **Marketplace Registration**: `lola market add ` fetches marketplace catalogs to `~/.lola/market/` (reference) and `~/.lola/market/cache/` (full catalog) -5. **Module Discovery**: `lola search ` searches both the local module registry and enabled marketplace caches (use `--mod` or `--market` to scope); `lola mod search ` is a deprecated alias for `lola search --mod`; `lola install ` auto-adds from marketplace if not in registry - -### Installation Scopes - -Lola supports two installation scopes: - -- **Project scope** (default): Installs to project directories (`.claude/`, `.cursor/`, etc.) -- **User scope**: Installs to user home directories (`~/.claude/`, `~/.cursor/`, etc.) - -#### Examples - -Install to current project (default): -```bash -lola install my-module -``` - -Install globally for your user: -```bash -lola install my-module --scope user -``` - -Install to specific project: -```bash -lola install my-module /path/to/project -``` +1. **Module Registration**: `lola mod add ` fetches + modules (from git, zip, tar, or folder) to `~/.lola/modules/` +2. **Installation**: `lola install ` copies modules to + project's `.lola/modules/` and generates assistant-specific + files +3. **Updates**: `lola update` regenerates assistant files from + source modules +4. **Marketplace**: `lola market add ` fetches + catalogs; `lola search ` searches across all sources -List all installations: -```bash -lola list -``` +### Key Source Files -Uninstall from user scope only: -```bash -lola uninstall my-module --scope user -``` +- `src/lola/main.py` — CLI entry point +- `src/lola/cli/mod.py` — Module management +- `src/lola/cli/install.py` — Install/uninstall/update +- `src/lola/cli/market.py` — Marketplace management +- `src/lola/models.py` — Data models +- `src/lola/config.py` — Global paths +- `src/lola/targets/` — Assistant definitions +- `src/lola/parsers.py` — Source fetching (strategy pattern) -### Key Source Files +### Target Assistants -- `src/lola/main.py` - CLI entry point, registers all commands -- `src/lola/cli/mod.py` - Module management: add, rm, ls, info, init, update, search -- `src/lola/cli/install.py` - Install/uninstall/update commands (with marketplace integration) -- `src/lola/cli/market.py` - Marketplace management: add, ls, update, set (enable/disable), rm -- `src/lola/models.py` - Data models: Module, Skill, Command, Agent, Installation, InstallationRegistry, Marketplace -- `src/lola/market/manager.py` - MarketplaceRegistry class for marketplace operations -- `src/lola/market/search.py` - Search functionality across marketplace caches -- `src/lola/config.py` - Global paths (LOLA_HOME, MODULES_DIR, INSTALLED_FILE, MARKET_DIR, CACHE_DIR) -- `src/lola/targets.py` - Assistant definitions and file generators (ASSISTANTS dict, generate_* functions) -- `src/lola/parsers.py` - Source fetching (SourceHandler classes) and skill/command parsing -- `src/lola/frontmatter.py` - YAML frontmatter parsing +| Assistant | Skills | Commands | Agents | +|-----------|--------|----------|--------| +| claude-code | `.claude/skills/` | `.claude/commands/` | `.claude/agents/` | +| cursor | `.cursor/skills/` | `.cursor/commands/` | `.cursor/agents/` | +| gemini-cli | `GEMINI.md` | `.gemini/commands/` | N/A | +| opencode | `AGENTS.md` | `.opencode/commands/` | `.opencode/agents/` | +| copilot-cli | `.github/skills/` | `.github/prompts/` | `.github/agents/` | +| copilot-vscode | `.github/skills/` | `.github/prompts/` | `.github/agents/` | ### Module Structure -Modules use auto-discovery. Skills, commands, and agents are discovered from directory structure: - -``` +```text my-module/ - skills/ # Skills directory (required for skills) + skills/ skill-name/ - SKILL.md # Required: skill definition with frontmatter - scripts/ # Optional: supporting files - commands/ # Slash commands (*.md files) - deploy.md # Command entry file - deploy/ # Optional: co-named sidecar directory - step1.md # Supporting procedure files - step2.md - agents/ # Subagents (*.md files) + SKILL.md # Required: skill definition + scripts/ # Optional: supporting files + commands/ + deploy.md # Command entry file + deploy/ # Optional: sidecar directory + step1.md + agents/ + reviewer.md # Subagent definition ``` -Commands can be multi-file: an entry file `commands/.md` plus a co-named -sidecar directory `commands//` holding procedure files. During installation, -both the entry file and its sidecar directory (if present) are copied to the -target assistant's command directory. The sidecar is also removed on uninstall. - -### Marketplace Structure - -Marketplaces are YAML files with module catalogs: - -```yaml -name: Marketplace Name -description: Description of the marketplace -version: 1.0.0 -modules: - - name: module-name - description: Module description - version: 1.0.0 - repository: https://github.com/user/repo.git - tags: [tag1, tag2] -``` - -**Storage locations:** -- **Reference files**: `~/.lola/market/.yml` - Contains source URL and enabled status -- **Cache files**: `~/.lola/market/cache/.yml` - Full marketplace catalog - -**Key operations:** -- `MarketplaceRegistry.add(name, url)` - Downloads and validates marketplace, saves reference and cache -- `MarketplaceRegistry.search_module_all(name)` - Finds module across all enabled marketplaces -- `MarketplaceRegistry.select_marketplace(name, matches)` - Prompts user when module exists in multiple marketplaces -- `MarketplaceRegistry.update(name)` - Re-fetches marketplace from source URL -- Cache recovery: Automatically re-downloads from source URL if cache is missing - -### Target Assistants - -Defined in `targets.py` TARGETS dict. Each assistant has different output formats: +### Testing Patterns -| Assistant | Skills | Commands | Agents | -|-----------|--------|----------|--------| -| claude-code | `.claude/skills//SKILL.md` | `.claude/commands/.md` | `.claude/agents/.md` | -| copilot-cli | `.github/skills//SKILL.md` (project) / `~/.copilot/skills//SKILL.md` (user) | `.github/prompts/.prompt.md` (project) / `~/.copilot/prompts/.prompt.md` (user) | `.github/agents/.agent.md` (project) / `~/.copilot/agents/.agent.md` (user) | -| copilot-vscode | `.github/skills//SKILL.md` (project) / `~/.copilot/skills//SKILL.md` (user) | `.github/prompts/.prompt.md` (project only) | `.github/agents/.agent.md` (project) / `~/.copilot/agents/.agent.md` (user) | -| cursor | `.cursor/skills//SKILL.md` | `.cursor/commands/.md` | `.cursor/agents/.md` | -| gemini-cli | `GEMINI.md` (managed section) | `.gemini/commands/.toml` | N/A | -| openclaw | `~/.openclaw/workspace/skills//SKILL.md` | N/A | N/A | -| opencode | `AGENTS.md` (managed section) | `.opencode/commands/.md` | `.opencode/agents/.md` | - -`copilot-cli` and `copilot-vscode` share the same `.github/` (project) and -`~/.copilot/` (user) files and differ only in MCP handling: `copilot-cli` writes -MCP servers with the `mcpServers` key (`~/.copilot/mcp-config.json` at user -scope), while `copilot-vscode` writes them to `.vscode/mcp.json` using VS Code's -`servers` key. VS Code has no user-scope location for slash commands or MCP, so -those are skipped (with a warning) when installing `copilot-vscode` at user -scope. When no assistant is selected explicitly, `copilot-vscode` is preferred -over `copilot-cli` to avoid writing the same project files twice. - -Agent frontmatter is modified during generation: -- Claude Code: `name` (agent name) and `model: inherit` are added -- Copilot: `generate_agent` is passthrough (content copied as-is); skill frontmatter is rewritten to include `name` and `description` -- Cursor: `name` (agent name) and `model: inherit` are added -- OpenCode: `mode: subagent` is added - -**Backwards compatibility:** Uninstall also checks for old prefixed filenames -(`..md`, `..md`) so installs made before prefix -removal are cleaned up correctly. - -### Source Handlers - -`parsers.py` uses strategy pattern for fetching modules: -- `GitSourceHandler` - git clone with depth 1 -- `ZipSourceHandler` / `ZipUrlSourceHandler` - local/remote zip files -- `TarSourceHandler` / `TarUrlSourceHandler` - local/remote tar archives -- `FolderSourceHandler` - local directory copy +Tests use Click's `CliRunner` for CLI testing. Key fixtures +in `tests/conftest.py`: `mock_lola_home`, `sample_module`, +`registered_module`, `mock_assistant_paths`, +`marketplace_with_modules`. -### Testing Patterns +## Review Council Configuration -Tests use Click's `CliRunner` for CLI testing. Key fixtures in `tests/conftest.py`: -- `mock_lola_home` - patches LOLA_HOME, MODULES_DIR, INSTALLED_FILE to temp directory -- `sample_module` - creates test module with skill, command, and agent -- `registered_module` - sample_module copied into mock_lola_home -- `mock_assistant_paths` - creates mock assistant output directories -- `marketplace_with_modules` - creates marketplace with test modules -- `marketplace_disabled` - creates disabled marketplace for testing - -**Marketplace testing patterns:** -- HTTP requests are mocked using `unittest.mock.patch` with `urllib.request.urlopen` -- Marketplace YAML validation uses actual `Marketplace` model validation -- Tests verify both reference and cache files are created correctly -- Cache recovery is tested with missing cache files -- Multi-marketplace conflicts tested with multiple marketplace fixtures +Constitution: .specify/memory/constitution.md ## Lola Skills -These skills are installed by Lola and provide specialized capabilities. -When a task matches a skill's description, read the skill's SKILL.md file -to learn the detailed instructions and workflows. - -**How to use skills:** -1. Check if your task matches any skill description below -2. Use `read_file` to read the skill's SKILL.md for detailed instructions -3. Follow the instructions in the SKILL.md file +These skills are installed by Lola and provide specialized +capabilities. When a task matches a skill's description, read +the skill's SKILL.md file for detailed instructions. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b083054..2251f37 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -141,6 +141,35 @@ PR updates automatically! **Note**: For large changes, open an issue first to discuss your approach. +## Spec-Driven Development + +LoLa follows spec-driven development (SDD). See the full +decision in `docs/adr/spec-driven-development.md`. + +**When to write an ADR**: Architectural or process changes +require an ADR in `docs/adr/` before implementation. Copy +`docs/adr/template.md` and open a PR. + +**When to write a spec**: Significant features (3+ stories or +cross-module scope) benefit from a proposal in +`openspec/changes/` or `specs/`. No specific format is +mandated — use whatever workflow fits. + +**For small changes**: Bug fixes and minor improvements can go +straight to a PR. Link the relevant issue if one exists. + +**Project standards**: Read `.specify/memory/constitution.md` +for the full set of principles, quality gates, and complexity +budgets that all contributions must follow. + +**PR template**: Every pull request uses +`.github/PULL_REQUEST_TEMPLATE.md`. It includes a +`Spec / ADR Reference` field — fill it in when your PR +implements an architectural decision or spec. + +**AI agents**: Start from `AGENTS.md` — it contains a topic +guide pointing to the right documentation for any task. + ## AI-Assisted Contributions We welcome contributions made with AI coding assistants! As an diff --git a/openspec/config.yaml b/openspec/config.yaml new file mode 100644 index 0000000..63b3b47 --- /dev/null +++ b/openspec/config.yaml @@ -0,0 +1,28 @@ +schema: spec-driven + +context: | + Lola is an AI skills package manager — write AI context/skills + once, install them to Claude Code, Cursor, Gemini CLI, OpenCode, + and more. + + Tech stack: Python 3.13+ (src/lola/) and Go 1.26+ (cmd/, internal/) + Constitution: .specify/memory/constitution.md + ADRs: docs/adr/ (kebab-case filenames, no numbers) + SDD process: docs/adr/spec-driven-development.md + + Key conventions: + - Conventional Commits (feat, fix, docs, chore, test, refactor) + - 80-character line limit (code, markdown, comments) + - Python: ruff + ty check + mypy, uv for deps + - Go: gofmt + golangci-lint, testify + -race + - >80% test coverage on changed source files + - E2E BDD tests required for new CLI commands (e2e/features/) + - Commit subjects: max 50 chars (tpope 50/72 rule) + +rules: + proposal: + - Keep proposals under 500 words + - Always include a "Non-goals" section + - Reference the relevant ADR if one exists + tasks: + - Each task must be independently testable