Files
AIPass/setup.sh
T
AIPassandClaude Opus 4.6 6f2333ec67 S58: README rewrite + HERALD.md + fresh install fixes (#140)
* docs: rewrite README with verified claims + add HERALD.md progress tracker

README rewritten and verified against live system:
- All commands tested and confirmed working
- drone systems shows 23 (15 core + 8 test), documented correctly
- seedgo at 33 standards (was incorrectly stated as 34)
- Docker setup-workspace.sh fork URL noted
- setup.sh sudo requirement documented
- prax monitor marked as interactive
- Added Standards, Communication, and Structure sections
- Removed session tracking (moved to HERALD.md)
- Branch table expanded with roles and descriptions

HERALD.md: Living progress doc for Patrick to read between sessions.
Contains session history, active DPLANs, milestones, known issues.

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

* fix(install): registry format mismatch + seedgo CLI entry point

Bugs found via fresh Docker install test:

1. setup.sh generated AIPASS_REGISTRY.json with branches as dict (keyed
   by name), but seedgo's discovery.py expected a list of dicts. Result:
   `drone @seedgo audit aipass` was completely broken on any fresh install.
   Fix: setup.sh now generates list format (matching existing registry).

2. setup.sh verified `seedgo --help` but seedgo has no CLI entry point —
   it's only accessible via `drone @seedgo`. Fix: check `drone @seedgo --help`
   instead, remove broken symlink attempt.

3. seedgo discovery.py now handles both list and dict registry formats
   defensively (in case old registries exist).

4. README updated to remove seedgo standalone CLI claims.

Verified: full fresh install in Docker container — setup.sh completes
successfully, drone systems shows 15 branches, seedgo audit runs clean.

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

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 12:10:19 -07:00

379 lines
12 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# AIPass setup script
# Creates a venv, installs the package in editable mode, and verifies CLI entry points.
#
set -euo pipefail
# cd to repo root (where this script lives) so it works from anywhere
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
echo "=== AIPass Setup ==="
echo "Repo root: $SCRIPT_DIR"
echo ""
# --- Check python3 exists ---
if ! command -v python3 &>/dev/null; then
echo "FAIL: python3 not found. Install Python 3.10+ and try again."
exit 1
fi
PY_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
echo "Found python3 $PY_VERSION"
# --- Check minimum version ---
PY_OK=$(python3 -c 'import sys; print(int(sys.version_info >= (3, 10)))')
if [ "$PY_OK" != "1" ]; then
echo "FAIL: Python 3.10+ required, found $PY_VERSION"
exit 1
fi
# --- Create venv ---
if [ -d ".venv" ]; then
echo "Existing .venv found — removing it for a clean install."
rm -rf .venv
fi
echo "Creating virtual environment at .venv ..."
python3 -m venv .venv
# --- Activate and install ---
source .venv/bin/activate
echo "Upgrading pip ..."
pip install --upgrade pip --quiet
echo "Installing aipass in editable mode (with dev extras) ..."
pip install -e ".[dev]" --quiet
# --- Verify CLI entry points ---
FAIL=0
echo ""
echo "Verifying entry points ..."
if drone --help &>/dev/null; then
echo " drone ... ok"
else
echo " drone ... FAILED"
FAIL=1
fi
# seedgo is accessed via drone @seedgo, not as a standalone CLI
if drone @seedgo --help &>/dev/null; then
echo " seedgo ... ok (via drone @seedgo)"
else
echo " seedgo ... FAILED"
FAIL=1
fi
# --- Create secrets directory ---
SECRETS_DIR="$HOME/.secrets/aipass"
if [ ! -d "$SECRETS_DIR" ]; then
echo "Creating secrets directory at $SECRETS_DIR ..."
mkdir -p "$SECRETS_DIR"
chmod 700 "$HOME/.secrets"
echo " ~/.secrets/aipass/ ... created"
else
echo "Secrets directory already exists — skipping"
fi
# --- Seed .env template into secrets ---
if [ ! -f "$SECRETS_DIR/.env" ] && [ -f ".env.example" ]; then
cp .env.example "$SECRETS_DIR/.env"
echo " Copied .env.example → ~/.secrets/aipass/.env (add your API keys there)"
fi
# --- Generate branch registry ---
if [ ! -f "AIPASS_REGISTRY.json" ]; then
echo "Generating AIPASS_REGISTRY.json ..."
python3 - "$SCRIPT_DIR" << 'PYEOF'
import json, sys, os
from pathlib import Path
from datetime import date
repo_root = sys.argv[1]
src_dir = Path(repo_root) / "src" / "aipass"
today = date.today().isoformat()
branches = []
# Discover modules under src/aipass/
for d in sorted(src_dir.iterdir()):
if d.is_dir() and not d.name.startswith(("_", ".")):
branches.append({
"name": d.name,
"path": str(d),
"profile": "library",
"description": "",
"email": f"@{d.name}",
"status": "active",
"created": today,
"last_active": today,
})
# Add external branches: commons and skills
for ext_name in ["commons", "skills"]:
ext_path = Path(repo_root) / "src" / ext_name
if ext_path.is_dir():
branches.append({
"name": ext_name,
"path": str(ext_path),
"profile": "library",
"description": "",
"email": f"@{ext_name}",
"status": "active",
"created": today,
"last_active": today,
})
registry = {
"metadata": {
"version": "1.0.0",
"last_updated": today,
"total_branches": len(branches),
},
"branches": branches,
}
out = Path(repo_root) / "AIPASS_REGISTRY.json"
out.write_text(json.dumps(registry, indent=2) + "\n")
print(f" {len(branches)} branches registered")
PYEOF
else
echo "AIPASS_REGISTRY.json already exists — skipping"
fi
# --- Bootstrap branch identity and memory files ---
echo ""
echo "Bootstrapping branch identity files ..."
DATE_TODAY=$(date +%Y-%m-%d)
bootstrap_branch() {
local name="$1"
local path="$2"
local citizen_class="$3"
local role="$4"
local created=0
# .trinity/passport.json
mkdir -p "$path/.trinity"
if [ ! -f "$path/.trinity/passport.json" ]; then
cat > "$path/.trinity/passport.json" << JSONEOF
{
"document_metadata": {
"document_type": "identity",
"document_name": "${name}.PASSPORT",
"version": "1.0.0",
"schema_version": "1.0.0",
"created": "${DATE_TODAY}",
"last_updated": "${DATE_TODAY}",
"managed_by": "${name}"
},
"identity": {
"name": "${name}",
"citizen_class": "${citizen_class}",
"role": "${role}",
"status": "active"
}
}
JSONEOF
created=1
fi
# .trinity/local.json
if [ ! -f "$path/.trinity/local.json" ]; then
cat > "$path/.trinity/local.json" << JSONEOF
{
"document_metadata": {
"document_type": "session_history",
"document_name": "${name}.LOCAL",
"version": "1.0.0",
"schema_version": "1.0.0",
"created": "${DATE_TODAY}",
"last_updated": "${DATE_TODAY}",
"managed_by": "${name}",
"tags": ["session_tracking", "work_log", "${name}"],
"limits": {"max_lines": 600, "note": "Auto-rollover when max_lines exceeded"},
"status": {"health": "healthy", "current_lines": 0, "last_health_check": "${DATE_TODAY}"}
},
"active_tasks": {
"today_focus": "First session — explore codebase and capabilities",
"recently_completed": []
},
"key_learnings": {},
"sessions": []
}
JSONEOF
created=1
fi
# .trinity/observations.json
if [ ! -f "$path/.trinity/observations.json" ]; then
cat > "$path/.trinity/observations.json" << JSONEOF
{
"document_metadata": {
"document_type": "collaboration_patterns",
"document_name": "${name}.OBSERVATIONS",
"version": "1.0.0",
"schema_version": "1.0.0",
"created": "${DATE_TODAY}",
"last_updated": "${DATE_TODAY}",
"managed_by": "${name}",
"tags": ["collaboration", "patterns", "${name}"],
"limits": {"max_lines": 600, "note": "Auto-rollover when max_lines exceeded"},
"status": {"health": "healthy", "current_lines": 0, "last_health_check": "${DATE_TODAY}"}
},
"guidelines": {
"purpose": "Capture collaboration patterns and experiential insights over time",
"chronological_order": "Newest entries at TOP, oldest at BOTTOM - NEVER reorder"
},
"observations": [
{
"date": "${DATE_TODAY}",
"session": 1,
"entries": [
{"title": "First Contact", "detail": "Branch initialized. Ready to begin capturing collaboration patterns."}
]
}
]
}
JSONEOF
created=1
fi
# .seedgo/bypass.json
mkdir -p "$path/.seedgo"
if [ ! -f "$path/.seedgo/bypass.json" ]; then
echo '{}' > "$path/.seedgo/bypass.json"
created=1
fi
# .ai_mail.local/inbox.json
mkdir -p "$path/.ai_mail.local"
if [ ! -f "$path/.ai_mail.local/inbox.json" ]; then
echo '{"inbox": []}' > "$path/.ai_mail.local/inbox.json"
created=1
fi
if [ "$created" -eq 1 ]; then
echo " @${name} ... bootstrapped"
else
echo " @${name} ... exists (skipped)"
fi
}
# Branches inside src/aipass/
bootstrap_branch "drone" "$SCRIPT_DIR/src/aipass/drone" "builder" "Command routing and module discovery"
bootstrap_branch "seedgo" "$SCRIPT_DIR/src/aipass/seedgo" "builder" "Standards enforcement and code auditing"
bootstrap_branch "prax" "$SCRIPT_DIR/src/aipass/prax" "builder" "Logging and monitoring system"
bootstrap_branch "cli" "$SCRIPT_DIR/src/aipass/cli" "builder" "Display formatting service"
bootstrap_branch "flow" "$SCRIPT_DIR/src/aipass/flow" "builder" "Workflow and plan management"
bootstrap_branch "ai_mail" "$SCRIPT_DIR/src/aipass/ai_mail" "builder" "Inter-agent messaging and dispatch"
bootstrap_branch "api" "$SCRIPT_DIR/src/aipass/api" "builder" "LLM access and model routing"
bootstrap_branch "trigger" "$SCRIPT_DIR/src/aipass/trigger" "builder" "Event-driven automation"
bootstrap_branch "spawn" "$SCRIPT_DIR/src/aipass/spawn" "builder" "Branch lifecycle management"
bootstrap_branch "devpulse" "$SCRIPT_DIR/src/aipass/devpulse" "manager" "Orchestration hub and coordination"
bootstrap_branch "backup" "$SCRIPT_DIR/src/aipass/backup" "builder" "Multi-mode backup system"
bootstrap_branch "daemon" "$SCRIPT_DIR/src/aipass/daemon" "builder" "Background scheduler"
bootstrap_branch "memory" "$SCRIPT_DIR/src/aipass/memory" "builder" "Vector memory bank"
# External branches
bootstrap_branch "commons" "$SCRIPT_DIR/src/commons" "builder" "Social network for branches"
bootstrap_branch "skills" "$SCRIPT_DIR/src/skills" "builder" "Capability framework"
echo " 15 branches bootstrapped"
# --- Install Claude Code hooks ---
CLAUDE_SETTINGS="$HOME/.claude/settings.json"
if [ -d "$SCRIPT_DIR/.claude/hooks" ]; then
echo "Installing Claude Code hooks ..."
mkdir -p "$HOME/.claude"
python3 - "$SCRIPT_DIR" "$CLAUDE_SETTINGS" << 'PYEOF'
import json
import sys
from pathlib import Path
repo_root = sys.argv[1]
settings_path = Path(sys.argv[2])
hooks_dir = f"{repo_root}/.claude/hooks"
# Load existing settings or start fresh
if settings_path.exists():
settings = json.loads(settings_path.read_text())
else:
settings = {}
# Build hooks config with absolute paths
settings["hooks"] = {
"UserPromptSubmit": [
{"hooks": [{"type": "command", "command": f"cat {repo_root}/.aipass/aipass_global_prompt.md 2>/dev/null || true"}]},
{"hooks": [{"type": "command", "command": f"python3 {hooks_dir}/branch_prompt_loader.py"}]},
{"hooks": [{"type": "command", "command": f"python3 {hooks_dir}/identity_injector.py"}]},
{"hooks": [{"type": "command", "command": f"python3 {hooks_dir}/email_notification.py"}]},
],
"PreToolUse": [
{"matcher": "Bash|Edit|MultiEdit|Write|Read|Grep|Glob|WebSearch|WebFetch|Task",
"hooks": [{"type": "command", "command": f"python3 {hooks_dir}/tool_use_sound.py"}]},
],
"PostToolUse": [
{"matcher": "Edit|MultiEdit|Write|NotebookEdit",
"hooks": [{"type": "command", "command": f"python3 {hooks_dir}/auto_fix_diagnostics.py"}]},
],
"Stop": [
{"hooks": [{"type": "command", "command": f"python3 {hooks_dir}/stop_sound.py"}]},
],
"Notification": [
{"hooks": [{"type": "command", "command": f"python3 {hooks_dir}/notification_sound.py"}]},
],
"PreCompact": [
{"matcher": "manual", "hooks": [{"type": "command", "command": f"python3 {hooks_dir}/pre_compact.py", "timeout": 60}]},
{"matcher": "auto", "hooks": [{"type": "command", "command": f"python3 {hooks_dir}/pre_compact.py", "timeout": 60}]},
],
}
settings_path.write_text(json.dumps(settings, indent=2) + "\n")
print(f" hooks -> {settings_path}")
PYEOF
else
echo "Skipping hooks (no .claude/hooks/ directory found)"
fi
# --- Create global symlinks for CLI tools ---
echo ""
echo "Creating global symlinks ..."
VENV_BIN="$SCRIPT_DIR/.venv/bin"
LOCAL_BIN="/usr/local/bin"
for cmd in drone; do
if [ -f "$VENV_BIN/$cmd" ]; then
if sudo ln -sf "$VENV_BIN/$cmd" "$LOCAL_BIN/$cmd" 2>/dev/null; then
echo " $LOCAL_BIN/$cmd -> $VENV_BIN/$cmd"
else
echo " WARN: Could not create symlink for $cmd (try running with sudo)"
echo " Manual fix: sudo ln -sf $VENV_BIN/$cmd $LOCAL_BIN/$cmd"
fi
fi
done
# --- Result ---
echo ""
if [ "$FAIL" -eq 0 ]; then
echo "=== Setup complete ==="
echo ""
echo "drone is available globally via /usr/local/bin symlink."
echo "seedgo is accessed via: drone @seedgo"
echo "No venv activation needed for CLI commands."
echo ""
else
echo "=== Setup finished with errors ==="
echo "The venv was created and the package was installed, but one or more"
echo "CLI entry points failed verification. Check the output above."
exit 1
fi