tmux observer
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add Makefile, AGENTS.md, and CLAUDE.md for agent infrastructure

Makefile provides install (uv + editable), test, format, ci, clean
targets following the pattern from sibling projects. AGENTS.md covers
project overview, source layout, build commands, dev principles, and
file header requirements. CLAUDE.md symlinks to AGENTS.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+200
+3
.gitignore
··· 5 5 dist/ 6 6 build/ 7 7 .pytest_cache/ 8 + .installed 9 + .mypy_cache/ 10 + .ruff_cache/
+101
AGENTS.md
··· 1 + # AGENTS.md 2 + 3 + Development guidelines for solstone-tmux, a standalone tmux terminal observer for solstone. 4 + 5 + ## Project Overview 6 + 7 + solstone-tmux is a companion app that runs alongside the main solstone journal. It captures all active tmux terminal sessions every 5 seconds, accumulates captures into 5-minute segments in a local cache, and syncs completed segments to the solstone ingest API. Pure Python, no system dependencies beyond tmux itself. Works offline -- segments sync when the server becomes available. Recovers incomplete segments on startup after crashes. 8 + 9 + This is a **solstone observer** -- a standalone capture agent that feeds data into a solstone journal. It follows the same patterns as solstone-macos (the macOS screen/audio observer) but captures terminal content instead of screen recordings. 10 + 11 + ## Source Layout 12 + 13 + ``` 14 + src/solstone_tmux/ 15 + __init__.py Package init, version 16 + cli.py CLI entry point (run, setup, install-service, status) 17 + config.py Config loading/persistence (~/.local/share/solstone-tmux/) 18 + capture.py Tmux capture library (polls sessions, panes, deduplication) 19 + observer.py Main capture loop with segment rotation 20 + streams.py Stream name derivation (hostname.tmux convention) 21 + sync.py Background sync service (uploads segments to server) 22 + upload.py HTTP upload client for solstone ingest API 23 + recovery.py Crash recovery for orphaned .incomplete segments 24 + tests/ 25 + test_capture.py Capture result serialization, hashing, JSONL writing 26 + test_config.py Config round-trip, defaults, permissions 27 + test_streams.py Stream name derivation and hostname stripping 28 + test_sync.py Recovery and segment collection logic 29 + contrib/ 30 + solstone-tmux.service Reference systemd unit file 31 + ``` 32 + 33 + ## Build and Test Commands 34 + 35 + ```bash 36 + make install # Create venv, install package in editable mode with dev deps 37 + make test # Run all tests with pytest 38 + make test-only TEST=tests/test_capture.py # Run a specific test file 39 + make test-only TEST="-k test_function_name" # Run tests matching a pattern 40 + make format # Auto-format and lint with ruff 41 + make ci # Full CI: format check + lint + tests 42 + make clean # Remove build artifacts and caches 43 + make uninstall # Remove venv and all artifacts 44 + make clean-install # Clean everything and reinstall from scratch 45 + ``` 46 + 47 + ## Development Principles 48 + 49 + - **Pure Python, minimal dependencies.** Runtime dependency is `requests` only. No frameworks, no heavy libraries. Keep it lean. 50 + - **Stdlib over libraries.** Use `subprocess` for tmux interaction, `asyncio` for the event loop, `dataclasses` for data structures. 51 + - **Atomic writes.** Write to `.tmp` then `os.rename()` for config and state persistence. 52 + - **Offline-first.** Captures always write to local cache. Sync is best-effort with retry and circuit breaker. 53 + - **Crash recovery.** `.incomplete` segment directories get recovered on startup. `.failed` directories are quarantined. 54 + - **Test everything, mock external state.** Tests must never call real tmux or real HTTP endpoints. Use `tmp_path`, monkeypatch, and fixtures to isolate completely. 55 + 56 + ## File Headers 57 + 58 + All Python source files must include this header as the first two lines: 59 + 60 + ```python 61 + # SPDX-License-Identifier: AGPL-3.0-only 62 + # Copyright (c) 2026 sol pbc 63 + ``` 64 + 65 + Add this to new `.py` files in `src/solstone_tmux/` and `tests/`. Do not add headers to TOML, Makefile, or markdown files. 66 + 67 + ## Architecture Notes 68 + 69 + ### Capture Loop 70 + 71 + The observer (`observer.py`) runs a single `asyncio` event loop. Every 5 seconds it polls tmux for active sessions, captures changed panes, and accumulates captures in memory. Every 5 minutes (configurable), it finalizes the segment: writes JSONL files to disk and triggers the sync service. 72 + 73 + ### Segment Format 74 + 75 + Segments live under `~/.local/share/solstone-tmux/captures/YYYYMMDD/stream/HHMMSS_DDD/` where `DDD` is duration in seconds. During recording, the directory has a `.incomplete` suffix. Each segment contains one JSONL file per tmux session (`tmux_{session}_screen.jsonl`). 76 + 77 + ### Sync Service 78 + 79 + The `SyncService` runs as a background `asyncio` task. It walks cached days newest-to-oldest, queries the server for existing segments, and uploads missing ones. A circuit breaker opens after 3 consecutive failures. 80 + 81 + ### Registration 82 + 83 + Observer registration tries `sol observer create` via CLI first (works without a running server if sol is on PATH), falling back to HTTP registration at the server's `/app/observer/api/create` endpoint. 84 + 85 + ## Config 86 + 87 + Config file: `~/.local/share/solstone-tmux/config/config.json` 88 + 89 + ```json 90 + { 91 + "server_url": "http://localhost:5015", 92 + "key": "<observer-api-key>", 93 + "stream": "<hostname>.tmux", 94 + "capture_interval": 5, 95 + "segment_interval": 300 96 + } 97 + ``` 98 + 99 + ## License 100 + 101 + AGPL-3.0-only. Copyright (c) 2026 sol pbc.
+1
CLAUDE.md
··· 1 + AGENTS.md
+89
Makefile
··· 1 + # solstone-tmux Makefile 2 + # Standalone tmux terminal observer for solstone 3 + 4 + .PHONY: install test test-only format ci clean clean-install uninstall 5 + 6 + # Default target 7 + all: install 8 + 9 + # Virtual environment directory 10 + VENV := .venv 11 + VENV_BIN := $(VENV)/bin 12 + PYTHON := $(VENV_BIN)/python 13 + 14 + # Require uv 15 + UV := $(shell command -v uv 2>/dev/null) 16 + ifndef UV 17 + $(error uv is not installed. Install it: curl -LsSf https://astral.sh/uv/install.sh | sh) 18 + endif 19 + 20 + # Venv tool shortcuts 21 + PYTEST := $(VENV_BIN)/pytest 22 + RUFF := $(VENV_BIN)/ruff 23 + 24 + # Marker file to track installation 25 + .installed: pyproject.toml 26 + @echo "Installing solstone-tmux with uv..." 27 + $(UV) venv --quiet --allow-existing $(VENV) 28 + $(UV) pip install --quiet -e ".[dev]" --python $(PYTHON) 29 + @touch .installed 30 + 31 + # Install package in editable mode with dev dependencies 32 + install: .installed 33 + 34 + # Run all tests 35 + test: .installed 36 + @echo "Running tests..." 37 + $(PYTEST) tests/ -q 38 + 39 + # Run a specific test file or pattern 40 + test-only: .installed 41 + @if [ -z "$(TEST)" ]; then \ 42 + echo "Usage: make test-only TEST=<test_file_or_pattern>"; \ 43 + echo "Example: make test-only TEST=tests/test_capture.py"; \ 44 + echo "Example: make test-only TEST=\"-k test_function_name\""; \ 45 + exit 1; \ 46 + fi 47 + $(PYTEST) $(TEST) 48 + 49 + # Auto-format and fix code, then report remaining issues 50 + format: .installed 51 + @echo "Formatting with ruff..." 52 + @$(RUFF) format . 53 + @$(RUFF) check --fix . 54 + @echo "" 55 + @echo "Checking for remaining issues..." 56 + @$(RUFF) check . || { echo ""; echo "Issues above need manual fixes."; exit 1; } 57 + @echo "" 58 + @echo "All clean!" 59 + 60 + # Run CI checks (what CI would run) 61 + ci: .installed 62 + @echo "Running CI checks..." 63 + @echo "=== Checking formatting ===" 64 + @$(RUFF) format --check . || { echo "Run 'make format' to fix formatting"; exit 1; } 65 + @echo "" 66 + @echo "=== Running ruff ===" 67 + @$(RUFF) check . || { echo "Run 'make format' to auto-fix"; exit 1; } 68 + @echo "" 69 + @echo "=== Running tests ===" 70 + @$(MAKE) test 71 + @echo "" 72 + @echo "All CI checks passed!" 73 + 74 + # Clean build artifacts and caches 75 + clean: 76 + @echo "Cleaning build artifacts..." 77 + rm -rf build/ dist/ *.egg-info/ src/*.egg-info/ 78 + rm -rf .pytest_cache/ .mypy_cache/ .ruff_cache/ 79 + find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true 80 + find . -type f -name "*.pyc" -delete 2>/dev/null || true 81 + rm -f .installed 82 + 83 + # Remove venv and all artifacts 84 + uninstall: clean 85 + @echo "Removing virtual environment..." 86 + rm -rf $(VENV) 87 + 88 + # Clean everything and reinstall 89 + clean-install: uninstall install
+6
pyproject.toml
··· 9 9 "requests", 10 10 ] 11 11 12 + [project.optional-dependencies] 13 + dev = [ 14 + "pytest", 15 + "ruff", 16 + ] 17 + 12 18 [project.scripts] 13 19 solstone-tmux = "solstone_tmux.cli:main" 14 20