From 3b58fa4f8b140975c6c01e84ad4004292a88e045 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Sat, 10 Jan 2026 10:37:47 +0100 Subject: [PATCH] refactor: reorganize into monorepo with separate subprojects Structure webber into three independent subprojects: - webber-api/: FastAPI backend server with all agent code - webber-cli/: Standalone CLI client (renamed from cli/ to webber_cli/) - webber-sandbox/: Test project for functional testing Key changes: - Each subproject has its own .venv (Python 3.12+) - Added sandbox.sh for managing test project templates - Created sandbox-templates/ with calculator-cli and empty starter - Updated CI/CD for prefixed tags (api/v*, cli/v*) - Added comprehensive AGENTS.md with operational instructions - Added gitignore filtering to glob and grep tools - Created pyproject.toml for each subproject Co-Authored-By: Claude Opus 4.5 --- .gitea/workflows/{build.yml => build-api.yml} | 30 +- .gitea/workflows/build-cli.yml | 25 ++ .gitignore | 11 + AGENTS.md | 305 +++++++++++----- README.md | 228 ++++-------- chat.sh | 14 - sandbox-templates/calculator-cli/.gitignore | 25 ++ sandbox-templates/calculator-cli/TASKS.md | 64 ++++ .../calculator-cli/pyproject.toml | 17 + .../calculator-cli/requirements.txt | 2 + .../calculator-cli/src/calculator/__init__.py | 3 + .../calculator-cli/src/calculator/main.py | 55 +++ .../src/calculator/operations.py | 43 +++ .../calculator-cli/tests/__init__.py | 1 + .../calculator-cli/tests/test_operations.py | 46 +++ sandbox-templates/empty/.gitignore | 25 ++ sandbox-templates/empty/TASKS.md | 18 + sandbox-templates/empty/pyproject.toml | 17 + sandbox-templates/empty/requirements.txt | 2 + .../empty/src/myproject/__init__.py | 3 + sandbox-templates/empty/tests/__init__.py | 1 + sandbox.sh | 193 ++++++++++ .env.example => webber-api/.env.example | 0 webber-api/AGENTS.md | 128 +++++++ Dockerfile => webber-api/Dockerfile | 0 webber-api/docs/COVERAGE.md | 185 ++++++++++ {docs => webber-api/docs}/architecture.md | 0 .../docs/fastapi-best-practices.md | 0 .../docs}/mistral-instructions.md | 0 pyproject.toml => webber-api/pyproject.toml | 12 +- .../requirements-dev.txt | 0 .../requirements.txt | 1 + {src => webber-api/src}/__init__.py | 0 {src => webber-api/src}/cli/__init__.py | 0 .../src}/cli/commands/__init__.py | 0 {src => webber-api/src}/cli/commands/chat.py | 0 .../src}/cli/commands/explore.py | 0 .../src}/cli/commands/version.py | 0 {src => webber-api/src}/cli/main.py | 0 .../src}/cli/session/__init__.py | 0 .../src}/cli/session/context.py | 0 {src => webber-api/src}/cli/session/loop.py | 0 {src => webber-api/src}/cli/theme.py | 0 {src => webber-api/src}/cli/ui/__init__.py | 0 {src => webber-api/src}/cli/ui/console.py | 0 {src => webber-api/src}/cli/ui/display.py | 0 {src => webber-api/src}/domains/__init__.py | 0 .../src}/domains/agents/README.md | 0 .../src}/domains/agents/__init__.py | 0 .../src}/domains/agents/base.py | 0 .../src}/domains/agents/explore/__init__.py | 0 .../src}/domains/agents/explore/agent.py | 0 .../domains/agents/explore/example-prompt.md | 0 .../src}/domains/agents/explore/prompts.py | 0 .../src}/domains/agents/explore/tools.py | 0 .../agents/main-system-prompt-reference.md | 0 .../src}/domains/agents/plan/__init__.py | 0 .../domains/agents/plan/example-prompt.md | 0 .../src}/domains/agents/router.py | 0 .../src}/domains/agents/schemas.py | 0 .../src}/domains/agents/task/__init__.py | 0 .../domains/agents/task/example-prompt.md | 0 .../src}/domains/agents/utilities/README.md | 0 .../utilities/askuserquestion-prompt.md | 0 .../conversation-summarization-prompt.md | 0 .../utilities/security-review-prompt.md | 0 .../agents/utilities/session-title-prompt.md | 0 .../agents/utilities/todowrite-prompt.md | 0 .../src}/domains/auth/README.md | 0 .../src}/domains/auth/__init__.py | 0 .../src}/domains/health/__init__.py | 0 .../src}/domains/health/controller.py | 0 .../src}/domains/health/router.py | 0 {src => webber-api/src}/domains/router.py | 0 .../src}/domains/tools/README.md | 0 .../src}/domains/tools/__init__.py | 0 {src => webber-api/src}/domains/tools/base.py | 0 .../src}/domains/tools/file/__init__.py | 0 .../src}/domains/tools/file/example-prompt.md | 0 .../src}/domains/tools/file/glob.py | 18 +- .../src}/domains/tools/file/read.py | 0 webber-api/src/domains/tools/gitignore.py | 152 ++++++++ .../src}/domains/tools/search/__init__.py | 0 .../domains/tools/search/example-prompt.md | 0 .../src}/domains/tools/search/grep.py | 18 +- .../src}/domains/tools/shell/__init__.py | 0 .../src}/domains/tools/shell/bash.py | 0 .../domains/tools/shell/example-prompt.md | 0 {src => webber-api/src}/main.py | 0 {src => webber-api/src}/ollama/__init__.py | 0 {src => webber-api/src}/ollama/provider.py | 0 {src => webber-api/src}/shared/__init__.py | 0 {src => webber-api/src}/shared/auth.py | 0 {src => webber-api/src}/shared/base.py | 0 {src => webber-api/src}/shared/config.py | 0 {src => webber-api/src}/shared/context.py | 0 {src => webber-api/src}/shared/exceptions.py | 0 {src => webber-api/src}/shared/logging.py | 0 {tests => webber-api/tests}/__init__.py | 0 {tests => webber-api/tests}/conftest.py | 0 .../tests}/test_agents_api.py | 0 webber-api/tests/test_gitignore.py | 330 ++++++++++++++++++ {tests => webber-api/tests}/test_health.py | 0 {tests => webber-api/tests}/test_tools.py | 0 wakeup.sh => webber-api/wakeup.sh | 0 webber-cli/README.md | 42 +++ webber-cli/pyproject.toml | 40 +++ webber-cli/requirements.txt | 4 + {cli => webber-cli/webber_cli}/__init__.py | 0 {cli => webber-cli/webber_cli}/client.py | 0 {cli => webber-cli/webber_cli}/main.py | 4 +- {cli => webber-cli/webber_cli}/theme.py | 0 webber-sandbox/.gitignore | 25 ++ webber-sandbox/TASKS.md | 64 ++++ webber-sandbox/pyproject.toml | 17 + webber-sandbox/requirements.txt | 2 + webber-sandbox/src/calculator/__init__.py | 3 + webber-sandbox/src/calculator/main.py | 55 +++ webber-sandbox/src/calculator/operations.py | 43 +++ webber-sandbox/tests/__init__.py | 1 + webber-sandbox/tests/test_operations.py | 46 +++ 121 files changed, 2034 insertions(+), 284 deletions(-) rename .gitea/workflows/{build.yml => build-api.yml} (56%) create mode 100644 .gitea/workflows/build-cli.yml delete mode 100755 chat.sh create mode 100644 sandbox-templates/calculator-cli/.gitignore create mode 100644 sandbox-templates/calculator-cli/TASKS.md create mode 100644 sandbox-templates/calculator-cli/pyproject.toml create mode 100644 sandbox-templates/calculator-cli/requirements.txt create mode 100644 sandbox-templates/calculator-cli/src/calculator/__init__.py create mode 100644 sandbox-templates/calculator-cli/src/calculator/main.py create mode 100644 sandbox-templates/calculator-cli/src/calculator/operations.py create mode 100644 sandbox-templates/calculator-cli/tests/__init__.py create mode 100644 sandbox-templates/calculator-cli/tests/test_operations.py create mode 100644 sandbox-templates/empty/.gitignore create mode 100644 sandbox-templates/empty/TASKS.md create mode 100644 sandbox-templates/empty/pyproject.toml create mode 100644 sandbox-templates/empty/requirements.txt create mode 100644 sandbox-templates/empty/src/myproject/__init__.py create mode 100644 sandbox-templates/empty/tests/__init__.py create mode 100755 sandbox.sh rename .env.example => webber-api/.env.example (100%) create mode 100644 webber-api/AGENTS.md rename Dockerfile => webber-api/Dockerfile (100%) create mode 100644 webber-api/docs/COVERAGE.md rename {docs => webber-api/docs}/architecture.md (100%) rename fastapi-best-practices.md => webber-api/docs/fastapi-best-practices.md (100%) rename {docs => webber-api/docs}/mistral-instructions.md (100%) rename pyproject.toml => webber-api/pyproject.toml (88%) rename requirements-dev.txt => webber-api/requirements-dev.txt (100%) rename requirements.txt => webber-api/requirements.txt (89%) rename {src => webber-api/src}/__init__.py (100%) rename {src => webber-api/src}/cli/__init__.py (100%) rename {src => webber-api/src}/cli/commands/__init__.py (100%) rename {src => webber-api/src}/cli/commands/chat.py (100%) rename {src => webber-api/src}/cli/commands/explore.py (100%) rename {src => webber-api/src}/cli/commands/version.py (100%) rename {src => webber-api/src}/cli/main.py (100%) rename {src => webber-api/src}/cli/session/__init__.py (100%) rename {src => webber-api/src}/cli/session/context.py (100%) rename {src => webber-api/src}/cli/session/loop.py (100%) rename {src => webber-api/src}/cli/theme.py (100%) rename {src => webber-api/src}/cli/ui/__init__.py (100%) rename {src => webber-api/src}/cli/ui/console.py (100%) rename {src => webber-api/src}/cli/ui/display.py (100%) rename {src => webber-api/src}/domains/__init__.py (100%) rename {src => webber-api/src}/domains/agents/README.md (100%) rename {src => webber-api/src}/domains/agents/__init__.py (100%) rename {src => webber-api/src}/domains/agents/base.py (100%) rename {src => webber-api/src}/domains/agents/explore/__init__.py (100%) rename {src => webber-api/src}/domains/agents/explore/agent.py (100%) rename {src => webber-api/src}/domains/agents/explore/example-prompt.md (100%) rename {src => webber-api/src}/domains/agents/explore/prompts.py (100%) rename {src => webber-api/src}/domains/agents/explore/tools.py (100%) rename {src => webber-api/src}/domains/agents/main-system-prompt-reference.md (100%) rename {src => webber-api/src}/domains/agents/plan/__init__.py (100%) rename {src => webber-api/src}/domains/agents/plan/example-prompt.md (100%) rename {src => webber-api/src}/domains/agents/router.py (100%) rename {src => webber-api/src}/domains/agents/schemas.py (100%) rename {src => webber-api/src}/domains/agents/task/__init__.py (100%) rename {src => webber-api/src}/domains/agents/task/example-prompt.md (100%) rename {src => webber-api/src}/domains/agents/utilities/README.md (100%) rename {src => webber-api/src}/domains/agents/utilities/askuserquestion-prompt.md (100%) rename {src => webber-api/src}/domains/agents/utilities/conversation-summarization-prompt.md (100%) rename {src => webber-api/src}/domains/agents/utilities/security-review-prompt.md (100%) rename {src => webber-api/src}/domains/agents/utilities/session-title-prompt.md (100%) rename {src => webber-api/src}/domains/agents/utilities/todowrite-prompt.md (100%) rename {src => webber-api/src}/domains/auth/README.md (100%) rename {src => webber-api/src}/domains/auth/__init__.py (100%) rename {src => webber-api/src}/domains/health/__init__.py (100%) rename {src => webber-api/src}/domains/health/controller.py (100%) rename {src => webber-api/src}/domains/health/router.py (100%) rename {src => webber-api/src}/domains/router.py (100%) rename {src => webber-api/src}/domains/tools/README.md (100%) rename {src => webber-api/src}/domains/tools/__init__.py (100%) rename {src => webber-api/src}/domains/tools/base.py (100%) rename {src => webber-api/src}/domains/tools/file/__init__.py (100%) rename {src => webber-api/src}/domains/tools/file/example-prompt.md (100%) rename {src => webber-api/src}/domains/tools/file/glob.py (82%) rename {src => webber-api/src}/domains/tools/file/read.py (100%) create mode 100644 webber-api/src/domains/tools/gitignore.py rename {src => webber-api/src}/domains/tools/search/__init__.py (100%) rename {src => webber-api/src}/domains/tools/search/example-prompt.md (100%) rename {src => webber-api/src}/domains/tools/search/grep.py (88%) rename {src => webber-api/src}/domains/tools/shell/__init__.py (100%) rename {src => webber-api/src}/domains/tools/shell/bash.py (100%) rename {src => webber-api/src}/domains/tools/shell/example-prompt.md (100%) rename {src => webber-api/src}/main.py (100%) rename {src => webber-api/src}/ollama/__init__.py (100%) rename {src => webber-api/src}/ollama/provider.py (100%) rename {src => webber-api/src}/shared/__init__.py (100%) rename {src => webber-api/src}/shared/auth.py (100%) rename {src => webber-api/src}/shared/base.py (100%) rename {src => webber-api/src}/shared/config.py (100%) rename {src => webber-api/src}/shared/context.py (100%) rename {src => webber-api/src}/shared/exceptions.py (100%) rename {src => webber-api/src}/shared/logging.py (100%) rename {tests => webber-api/tests}/__init__.py (100%) rename {tests => webber-api/tests}/conftest.py (100%) rename {tests => webber-api/tests}/test_agents_api.py (100%) create mode 100644 webber-api/tests/test_gitignore.py rename {tests => webber-api/tests}/test_health.py (100%) rename {tests => webber-api/tests}/test_tools.py (100%) rename wakeup.sh => webber-api/wakeup.sh (100%) create mode 100644 webber-cli/README.md create mode 100644 webber-cli/pyproject.toml create mode 100644 webber-cli/requirements.txt rename {cli => webber-cli/webber_cli}/__init__.py (100%) rename {cli => webber-cli/webber_cli}/client.py (100%) rename {cli => webber-cli/webber_cli}/main.py (98%) rename {cli => webber-cli/webber_cli}/theme.py (100%) create mode 100644 webber-sandbox/.gitignore create mode 100644 webber-sandbox/TASKS.md create mode 100644 webber-sandbox/pyproject.toml create mode 100644 webber-sandbox/requirements.txt create mode 100644 webber-sandbox/src/calculator/__init__.py create mode 100644 webber-sandbox/src/calculator/main.py create mode 100644 webber-sandbox/src/calculator/operations.py create mode 100644 webber-sandbox/tests/__init__.py create mode 100644 webber-sandbox/tests/test_operations.py diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build-api.yml similarity index 56% rename from .gitea/workflows/build.yml rename to .gitea/workflows/build-api.yml index 8ae16c5..9906900 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build-api.yml @@ -1,20 +1,31 @@ -name: Build and Push +name: Build and Push API on: push: tags: - - 'v*' + - 'api/v*' + +env: + IMAGE_NAME: git.schweitz.internal/jpmschweitzer/webber-api jobs: release: runs-on: ubuntu-latest steps: + - name: Extract version from tag + id: version + run: | + # Extract version from api/v0.3.0 -> v0.3.0 + VERSION="${{ github.ref_name }}" + VERSION="${VERSION#api/}" + echo "version=$VERSION" >> $GITHUB_OUTPUT + - name: Create Gitea Release run: | curl -sf -X POST \ -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ -H "Content-Type: application/json" \ - -d '{"tag_name": "${{ github.ref_name }}", "name": "Release ${{ github.ref_name }}", "body": "Automated release for ${{ github.ref_name }}"}' \ + -d '{"tag_name": "${{ github.ref_name }}", "name": "API Release ${{ steps.version.outputs.version }}", "body": "Automated release for webber-api ${{ steps.version.outputs.version }}"}' \ "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases" build: @@ -23,6 +34,13 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Extract version from tag + id: version + run: | + VERSION="${{ github.ref_name }}" + VERSION="${VERSION#api/}" + echo "version=$VERSION" >> $GITHUB_OUTPUT + - name: Login to Gitea Registry uses: docker/login-action@v3 with: @@ -33,11 +51,11 @@ jobs: - name: Build and push uses: docker/build-push-action@v5 with: - context: . + context: ./webber-api push: true tags: | - git.schweitz.internal/jpmschweitzer/webber:latest - git.schweitz.internal/jpmschweitzer/webber:${{ github.ref_name }} + ${{ env.IMAGE_NAME }}:latest + ${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }} - name: Trigger Watchtower update if: success() diff --git a/.gitea/workflows/build-cli.yml b/.gitea/workflows/build-cli.yml new file mode 100644 index 0000000..1d8a97f --- /dev/null +++ b/.gitea/workflows/build-cli.yml @@ -0,0 +1,25 @@ +name: Build and Release CLI + +on: + push: + tags: + - 'cli/v*' + +# TODO: Implement CLI installer build +# This workflow will be implemented when CLI distribution is ready. +# Possible targets: +# - PyPI package +# - Standalone binary (PyInstaller) +# - Platform-specific installers + +jobs: + placeholder: + runs-on: ubuntu-latest + steps: + - name: Extract version from tag + id: version + run: | + VERSION="${{ github.ref_name }}" + VERSION="${VERSION#cli/}" + echo "CLI release triggered for version: $VERSION" + echo "TODO: Implement CLI build and distribution" diff --git a/.gitignore b/.gitignore index a1ce5a0..18b29fe 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,14 @@ Thumbs.db # Project specific *.db *.sqlite3 + +# Monorepo - subproject venvs (explicit for clarity) +webber-api/.venv/ +webber-cli/.venv/ +webber-sandbox/.venv/ + +# Sandbox marker file +webber-sandbox/.current_template + +# Ruff cache +.ruff_cache/ diff --git a/AGENTS.md b/AGENTS.md index aa3fa85..0bfb379 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,128 +1,245 @@ - -# AGENTS.md +# Webber Monorepo - Agent Instructions > **Start every session by reading this file.** -> This file outlines the operational protocols, coding standards, and architectural decisions for this FastAPI project. +> This file contains everything you need to work with this codebase efficiently. -## 1. Agent Operational Protocols +## Quick Reference -### ๐Ÿง  Work Patterns (Plan-Act-Reflect) -* **Plan:** Before writing code, briefly outline your plan. Identify which files you will touch and what the side effects might be. -* **Act:** Execute the changes in small, atomic steps. -* **Reflect:** After coding, verify your work. Did you break existing tests? Did you add new tests? - -### ๐Ÿ›ก๏ธ Git Discipline -* **ALWAYS add the relevant tests for the added code** Make sure to keep the test coverage up as we go, and run tests before commiting. -* **NEVER commit to `main` or `master` directly.** Always create a feature branch: `feature/your-feature-name` or `fix/issue-description`. -* **Commit Messages:** Use the [Conventional Commits](https://www.conventionalcommits.org/) format. - * `feat: add user login endpoint` - * `fix: resolve database connection timeout` - * `refactor: split monolith dependency file` -* **Atomic Commits:** Keep commits small. One logical change = one commit. - -### ๐Ÿ“ Changelog Maintenance -* **Update `CHANGELOG.md`** with every user-facing change. -* Format: `## [Unreleased] - YYYY-MM-DD` followed by `### Added`, `### Changed`, or `### Fixed`. - -### ๐Ÿš€ Release Flow -When changes are ready for deployment: - -1. **Ask user if deploy cycle is desired ** - -2. **Update version** in `pyproject.toml`: - - Bug fixes: bump patch version (1.8.3 โ†’ 1.8.4) - - New features: bump minor version (1.8.4 โ†’ 1.9.0) - -3. **Update CHANGELOG.md**: - - Move items from `[Unreleased]` to new version section - - Add release date: `## [1.8.4] - 2025-12-16` - -4. **Commit and tag**: - ```bash - git add -A - git commit -m "fix: description of changes" - git tag v1.8.4 - git push origin main --tags - ``` - -5. **CI/CD triggers automatically**: - - Gitea CI builds Docker image on new version tag (starts with "v") - - Watchtower pulls and deploys to production - - Verify deployment: `curl http://192.168.86.149:8086/health` +| Action | Command | +|--------|---------| +| Start API server | `cd webber-api && ./wakeup.sh` | +| View API logs | `tail -f webber-api/logs/server.log` | +| Run API tests | `cd webber-api && .venv/bin/python -m pytest tests/ -v` | +| Check CLI status | `cd webber-cli && .venv/bin/webber-cli status` | +| Load sandbox | `./sandbox.sh load calculator-cli` | +| Explore sandbox | `cd webber-cli && .venv/bin/webber-cli explore "query" -d ../webber-sandbox` | --- -### ๐Ÿงช Local Development Setup +## Repository Structure -* **Always test locally first** before committing and deploying. The build-deploy loop is slow. -* **Only deploy** when a phase or feature is complete and tested locally -* **Environment**: Copy `.env.example` to `.env` and configure for your local setup +``` +webber/ +โ”œโ”€โ”€ webber-api/ # FastAPI backend server +โ”‚ โ”œโ”€โ”€ src/ # API source code +โ”‚ โ”œโ”€โ”€ tests/ # API tests (pytest) +โ”‚ โ”œโ”€โ”€ docs/ # Architecture docs, COVERAGE.md +โ”‚ โ”œโ”€โ”€ logs/ # Runtime logs (server.log) +โ”‚ โ”œโ”€โ”€ .venv/ # API virtual environment +โ”‚ โ”œโ”€โ”€ wakeup.sh # Dev server startup script +โ”‚ โ””โ”€โ”€ AGENTS.md # API-specific development guide +โ”‚ +โ”œโ”€โ”€ webber-cli/ # CLI client +โ”‚ โ”œโ”€โ”€ webber_cli/ # Python package (underscore!) +โ”‚ โ”œโ”€โ”€ .venv/ # CLI virtual environment +โ”‚ โ””โ”€โ”€ README.md # CLI usage guide +โ”‚ +โ”œโ”€โ”€ webber-sandbox/ # Active test project (contents swappable) +โ”‚ โ”œโ”€โ”€ src/ # Current project source +โ”‚ โ”œโ”€โ”€ tests/ # Current project tests +โ”‚ โ”œโ”€โ”€ .venv/ # Sandbox virtual environment +โ”‚ โ””โ”€โ”€ TASKS.md # Tasks for Webber to complete +โ”‚ +โ”œโ”€โ”€ sandbox-templates/ # Template storage +โ”‚ โ”œโ”€โ”€ calculator-cli/ # Simple CLI with intentional bugs +โ”‚ โ””โ”€โ”€ empty/ # Blank starter project +โ”‚ +โ”œโ”€โ”€ sandbox.sh # Sandbox management script +โ””โ”€โ”€ AGENTS.md # THIS FILE +``` -#### โš ๏ธ CRITICAL: Starting the Local Server +--- -**ALWAYS use `./wakeup.sh` to start the local server. NEVER use raw uvicorn commands.** +## Development Workflow + +### 1. Start the API Server ```bash +cd webber-api ./wakeup.sh ``` -The wakeup script provides: -- **Port conflict detection** - Warns if port 8086 is already in use -- **Virtual environment activation** - Ensures correct Python environment -- **Centralized logging** - All logs written to `logs/server.log` for easy tailing -- **Auto-reload** - Code changes picked up automatically (except requirements.txt changes) -- **Consistent configuration** - Same startup every time +- **Port:** 8095 (dev), 8086 (production Docker) +- **Logs:** `webber-api/logs/server.log` +- **Health check:** `curl http://localhost:8095/health` +- **API docs:** http://localhost:8095/docs + +To stop: `Ctrl+C` or `pkill -f "uvicorn src.main:app"` + +### 2. Run Tests -To monitor logs in another terminal: ```bash -tail -f logs/server.log +# API tests (39 tests) +cd webber-api +.venv/bin/python -m pytest tests/ -v + +# With coverage +.venv/bin/python -m pytest tests/ --cov=src + +# Single test file +.venv/bin/python -m pytest tests/test_tools.py -v ``` -To stop the server: Press `Ctrl+C` +### 3. Use the CLI -To kill a stuck server: ```bash +cd webber-cli + +# Check API connection +.venv/bin/webber-cli status + +# Explore a directory +.venv/bin/webber-cli explore "find all python files" -d ../webber-sandbox + +# Interactive chat mode +.venv/bin/webber-cli chat -d ../webber-sandbox +``` + +**Note:** The API server must be running for CLI commands to work. + +--- + +## Sandbox Management + +The sandbox is a swappable test project for functional testing. + +### Available Templates + +| Template | Description | +|----------|-------------| +| `calculator-cli` | Python CLI with intentional bugs (div-by-zero, missing tests) | +| `empty` | Blank starter project | + +### Commands + +```bash +# List available templates +./sandbox.sh list + +# Load a template (clears sandbox, preserves .venv) +./sandbox.sh load calculator-cli + +# Reset to last loaded template +./sandbox.sh reset + +# Save current sandbox as new template +./sandbox.sh save my-template + +# Check current status +./sandbox.sh status +``` + +### After Loading a Template + +```bash +cd webber-sandbox +source .venv/bin/activate # Create .venv first if missing +pip install -r requirements.txt + +# Read the tasks +cat TASKS.md + +# Run the project's tests +pytest tests/ -v +``` + +--- + +## Testing Webber's Capabilities + +### Scenario: Find bugs in calculator-cli + +```bash +# 1. Load the template +./sandbox.sh load calculator-cli + +# 2. Have Webber explore it +cd webber-cli +.venv/bin/webber-cli explore "find all bugs in the code" -d ../webber-sandbox + +# 3. Check TASKS.md for expected bugs +cat ../webber-sandbox/TASKS.md +``` + +### Known bugs in calculator-cli: +- Division by zero not handled (`operations.py:divide`) +- Invalid operation causes KeyError (`main.py:get_operation`) +- Power function broken for fractional exponents +- Missing tests for divide and power functions + +--- + +## Key Files for Debugging + +| File | Purpose | +|------|---------| +| `webber-api/logs/server.log` | API server logs | +| `webber-api/src/domains/agents/explore/prompts.py` | Explore agent system prompts | +| `webber-api/src/domains/agents/explore/agent.py` | Explore agent implementation | +| `webber-api/src/ollama/provider.py` | Ollama integration (sanitizes content:null) | +| `webber-api/docs/COVERAGE.md` | Feature coverage and known issues | + +--- + +## Versioning & Releases + +Uses prefixed tags: +- `api/v0.3.0` โ†’ Triggers API Docker build +- `cli/v0.1.0` โ†’ Triggers CLI build (future) + +```bash +# API release +cd webber-api +# Update version in pyproject.toml +git add -A && git commit -m "chore: release api v0.3.0" +git tag api/v0.3.0 +git push origin main --tags +``` + +--- + +## Troubleshooting + +### API server won't start +```bash +# Check if port is in use +lsof -i :8095 + +# Kill stuck process pkill -f "uvicorn src.main:app" -# or -kill $(lsof -t -i:8086) ``` -#### Testing - -**Test REST endpoints** against `http://localhost:8086`: +### CLI can't connect ```bash -curl http://localhost:8086/health -curl http://localhost:8086/ -curl http://localhost:8086/docs # Swagger UI +# Check API is running +curl http://localhost:8095/health + +# Check CLI config +echo $WEBBER_API_URL # Should be http://localhost:8095 ``` -**Running tests**: Always use the venv explicitly to avoid environment mismatches: +### Ollama errors ```bash -.venv/bin/python -m pytest tests/ # All tests -.venv/bin/python -m pytest tests/ -v # Verbose output -.venv/bin/python -m pytest tests/ --cov # With coverage +# Check Ollama is running +curl http://192.168.86.149:11434/api/tags + +# Check model is available +curl http://192.168.86.149:11434/api/tags | grep mistral-nemo +``` + +### Tests failing +```bash +# Run with verbose output +cd webber-api +.venv/bin/python -m pytest tests/ -v --tb=short ``` --- -## 1.5 Known Issues & Future Improvements +## Known Limitations -### Explore Agent +1. **Model hallucination** - Mistral Nemo sometimes makes up file contents instead of using tool results +2. **No conversation memory** - CLI chat mode doesn't persist between sessions +3. **No streaming** - Responses appear all at once -- **Gitignore Support**: The filesystem tools (`glob_files`, `grep_content`) currently do NOT honor `.gitignore`. They return results from ignored directories like `.venv/`, `node_modules/`, etc. This should be fixed to filter out gitignored files by default. - -- **Model Hallucination**: Mistral Nemo sometimes hallucinates file contents instead of using actual tool results. Consider using a more capable model (codestral, qwen2.5-coder) or adding response validation. - -- **Ollama Provider**: We use a custom `WebberOllamaProvider` (ported from tatlock) that sanitizes `content: null` to `content: ""` for assistant messages with tool calls. This works around an Ollama API limitation. - ---- - -## 2. FastAPI Architecture & Best Practices -*Reference: [FastAPI Best Practices](https://github.com/zhanymkanov/fastapi-best-practices)* - -### ๐Ÿ“‚ Project Structure (Directory-based, NOT File-type based) -Do **not** group files by type (e.g., one huge `routers` folder). Group by **domain/module** inside a `src/` directory. - -**Correct Structure:** -```text -to be determined \ No newline at end of file +See `webber-api/docs/COVERAGE.md` for full feature coverage status. diff --git a/README.md b/README.md index 9ae7b22..c5ec4c1 100644 --- a/README.md +++ b/README.md @@ -1,171 +1,87 @@ -# Webber +# Webber - Multi-Agent AI Development System -Multi-Agent AI Development System - a FastAPI-based service that orchestrates local LLM agents for code exploration, planning, and task execution. +A Claude Code-inspired development assistant powered by local LLMs via Ollama. -## Overview +## Structure -Webber provides autonomous AI agents similar to Claude Code but running locally with configurable models via Ollama. It's designed for: +This is a monorepo containing three subprojects: -- **Explore Agent** - Fast codebase navigation and code search -- **Plan Agent** - Implementation design and step-by-step planning -- **Task Agent** - Autonomous multi-step code generation and modification +| Directory | Description | +|-----------|-------------| +| `webber-api/` | FastAPI backend server with agent orchestration | +| `webber-cli/` | Command-line client for interacting with the API | +| `webber-sandbox/` | Test project for functional testing | -Built on [PydanticAI](https://ai.pydantic.dev/) for structured LLM interactions. +### Additional Directories + +| Directory | Description | +|-----------|-------------| +| `sandbox-templates/` | Reusable project templates for the sandbox | +| `.gitea/workflows/` | CI/CD workflows for releases | ## Quick Start -### Prerequisites +### 1. Start the API Server + +```bash +cd webber-api +python3.12 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt -r requirements-dev.txt +./wakeup.sh +``` + +### 2. Set Up the CLI + +```bash +cd webber-cli +python3.12 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +pip install -e . + +# Test connection +webber-cli status +``` + +### 3. Load a Sandbox Project + +```bash +# From repo root +./sandbox.sh list +./sandbox.sh load calculator-cli + +cd webber-sandbox +python3.12 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +``` + +### 4. Explore with Webber + +```bash +cd webber-cli +webber-cli explore "find all bugs in the code" -d ../webber-sandbox +``` + +## Versioning + +This project uses prefixed tags for independent release cycles: + +- `api/v0.3.0` - Triggers API Docker build and deployment +- `cli/v0.1.0` - Triggers CLI installer build (future) + +## Requirements - Python 3.12+ -- [Ollama](https://ollama.ai/) with models installed -- (Optional) Tatlock for multi-tenant authentication +- Ollama running with `mistral-nemo:latest` model +- Docker (for production deployment) -### Installation +## Documentation -```bash -# Clone the repository -git clone https://git.schweitz.internal/jpmschweitzer/webber.git -cd webber - -# Create virtual environment -python -m venv .venv -source .venv/bin/activate - -# Install dependencies -pip install -r requirements.txt - -# For development (includes testing and linting tools) -pip install -r requirements-dev.txt -``` - -### Configuration - -```bash -# Copy example config -cp .env.example .env - -# Edit .env with your settings -# At minimum, configure OLLAMA_URL to point to your Ollama instance -``` - -### Running - -```bash -# Development (with auto-reload) -./wakeup.sh - -# Or manually -uvicorn src.main:app --host 0.0.0.0 --port 8086 --reload -``` - -The service will be available at `http://localhost:8086`. API docs at `/docs`. - -## Configuration - -All settings via environment variables or `.env` file: - -| Variable | Default | Description | -|----------|---------|-------------| -| `DEBUG` | `false` | Enable debug mode | -| `LOG_LEVEL` | `INFO` | Logging level | -| `PORT` | `8086` | Server port | -| `OLLAMA_URL` | `http://192.168.86.149:11434` | Ollama API URL | -| `OLLAMA_AGENT_MODEL` | `mistral-nemo-large:latest` | Model for agent reasoning | -| `OLLAMA_EMBED_MODEL` | `nomic-embed-text:latest` | Model for embeddings | -| `TOOL_TIMEOUT_SECONDS` | `120` | Tool execution timeout | -| `SANDBOX_ENABLED` | `true` | Sandbox tool execution | -| `ALLOWED_PATHS` | `[]` | Paths accessible to tools | - -See [.env.example](.env.example) for full configuration options. - -## Development - -### Code Quality - -```bash -# Type checking -mypy src/ - -# Linting -ruff check src/ tests/ - -# Auto-fix lint issues -ruff check src/ tests/ --fix - -# Format code -ruff format src/ tests/ -``` - -### Testing - -```bash -# Run all tests -pytest tests/ -v - -# With coverage -pytest tests/ --cov=src --cov-report=html -``` - -### Security Audit - -```bash -# Check dependencies for CVEs -pip-audit -``` - -## Architecture - -Webber uses a domain-based architecture with clean separation of concerns: - -``` -src/ -โ”œโ”€โ”€ main.py # FastAPI app entry point -โ”œโ”€โ”€ shared/ # Cross-cutting infrastructure -โ”‚ โ”œโ”€โ”€ base.py # BaseController, BaseSchema -โ”‚ โ”œโ”€โ”€ config.py # Settings from pyproject.toml + env -โ”‚ โ”œโ”€โ”€ logging.py # @logged decorator with timing -โ”‚ โ””โ”€โ”€ exceptions.py # Exception hierarchy -โ””โ”€โ”€ domains/ # Feature domains - โ”œโ”€โ”€ health/ # Health check endpoints - โ”œโ”€โ”€ agents/ # Agent orchestration - โ””โ”€โ”€ tools/ # Tool execution (file, shell, search) -``` - -See [docs/architecture.md](docs/architecture.md) for detailed patterns and conventions. - -## API Endpoints - -| Endpoint | Method | Description | -|----------|--------|-------------| -| `/` | GET | Service information | -| `/health` | GET | Health check for monitoring | -| `/docs` | GET | Interactive API documentation | - -## Docker - -```bash -# Build -docker build -t webber . - -# Run -docker run -p 8086:8086 --env-file .env webber -``` - -The container includes a healthcheck that pings `/health` every 30 seconds. - -## Deployment - -Deployed via Gitea Actions CI/CD: - -1. Tag a release (`git tag v0.x.x && git push --tags`) -2. Workflow builds and pushes Docker image -3. Watchtower auto-deploys to production - -Production runs in Portainer `agents` stack on the `docker-dataplane` network. - -## Status - -**Alpha** - Core infrastructure is complete. Agent and tool implementations are in progress. +- `webber-api/AGENTS.md` - API development guidelines +- `webber-api/docs/` - Architecture and coverage docs +- `webber-cli/README.md` - CLI usage guide ## License diff --git a/chat.sh b/chat.sh deleted file mode 100755 index 32e2793..0000000 --- a/chat.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -# Start webber chat session -# Usage: ./chat.sh [directory] - -DIR="${1:-.}" - -cd /mnt/media/Projects/webber -source .venv/bin/activate - -echo "Starting Webber chat..." -echo "Working directory: $(realpath "$DIR")" -echo "" - -webber chat -d "$DIR" diff --git a/sandbox-templates/calculator-cli/.gitignore b/sandbox-templates/calculator-cli/.gitignore new file mode 100644 index 0000000..75beaa3 --- /dev/null +++ b/sandbox-templates/calculator-cli/.gitignore @@ -0,0 +1,25 @@ +# Python +__pycache__/ +*.py[cod] +*.so +.Python +build/ +dist/ +*.egg-info/ + +# Virtual environments +.venv/ +venv/ + +# Testing +.pytest_cache/ +.coverage +htmlcov/ + +# IDE +.idea/ +.vscode/ +*.swp + +# Project +.current_template diff --git a/sandbox-templates/calculator-cli/TASKS.md b/sandbox-templates/calculator-cli/TASKS.md new file mode 100644 index 0000000..180a26c --- /dev/null +++ b/sandbox-templates/calculator-cli/TASKS.md @@ -0,0 +1,64 @@ +# Calculator CLI - Tasks for Webber + +A simple calculator with intentional bugs and missing features for testing Webber's capabilities. + +## Bugs to Fix + +### High Priority +- [ ] **Division by zero** - `operations.py:divide()` crashes when dividing by zero instead of returning an error +- [ ] **Invalid operation name** - `main.py:get_operation()` raises KeyError for unknown operations instead of helpful error message + +### Medium Priority +- [ ] **Power function broken** - `operations.py:power()` doesn't handle negative exponents or fractional exponents correctly +- [ ] **No input validation** - `main.py` doesn't validate that command-line arguments are valid numbers + +## Missing Tests + +- [ ] Add `TestDivide` class with tests for: + - Normal division + - Division by zero (should test error handling once bug is fixed) + - Division with negative numbers + +- [ ] Add `TestPower` class with tests for: + - Positive integer exponents + - Zero exponent (should return 1) + - Negative exponents + +- [ ] Complete existing test classes: + - `test_add_zero` + - `test_add_floats` + - `test_subtract_negative` + - `test_multiply_by_zero` + +## Features to Add + +- [ ] **Expose power operation** - Add 'pow' to the operations dictionary in `main.py` +- [ ] **Add modulo operation** - Implement `modulo(a, b)` in operations.py +- [ ] **Add --verbose flag** - Show step-by-step calculation +- [ ] **Add history command** - Track and display recent calculations +- [ ] **Add REPL mode** - Interactive calculator loop + +## Code Quality + +- [ ] Add type hints to all functions +- [ ] Add docstrings following Google style +- [ ] Fix any linting errors (run `ruff check src/`) + +## How to Run + +```bash +# Setup +python3.12 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt + +# Run calculator +python -m calculator.main 10 5 add +python -m calculator.main 10 5 div + +# Run tests +pytest tests/ -v + +# See failing tests (division by zero) +python -m calculator.main 10 0 div +``` diff --git a/sandbox-templates/calculator-cli/pyproject.toml b/sandbox-templates/calculator-cli/pyproject.toml new file mode 100644 index 0000000..72dfead --- /dev/null +++ b/sandbox-templates/calculator-cli/pyproject.toml @@ -0,0 +1,17 @@ +[project] +name = "calculator" +version = "0.1.0" +description = "A simple calculator CLI with some bugs" +requires-python = ">=3.12" + +[build-system] +requires = ["setuptools>=75.0"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["src"] +addopts = "-v" diff --git a/sandbox-templates/calculator-cli/requirements.txt b/sandbox-templates/calculator-cli/requirements.txt new file mode 100644 index 0000000..2310ed2 --- /dev/null +++ b/sandbox-templates/calculator-cli/requirements.txt @@ -0,0 +1,2 @@ +# Calculator CLI dependencies +pytest>=8.0.0 diff --git a/sandbox-templates/calculator-cli/src/calculator/__init__.py b/sandbox-templates/calculator-cli/src/calculator/__init__.py new file mode 100644 index 0000000..393ee82 --- /dev/null +++ b/sandbox-templates/calculator-cli/src/calculator/__init__.py @@ -0,0 +1,3 @@ +"""Calculator CLI - A simple calculator with some bugs for testing.""" + +__version__ = "0.1.0" diff --git a/sandbox-templates/calculator-cli/src/calculator/main.py b/sandbox-templates/calculator-cli/src/calculator/main.py new file mode 100644 index 0000000..a518929 --- /dev/null +++ b/sandbox-templates/calculator-cli/src/calculator/main.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +""" +Calculator CLI - A simple command-line calculator. + +NOTE: This file contains intentional bugs for testing purposes. + +Usage: + python -m calculator.main 10 5 add + python -m calculator.main 10 5 sub + python -m calculator.main 10 5 mul + python -m calculator.main 10 5 div +""" +import sys + +from calculator.operations import add, subtract, multiply, divide + + +def get_operation(op_name: str): + """ + Get the operation function by name. + + BUG: No validation - invalid operation names cause KeyError! + """ + operations = { + "add": add, + "sub": subtract, + "mul": multiply, + "div": divide, + # BUG: 'power' is implemented in operations.py but not exposed here + } + # BUG: Should handle KeyError gracefully + return operations[op_name] + + +def main(): + """Main entry point.""" + if len(sys.argv) != 4: + print("Usage: python -m calculator.main ") + print("Operations: add, sub, mul, div") + sys.exit(1) + + # BUG: No validation that a and b are valid numbers + a = float(sys.argv[1]) + b = float(sys.argv[2]) + op_name = sys.argv[3] + + # BUG: This will crash with KeyError for invalid operation + operation = get_operation(op_name) + result = operation(a, b) + + print(f"Result: {result}") + + +if __name__ == "__main__": + main() diff --git a/sandbox-templates/calculator-cli/src/calculator/operations.py b/sandbox-templates/calculator-cli/src/calculator/operations.py new file mode 100644 index 0000000..c5d744b --- /dev/null +++ b/sandbox-templates/calculator-cli/src/calculator/operations.py @@ -0,0 +1,43 @@ +""" +Math operations for the calculator. + +NOTE: This file contains intentional bugs for testing purposes. +""" + + +def add(a: float, b: float) -> float: + """Add two numbers.""" + return a + b + + +def subtract(a: float, b: float) -> float: + """Subtract b from a.""" + return a - b + + +def multiply(a: float, b: float) -> float: + """Multiply two numbers.""" + return a * b + + +def divide(a: float, b: float) -> float: + """ + Divide a by b. + + BUG: Does not handle division by zero! + """ + # BUG: No check for b == 0 + return a / b + + +def power(a: float, b: float) -> float: + """ + Raise a to the power of b. + + BUG: Negative exponents not handled correctly for some cases. + """ + # BUG: This naive implementation has issues with negative bases and fractional exponents + result = 1 + for _ in range(int(b)): + result *= a + return result diff --git a/sandbox-templates/calculator-cli/tests/__init__.py b/sandbox-templates/calculator-cli/tests/__init__.py new file mode 100644 index 0000000..c645ea4 --- /dev/null +++ b/sandbox-templates/calculator-cli/tests/__init__.py @@ -0,0 +1 @@ +"""Calculator tests.""" diff --git a/sandbox-templates/calculator-cli/tests/test_operations.py b/sandbox-templates/calculator-cli/tests/test_operations.py new file mode 100644 index 0000000..31ab15a --- /dev/null +++ b/sandbox-templates/calculator-cli/tests/test_operations.py @@ -0,0 +1,46 @@ +""" +Tests for calculator operations. + +NOTE: Test coverage is intentionally incomplete for testing purposes. +""" +import pytest + +from calculator.operations import add, subtract, multiply + + +class TestAdd: + """Tests for add operation.""" + + def test_add_positive_numbers(self): + assert add(2, 3) == 5 + + def test_add_negative_numbers(self): + assert add(-2, -3) == -5 + + # MISSING: test_add_zero, test_add_floats + + +class TestSubtract: + """Tests for subtract operation.""" + + def test_subtract_positive(self): + assert subtract(5, 3) == 2 + + # MISSING: test_subtract_negative, test_subtract_resulting_negative + + +class TestMultiply: + """Tests for multiply operation.""" + + def test_multiply_positive(self): + assert multiply(3, 4) == 12 + + # MISSING: test_multiply_by_zero, test_multiply_negative + + +# MISSING: TestDivide class entirely! +# - test_divide_positive +# - test_divide_by_zero (should test error handling) +# - test_divide_negative + +# MISSING: TestPower class entirely! diff --git a/sandbox-templates/empty/.gitignore b/sandbox-templates/empty/.gitignore new file mode 100644 index 0000000..75beaa3 --- /dev/null +++ b/sandbox-templates/empty/.gitignore @@ -0,0 +1,25 @@ +# Python +__pycache__/ +*.py[cod] +*.so +.Python +build/ +dist/ +*.egg-info/ + +# Virtual environments +.venv/ +venv/ + +# Testing +.pytest_cache/ +.coverage +htmlcov/ + +# IDE +.idea/ +.vscode/ +*.swp + +# Project +.current_template diff --git a/sandbox-templates/empty/TASKS.md b/sandbox-templates/empty/TASKS.md new file mode 100644 index 0000000..fc58b72 --- /dev/null +++ b/sandbox-templates/empty/TASKS.md @@ -0,0 +1,18 @@ +# My Project - Tasks + +A blank starter template. Define your own tasks here. + +## Setup + +```bash +python3.12 -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +``` + +## Tasks + +- [ ] Define your project goals +- [ ] Add source files to `src/myproject/` +- [ ] Add tests to `tests/` +- [ ] Update `requirements.txt` with dependencies diff --git a/sandbox-templates/empty/pyproject.toml b/sandbox-templates/empty/pyproject.toml new file mode 100644 index 0000000..cdad2b5 --- /dev/null +++ b/sandbox-templates/empty/pyproject.toml @@ -0,0 +1,17 @@ +[project] +name = "myproject" +version = "0.1.0" +description = "A blank starter project" +requires-python = ">=3.12" + +[build-system] +requires = ["setuptools>=75.0"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["src"] +addopts = "-v" diff --git a/sandbox-templates/empty/requirements.txt b/sandbox-templates/empty/requirements.txt new file mode 100644 index 0000000..4e45d2f --- /dev/null +++ b/sandbox-templates/empty/requirements.txt @@ -0,0 +1,2 @@ +# Add your dependencies here +pytest>=8.0.0 diff --git a/sandbox-templates/empty/src/myproject/__init__.py b/sandbox-templates/empty/src/myproject/__init__.py new file mode 100644 index 0000000..f44a649 --- /dev/null +++ b/sandbox-templates/empty/src/myproject/__init__.py @@ -0,0 +1,3 @@ +"""My Project - A blank starter template.""" + +__version__ = "0.1.0" diff --git a/sandbox-templates/empty/tests/__init__.py b/sandbox-templates/empty/tests/__init__.py new file mode 100644 index 0000000..251790d --- /dev/null +++ b/sandbox-templates/empty/tests/__init__.py @@ -0,0 +1 @@ +"""Tests for myproject.""" diff --git a/sandbox.sh b/sandbox.sh new file mode 100755 index 0000000..398cf89 --- /dev/null +++ b/sandbox.sh @@ -0,0 +1,193 @@ +#!/bin/bash +# Sandbox management script for Webber testing +# +# Usage: +# ./sandbox.sh list - List available templates +# ./sandbox.sh load