./aipass repo-root launcher — cold-clone entry (DPLAN-0234 Strand B, built by @aipass)

- New stdlib-only bash launcher at repo root: pre-setup only 'install' works (delegates to setup.sh, full flag pass-through); post-setup execs the venv aipass binary transparently
- 13 launcher tests (tests/test_launcher.py), bypass.json architecture entry for the test file
- README Quick Start leads with ./aipass install; CHANGELOG entry
This commit is contained in:
AIOSAI
2026-07-05 15:50:01 -07:00
parent 378ac1f98c
commit c8e1f07fdb
5 changed files with 280 additions and 4 deletions
+14
View File
@@ -13,6 +13,20 @@ PyPI version — not the changelog header.
### Added ### Added
- **`./aipass` — repo-root cold-clone launcher (DPLAN-0234 Strand B).** The
branded entry point for the clone-first flow: `git clone`, `cd AIPass`,
`./aipass install` — three commands to a working AIPass. Stdlib-only bash
(zero deps, runs before anything is installed): pre-setup, only the `install`
verb exists and delegates to `setup.sh` with full flag pass-through
(`--no-init` / `--with-init` / `--project`); any other verb prints help
pointing at `./aipass install`. Post-setup the launcher turns transparent —
it execs the venv `aipass` binary for everything, so `./aipass doctor` just
works. Bare `aipass` always resolves to the PATH binary; the launcher only
ever runs as an explicit `./aipass`. 13 launcher tests (file properties,
pre-setup help, install delegation, post-setup forwarding), @aipass suite
622 green, seedgo 100%. README Quick Start now leads with `./aipass install`.
(built by @aipass)
- **`./setup.sh` chains into `aipass init run` — clone-first one-command install - **`./setup.sh` chains into `aipass init run` — clone-first one-command install
(DPLAN-0234 Strand A).** Distribution is git-clone, not pip: the framework (DPLAN-0234 Strand A).** Distribution is git-clone, not pip: the framework
changes constantly, so a PyPI snapshot goes stale while a clone is always changes constantly, so a PyPI snapshot goes stale while a clone is always
+6 -4
View File
@@ -32,10 +32,11 @@ AIPass is a CLI-native scaffold that adds **persistent memory, identity, and coo
```bash ```bash
git clone https://github.com/AIOSAI/AIPass.git git clone https://github.com/AIOSAI/AIPass.git
cd AIPass && ./setup.sh # installs everything, then walks you into your first project cd AIPass
./aipass install # installs everything, then walks you into your first project
``` ```
Setup builds the environment, puts `aipass` + `drone` on your PATH, then chains straight into a guided init that creates your project, your first agent, and opens a terminal where that agent is already running. Say "hi" — it knows who it is. Come back tomorrow — it remembers. One command does it all: builds the environment, puts `aipass` + `drone` on your PATH, then chains straight into a guided init that creates your project, your first agent, and opens a terminal where that agent is already running. Say "hi" — it knows who it is. Come back tomorrow — it remembers.
This is the base framework. It gives your agents the infrastructure to persist, communicate, and organize — everything else you build on top. This is the base framework. It gives your agents the infrastructure to persist, communicate, and organize — everything else you build on top.
@@ -81,10 +82,11 @@ aipass init agent my-agent # Full agent: apps, mail, memory, identity
```bash ```bash
git clone https://github.com/AIOSAI/AIPass.git git clone https://github.com/AIOSAI/AIPass.git
cd AIPass && ./setup.sh # Creates venv, installs, puts `aipass` + `drone` on your PATH, bootstraps 17 agents cd AIPass
./aipass install # Creates venv, installs, puts `aipass` + `drone` on your PATH, bootstraps 17 agents
``` ```
On an interactive terminal, setup ends by chaining into `aipass init run` — one command takes you from clone to a working first project. Pass `--no-init` to skip the chain, `--project <dir>` to pick where the project lands (CI and piped shells skip automatically). On an interactive terminal, install ends by chaining into `aipass init run` — one command takes you from clone to a working first project. Pass `--no-init` to skip the chain, `--project <dir>` to pick where the project lands (CI and piped shells skip automatically). `./aipass` is a thin repo-root launcher over `setup.sh`; after setup it simply forwards to the installed `aipass` binary.
### 2. Your own project (if you skipped the chain) ### 2. Your own project (if you skipped the chain)
Executable
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# aipass — cold-clone entry point (pre-venv launcher)
#
# The first command after cloning:
# git clone https://github.com/AIOSAI/AIPass.git
# cd AIPass
# ./aipass install
#
# Pre-setup: only `install` is available — delegates to setup.sh.
# Post-setup: forwards everything to the venv-installed aipass binary.
#
# Stdlib-only, zero AIPass imports, zero third-party deps.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# --- Post-setup: forward to the real binary ---
if [[ -x "$SCRIPT_DIR/.venv/bin/aipass" ]]; then
exec "$SCRIPT_DIR/.venv/bin/aipass" "$@"
fi
if [[ -x "$SCRIPT_DIR/.venv/Scripts/aipass.exe" ]]; then
exec "$SCRIPT_DIR/.venv/Scripts/aipass.exe" "$@"
fi
if [[ -x "$SCRIPT_DIR/.venv/Scripts/aipass" ]]; then
exec "$SCRIPT_DIR/.venv/Scripts/aipass" "$@"
fi
# --- Pre-setup: only 'install' works ---
if [[ "${1:-}" == "install" ]]; then
shift
exec bash "$SCRIPT_DIR/setup.sh" "$@"
fi
# --- Help (no venv, no 'install' verb) ---
cat <<'HELP'
AIPass is not set up yet.
./aipass install # set up AIPass (venv + deps + hooks)
./aipass install --no-init # set up without the guided first-project setup
./aipass install --project DIR # set the first-project directory
After setup, all aipass commands become available:
./aipass doctor # system health
./aipass help # how-does-X-work Q&A
./aipass init run # scaffold a new project
Quick start:
git clone https://github.com/AIOSAI/AIPass.git
cd AIPass
./aipass install
HELP
+5
View File
@@ -101,6 +101,11 @@
"standard": "encapsulation", "standard": "encapsulation",
"reason": "Unit tests must import the install module directly to test home resolution, clone, and setup orchestration in isolation. Entry-point imports would defeat the purpose of unit testing." "reason": "Unit tests must import the install module directly to test home resolution, clone, and setup orchestration in isolation. Entry-point imports would defeat the purpose of unit testing."
}, },
{
"file": "tests/test_launcher.py",
"standard": "architecture",
"reason": "Test file lives in tests/ by convention — not in apps/. Standard 3-layer structure applies to production code only."
},
{ {
"file": "tests/test_ping_sweep.py", "file": "tests/test_ping_sweep.py",
"standard": "architecture", "standard": "architecture",
+203
View File
@@ -0,0 +1,203 @@
# =================== AIPass ====================
# Name: test_launcher.py
# Description: Tests for the repo-root ./aipass cold-clone launcher
# Version: 1.0.0
# Created: 2026-07-05
# Modified: 2026-07-05
# =============================================
"""Tests for the repo-root ./aipass cold-clone launcher (bash script)."""
from __future__ import annotations
import os
import shutil
import subprocess
import sys
from pathlib import Path
import pytest
pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="bash launcher requires Unix")
REPO_ROOT = Path(__file__).resolve().parents[4]
LAUNCHER = REPO_ROOT / "aipass"
_EXEC_BITS = 0o111
def _make_executable(path: Path) -> None:
"""Set executable bits on a file."""
path.chmod(path.stat().st_mode | _EXEC_BITS)
def _isolated_launcher(tmp_path: Path) -> Path:
"""Copy the launcher to a temp dir (isolated from real .venv)."""
dest = tmp_path / "aipass"
shutil.copy2(LAUNCHER, dest)
_make_executable(dest)
return dest
def _fake_venv_binary(tmp_path: Path, body: str) -> Path:
"""Create a fake .venv/bin/aipass that runs the given bash body."""
venv_bin = tmp_path / ".venv" / "bin"
venv_bin.mkdir(parents=True)
fake = venv_bin / "aipass"
fake.write_text(f"#!/usr/bin/env bash\n{body}\n")
_make_executable(fake)
return fake
class TestLauncherProperties:
"""Verify the launcher file's basic properties."""
def test_exists(self):
"""Launcher file exists at repo root."""
assert LAUNCHER.is_file()
def test_executable(self):
"""Launcher has executable permission."""
assert os.access(LAUNCHER, os.X_OK)
def test_shebang(self):
"""Launcher starts with bash shebang."""
first_line = LAUNCHER.read_text().splitlines()[0]
assert first_line == "#!/usr/bin/env bash"
def test_no_python_imports(self):
"""Launcher contains no Python imports (stdlib-only bash)."""
content = LAUNCHER.read_text()
assert "import " not in content
assert "from " not in content
class TestPreSetupHelp:
"""Pre-setup (no venv): bare ./aipass prints help text."""
def test_no_args_shows_help(self, tmp_path):
"""No args and no venv prints setup instructions."""
launcher = _isolated_launcher(tmp_path)
result = subprocess.run(
["bash", str(launcher)],
capture_output=True,
text=True,
timeout=5,
)
assert "not set up yet" in result.stdout
assert "./aipass install" in result.stdout
def test_unknown_verb_shows_help(self, tmp_path):
"""Unknown verb pre-setup prints help, not an error."""
launcher = _isolated_launcher(tmp_path)
result = subprocess.run(
["bash", str(launcher), "doctor"],
capture_output=True,
text=True,
timeout=5,
)
assert "not set up yet" in result.stdout
def test_help_mentions_quick_start(self, tmp_path):
"""Help text includes the Quick start snippet."""
launcher = _isolated_launcher(tmp_path)
result = subprocess.run(
["bash", str(launcher)],
capture_output=True,
text=True,
timeout=5,
)
assert "Quick start" in result.stdout
assert "git clone" in result.stdout
class TestPreSetupInstall:
"""Pre-setup: ./aipass install delegates to setup.sh."""
def test_install_calls_setup_sh(self, tmp_path):
"""./aipass install execs setup.sh in the same directory."""
launcher = _isolated_launcher(tmp_path)
setup = tmp_path / "setup.sh"
setup.write_text('#!/usr/bin/env bash\necho "SETUP_CALLED $@"\n')
_make_executable(setup)
result = subprocess.run(
["bash", str(launcher), "install"],
capture_output=True,
text=True,
timeout=5,
)
assert "SETUP_CALLED" in result.stdout
def test_install_passes_flags(self, tmp_path):
"""Flags after 'install' pass through to setup.sh."""
launcher = _isolated_launcher(tmp_path)
setup = tmp_path / "setup.sh"
setup.write_text('#!/usr/bin/env bash\necho "FLAGS:$@"\n')
_make_executable(setup)
result = subprocess.run(
["bash", str(launcher), "install", "--no-init"],
capture_output=True,
text=True,
timeout=5,
)
assert "--no-init" in result.stdout
def test_install_passes_project_flag(self, tmp_path):
"""--project and its value pass through to setup.sh."""
launcher = _isolated_launcher(tmp_path)
setup = tmp_path / "setup.sh"
setup.write_text('#!/usr/bin/env bash\necho "FLAGS:$@"\n')
_make_executable(setup)
result = subprocess.run(
["bash", str(launcher), "install", "--project", str(tmp_path / "proj")],
capture_output=True,
text=True,
timeout=5,
)
assert "--project" in result.stdout
class TestPostSetupForwarding:
"""Post-setup: forwards to the venv binary."""
def test_forwards_to_venv_binary(self, tmp_path):
"""With venv binary present, verbs forward to it."""
launcher = _isolated_launcher(tmp_path)
_fake_venv_binary(tmp_path, 'echo "VENV_AIPASS $@"')
result = subprocess.run(
["bash", str(launcher), "doctor"],
capture_output=True,
text=True,
timeout=5,
)
assert "VENV_AIPASS doctor" in result.stdout
def test_forwards_install_post_setup(self, tmp_path):
"""Post-setup ./aipass install goes to venv binary, not setup.sh."""
launcher = _isolated_launcher(tmp_path)
_fake_venv_binary(tmp_path, 'echo "VENV_AIPASS $@"')
result = subprocess.run(
["bash", str(launcher), "install"],
capture_output=True,
text=True,
timeout=5,
)
assert "VENV_AIPASS install" in result.stdout
def test_no_args_forwards_post_setup(self, tmp_path):
"""Post-setup bare ./aipass forwards to venv binary."""
launcher = _isolated_launcher(tmp_path)
_fake_venv_binary(tmp_path, 'echo "VENV_AIPASS_NOARGS"')
result = subprocess.run(
["bash", str(launcher)],
capture_output=True,
text=True,
timeout=5,
)
assert "VENV_AIPASS_NOARGS" in result.stdout