diff --git a/README.md b/README.md index ec43f1a..5c1664b 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,13 @@ Minimal workspace orchestrator for tmuxp + Claude Code + Neovim. ./cli/devman ``` +## Documentation + +- [Getting started](docs/getting-started.md) +- [Workspace schema](docs/workspace-schema.md) +- [Claude integration](docs/claude-integration.md) +- [Troubleshooting](docs/troubleshooting.md) + ## NixOS + Home Manager (flakes) Use a flake input pinned with `git+` to avoid GitHub API rate limits. @@ -66,9 +73,3 @@ Use `cli/setup_config.py` to validate `.env.example`, `.toml.example`, or ```bash uv run ./cli/setup_config.py validate .env.example ``` - -## Templates - -- `templates/workspace-min`: starter `.devman/` layout for a single repo. -- `templates/workspace-nixos-collection`: example multi-repo NixOS workspace - with tmuxp + mini.sessions defaults. diff --git a/docs/claude-integration.md b/docs/claude-integration.md new file mode 100644 index 0000000..1eb1df2 --- /dev/null +++ b/docs/claude-integration.md @@ -0,0 +1,43 @@ +# Claude Code integration + +## Requirements + +Claude Code integration requires the `claude` CLI to be installed and available +on your `PATH`. You can verify this with: + +```bash +claude --version +``` + +## Workspace configuration + +Configure Claude Code in `.devman/devman.toml`: + +```toml +[claude_code] +interaction = "interaction.md" # optional +emit_project_config = false # optional +``` + +- `interaction` points to the interaction guide used by Claude Code. +- `emit_project_config` controls whether devman writes `.claude/project.json` + and an optional `CLAUDE.md` to the workspace root. + +## What devman writes + +When enabled, devman will: + +- Create `.claude/settings.json` with workspace metadata. +- Optionally emit `.claude/project.json` plus `CLAUDE.md` instructions so Claude + Code can load workspace-specific context. + +## Helpful commands + +```bash +./cli/devman doctor +./cli/devman up +./cli/devman bootstrap +``` + +If the CLI is missing, these commands will surface a message pointing you to +https://claude.ai/code. diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..a03255f --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,67 @@ +# Getting Started + +## Overview + +devman is a minimal workspace orchestrator for tmuxp, Claude Code, and Neovim. +It discovers `.devman/` workspaces, builds an index, and launches tmux sessions +with the right editor context. + +## Prerequisites + +- **Python 3.11+** (use `uv` for environments). +- **tmux** + **tmuxp** for session management. +- **Neovim** (`nvim`) if you want editor/session bootstrapping. +- **Claude Code** (`claude`) is required for Claude integration. + +## Create a workspace + +A workspace is any project root that contains a `.devman/` directory. The +minimum requirement is the directory itself, but a `devman.toml` file provides +metadata and paths. + +Example `.devman/devman.toml`: + +```toml +[workspace] +name = "my-app" + +[tmuxp] +workspace = "workspace.tmuxp.yaml" +session_name = "my-app" + +[claude_code] +interaction = "interaction.md" +emit_project_config = false + +[nvim] +init = "nvim/init.lua" +listen = ".devman/.state/nvim.sock" +sessions_dir = "sessions" +default_session = "home.vim" +``` + +For the full schema, see [`docs/workspace-schema.md`](workspace-schema.md). + +## Build the index + +```bash +./cli/devman index rebuild +``` + +## Launch devman + +```bash +./cli/devman +``` + +## Switch workspaces + +```bash +./cli/devman switch +``` + +## Next steps + +- Review the workspace schema in [`docs/workspace-schema.md`](workspace-schema.md). +- Configure Claude Code in [`docs/claude-integration.md`](claude-integration.md). +- If something fails, check [`docs/troubleshooting.md`](troubleshooting.md). diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..14c5eec --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,35 @@ +# Troubleshooting + +## Verify external tools + +Run the doctor command to check for required dependencies: + +```bash +./cli/devman doctor +``` + +Ensure these tools are installed: + +- `tmux` +- `tmuxp` +- `nvim` (if you use Neovim integration) +- `claude` + +## Workspace not found + +- Confirm the workspace root contains a `.devman/` directory. +- Rebuild the index: + +```bash +./cli/devman index rebuild +``` + +## tmuxp session issues + +- Verify the `workspace.tmuxp.yaml` path in `.devman/devman.toml`. +- Confirm the file exists relative to `.devman/`. + +## Claude Code not detected + +- Ensure the `claude` CLI is on your `PATH`. +- Re-run `./cli/devman doctor` to confirm availability. diff --git a/docs/workspace-schema.md b/docs/workspace-schema.md new file mode 100644 index 0000000..7b262b3 --- /dev/null +++ b/docs/workspace-schema.md @@ -0,0 +1,51 @@ +# Workspace schema + +This document defines the `.devman/devman.toml` schema used by devman. + +## Overview + +- **Required** fields are listed explicitly. +- All paths are relative to the `.devman/` directory unless noted. +- TOML overrides `.env` values if both are present. + +## Schema + +```toml +[workspace] +name = "my-app" # required +tags = ["api", "web"] # optional +group = "client-x" # optional + +[tmuxp] +workspace = "workspace.tmuxp.yaml" # optional +session_name = "my-app" # optional + +[claude_code] +interaction = "interaction.md" # optional +emit_project_config = false # optional + +[nvim] +init = "nvim/init.lua" # optional +listen = ".devman/.state/nvim.sock" # optional (relative to workspace root) +sessions_dir = "sessions" # optional +default_session = "home.vim" # optional +``` + +## Precedence rules + +1. `.devman/devman.toml` is the primary configuration source. +2. `.devman/.env` may supply environment toggles for: + - `DEVMAN_TMUXP_WORKSPACE` + - `DEVMAN_SESSION_NAME` +3. If TOML fields are present they always override `.env` values. + +## Required files + +A workspace is valid if `.devman/` exists. The following files are optional but +recommended for full functionality: + +- `.devman/devman.toml` +- `.devman/interaction.md` +- `.devman/workspace.tmuxp.yaml` +- `.devman/nvim/init.lua` +- `.devman/sessions/home.vim` diff --git a/pyproject.toml b/pyproject.toml index 7ee33fa..fe7ae22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,9 +72,13 @@ warn_unused_configs = true disallow_untyped_defs = true [tool.pytest.ini_options] -testpaths = ["src/tests"] +testpaths = ["tests/unit", "tests/integration"] addopts = "-v --cov=src/devman --cov-report=html" python_files = ["test_*.py", "*_test.py"] +markers = [ + "unit: fast, isolated tests", + "integration: integration or external dependency coverage", +] [tool.coverage.report] exclude_lines = [ diff --git a/src/tests/run_tests.py b/scripts/run_tests.py similarity index 87% rename from src/tests/run_tests.py rename to scripts/run_tests.py index 4c59760..be15e00 100644 --- a/src/tests/run_tests.py +++ b/scripts/run_tests.py @@ -10,7 +10,7 @@ # ] # /// -# tests/run_tests.py +# scripts/run_tests.py """Test runner script for devman library.""" from __future__ import annotations @@ -22,8 +22,9 @@ def main() -> None: """Run pytest with coverage and formatting.""" - test_dir = Path(__file__).parent - src_dir = test_dir.parent / "src" + repo_root = Path(__file__).resolve().parent.parent + test_dir = repo_root / "tests" + src_dir = repo_root / "src" cmd = [ sys.executable, diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..dd449a3 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +uv sync --extra dev +uv run pytest diff --git a/src/tests/conftest.py b/tests/conftest.py similarity index 100% rename from src/tests/conftest.py rename to tests/conftest.py diff --git a/tests/fixtures/.gitkeep b/tests/fixtures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/tests/test_claude_code_integration.py b/tests/integration/test_claude_code_integration.py similarity index 97% rename from src/tests/test_claude_code_integration.py rename to tests/integration/test_claude_code_integration.py index 7cbf759..1e3f215 100644 --- a/src/tests/test_claude_code_integration.py +++ b/tests/integration/test_claude_code_integration.py @@ -9,6 +9,8 @@ from devman.claude_code import check_claude_code, generate_claude_code_settings from devman.models.workspace import WorkspaceConfig +pytestmark = pytest.mark.integration + def test_generate_claude_code_settings(tmp_path: Path) -> None: """Generate settings payload for Claude Code.""" diff --git a/src/tests/test_config.py b/tests/unit/test_config.py similarity index 98% rename from src/tests/test_config.py rename to tests/unit/test_config.py index bc5c73c..5818d47 100644 --- a/src/tests/test_config.py +++ b/tests/unit/test_config.py @@ -1,4 +1,4 @@ -# tests/test_config.py +# tests/unit/test_config.py """Test configuration models.""" from __future__ import annotations @@ -8,6 +8,8 @@ from devman.config import ProjectConfig +pytestmark = pytest.mark.unit + class TestProjectConfig: """Test ProjectConfig model.""" diff --git a/src/tests/test_workspace_config.py b/tests/unit/test_workspace_config.py similarity index 98% rename from src/tests/test_workspace_config.py rename to tests/unit/test_workspace_config.py index a8b26a4..c6ec2c2 100644 --- a/src/tests/test_workspace_config.py +++ b/tests/unit/test_workspace_config.py @@ -4,8 +4,12 @@ from pathlib import Path +import pytest + from devman.models.workspace import WorkspaceConfig +pytestmark = pytest.mark.unit + def test_minimal_workspace_config(tmp_path: Path) -> None: """Ensure minimal .devman directory loads defaults."""