* 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>
379 lines
12 KiB
Bash
Executable File
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
|