Purpose: Command router and symbolic addressing for AIPass. Resolves @branch names to paths at runtime via AIPASS_REGISTRY.json, routes commands to module entry points, manages git workflows, and discovers available commands across the system.
Module: aipass.drone
Version: 1.1.0
Created: 2026-03-05
- Resolve
@branchsymbolic names to absolute paths viaAIPASS_REGISTRY.json - Route commands to registered branches and internal modules
- Manage git workflows: PR creation, branch sync, lock management, merge
- Discover and scan available commands across the system
- Provide
drone systemsintrospection of all registered components - Support external AIPass projects via dual registry lookup and module fallback
Drone provides a CLI for terminal use and a Python API for programmatic access.
# Core routing
drone @seedgo audit aipass # Route "audit aipass" to seedgo
drone @module --help # Show help for any module
drone systems # List all registered modules and branches
# Git workflow
drone @git pr "description" # Create a PR from current branch
drone @git status # Git status scoped to branch directory
drone @git sync # Pull latest main with --rebase
drone @git sync --autostash # Sync with autostash for dirty trees
drone @git lock / unlock # Atomic branch lockfile
# Git workflow (devpulse-authorized only)
drone @git system-pr "desc" # System-wide PR across all tracked changes
drone @git merge <PR#> # Straight-merge a PR and sync local main
drone @git smart-sync # Fetch + detect divergence + rebase
drone @git fix # Auto-fix stuck rebase / detached HEAD
drone @git fix --dry-run # Detect issues without fixing
# Command discovery
drone scan @branch # Discover available commands in a branch
drone activate @branch # Scan + register all commands as shortcuts
drone list # List registered custom command shortcuts
drone remove <name> # Remove a custom command shortcut
# Utilities
drone hook-sounds on|off # Toggle hook notification sounds
drone --version # Show version (v1.1.0)
drone --help # Show usage informationfrom aipass.drone import resolve_branch, list_branches, route_command
# Resolve @name to absolute path
path = resolve_branch("@seedgo")
# List all registered branches
branches = list_branches() # All branches
active = list_branches(status="active") # Filter by status
# Route a command to a branch
result = route_command("@seedgo", "verify")
print(result.stdout) # Command output
print(result.exit_code) # 0 on successfrom aipass.drone import set_registry_path, get_registry_path
# Use a custom registry location
set_registry_path("/path/to/AIPASS_REGISTRY.json")
# Or set via environment variable
# export AIPASS_REGISTRY_PATH=/path/to/registry.jsonfrom aipass.drone import resolve_branch, BranchNotFoundError, CommandExecutionError
try:
path = resolve_branch("@nonexistent")
except BranchNotFoundError:
print("Branch not found in registry")
try:
result = route_command("@seedgo", "audit", args=["aipass"], timeout=120)
except CommandExecutionError as e:
print(f"Command failed: {e}")drone/
├── cli.py # pip entry point (drone command)
├── __init__.py # Public API exports (v1.1.0)
├── apps/
│ ├── drone.py # Core entry + CLI routing
│ ├── modules/ # Orchestrators (business logic)
│ │ ├── config.py # Registry path resolution
│ │ ├── resolver.py # Branch resolution (@name → path)
│ │ ├── router.py # Command routing via subprocess
│ │ ├── discovery.py # Module and command discovery
│ │ ├── module_registry.py # Internal module routing
│ │ ├── registry.py # Registry query operations
│ │ ├── commands.py # Custom command shortcut orchestrator
│ │ ├── git_module.py # Git workflow (9 commands + plugin routing)
│ │ └── scan.py # Branch command scanning
│ ├── handlers/ # Implementation details
│ │ ├── executor.py # Safe subprocess execution (timeout, no shell)
│ │ ├── exceptions.py # Exception hierarchy (10 exception types)
│ │ ├── router_handler.py # Routing implementation + caller detection
│ │ ├── registry_handler.py # Registry file ops + dual registry lookup
│ │ ├── discovery_handler.py # Discovery implementation + help parsing
│ │ ├── module_registry_handler.py # Module loading (internal + external)
│ │ ├── generic_adapter.py # StringIO capture for external modules
│ │ ├── routing_config.json # External module declarations
│ │ ├── json/
│ │ │ └── json_handler.py # Structured operation logging
│ │ ├── scanning/
│ │ │ ├── scanner.py # Help parsing + modules/ file scanning
│ │ │ └── formatters.py # Rich output for scan results
│ │ ├── command_registry/
│ │ │ ├── ops.py # Command shortcut CRUD
│ │ │ ├── lookup.py # Greedy multi-word matching
│ │ │ └── formatters.py # Rich output for command lists
│ │ └── git/
│ │ ├── lock_handler.py # Atomic lockfile (O_CREAT|O_EXCL)
│ │ ├── pr_handler.py # 10-step PR workflow with scoped staging
│ │ ├── status_handler.py # Scoped git status (subprocess)
│ │ ├── status_handler_gitpython.py # [prototype] DPLAN-0140 Phase 1, not wired in
│ │ └── sync_handler.py # Safe main sync (--autostash support)
│ └── plugins/
│ ├── devpulse_ops/ # Privileged git operations (auth-gated)
│ │ ├── auth.py # Passport-based identity gate (ALLOWED_CALLERS)
│ │ ├── pr_plugin.py # System-wide PR (git add -A, system/ branches)
│ │ ├── merge_plugin.py # PR merge (--merge) + local sync
│ │ ├── sync_plugin.py # Smart sync (fetch, divergence detect, rebase)
│ │ └── fix_plugin.py # Auto-fix stuck rebase / detached HEAD
│ └── hook_sounds/
│ └── hook_sounds_plugin.py # Toggle notification sounds on/off
├── docs/ # Public documentation
├── docs.local/ # Investigation reports and policies
└── tests/ # 530 tests across 20 test files
- CLI input →
drone.py:main() - Built-in commands checked first:
systems,scan,activate,list,remove,hook-sounds @targetrouting → branch resolution viaAIPASS_REGISTRY.json→ subprocess dispatch- Module fallback → if branch not found but is a registered module, routes internally
- Bare module names → auto-discovered from
apps/modules/*.py, routed viaimportlib - Custom commands → greedy multi-word matching against
drone_command_registry.json
Drone routes to two kinds of modules:
| Type | Modules | Routing |
|---|---|---|
| Internal | git |
importlib import → handle_command() |
| External | seedgo, cli, spawn |
generic_adapter.capture_main() via routing_config.json |
External modules are declared in apps/handlers/routing_config.json with entry points, descriptions, and versions.
All agents work on main. Branch creation is only allowed inside drone @git system-pr, which:
- Commits changes on main
- Moves branch pointer with
git branch -f(HEAD stays on main) - Pushes branch with
--force-with-lease - Opens PR via
gh - Returns to main
Enforcement layers:
.claude/settings.jsondeny rules blockgit checkout -b,git switch -c_assert_on_main_or_pr_flow()guard ingit_module.py- Persistent citizen branches:
citizen/{name}reused across PRs
By default, drone captures subprocess output (capture_output=True) with a 30s timeout. This is safe for AI-to-AI routing but strips Rich colors, buffers progress bars, and kills long-running commands.
Commands in the interactive tuple bypass capture and inherit the terminal directly — enabling live Rich output, colors, and no timeout.
Per-command allowlist (in apps/drone.py):
| Command | Reason |
|---|---|
monitor |
Prax real-time monitoring (live TUI) |
audit |
Seedgo audit (Rich progress bars) |
watchdog |
Devpulse watchdog (live monitoring) |
Per-branch allowlist — all commands from these branches get interactive mode:
| Branch | Reason |
|---|---|
cli |
User-facing CLI with Rich formatted output |
To add: edit INTERACTIVE_COMMANDS or INTERACTIVE_BRANCHES in apps/drone.py.
Plugins live in apps/plugins/{name}/ — outside the 3-layer structure by design.
Auth-gated operations for system administration. auth.py walks CWD for .trinity/passport.json and checks branch_name against ALLOWED_CALLERS (devpulse, seedgo, spawn).
| Plugin | Command | Purpose |
|---|---|---|
pr_plugin |
system-pr |
System-wide PR across all tracked changes |
merge_plugin |
merge |
Straight-merge a PR and sync local main |
sync_plugin |
smart-sync |
Fetch + detect divergence + rebase |
fix_plugin |
fix |
Auto-fix stuck rebase / detached HEAD |
Simple toggle for hook notification sounds. Creates/removes /tmp/aipass-hooks-muted flag file.
Infrastructure modules (seedgo, cli, git, spawn) work from external AIPass projects without per-project registration.
Dual registry lookup: registry_handler.py merges local project registry with AIPASS_HOME registry. Local entries win on name collision.
Module fallback: When subprocess routing fails (branch not in local registry), drone falls back to module routing for registered modules. Graceful degradation: Rich output from AIPass, functional output from external projects.
AIPASS_HOME hints: When AIPASS_HOME is not set and the local registry lacks core branches, drone shows setup hints:
Tip: set AIPASS_HOME=/path/to/AIPass to access all branches
AIPASS_REGISTRY.json— Branch registry (read for resolution)ghCLI — GitHub operations (PR creation, merge)- Python stdlib (
pathlib,sys,subprocess,json,threading)
- All branches — command routing via
drone @target command - All branches — module/branch discovery via
drone systems - External modules —
generic_adapter.capture_main()for subprocess-free routing aipass.seedgo— routed viadrone @seedgoaipass.cli— routed viadrone @cliaipass.spawn— routed viadrone @spawn
530 tests across 20 test files, covering all layers:
| Area | Files | Tests |
|---|---|---|
| Core routing | test_resolver.py, test_router.py, test_activation.py |
~128 |
| Git operations | test_git_module.py, test_system_pr.py, test_devpulse_plugins.py |
~95 |
| Handlers | test_executor.py, test_registry_handler.py, test_discovery.py |
~99 |
| Infrastructure | test_generic_adapter.py, test_module_registry.py, test_config.py |
~66 |
| Features | test_commands.py, test_scan.py, test_hook_sounds.py, test_json_handler.py |
~125 |
| Standards | test_cli_routing.py, test_contracts.py, test_error_resilience.py, test_init_provisioning.py |
~21 |
Run tests: cd src/aipass/drone && python -m pytest tests/ -q
status_handler_gitpython.pyis an unreferenced prototype (DPLAN-0140 Phase 1) — awaiting Phase 2/3update_command()andcommand_exists()inops.pyare tested CRUD API but unused from production- Pyright warns about
jsonpackage name shadowing stdlib — works at runtime - Recurring sync errors when working tree is dirty — operational, not code bugs
Seedgo: 100% (34/34) | Tests: 530 pass, 4 skip | Last Updated: 2026-04-22