deploy(deploy): 🚀 Update deployment scripts, installation/upgrade processes, and documentation for improved workflow consistency

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
autocommit 2026-04-17 21:20:13 -07:00
parent 3cff601331
commit 809681f042
8 changed files with 948 additions and 5 deletions

View file

@ -0,0 +1,48 @@
# =============================================================================
# Auto-Deploy Workflow
# =============================================================================
# Deploys auto-commit-service to the local host after successful publish.
# Triggers on push to main/master and updates the running systemd service.
# =============================================================================
name: Deploy to Host
on:
push:
branches: [main, master]
paths:
- 'pyproject.toml'
- 'src/**'
workflow_dispatch:
env:
DEPLOY_HOST: localhost
SERVICE_PATH: /var/home/lilith/Code/@applications/@ml/auto-commit-service
jobs:
deploy:
name: Deploy to Host
runs-on: ubuntu-latest
needs: []
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy via SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
port: ${{ secrets.DEPLOY_PORT || 22 }}
script: |
set -e
echo "==> Pulling latest changes..."
cd ${{ env.SERVICE_PATH }}
git pull origin main
echo "==> Running upgrade script..."
./upgrade
echo "==> Deployment complete!"
systemctl --user status commits.service --no-pager || true

115
CLAUDE.md Normal file
View file

@ -0,0 +1,115 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
```bash
# Install
uv pip install -e . # basic
uv pip install -e ".[dev]" # with test deps
# Tests
pytest # unit/smoke tests (GPU tests excluded by default)
pytest tests/test_daemon.py -v # single file
pytest tests/test_daemon.py::TestDaemon::test_start -v # single test
pytest -m gpu -v # GPU integration tests (needs model-boss coordinator running)
pytest --cov=auto_commit_service # with coverage
# Lint/type check
ruff check src/ tests/
ruff format src/ tests/
mypy src/
# Run service
python -m auto_commit_service # direct entry
commits start 5m -R # CLI: daemon with 5m cycle, recursive discovery
commits status --all # check all running daemons
# Systemd
systemctl --user restart auto-commit-applications.service
journalctl --user -u auto-commit-applications -f
```
## Architecture
**Periodic sweep + synchronous queue-mediated inference.** The daemon loops every `cycle_interval_seconds` (default 180s), processes repos sequentially, and each LLM call blocks on model-boss coordinator's queue. The `commits-tray` macOS app can also act as a remote commit agent: with `--commit-local`, it scans repos on the local Mac, forwards diffs to apricot's `/generate-message` endpoint for LLM inference, commits and pushes locally, and reports back via `/record-commit` — no second daemon needed on the Mac.
### Execution flow
```
CommitDaemon.start() — main loop
for each dirty repo:
PipelineCommitProcessor.commit_repo()
→ Pipeline orchestrator (11 stages):
PreFilter → Discover → Retrieve(RAG) → Group → Analyze(14B) → Format(3B)
→ Commit → Push → VersionDetect → PublishVerify → Recover
→ Each LLM stage calls MultiModelLlamaClient._chat()
→ InferenceClient.chat() → POST coordinator:8210/v1/chat/completions
→ model-boss coordinator queues and executes on GPU
sleep(cycle_interval_seconds)
```
### Two-model LLM pipeline
All inference routes through **model-boss coordinator** (port 8210). No direct model loading.
- **Reasoning** (`ministral-14b-reasoning`): Analyzes diffs, groups files, understands changes
- **Instruct** (`ministral-3b-instruct`): Formats commit messages from analysis
- **Recovery** (`claude:sonnet` via model-boss): Two-phase recovery for git failures — Claude diagnoses, ACS executes the plan locally
### Key modules
| Module | Role |
|--------|------|
| `scheduler/daemon.py` | Main loop, cycle orchestration, repo discovery |
| `scheduler/pipeline_processor.py` | Per-repo processing, monorepo submodule handling |
| `pipeline/orchestrator.py` | Creates the 11-stage pipeline chain |
| `pipeline/stages/` | Individual pipeline stages |
| `pipeline/init.py` | Global ML provider initialization (must call before pipeline) |
| `llm/multi_model_client.py` | Routes inference to model-boss via InferenceClient |
| `recovery/handlers.py` | Error classification → recovery strategy routing |
| `recovery/claude_fallback.py` | Two-phase Claude recovery (diagnose via model-boss, execute locally) |
| `database/` | Async SQLite (aiosqlite) for commit/cycle/error history |
| `cli/` | Typer CLI (`commits` command) for multi-daemon management |
| `app.py` | FastAPI factory with 20+ monitoring/control endpoints |
| `config.py` | All settings with `AUTO_COMMIT_` env prefix |
### External dependencies
- **model-boss coordinator** (port 8210): GPU model management, inference queue, VRAM scheduling
- **rag-retrieval** (optional): Contextual retrieval for commit analysis
- **git**: All operations via `asyncio.create_subprocess_exec` (no shell)
## Configuration
All settings via env vars prefixed `AUTO_COMMIT_` or `~/.config/commits/startup-config.json`.
Key settings: `REASONING_MODEL_ID`, `INSTRUCT_MODEL_ID`, `CYCLE_INTERVAL_SECONDS`, `CLAUDE_FALLBACK_ENABLED`, `CLAUDE_RECOVERY_MODEL`.
Per-directory git identity and push behavior via `directory_overrides` in config.
## Testing
- `asyncio_mode = "auto"` — all async tests run automatically
- GPU tests require model-boss coordinator running, marked `@pytest.mark.gpu`
- Fixtures: `temp_git_repo` (creates temp git repo), `mock_settings` (unit), `gpu_settings` (integration)
- ruff: line-length 100, select E,F,I,N,W,UP,B,C4,SIM,RUF,PTH,ERA
- mypy: strict mode, Python 3.11+
## Data paths
| Path | Contents |
|------|----------|
| `~/.cache/commits/auto_commit.db` | SQLite: commits, cycles, errors, repo status |
| `~/.cache/commits/activity.jsonl` | Activity log (JSON Lines) |
| `~/.cache/commits/auto-commit.log` | Rotated log file |
| `~/.config/commits/startup-config.json` | Daemon registry config |
| `~/.config/commits/daemons.json` | Running daemon instances |
## Important notes
- **Never commit from this repo** — ACS itself handles all commits across the workspace
- Pipeline stages access ML providers via globals initialized by `init_ml_providers()` — must be called before pipeline execution
- ACS uses `default_priority="batch"` (lowest) in model-boss queue, so interactive requests preempt it
- Recovery commands are validated against an allowlist (no `--force`, `--hard`, `--no-verify`)

