Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Rules for AI coding agents (Claude Code, Codex, Aider, etc.) helping work on thi

Agents do not open PRs against this repo. The human contributor reviews the change end-to-end first, *then* opens the PR themselves. If a maintainer sees a PR opened directly by a `[bot]` account or a fresh account that obviously didn't write the description, it gets closed.

Exception: when AnmolNoor is the author actively working on the change, agents may follow direct PR instructions from AnmolNoor without this rule blocking the PR.

## Surgical changes

- Touch only what the task requires.
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ If any of that is a dealbreaker, that's totally fair — come back in a few mont
./scripts/uv run foundation
```

You'll need Python 3.12 and an API key for either OpenAI or Ollama. See [`docs/TECHNICAL.md`](docs/TECHNICAL.md) for full setup, configuration, the CLI surface, and architecture notes.
You'll need Python 3.12 and an API key for either OpenAI or Ollama. On a fresh Mac, start with the macOS walkthrough in [`docs/how-to-use.md`](docs/how-to-use.md). See [`docs/TECHNICAL.md`](docs/TECHNICAL.md) for the full configuration surface, CLI commands, and architecture notes.

## Where things live

- [`docs/how-to-use.md`](docs/how-to-use.md) — macOS setup and first-run commands.
- [`docs/TECHNICAL.md`](docs/TECHNICAL.md) — the detailed README: features, configuration, commands, layout, limitations.
- [`docs/monitor-protocol.md`](docs/monitor-protocol.md) — event-log wire format and live transports.
- [`plans/`](plans/) — stage-by-stage implementation plans. These are the prompts/specs the agent worked from. Probably the most honest record of how the project actually got built.
Expand Down
122 changes: 122 additions & 0 deletions docs/how-to-use.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# How to Use Foundation CLI

This is the shortest path for a macOS user trying the repo from a fresh machine.

## 1. Install Python 3.12

Foundation CLI requires Python 3.12. On macOS, Homebrew is the simplest install path:

```bash
brew install python@3.12
python3.12 --version
```

If `python3.12` is still not on your `PATH`, use the explicit Homebrew path when bootstrapping:

```bash
PYTHON=/opt/homebrew/bin/python3.12 ./scripts/bootstrap.sh
```

On Intel Macs, the Homebrew path may be:

```bash
PYTHON=/usr/local/bin/python3.12 ./scripts/bootstrap.sh
```

## 2. Bootstrap the repo

```bash
./scripts/bootstrap.sh
./scripts/uv run foundation --help
```

## 3. Configure Ollama Cloud

Create the Foundation config directory:

```bash
mkdir -p "$HOME/Library/Application Support/foundation"
```

Create `config.toml`:

```bash
cat > "$HOME/Library/Application Support/foundation/config.toml" <<'EOF'
[provider]
name = "ollama"
model = "qwen3.5:397b-cloud"
base_url = "https://ollama.com/api"
request_timeout_seconds = 180
api_key_env_var = "OLLAMA_API_KEY"
EOF
```

Add your Ollama Cloud API key to the paired env file:

```bash
cat > "$HOME/Library/Application Support/foundation/foundation.env" <<'EOF'
OLLAMA_API_KEY=your-ollama-cloud-api-key
EOF
```

Then verify the setup:

```bash
./scripts/uv run foundation doctor
```

You want to see `Provider: ollama`, `Base URL: https://ollama.com/api`, and a secret lookup line saying credentials resolved from `$OLLAMA_API_KEY`.

## OpenAI

To use OpenAI instead, use this provider config:

```bash
cat > "$HOME/Library/Application Support/foundation/config.toml" <<'EOF'
[provider]
name = "openai"
model = "gpt-5-mini"
request_timeout_seconds = 180
api_key_env_var = "OPENAI_API_KEY"
EOF
```

Add your OpenAI API key to the paired env file:

```bash
cat > "$HOME/Library/Application Support/foundation/foundation.env" <<'EOF'
OPENAI_API_KEY=your-openai-api-key
EOF
```

Then verify with:

```bash
./scripts/uv run foundation doctor
```

You want to see `Provider: openai`, `Base URL: https://api.openai.com/v1`, and a secret lookup line saying credentials resolved from `$OPENAI_API_KEY`.

## 4. Start Foundation

```bash
./scripts/uv run foundation
```

## Local Ollama

Local Ollama usually does not need an API key. Use a local model name and local base URL instead:

