feat: project-orchestrator — engine@v0.1.0 submodule, PO config, fleet.toml registry, mgmt scripts, docs, Nix
The PO is itself a project using the agent-orchestrator harness (engine/ submodule pinned at v0.1.0). Adds: agents.toml (one persistent fleet-management agent) + prompts/; fleet.toml (the sole project<->harness<->ref registry) + docs/fleet-registry.md; scripts/ (fleet.py + create/start/stop/update-project.sh); docs/manage-projects.md + docs/bootstrap.md; flake.nix/.lock devShell (python311+tmux+git); README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
117
scripts/create-project.sh
Executable file
117
scripts/create-project.sh
Executable file
@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env bash
|
||||
# create-project.sh — scaffold a NEW project that uses a harness.
|
||||
#
|
||||
# Produces a self-contained project repo: the chosen harness vendored as the `engine/` submodule at
|
||||
# a pinned ref, plus a harness config scaffolded by the harness's own `init`. The project contains
|
||||
# NO project-orchestrator / fleet metadata — knowledge is one-directional (PO → project). Registering
|
||||
# the project in the PO's fleet.toml is a SEPARATE, PO-side step (use --register, or edit fleet.toml
|
||||
# by hand); nothing about the fleet ever lands inside the project repo.
|
||||
#
|
||||
# Usage:
|
||||
# scripts/create-project.sh <name> [options]
|
||||
#
|
||||
# Options:
|
||||
# --dir <parent> parent directory to create the project under (default: ./projects)
|
||||
# --engine-url <url> harness repo to vendor as engine/ (default: the agent-orchestrator repo)
|
||||
# --ref <ref> harness ref to pin the submodule at (default: v0.1.0)
|
||||
# --prefix <prefix> session_prefix to write into the project's config (default: <name>-)
|
||||
# --register also append a [[project]] entry to this PO's fleet.toml (PO-side only)
|
||||
# --no-commit leave the project tree uncommitted (default: make an initial commit)
|
||||
#
|
||||
# Drive it by hand afterwards, exactly like any project:
|
||||
# cd <parent>/<name> && python3 engine/agents.py status
|
||||
set -euo pipefail
|
||||
|
||||
die() { echo "create-project: $*" >&2; exit 1; }
|
||||
|
||||
[ $# -ge 1 ] || die "usage: create-project.sh <name> [--dir P] [--engine-url U] [--ref R] [--prefix X] [--register] [--no-commit]"
|
||||
NAME="$1"; shift
|
||||
[[ "$NAME" =~ ^[a-z0-9][a-z0-9-]*$ ]] || die "name must be kebab-case (got: $NAME)"
|
||||
|
||||
PO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
PARENT="$PO_ROOT/projects"
|
||||
ENGINE_URL="https://git.autonomic.zone/recipe-maintainers/agent-orchestrator.git"
|
||||
REF="v0.1.0"
|
||||
PREFIX=""
|
||||
REGISTER=0
|
||||
COMMIT=1
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--dir) PARENT="$2"; shift 2;;
|
||||
--engine-url) ENGINE_URL="$2"; shift 2;;
|
||||
--ref) REF="$2"; shift 2;;
|
||||
--prefix) PREFIX="$2"; shift 2;;
|
||||
--register) REGISTER=1; shift;;
|
||||
--no-commit) COMMIT=0; shift;;
|
||||
*) die "unknown option: $1";;
|
||||
esac
|
||||
done
|
||||
PREFIX="${PREFIX:-${NAME}-}"
|
||||
|
||||
command -v git >/dev/null || die "git not on PATH"
|
||||
command -v python3 >/dev/null || die "python3 not on PATH"
|
||||
|
||||
DEST="$PARENT/$NAME"
|
||||
[ -e "$DEST" ] && die "$DEST already exists — refusing to overwrite"
|
||||
mkdir -p "$PARENT"
|
||||
|
||||
echo "create-project: scaffolding '$NAME' at $DEST (engine $ENGINE_URL @ $REF)"
|
||||
git init -q -b main "$DEST"
|
||||
cd "$DEST"
|
||||
|
||||
# 1) vendor the harness as a pinned submodule under engine/
|
||||
git -c protocol.version=2 submodule add -q "$ENGINE_URL" engine
|
||||
( cd engine && git fetch -q --tags origin && git checkout -q "$REF" )
|
||||
git add .gitmodules engine
|
||||
|
||||
# 2) scaffold the harness config + prompts via the harness's OWN init (no PO/fleet metadata)
|
||||
python3 engine/agents.py init . >/dev/null
|
||||
# stamp the chosen session_prefix into the scaffolded config (keeps namespaces unique per project)
|
||||
python3 - "$PREFIX" <<'PY'
|
||||
import re, sys, pathlib
|
||||
prefix = sys.argv[1]
|
||||
p = pathlib.Path("agents.toml")
|
||||
txt = p.read_text()
|
||||
txt = re.sub(r'session_prefix\s*=\s*"[^"]*"', f'session_prefix = "{prefix}"', txt, count=1)
|
||||
p.write_text(txt)
|
||||
PY
|
||||
|
||||
# 3) ignore runtime state; the project knows nothing about any PO
|
||||
cat > .gitignore <<'EOF'
|
||||
# runtime state + logs (never committed)
|
||||
.ao-state/
|
||||
*.log
|
||||
__pycache__/
|
||||
*.pyc
|
||||
result
|
||||
EOF
|
||||
|
||||
git add agents.toml prompts .gitignore 2>/dev/null || true
|
||||
|
||||
if [ "$COMMIT" -eq 1 ]; then
|
||||
git -c user.name="project-orchestrator" -c user.email="po@localhost" \
|
||||
commit -q -m "init: scaffold $NAME (engine @ $REF)"
|
||||
fi
|
||||
|
||||
echo "create-project: done — $DEST"
|
||||
echo " engine pinned at: $(cd engine && git rev-parse HEAD) ($REF)"
|
||||
echo " config: agents.toml (session_prefix = $PREFIX)"
|
||||
echo " verify it: ( cd $DEST && python3 engine/agents.py status )"
|
||||
|
||||
if [ "$REGISTER" -eq 1 ]; then
|
||||
echo "create-project: registering '$NAME' in $PO_ROOT/fleet.toml"
|
||||
cat >> "$PO_ROOT/fleet.toml" <<EOF
|
||||
|
||||
[[project]]
|
||||
name = "$NAME"
|
||||
location = "$DEST"
|
||||
harness = "agent-orchestrator"
|
||||
ref = "$REF"
|
||||
enabled = false
|
||||
secrets = ".env"
|
||||
config = "agents.toml"
|
||||
notes = "Created by scripts/create-project.sh"
|
||||
EOF
|
||||
python3 "$PO_ROOT/scripts/fleet.py" --file "$PO_ROOT/fleet.toml" validate
|
||||
fi
|
||||
Reference in New Issue
Block a user