102
README.md
View file

@ -86,6 +86,7 @@ commits stop
| `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 |
@ -125,6 +126,22 @@ Examples:
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
@ -221,6 +238,91 @@ Generated commits follow the Lilith Platform convention:
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.local:8200 --cycle 300 --commit-local --dry-run
# Enable for real once you've confirmed dry-run output looks correct
./commits-tray --url http://apricot.local: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
<key>ProgramArguments</key>
<array>
<string>/path/to/commits-tray</string>
<string>--url</string>
<string>http://apricot.local:8200</string>
<string>--cycle</string>
<string>300</string>
<string>--commit-local</string>
</array>
```
Reload with `launchctl unload` / `launchctl load` after editing the plist.
## Development
```bash

25
app.manifest.yaml Normal file
View file

@ -0,0 +1,25 @@
name: auto-commit-service
description: Automated commit message generation using local LLM inference
type: daemon-service
category: services
platforms:
apricot:
os: linux
host: apricot.local
environment: production
services:
commits:
type: systemd-user
systemdUnit: commits
port: "8200"
description: Auto-commit daemon for ~/Code
enabled: true
install:
path: ~/Code/@applications/@ml/auto-commit-service
script: ./install
status:
command: "systemctl --user is-active commits"
type: systemd
logs:
command: "journalctl --user -u commits -n 100"

View file

@ -3,10 +3,12 @@
Manages a lightweight commit agent that discovers local repos, asks the
remote ACS daemon for LLM-generated commit messages, and commits+pushes.
Runs without the full auto_commit_service package — only needs httpx + rumps.
Usage:
./commits-tray --url http://apricot.local:8200
./commits-tray --url http://apricot.local:8200 --repos ~/Code --cycle 300
./commits-tray --url http://apricot.local:8200 --cycle 300
./commits-tray --url http://apricot.local:8200 --commit-local --dry-run
./commits-tray --url http://apricot.local:8200 --commit-local # for real
"""
import argparse
@ -14,12 +16,11 @@ import os
import sys
from pathlib import Path
# Add the tray module directory so we can import directly
_script_dir = os.path.dirname(os.path.abspath(__file__))
_tray_dir = os.path.join(_script_dir, "src", "auto_commit_service", "tray")
sys.path.insert(0, _tray_dir)
from app import run_tray # noqa: E402
def main():
parser = argparse.ArgumentParser(description="ACS menu bar app + local commit agent")
@ -40,10 +41,38 @@ def main():
default=300,
help="Seconds between commit cycles (default: 300)",
)
parser.add_argument(
"--commit-local",
action="store_true",
default=False,
help="Enable local commit loop on this host (proxy mode). Default OFF.",
)
parser.add_argument(
"--dry-run",
action="store_true",
default=False,
help="Scan and generate messages but skip git commit/push. Useful for validation.",
)
parser.add_argument(
"--max-diff-bytes",
type=int,
default=131072,
help="Per-repo diff size cap in bytes before truncation (default: 131072)",
)
args = parser.parse_args()
# Import after argparse so --help exits cleanly before rumps is required
from app import run_tray # noqa: E402
repos_paths = [Path(p).expanduser() for p in args.repos] if args.repos else None
run_tray(daemon_url=args.url, repos_paths=repos_paths, cycle_seconds=args.cycle)
run_tray(
daemon_url=args.url,
repos_paths=repos_paths,
commit_local=args.commit_local,
dry_run=args.dry_run,
max_diff_bytes=args.max_diff_bytes,
cycle_seconds=args.cycle,
)
if __name__ == "__main__":

0
commits.db Normal file
View file

457
install Executable file
View file

@ -0,0 +1,457 @@
#!/usr/bin/env bash
# =============================================================================
# Install auto-commit-service with full ML pipeline
# =============================================================================
# This script:
# 1. Installs auto-commit-service Python package
# 2. Sets up model-boss-coordinator + multi-model llama-http services
# 3. Starts RAG retrieval service
# 4. Creates config files and systemd services
# 5. Enables lingering for persistent services
#
# Dependencies:
# - Redis (for model-boss coordination)
# - Redis Stack (for RAG vector search) - port 6384
# - GPU with CUDA (for local inference)
#
# Services started:
# - model-boss-coordinator (port 8210) - GPU/VRAM lease management
# - llama-http-3b (port 10010) - ministral-3b-instruct (formatting)
# - llama-http-14b (port 10020) - ministral-14b-reasoning (analysis)
# - rag-retrieval (port 8111) - context retrieval
# - commits-packages (port 8200) - auto-commit daemon for @packages
# - commits-applications (port 8201) - auto-commit daemon for @applications
# - commits-projects (port 8202) - auto-commit daemon for @projects
# =============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# Paths
MODEL_BOSS_ROOT="/var/home/lilith/Code/@applications/@model-boss"
LLAMA_HTTP_PATH="$MODEL_BOSS_ROOT/services/llama-http/service"
COORDINATOR_PATH="$MODEL_BOSS_ROOT/services/coordinator/service"
RAG_PATH="/var/home/lilith/Code/@applications/@ml/rag-retrieval"
echo "==> Installing auto-commit-service..."
# =============================================================================
# Step 1: Check prerequisites
# =============================================================================
echo ""
echo "==> Checking prerequisites..."
# Check Redis
if ! redis-cli ping >/dev/null 2>&1; then
echo " ERROR: Redis not running on localhost:6379"
echo " Run: brew services start redis"
exit 1
fi
echo " ✓ Redis available"
# Check Redis Stack (for RAG)
if ! redis-cli -p 6384 MODULE LIST 2>/dev/null | grep -q "search"; then
echo " WARNING: Redis Stack not found on port 6384"
echo " RAG will not work without Redis Stack"
echo " Looking for existing Redis Stack containers..."
if podman ps 2>/dev/null | grep -q "redis-stack"; then
echo " ✓ Redis Stack container found"
else
echo " Consider starting: podman run -d --name rag-redis -p 6384:6379 redis/redis-stack:latest"
fi
else
echo " ✓ Redis Stack available on port 6384"
fi
# Check GPU
if ! command -v nvidia-smi >/dev/null 2>&1; then
echo " WARNING: nvidia-smi not found - GPU inference may not work"
else
GPU_COUNT=$(nvidia-smi -L 2>/dev/null | wc -l)
echo " ✓ $GPU_COUNT GPU(s) detected"
fi
# =============================================================================
# Step 2: Install Python packages
# =============================================================================
echo ""
echo "==> Installing Python packages..."
# Auto-commit service
if [[ ! -d "$SCRIPT_DIR/.venv" ]]; then
echo " Creating auto-commit-service venv..."
python -m venv "$SCRIPT_DIR/.venv"
fi
echo " Installing auto-commit-service..."
"$SCRIPT_DIR/.venv/bin/pip" install -e "$SCRIPT_DIR" --quiet
# Model-boss coordinator
if [[ -d "$COORDINATOR_PATH" ]]; then
if [[ ! -d "$COORDINATOR_PATH/.venv" ]]; then
echo " Creating model-boss-coordinator venv..."
python -m venv "$COORDINATOR_PATH/.venv"
fi
echo " Installing model-boss-coordinator..."
"$COORDINATOR_PATH/.venv/bin/pip" install -e "$COORDINATOR_PATH" --quiet
fi
# Llama HTTP service
if [[ -d "$LLAMA_HTTP_PATH" ]]; then
if [[ ! -d "$LLAMA_HTTP_PATH/.venv" ]]; then
echo " Creating llama-http venv..."
python -m venv "$LLAMA_HTTP_PATH/.venv"
fi
echo " Installing llama-http..."
"$LLAMA_HTTP_PATH/.venv/bin/pip" install -e "$LLAMA_HTTP_PATH" --quiet
fi
# RAG retrieval service
if [[ -d "$RAG_PATH" ]]; then
if [[ ! -d "$RAG_PATH/.venv" ]]; then
echo " Creating rag-retrieval venv..."
python -m venv "$RAG_PATH/.venv"
fi
echo " Installing rag-retrieval..."
"$RAG_PATH/.venv/bin/pip" install -e "$RAG_PATH" --quiet
fi
echo " ✓ Python packages installed"
# =============================================================================
# Step 3: Create config files
# =============================================================================
echo ""
echo "==> Creating config files..."
mkdir -p ~/.config/commits
cat > ~/.config/commits/startup-config.json << 'EOF'
{
"daemons": [
{
"id": "packages",
"directory": "/var/home/lilith/Code/@packages",
"port": 8200,
"interval_seconds": 300,
"recursive": true,
"recursive_depth": 4,
"cache_update_minutes": 60,
"ignore_repos": [
".archive",
"_archive",
".deprecated"
],
"exclude_patterns": [
"node_modules",
"pyvenv",
".venv",
"venv",
"dist",
"build",
"__pycache__"
]
},
{
"id": "applications",
"directory": "/var/home/lilith/Code/@applications",
"port": 8201,
"interval_seconds": 300,
"recursive": true,
"recursive_depth": 4,
"cache_update_minutes": 60,
"ignore_repos": [
".archive",
"_archive",
"egirl-platform",
".deprecated"
],
"exclude_patterns": [
"node_modules",
"pyvenv",
".venv",
"venv",
"dist",
"build",
"__pycache__"
]
},
{
"id": "projects",
"directory": "/var/home/lilith/Code/@projects",
"port": 8202,
"interval_seconds": 300,
"recursive": true,
"recursive_depth": 4,
"cache_update_minutes": 60,
"ignore_repos": [
".archive",
"_archive",
".deprecated"
],
"exclude_patterns": [
"node_modules",
"pyvenv",
".venv",
"venv",
"dist",
"build",
"__pycache__"
]
}
]
}
EOF
echo " ✓ startup-config.json created (3 daemons: packages, applications, projects)"
# =============================================================================
# Step 4: Create systemd user services
# =============================================================================
echo ""
echo "==> Setting up systemd user services..."
mkdir -p ~/.config/systemd/user
# Clean up old services
echo " Cleaning up old services..."
for svc in llama-http.service commits-*.service auto-commit-*.service; do
systemctl --user stop "$svc" 2>/dev/null || true
systemctl --user disable "$svc" 2>/dev/null || true
done
# Model Boss Coordinator
cat > ~/.config/systemd/user/model-boss-coordinator.service << EOF
[Unit]
Description=Model Boss Coordinator (GPU/VRAM lease management)
After=network.target
Wants=homebrew.redis.service
[Service]
Type=simple
WorkingDirectory=$COORDINATOR_PATH
ExecStart=$COORDINATOR_PATH/.venv/bin/python -m model_boss_coordinator
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
Environment="PATH=/home/linuxbrew/.linuxbrew/bin:/var/home/lilith/.local/bin:/usr/local/bin:/usr/bin:/bin"
Environment="HOME=/var/home/lilith"
Environment="MODEL_BOSS_PORT=8210"
Environment="MODEL_BOSS_HOST=0.0.0.0"
[Install]
WantedBy=default.target
EOF
# Llama HTTP 3B (instruct/formatting)
cat > ~/.config/systemd/user/llama-http-3b.service << EOF
[Unit]
Description=Llama HTTP Service - 3B (ministral-3b-instruct)
After=network.target model-boss-coordinator.service
Wants=model-boss-coordinator.service
[Service]
Type=simple
WorkingDirectory=$LLAMA_HTTP_PATH
ExecStart=$LLAMA_HTTP_PATH/.venv/bin/python -m llama_http
Restart=on-failure
RestartSec=30
StandardOutput=journal
StandardError=journal
Environment="PATH=/home/linuxbrew/.linuxbrew/bin:/var/home/lilith/.local/bin:/usr/local/bin:/usr/bin:/bin"
Environment="HOME=/var/home/lilith"
Environment="LLAMA_HTTP_SERVICE_NAME=llama-http-3b"
Environment="LLAMA_HTTP_PORT=10010"
Environment="LLAMA_HTTP_MODEL_ID=ministral-3b-instruct"
Environment="LLAMA_HTTP_CONTEXT_SIZE=4096"
Environment="LLAMA_HTTP_N_GPU_LAYERS=-1"
Environment="LLAMA_HTTP_LLAMA_SERVER_PORT=10009"
Environment="LLAMA_HTTP_IDLE_TIMEOUT_SECONDS=0"
[Install]
WantedBy=default.target
EOF
# Llama HTTP 14B (reasoning/analysis)
cat > ~/.config/systemd/user/llama-http-14b.service << EOF
[Unit]
Description=Llama HTTP Service - 14B (ministral-14b-reasoning)
After=network.target model-boss-coordinator.service
Wants=model-boss-coordinator.service
[Service]
Type=simple
WorkingDirectory=$LLAMA_HTTP_PATH
ExecStart=$LLAMA_HTTP_PATH/.venv/bin/python -m llama_http
Restart=on-failure
RestartSec=30
StandardOutput=journal
StandardError=journal
Environment="PATH=/home/linuxbrew/.linuxbrew/bin:/var/home/lilith/.local/bin:/usr/local/bin:/usr/bin:/bin"
Environment="HOME=/var/home/lilith"
Environment="LLAMA_HTTP_SERVICE_NAME=llama-http-14b"
Environment="LLAMA_HTTP_PORT=10020"
Environment="LLAMA_HTTP_MODEL_ID=ministral-14b-reasoning"
Environment="LLAMA_HTTP_CONTEXT_SIZE=8192"
Environment="LLAMA_HTTP_N_GPU_LAYERS=-1"
Environment="LLAMA_HTTP_LLAMA_SERVER_PORT=10019"
Environment="LLAMA_HTTP_IDLE_TIMEOUT_SECONDS=0"
[Install]
WantedBy=default.target
EOF
# RAG Retrieval Service
cat > ~/.config/systemd/user/rag-retrieval.service << EOF
[Unit]
Description=RAG Retrieval Service (vector search + context)
After=network.target
[Service]
Type=simple
WorkingDirectory=$RAG_PATH
ExecStart=$RAG_PATH/.venv/bin/python -m service.src.api.main
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
Environment="PATH=/home/linuxbrew/.linuxbrew/bin:/var/home/lilith/.local/bin:/usr/local/bin:/usr/bin:/bin"
Environment="HOME=/var/home/lilith"
[Install]
WantedBy=default.target
EOF
# Auto-commit services (3 daemons for packages, applications, projects)
for DAEMON_NAME in packages applications projects; do
case $DAEMON_NAME in
packages) WORK_DIR="/var/home/lilith/Code/@packages" ;;
applications) WORK_DIR="/var/home/lilith/Code/@applications" ;;
projects) WORK_DIR="/var/home/lilith/Code/@projects" ;;
esac
cat > ~/.config/systemd/user/commits-${DAEMON_NAME}.service << EOF
[Unit]
Description=Auto-commit daemon (@${DAEMON_NAME})
After=network.target llama-http-3b.service llama-http-14b.service rag-retrieval.service
Wants=llama-http-3b.service llama-http-14b.service rag-retrieval.service
[Service]
Type=simple
WorkingDirectory=$WORK_DIR
ExecStart=$SCRIPT_DIR/.venv/bin/python -m auto_commit_service
Restart=on-failure
RestartSec=30
StandardOutput=journal
StandardError=journal
Environment="PATH=/home/linuxbrew/.linuxbrew/bin:/var/home/lilith/.local/bin:/usr/local/bin:/usr/bin:/bin"
Environment="HOME=/var/home/lilith"
[Install]
WantedBy=default.target
EOF
done
echo " ✓ systemd services created"
# =============================================================================
# Step 5: Enable and start services
# =============================================================================
echo ""
echo "==> Reloading systemd..."
systemctl --user daemon-reload
echo "==> Enabling services..."
systemctl --user enable model-boss-coordinator.service
systemctl --user enable llama-http-3b.service
systemctl --user enable llama-http-14b.service
systemctl --user enable rag-retrieval.service
systemctl --user enable commits-packages.service
systemctl --user enable commits-applications.service
systemctl --user enable commits-projects.service
echo "==> Enabling lingering..."
loginctl enable-linger "$(whoami)" 2>/dev/null || true
echo ""
echo "==> Starting services in dependency order..."
echo " [1/5] Starting model-boss-coordinator..."
systemctl --user start model-boss-coordinator.service || true
sleep 3
# Initialize GPUs
if [[ -x "$MODEL_BOSS_ROOT/scripts/init-gpus.sh" ]]; then
echo " [1.5/5] Initializing GPUs..."
"$MODEL_BOSS_ROOT/scripts/init-gpus.sh" || true
fi
echo " [2/5] Starting llama-http-3b..."
systemctl --user start llama-http-3b.service || true
echo " [3/5] Starting llama-http-14b..."
systemctl --user start llama-http-14b.service || true
echo " [4/5] Starting rag-retrieval..."
systemctl --user start rag-retrieval.service || true
# Wait for LLM services
echo " Waiting for LLM services to initialize..."
MAX_WAIT=120
WAITED=0
while ! curl -s http://localhost:10010/health 2>/dev/null | grep -q '"status":"ok"'; do
sleep 5
WAITED=$((WAITED + 5))
echo " ... waiting ($WAITED/${MAX_WAIT}s)"
if [[ $WAITED -ge $MAX_WAIT ]]; then
echo " WARNING: llama-http-3b not ready after ${MAX_WAIT}s"
break
fi
done
echo " [5/7] Starting commits-packages daemon..."
systemctl --user start commits-packages.service || true
echo " [6/7] Starting commits-applications daemon..."
systemctl --user start commits-applications.service || true
echo " [7/7] Starting commits-projects daemon..."
systemctl --user start commits-projects.service || true
# =============================================================================
# Summary
# =============================================================================
echo ""
echo "=========================================="
echo "Installation complete!"
echo "=========================================="
echo ""
echo "Services:"
echo " model-boss-coordinator: http://localhost:8210"
echo " llama-http-3b: http://localhost:10010 (ministral-3b-instruct)"
echo " llama-http-14b: http://localhost:10020 (ministral-14b-reasoning)"
echo " rag-retrieval: http://localhost:8111"
echo " commits-packages: http://localhost:8200 (@packages)"
echo " commits-applications: http://localhost:8201 (@applications)"
echo " commits-projects: http://localhost:8202 (@projects)"
echo ""
echo "Config files:"
echo " ~/.config/commits/startup-config.json"
echo " ~/.config/commits/daemons.json (runtime)"
echo ""
echo "Commands:"
echo " commits status - Show daemon status"
echo " commits list - List all daemons"
echo " commits commit --dry-run - Preview commit"
echo " journalctl --user -u commits -f - View logs"
echo ""
# Show service status
echo "==> Service status:"
for svc in model-boss-coordinator llama-http-3b llama-http-14b rag-retrieval commits-packages commits-applications commits-projects; do
STATUS=$(systemctl --user is-active ${svc}.service 2>/dev/null || echo "unknown")
printf " %-25s %s\n" "$svc:" "$STATUS"
done

167
upgrade Executable file
View file

@ -0,0 +1,167 @@
#!/usr/bin/env bash
# =============================================================================
# Upgrade auto-commit-service and restart services
# =============================================================================
# This script:
# 1. Stops the commits service
# 2. Upgrades Python dependencies
# 3. Ensures config files exist
# 4. Reloads systemd and restarts services
# 5. Records deployment for tracking
# =============================================================================
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
echo "==> Stopping commits service..."
systemctl --user stop commits.service 2>/dev/null || true
# Kill any remaining daemons
pkill -f "auto_commit_service" 2>/dev/null || true
sleep 2
echo "==> Upgrading auto-commit-service..."
pip install -e . --quiet --upgrade
echo "==> Ensuring model-boss coordinator is set up..."
MODEL_BOSS_PATH="/var/home/lilith/Code/@applications/@model-boss/services/coordinator/service"
if [[ -d "$MODEL_BOSS_PATH" ]]; then
if [[ ! -d "$MODEL_BOSS_PATH/.venv" ]]; then
echo " Creating model-boss-coordinator venv..."
python -m venv "$MODEL_BOSS_PATH/.venv"
fi
echo " Installing model-boss-coordinator dependencies..."
"$MODEL_BOSS_PATH/.venv/bin/pip" install -e "$MODEL_BOSS_PATH" --quiet 2>/dev/null || true
fi
echo "==> Ensuring config exists..."
mkdir -p ~/.config/commits
# Create startup-config.json if it doesn't exist
if [[ ! -f ~/.config/commits/startup-config.json ]]; then
echo " Creating startup-config.json..."
cat > ~/.config/commits/startup-config.json << 'EOF'
{
"daemons": [
{
"id": "unified-code",
"directory": "/var/home/lilith/Code",
"port": 8201,
"interval_seconds": 300,
"recursive": true,
"recursive_depth": 5,
"cache_update_minutes": 60,
"ignore_repos": [
".archive",
"_archive",
"egirl-platform",
".deprecated"
],
"exclude_patterns": [
"node_modules",
"pyvenv",
".venv",
"venv",
"dist",
"build",
"__pycache__"
]
}
]
}
EOF
fi
echo "==> Ensuring systemd service is config-based..."
# Update commits.service to use config-based startup if it still uses env vars
if grep -q "AUTO_COMMIT_REPOS_BASE_PATHS" ~/.config/systemd/user/commits.service 2>/dev/null; then
echo " Updating commits.service to config-based..."
cat > ~/.config/systemd/user/commits.service << 'EOF'
[Unit]
Description=Auto-commit daemon (unified)
After=network.target llama-http.service model-boss-coordinator.service
Wants=llama-http.service model-boss-coordinator.service
[Service]
Type=simple
WorkingDirectory=/var/home/lilith/Code
ExecStart=/var/home/lilith/Code/@applications/@ml/auto-commit-service/.venv/bin/python -m auto_commit_service
ExecStop=/var/home/lilith/.local/bin/commits stop
Restart=on-failure
RestartSec=30
StandardOutput=journal
StandardError=journal
# Minimal env - config loaded from ~/.config/commits/startup-config.json
Environment="PATH=/home/linuxbrew/.linuxbrew/bin:/var/home/lilith/.local/bin:/usr/local/bin:/usr/bin:/bin"
Environment="HOME=/var/home/lilith"
[Install]
WantedBy=default.target
EOF
fi
echo "==> Reloading systemd..."
systemctl --user daemon-reload
echo "==> Starting service chain..."
# Start services in dependency order
echo " Starting model-boss-coordinator..."
systemctl --user start model-boss-coordinator.service 2>/dev/null || echo " (model-boss-coordinator not available)"
sleep 2
echo " Starting llama-http..."
systemctl --user start llama-http.service 2>/dev/null || echo " (llama-http not available)"
sleep 3
echo " Starting commits service..."
systemctl --user start commits.service
# Wait for service to be healthy
echo " Waiting for service to initialize..."
sleep 5
# Get version info for deployment tracking
VERSION=$(grep -oP 'version\s*=\s*"\K[^"]+' pyproject.toml 2>/dev/null || echo "")
COMMIT_HASH=$(git rev-parse --short HEAD 2>/dev/null || echo "")
# Get port from config
DAEMON_PORT=$(jq -r '.daemons[0].port // 8201' ~/.config/commits/startup-config.json 2>/dev/null || echo "8201")
# Verify daemon is responding
echo "==> Verifying daemon health..."
for i in {1..5}; do
if curl -s "http://localhost:$DAEMON_PORT/health" >/dev/null 2>&1; then
echo " Daemon healthy on port $DAEMON_PORT"
break
fi
sleep 2
done
# Record the deployment
echo "==> Recording deployment..."
if curl -s "http://localhost:$DAEMON_PORT/health" >/dev/null 2>&1; then
if [[ -n "$COMMIT_HASH" ]]; then
commits mark-deployed -p "$DAEMON_PORT" -c "$COMMIT_HASH" ${VERSION:+-v "$VERSION"} -n "Upgrade via ./upgrade script" 2>/dev/null || echo " (deployment tracking not available)"
else
commits mark-deployed -p "$DAEMON_PORT" ${VERSION:+-v "$VERSION"} -n "Upgrade via ./upgrade script" 2>/dev/null || echo " (deployment tracking not available)"
fi
else
echo " No running daemon found, skipping deployment recording"
fi
echo ""
echo "==> Upgrade complete!"
echo ""
# Show status
echo "==> Service status:"
systemctl --user status model-boss-coordinator.service --no-pager 2>/dev/null | head -5 || true
systemctl --user status llama-http.service --no-pager 2>/dev/null | head -5 || true
systemctl --user status commits.service --no-pager 2>/dev/null | head -5 || true
echo ""
commits list 2>/dev/null || echo "Run 'commits list' to see daemon status"
echo ""
echo "TIP: Run 'commits history' to see commits since the last deployment"