# Auto-Commit Service Automated commit message generation service using local LLM inference. Commits and pushes changes across lilith-platform repos every 15 minutes. ## Features - Generates commit messages using llama-service (local 3B model) - Processes multiple repositories in a cycle - Automatic push with conflict resolution - Claude Code fallback for complex failures - FastAPI endpoints for monitoring and manual triggers - **CLI for multi-daemon management** (`commits` command) ## Installation ```bash # Install the package cd /var/home/lilith/Code/@packages/@ml/auto-commit-service pip install -e . # Or with model-boss integration (recommended) pip install -e ".[model-boss]" # Or with dev dependencies pip install -e ".[dev]" ``` After installation, the `commits` CLI is available system-wide. ### Model Configuration The service requires a language model to generate commit messages. You have three options: 1. **Auto-load via model-boss (recommended)**: ```bash pip install -e ".[model-boss]" # Models will be auto-downloaded and cached # Default: ministral-3b-instruct ``` 2. **Manual model path**: ```bash export LLAMA_SERVICE_MODEL_PATH=/path/to/model.gguf ``` 3. **Disable auto-start** (use external llama-service): ```bash export AUTO_COMMIT_LLAMA_SERVICE_AUTOSTART=false ``` **Important**: If no model is configured, the service will fail to start and report as down. This prevents making commits with placeholder messages. ## CLI Usage The `commits` CLI manages multiple auto-commit daemon instances. ### Quick Start ```bash # Start daemon for current directory cd ~/Code/@packages commits start 5m -R # Every 5 min, recursive discovery # Check status commits status # Current directory commits status --all # All daemons # View report commits report # Comprehensive report commits report -A # All daemons summary # Trigger immediate cycle commits once # Refresh repos and commit commits once -A # All daemons # Stop daemon commits stop ``` ### Commands | Command | Description | |---------|-------------| | `commits start [interval] [OPTIONS]` | Start daemon for current directory | | `commits stop` | Stop daemon for current directory | | `commits status [-A]` | Show daemon status | | `commits once [-A]` | Refresh repos and trigger cycle | | `commits report [-A] [-C N] [--raw]` | Show comprehensive report | | `commits recent [-n N] [--full]` | Show last N commits with relative timestamps | | `commits trigger` | Manually trigger a commit cycle | | `commits enable` | Enable the daemon | | `commits disable` | Disable the daemon | | `commits logs` | Tail daemon logs | | `commits list` | List all running daemons | | `commits install-systemd DIR [OPTIONS]` | Install systemd user service | ### Start Options ```bash commits start [interval] [OPTIONS] Options: -R, --recursive Enable recursive git discovery --depth INT Recursion depth limit -C, --cache-update STR Cache refresh interval (default: 60m) Examples: commits start 5m # Every 5 minutes commits start -R 1h # Every hour, recursive commits start -R --depth 1 5m # Every 5 min, depth 1 ``` ### Report Options ```bash commits report [OPTIONS] Options: -A, --all-daemons Show reports for all daemons -C INT Show last N commits grouped by repo --raw Show raw JSON output Examples: commits report # Summary for current directory commits report -A # Summary for all daemons commits report -C 20 # Last 20 commits ``` ### Recent Commits ```bash commits recent [OPTIONS] Options: -n, --limit INT Number of commits to show (default: 50) -f, --full Fetch actual commit messages from git (slower) --raw Show raw JSON output Examples: commits recent # Last 50 commits with relative timestamps commits recent -n 10 # Last 10 commits commits recent --full # Include actual git commit messages ``` ### Systemd Integration ```bash # Install systemd user service commits install-systemd ~/Code/@packages 5m -R # Service commands systemctl --user status commits-var-home-lilith-Code-@packages systemctl --user stop commits-var-home-lilith-Code-@packages journalctl --user -u commits-var-home-lilith-Code-@packages -f ``` ## Configuration Environment variables (prefix: `AUTO_COMMIT_`): | Variable | Default | Description | |----------|---------|-------------| | `AUTO_COMMIT_LLAMA_SERVICE_URL` | `http://localhost:8000` | llama-service URL | | `AUTO_COMMIT_LLAMA_SERVICE_AUTOSTART` | `true` | Auto-start llama-service if down | | `AUTO_COMMIT_LLAMA_MODEL_ID` | `qwen2.5-1.5b-instruct` | Model ID for model-boss | | `AUTO_COMMIT_USE_MODEL_BOSS` | `true` | Use model-boss for model loading | | `AUTO_COMMIT_CYCLE_INTERVAL_SECONDS` | `900` | Seconds between cycles (15 min) | | `AUTO_COMMIT_ENABLED` | `true` | Enable daemon on startup | | `AUTO_COMMIT_CLAUDE_FALLBACK_ENABLED` | `true` | Enable Claude Code recovery | | `AUTO_COMMIT_HOST` | `0.0.0.0` | Service host | | `AUTO_COMMIT_PORT` | `8200` | Service port | ## Direct Service Usage If you prefer to run the service directly without the CLI: ### Run directly ```bash python -m auto_commit_service ``` ### Run with uvicorn ```bash uvicorn auto_commit_service:create_auto_commit_service --host 0.0.0.0 --port 8200 ``` ## API Endpoints | Endpoint | Method | Description | |----------|--------|-------------| | `/health` | GET | Service health check | | `/status` | GET | Daemon status and last cycle | | `/trigger` | POST | Manually trigger a cycle | | `/enable` | POST | Enable the daemon | | `/disable` | POST | Disable the daemon | | `/repos` | GET | List configured repositories | | `/repos/refresh-and-run` | POST | Refresh repo cache and trigger cycle | | `/report/summary` | GET | Comprehensive daemon report | | `/report/commits` | GET | Commit history | ### Examples ```bash # Check health curl http://localhost:8200/health # Get status curl http://localhost:8200/status # Trigger manual cycle curl -X POST http://localhost:8200/trigger # Disable daemon curl -X POST http://localhost:8200/disable ``` ## Commit Style Generated commits follow the Lilith Platform convention: - `✨` feat: New feature - `🔧` chore: Maintenance, config changes - `♻️` refactor: Code restructuring - `🐛` fix: Bug fix - `📝` docs: Documentation - `✅` test: Tests - `🔥` remove: Removing code/files ## Error Handling 1. **No model configured**: Service fails to start, reported as down (prevents bad commits) 2. **Push rejected**: Attempts `git pull --rebase` and retries 3. **Merge conflict**: Invokes Claude Code to resolve 4. **Hook failure**: Invokes Claude Code to fix 5. **LLM unavailable**: Service auto-starts if configured, otherwise skips cycle 6. **Infrastructure errors** (network, auth): Skips Claude recovery, reports error 7. **Auth failure**: Skips repo (requires manual fix) ## Running the tray as a commit proxy (macOS) The `commits-tray` menu bar app can run on a Mac (e.g. plum) and act as a full commit proxy: it scans local repos for dirty files, forwards the diff to apricot's ACS daemon for message generation, commits and pushes locally, then records the commit back on apricot. No second daemon process is needed on the Mac. ### Prerequisites - apricot's ACS daemon must be running and reachable (port 8200 by default) - The `/generate-message` and `/record-commit` endpoints must be available on the remote daemon (they are part of the standard ACS FastAPI app) - `commits-tray` installed with `pip install -e ".[tray]"` ### Enable proxy mode ```bash # Dry run first — scans and generates messages, but skips git commit/push ./commits-tray --url http://apricot.lan:8200 --cycle 300 --commit-local --dry-run # Enable for real once you've confirmed dry-run output looks correct ./commits-tray --url http://apricot.lan:8200 --cycle 300 --commit-local ``` Flags: | Flag | Default | Description | |------|---------|-------------| | `--commit-local` | off | Enable the local commit proxy loop. Off by default for safety. | | `--dry-run` | off | Scan and generate messages but skip `git commit`/`git push`. | | `--max-diff-bytes` | 131072 | Per-repo diff size cap (bytes) before truncation. | | `--cycle` | 300 | Seconds between commit cycles. | ### Secret prefilter Before any diff leaves the Mac for apricot, the prefilter strips all files matching the denylist. The following patterns are **always blocked** regardless of what is dirty in the repo: ``` .env .env.* *.pem *.key *.p12 *.pfx id_rsa id_rsa.* id_dsa id_dsa.* id_ecdsa id_ecdsa.* id_ed25519 id_ed25519.* *.asc .ssh/** **/.ssh/** **/secrets.yaml **/secrets.yml **/secrets.json secrets.yaml secrets.yml secrets.json .git/config .git/credentials **/.git/config **/.git/credentials **/credentials.json credentials.json **/.netrc .netrc **/*.keystore *.keystore **/*.jks *.jks ``` Exception: `.env.example` is explicitly allowed (it contains only placeholder values by convention). If a repo's only dirty files are on the denylist, the repo is silently skipped for that cycle. ### Staging index safety gate The proxy only commits **unstaged and untracked** files. If a repo has anything in the staging index (i.e. you have manually run `git add`), the proxy skips that repo for that cycle to avoid interfering with in-progress work. ### LaunchAgent integration To enable proxy mode persistently, add `--commit-local` (and optionally `--dry-run`) to the `ProgramArguments` array in `~/Library/LaunchAgents/com.lilith.commits-tray.plist`: ```xml ProgramArguments /path/to/commits-tray --url http://apricot.lan:8200 --cycle 300 --commit-local ``` Reload with `launchctl unload` / `launchctl load` after editing the plist. ## Development ```bash # Install dev dependencies pip install -e ".[dev]" # Run tests pytest # Run tests with coverage pytest --cov=auto_commit_service ``` ## Architecture ``` auto_commit_service/ ├── __init__.py # Package exports ├── __main__.py # Entry point ├── app.py # FastAPI application factory ├── config.py # Settings (AutoCommitSettings) ├── models.py # Pydantic models ├── cli/ # CLI module │ ├── __init__.py # Typer app and commands │ ├── registry.py # Daemon registry management │ └── utils.py # CLI utilities ├── git/ │ ├── operations.py # Async git commands │ ├── repository.py # Repository abstraction │ └── diff_parser.py # Diff summarization ├── llm/ │ ├── client.py # llama-service HTTP client │ └── prompts.py # Commit message prompts ├── scheduler/ │ ├── daemon.py # Main 900s loop │ └── processor.py # Per-repo commit logic └── recovery/ ├── handlers.py # Error type routing └── claude_fallback.py # Claude Code invocation ``` ## Dependencies - `lilith-ml-service-base`: FastAPI patterns, lifespan, health checks - `httpx`: Async HTTP client for llama-service - `pydantic`: Configuration and models - `uvicorn`: ASGI server - `typer`: CLI framework ## Configuration Files | File | Location | Purpose | |------|----------|---------| | Daemon registry | `~/.config/commits/daemons.json` | Tracks running daemon instances | | Logs | `/tmp/auto-commit-{port}.log` | Per-daemon log files |