From b880f2535af40b1bb73ab3b63933b53fd8b972a9 Mon Sep 17 00:00:00 2001 From: Lap Tran Date: Fri, 26 Jun 2026 13:54:28 -0400 Subject: [PATCH] fix(install): make install.sh idempotent + add onboard-project.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- README.md | 67 +++++++++---- scripts/automaton-cleanup.sh | 2 +- scripts/install.sh | 62 ++++++------ scripts/onboard-project.sh | 184 +++++++++++++++++++++++++++++++++++ 4 files changed, 266 insertions(+), 49 deletions(-) create mode 100755 scripts/onboard-project.sh diff --git a/README.md b/README.md index 88cf76a..e7238b8 100644 --- a/README.md +++ b/README.md @@ -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. -**Run these commands in your terminal:** +Choose one of the following methods: + +### Option A: One-liner (curl pipe, recommended) ```bash -# Clone the framework into the global config directory -git clone ~/.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 +curl -fsSL https://raw.githubusercontent.com//automaton/main/scripts/install.sh | bash -s -- ``` -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 ~/.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).* @@ -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. -### 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: > *"Onboard this project into automaton." @@ -60,17 +83,21 @@ The agent will automatically: 3. Generate your `.agent.md` and `.rules.md` files. 4. Initiate the "Exploration Ritual" to understand your codebase. -### Option B: The Manual Way -If you prefer to set it up manually, create a `.automaton/` directory in your project root and add: -- `.agent.md`: Project-specific configuration (Mode, rules, etc.). -- `.rules.md`: Project-specific constraints and past failure modes. +### Option C: The Manual Way +If you prefer to set it up manually: -Then install the git pre-commit hook: ```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 diff --git a/scripts/automaton-cleanup.sh b/scripts/automaton-cleanup.sh index 9b256f5..6621a26 100755 --- a/scripts/automaton-cleanup.sh +++ b/scripts/automaton-cleanup.sh @@ -1,2 +1,2 @@ #!/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" diff --git a/scripts/install.sh b/scripts/install.sh index 80229b9..4dad47e 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -3,27 +3,39 @@ set -e 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:-}" -if [ -z "$GIT_URL" ]; then - echo "ERROR: Git URL required." - echo "Usage: ./install.sh " - echo "Example: ./install.sh https://github.com/user/automaton.git" + +if [ ! -d "$FRAMEWORK_DIR" ]; then + # Fresh install — need a Git URL to clone + if [ -z "$GIT_URL" ]; then + echo "ERROR: Git URL required for fresh install." + echo "" + echo "Usage:" + echo " curl -fsSL https://raw.githubusercontent.com//automaton/main/scripts/install.sh | bash -s -- " + echo "" + echo " --- or ---" + echo "" + echo " git clone ~/.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 "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" +# --- Everything below is idempotent and runs on both fresh and existing installs --- -echo "" echo "=== VRAM / Context Detection ===" echo "Detecting your system's VRAM to recommend task decomposition settings..." echo "" @@ -31,7 +43,7 @@ echo "" # Run VRAM detection script if it exists if [ -f "$FRAMEWORK_DIR/scripts/vram_detect.py" ]; then # 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 ===") 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" # Extract key values from JSON using Python - recommended_k=$(echo "$json_output" | python3 -c 'import json,sys; print(json.load(sys.stdin)["recommended_k"])') - max_peak_kb=$(echo "$json_output" | python3 -c 'import json,sys; print(json.load(sys.stdin)["max_peak_context_kb"])') - headroom=$(echo "$json_output" | python3 -c 'import json,sys; print(json.load(sys.stdin)["headroom"])') - 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"])') + 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"])' 2>/dev/null || echo "?") + headroom=$(echo "$json_output" | python3 -c 'import json,sys; print(json.load(sys.stdin)["headroom"])' 2>/dev/null || echo "?") echo "" echo "=== Recommended VRAM Configuration ===" @@ -72,11 +81,8 @@ echo "" echo "Installation complete." echo "" echo "Next steps:" -echo " 1. cd into a project and run the onboarding prompt" -echo " 2. In each project that uses git, install the automaton hooks:" -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 " 1. Onboard a project: bash ~/.automaton/scripts/onboard-project.sh /path/to/project" +echo " 2. Or tell your agent: 'Onboard this project into automaton'" echo "" # Register pre-edit guards for detected harnesses diff --git a/scripts/onboard-project.sh b/scripts/onboard-project.sh new file mode 100755 index 0000000..7943fdf --- /dev/null +++ b/scripts/onboard-project.sh @@ -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//automaton/main/scripts/install.sh | bash -s -- " + echo " Or: git clone ~/.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 "" \ No newline at end of file