fix(install): make install.sh idempotent + add onboard-project.sh

- install.sh: no longer exits early when ~/.automaton exists.
  Skips the clone but runs all setup (VRAM detection, guards,
  self-improvement loop, virtualenv). Both curl|bash and
  git-clone + ./install.sh now work correctly.
- onboard-project.sh: new script that bootstraps automaton in a
  project — creates .automaton/ skeleton, detects models, writes
  config.md, inits git, installs hooks, adds .gitignore entries.
- README.md: fix install flow docs (curl|bash + clone-then-run),
  add onboard-project.sh as Option A for project setup
This commit is contained in:
Lap Tran
2026-06-26 13:54:28 -04:00
parent f980ccfe27
commit b880f2535a
4 changed files with 266 additions and 49 deletions
+47 -20
View File
@@ -6,22 +6,28 @@ A contract-based operating system for LLM agents, designed to enforce discipline
Before you can use the framework in any project, you must install the core logic into your local environment. Before you can use the framework in any project, you must install the core logic into your local environment.
**Run these commands in your terminal:** Choose one of the following methods:
### Option A: One-liner (curl pipe, recommended)
```bash ```bash
# Clone the framework into the global config directory curl -fsSL https://raw.githubusercontent.com/<your-org>/automaton/main/scripts/install.sh | bash -s -- <your-git-url>
git clone <your-git-url> ~/.automaton
# Enter the directory
cd ~/.automaton
# Make the installation script executable and run it
# You must provide the git URL as the first argument
chmod +x install.sh
./install.sh <your-git-url>
``` ```
The git URL is required because the framework uses it for self-updates and the self-improvement loop. Choose carefully -- it cannot be changed later without reinstalling. This clones the framework to `~/.automaton/`, runs VRAM detection, installs pre-edit guards, creates the self-improvement loop, and sets up the Python virtualenv.
### Option B: Clone first
```bash
git clone <your-git-url> ~/.automaton
bash ~/.automaton/scripts/install.sh
```
The script detects that `~/.automaton` already exists, skips the clone, and runs all setup steps (VRAM detection, guards, loop, virtualenv).
### Both methods do the same thing
The git URL is required on fresh install because the framework uses it for self-updates and the self-improvement loop. Choose carefully -- it cannot be changed later without reinstalling.
*Note: This creates the "brain" of the framework (prompts, state machines, and rules) in your home directory. A self-improvement loop is created and scheduled by default (see Loop Engineering below).* *Note: This creates the "brain" of the framework (prompts, state machines, and rules) in your home directory. A self-improvement loop is created and scheduled by default (see Loop Engineering below).*
@@ -49,7 +55,24 @@ If a project was set up under the old model (with copies of framework files), it
Once the framework is installed globally, you must "onboard" every individual project you work on. Once the framework is installed globally, you must "onboard" every individual project you work on.
### Option A: The Agent-Driven Way (Recommended) ### Option A: The Onboarding Script (Recommended)
```bash
bash ~/.automaton/scripts/onboard-project.sh /path/to/project
```
This will:
1. Create `.automaton/` skeleton if missing.
2. Run `detect_models.py --write` to probe local models (falls back to a minimal `models.json`).
3. Generate `config.md` with VRAM recommendations.
4. Write `project-name.md` from the directory name.
5. Initialize git if not already a repo.
6. Install git hooks (pre-commit + pre-push).
7. Add automaton entries to `.gitignore`.
8. Run `status.py --audit` to verify the setup.
### Option B: The Agent-Driven Way
If you want the agent to handle the configuration for you, navigate to your project root and run: If you want the agent to handle the configuration for you, navigate to your project root and run:
> *"Onboard this project into automaton." > *"Onboard this project into automaton."
@@ -60,17 +83,21 @@ The agent will automatically:
3. Generate your `.agent.md` and `.rules.md` files. 3. Generate your `.agent.md` and `.rules.md` files.
4. Initiate the "Exploration Ritual" to understand your codebase. 4. Initiate the "Exploration Ritual" to understand your codebase.
### Option B: The Manual Way ### Option C: The Manual Way
If you prefer to set it up manually, create a `.automaton/` directory in your project root and add: If you prefer to set it up manually:
- `.agent.md`: Project-specific configuration (Mode, rules, etc.).
- `.rules.md`: Project-specific constraints and past failure modes.
Then install the git pre-commit hook:
```bash ```bash
ln -sf ~/.automaton/scripts/git-hooks/pre-commit .git/hooks/pre-commit # Create the automaton directory
mkdir -p .automaton/tasks .automaton/loops .automaton/design
# Install git hooks
bash ~/.automaton/scripts/install-hooks.sh .
# Configure your model(s)
python3 ~/.automaton/scripts/detect_models.py --write --project .
``` ```
This hook blocks commits when no task is in `implement` or `doc_review` phase, preventing accidental code changes without a tracked task. Then add `.agent.md` and `.rules.md` for the agent.
### The Self-Improvement Loop is framework-scoped ### The Self-Improvement Loop is framework-scoped
+1 -1
View File
@@ -1,2 +1,2 @@
#!/usr/bin/env bash #!/usr/bin/env bash
python3 "/Users/laptran/.automaton/scripts/status.py" --cleanup-done --days 7 --project "/private/var/folders/f5/yv0dzbnx47x3yp8sc_2519gh0000gn/T/pytest-of-laptran/pytest-127/test_uninstall_via_disabled_re0" python3 "/Users/laptran/.automaton/scripts/status.py" --cleanup-done --days 7 --project "/private/var/folders/f5/yv0dzbnx47x3yp8sc_2519gh0000gn/T/pytest-of-laptran/pytest-128/test_uninstall_via_disabled_re0"
+34 -28
View File
@@ -3,27 +3,39 @@ set -e
FRAMEWORK_DIR="$HOME/.automaton" FRAMEWORK_DIR="$HOME/.automaton"
if [ -d "$FRAMEWORK_DIR" ]; then
echo "automaton already installed at $FRAMEWORK_DIR"
echo "Run './update.sh' to update."
exit 0
fi
GIT_URL="${1:-}" GIT_URL="${1:-}"
if [ -z "$GIT_URL" ]; then
echo "ERROR: Git URL required." if [ ! -d "$FRAMEWORK_DIR" ]; then
echo "Usage: ./install.sh <git-url>" # Fresh install — need a Git URL to clone
echo "Example: ./install.sh https://github.com/user/automaton.git" if [ -z "$GIT_URL" ]; then
echo "ERROR: Git URL required for fresh install."
echo ""
echo "Usage:"
echo " curl -fsSL https://raw.githubusercontent.com/<user>/automaton/main/scripts/install.sh | bash -s -- <git-url>"
echo ""
echo " --- or ---"
echo ""
echo " git clone <git-url> ~/.automaton"
echo " bash ~/.automaton/scripts/install.sh"
echo ""
echo "The framework is cloned to ~/.automaton. Choose your URL carefully"
echo "as it cannot be changed later without reinstalling."
exit 1
fi
echo "Cloning automaton from $GIT_URL to $FRAMEWORK_DIR..."
git clone "$GIT_URL" "$FRAMEWORK_DIR"
echo ""
else
echo "automaton already installed at $FRAMEWORK_DIR — running setup steps..."
if [ -n "$GIT_URL" ]; then
echo "Note: Git URL argument ignored because ~/.automaton already exists."
echo "To update, run: cd ~/.automaton && ./update.sh"
fi
echo "" echo ""
echo "The framework is cloned to ~/.automaton. Choose your URL carefully"
echo "as it cannot be changed later without reinstalling."
exit 1
fi fi
echo "Cloning automaton from $GIT_URL to $FRAMEWORK_DIR..." # --- Everything below is idempotent and runs on both fresh and existing installs ---
git clone "$GIT_URL" "$FRAMEWORK_DIR"
echo ""
echo "=== VRAM / Context Detection ===" echo "=== VRAM / Context Detection ==="
echo "Detecting your system's VRAM to recommend task decomposition settings..." echo "Detecting your system's VRAM to recommend task decomposition settings..."
echo "" echo ""
@@ -31,7 +43,7 @@ echo ""
# Run VRAM detection script if it exists # Run VRAM detection script if it exists
if [ -f "$FRAMEWORK_DIR/scripts/vram_detect.py" ]; then if [ -f "$FRAMEWORK_DIR/scripts/vram_detect.py" ]; then
# Run in project-dir context so it can read framework overhead # Run in project-dir context so it can read framework overhead
detection_output=$(cd "$FRAMEWORK_DIR" && python3 "$FRAMEWORK_DIR/scripts/vram_detect.py" 2>&1) detection_output=$(cd "$FRAMEWORK_DIR" && python3 "$FRAMEWORK_DIR/scripts/vram_detect.py" 2>&1 || true)
# Extract JSON output (the block after "=== JSON Output ===") # Extract JSON output (the block after "=== JSON Output ===")
json_output=$(echo "$detection_output" | sed -n '/=== JSON Output ===/,$p' | tail -n +2) json_output=$(echo "$detection_output" | sed -n '/=== JSON Output ===/,$p' | tail -n +2)
@@ -40,12 +52,9 @@ if [ -f "$FRAMEWORK_DIR/scripts/vram_detect.py" ]; then
echo "$detection_output" echo "$detection_output"
# Extract key values from JSON using Python # Extract key values from JSON using Python
recommended_k=$(echo "$json_output" | python3 -c 'import json,sys; print(json.load(sys.stdin)["recommended_k"])') recommended_k=$(echo "$json_output" | python3 -c 'import json,sys; print(json.load(sys.stdin)["recommended_k"])' 2>/dev/null || echo "?")
max_peak_kb=$(echo "$json_output" | python3 -c 'import json,sys; print(json.load(sys.stdin)["max_peak_context_kb"])') max_peak_kb=$(echo "$json_output" | python3 -c 'import json,sys; print(json.load(sys.stdin)["max_peak_context_kb"])' 2>/dev/null || echo "?")
headroom=$(echo "$json_output" | python3 -c 'import json,sys; print(json.load(sys.stdin)["headroom"])') headroom=$(echo "$json_output" | python3 -c 'import json,sys; print(json.load(sys.stdin)["headroom"])' 2>/dev/null || echo "?")
gpu_vram=$(echo "$json_output" | python3 -c 'import json,sys; print(json.load(sys.stdin)["gpu_vram_gb"])')
ram_gb=$(echo "$json_output" | python3 -c 'import json,sys; print(json.load(sys.stdin)["ram_gb"])')
model_context=$(echo "$json_output" | python3 -c 'import json,sys; print(json.load(sys.stdin)["model_context_kb"])')
echo "" echo ""
echo "=== Recommended VRAM Configuration ===" echo "=== Recommended VRAM Configuration ==="
@@ -72,11 +81,8 @@ echo ""
echo "Installation complete." echo "Installation complete."
echo "" echo ""
echo "Next steps:" echo "Next steps:"
echo " 1. cd into a project and run the onboarding prompt" echo " 1. Onboard a project: bash ~/.automaton/scripts/onboard-project.sh /path/to/project"
echo " 2. In each project that uses git, install the automaton hooks:" echo " 2. Or tell your agent: 'Onboard this project into automaton'"
echo " bash ~/.automaton/scripts/install-hooks.sh /path/to/project"
echo ""
echo "These hooks block commits and pushes when no task is in an edit-allowed phase."
echo "" echo ""
# Register pre-edit guards for detected harnesses # Register pre-edit guards for detected harnesses
+184
View File
@@ -0,0 +1,184 @@
#!/usr/bin/env bash
# onboard-project.sh — Bootstrap automaton in a new or existing project.
#
# Usage:
# bash ~/.automaton/scripts/onboard-project.sh /path/to/project
#
# Creates .automaton/ skeleton, detects models, creates config, inits git,
# installs hooks, and verifies everything works.
set -euo pipefail
FRAMEWORK_DIR="$HOME/.automaton"
PROJECT_DIR="${1:-}"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
info() { echo -e "${BLUE}INFO:${NC} $1"; }
ok() { echo -e "${GREEN}OK:${NC} $1"; }
warn() { echo -e "${YELLOW}WARN:${NC} $1"; }
error() { echo -e "${RED}ERROR:${NC} $1"; }
# --- Argument checks ---
if [ -z "$PROJECT_DIR" ]; then
error "Usage: bash ~/.automaton/scripts/onboard-project.sh /path/to/project"
exit 1
fi
PROJECT_DIR="$(cd "$PROJECT_DIR" 2>/dev/null && pwd)" || true
if [ -z "$PROJECT_DIR" ] || [ ! -d "$PROJECT_DIR" ]; then
echo ""
error "'$1' does not exist."
echo " Create it first: mkdir -p '$1'"
echo " Then re-run this script."
exit 1
fi
if [ ! -d "$FRAMEWORK_DIR/scripts" ]; then
error "Framework not found at $FRAMEWORK_DIR."
echo " Install the framework first:"
echo " curl -fsSL https://raw.githubusercontent.com/<user>/automaton/main/scripts/install.sh | bash -s -- <git-url>"
echo " Or: git clone <git-url> ~/.automaton && bash ~/.automaton/scripts/install.sh"
exit 1
fi
echo ""
echo "========================================"
echo " Automaton Project Onboarding"
echo " Project: $PROJECT_DIR"
echo "========================================"
echo ""
# --- Step 1: Create .automaton/ skeleton ---
AUTO_DIR="$PROJECT_DIR/.automaton"
if [ -d "$AUTO_DIR" ]; then
warn "$AUTO_DIR already exists — skipping skeleton creation"
else
info "Creating .automaton/ skeleton..."
mkdir -p "$AUTO_DIR/tasks" "$AUTO_DIR/loops" "$AUTO_DIR/design"
ok "Created $AUTO_DIR/"
fi
# --- Step 2: Models ---
MODELS_FILE="$AUTO_DIR/models.json"
if [ -f "$MODELS_FILE" ]; then
warn "$MODELS_FILE already exists — skipping model detection"
else
info "Probing local models..."
if python3 "$FRAMEWORK_DIR/scripts/detect_models.py" --write --project "$PROJECT_DIR" 2>/dev/null; then
ok "Detected models written to $MODELS_FILE"
else
info "Auto-detection failed. Creating minimal models.json..."
cat > "$MODELS_FILE" <<- 'EOF'
{
"models": [
{"name": "default-model", "provider": "local", "context": 32768}
],
"default": "default-model"
}
EOF
warn "Edit $MODELS_FILE to set your actual model(s)."
fi
fi
# --- Step 3: Config ---
CONFIG_FILE="$AUTO_DIR/config.md"
if [ -f "$CONFIG_FILE" ]; then
warn "$CONFIG_FILE already exists — skipping"
else
info "Creating config.md..."
PROJECT_NAME="$(basename "$PROJECT_DIR")"
if [ -f "$FRAMEWORK_DIR/scripts/vram_detect.py" ]; then
vram_output=$(cd "$FRAMEWORK_DIR" && python3 "$FRAMEWORK_DIR/scripts/vram_detect.py" 2>/dev/null || true)
json_part=$(echo "$vram_output" | sed -n '/=== JSON Output ===/,$p' | tail -n +2)
recommended=$(echo "$json_part" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("recommended_k","16"))' 2>/dev/null || echo "16")
else
recommended="16"
fi
cat > "$CONFIG_FILE" <<- EOF
# $PROJECT_NAME — Automaton Configuration
## VRAM Configuration
- **Auto-detect**: Yes
- **Target context**: ${recommended}k tokens
- **Headroom**: 25%
- **Max peak context per sub-task**: $((recommended * 3 / 4))k tokens
## Model Configuration
# Uses models.json for model divergence enforcement.
# Default model is read from models.json's "default" key.
EOF
ok "Created $CONFIG_FILE"
fi
# --- Step 4: Project name ---
NAME_FILE="$AUTO_DIR/project-name.md"
if [ -f "$NAME_FILE" ]; then
warn "$NAME_FILE already exists — skipping"
else
PROJECT_NAME="$(basename "$PROJECT_DIR")"
echo "$PROJECT_NAME" > "$NAME_FILE"
ok "Created $NAME_FILE ($PROJECT_NAME)"
fi
# --- Step 5: Git ---
GIT_DIR="$PROJECT_DIR/.git"
if [ -d "$GIT_DIR" ]; then
ok "Git repository already initialized"
else
info "Initializing git repository..."
cd "$PROJECT_DIR" && git init
ok "Git initialized"
fi
# --- Step 6: Git hooks ---
if [ -d "$GIT_DIR" ]; then
info "Installing git hooks..."
bash "$FRAMEWORK_DIR/scripts/install-hooks.sh" "$PROJECT_DIR"
fi
# --- Step 7: .gitignore ---
GITIGNORE="$PROJECT_DIR/.gitignore"
if [ -f "$GITIGNORE" ]; then
if ! grep -q ".automaton/tasks/" "$GITIGNORE" 2>/dev/null; then
echo "" >> "$GITIGNORE"
echo "# Automaton" >> "$GITIGNORE"
echo ".automaton/tasks/" >> "$GITIGNORE"
echo ".automaton/loops/*/worktree/" >> "$GITIGNORE"
warn "Added automaton entries to .gitignore"
fi
else
cat > "$GITIGNORE" <<- 'EOF'
# Automaton
.automaton/tasks/
.automaton/loops/*/worktree/
.automaton/loops/*/outputs/
EOF
ok "Created .gitignore with automaton entries"
fi
# --- Step 8: Verify ---
info "Verifying setup..."
cd "$FRAMEWORK_DIR"
python3 "$FRAMEWORK_DIR/scripts/status.py" --project "$PROJECT_DIR" --audit 2>&1 | head -5 || true
echo ""
echo "========================================"
echo -e "${GREEN} Onboarding complete!${NC}"
echo "========================================"
echo ""
echo " Project: $PROJECT_DIR"
echo " Config: $CONFIG_FILE"
echo " Models: $MODELS_FILE"
echo ""
echo " Next steps:"
echo " 1. cd $PROJECT_DIR"
echo " 2. Create a task:"
echo " python3 ~/.automaton/scripts/status.py --create-task my-first-task --project ."
echo " 3. Start working with your agent."
echo ""