```toml
[provider]
name = "ollama"
model = "gpt-oss:20b"
base_url = "http://localhost:11434/api"
request_timeout_seconds = 180
```

Then verify with:

```bash
./scripts/uv run foundation doctor
```
136 changes: 135 additions & 1 deletion scripts/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,144 @@ set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
VENV_DIR="${ROOT_DIR}/.venv"
PYTHON_BIN="${PYTHON:-python3.12}"
PIP_BIN="${VENV_DIR}/bin/pip"
UV_BIN="${VENV_DIR}/bin/uv"

resolve_command() {
local candidate="$1"

if [[ "${candidate}" == */* ]]; then
if [[ -x "${candidate}" ]]; then
printf '%s\n' "${candidate}"
return 0
fi
return 1
fi

command -v "${candidate}" 2>/dev/null
}

is_python_312() {
"$1" -c 'import sys; raise SystemExit(0 if sys.version_info[:2] == (3, 12) else 1)' >/dev/null 2>&1
}

print_python_install_hint() {
local os_name
os_name="$(uname -s 2>/dev/null || printf 'unknown')"

printf 'Python 3.12 is required to bootstrap Foundation CLI.\n' >&2

case "${os_name}" in
Darwin)
if command -v brew >/dev/null 2>&1; then
cat >&2 <<'EOF'

Install it with Homebrew:
brew install python@3.12

Then rerun:
./scripts/bootstrap.sh

If Homebrew installed Python outside your PATH, rerun with:
PYTHON=/opt/homebrew/bin/python3.12 ./scripts/bootstrap.sh
EOF
else
cat >&2 <<'EOF'

Install Homebrew from https://brew.sh, then run:
brew install python@3.12
./scripts/bootstrap.sh

Or install Python 3.12 from https://www.python.org/downloads/ and rerun with:
PYTHON=/path/to/python3.12 ./scripts/bootstrap.sh
EOF
fi
;;
Linux)
if command -v apt-get >/dev/null 2>&1; then
cat >&2 <<'EOF'

Install it with apt:
sudo apt-get update
sudo apt-get install -y python3.12 python3.12-venv
EOF
elif command -v dnf >/dev/null 2>&1; then
cat >&2 <<'EOF'

Install it with dnf:
sudo dnf install python3.12
EOF
elif command -v yum >/dev/null 2>&1; then
cat >&2 <<'EOF'

Install it with yum:
sudo yum install python3.12
EOF
elif command -v pacman >/dev/null 2>&1; then
cat >&2 <<'EOF'

Install it with pacman:
sudo pacman -S python
EOF
elif command -v apk >/dev/null 2>&1; then
cat >&2 <<'EOF'

Install it with apk:
sudo apk add python3
EOF
else
cat >&2 <<'EOF'

Install Python 3.12 with your system package manager, then rerun:
./scripts/bootstrap.sh
EOF
fi
;;
*)
cat >&2 <<'EOF'

Install Python 3.12, then rerun:
./scripts/bootstrap.sh
EOF
;;
esac
}

find_python_312() {
local python_override="${PYTHON:-}"
local candidate
local resolved

if [[ -n "${python_override}" ]]; then
if ! resolved="$(resolve_command "${python_override}")"; then
printf 'Requested Python interpreter not found: PYTHON=%s\n\n' "${python_override}" >&2
print_python_install_hint
return 1
fi

if ! is_python_312 "${resolved}"; then
printf 'Requested Python interpreter is not Python 3.12: %s\n\n' "${resolved}" >&2
print_python_install_hint
return 1
fi

printf '%s\n' "${resolved}"
return 0
fi

for candidate in python3.12 /opt/homebrew/bin/python3.12 /usr/local/bin/python3.12; do
if resolved="$(resolve_command "${candidate}")" && is_python_312 "${resolved}"; then
printf '%s\n' "${resolved}"
return 0
fi
done

print_python_install_hint
return 1
}

PYTHON_BIN="$(find_python_312)"

if [[ ! -d "${VENV_DIR}" ]]; then
"${PYTHON_BIN}" -m venv "${VENV_DIR}"
fi
Expand Down
1 change: 1 addition & 0 deletions src/foundation/models/orchestration.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class LoopStopReason(StrEnum):
MAX_ITERATIONS = "max_iterations"
MAX_ACTIONS = "max_actions"
NO_PROGRESS = "no_progress"
TERMINAL_POLICY_BLOCK = "terminal_policy_block"


class VerificationOutcome(StrEnum):
Expand Down
Loading