diff --git a/.agents/provision b/.agents/provision new file mode 100755 index 0000000000..692bb9b7c2 --- /dev/null +++ b/.agents/provision @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Agent worktree provisioning hook: set this tree up (uv venv/sync, .envrc, direnv). +SELF=$0 +case "$SELF" in + */*) ;; + *) SELF=$(command -v -- "$SELF") ;; +esac +SCRIPT_DIR="$(cd "$(dirname "$SELF")" && pwd)" +exec "$SCRIPT_DIR/../bin/worktree" provision "$@" diff --git a/bin/worktree b/bin/worktree index 5bd5d776e9..760a5b7309 100755 --- a/bin/worktree +++ b/bin/worktree @@ -6,9 +6,12 @@ # bin/worktree new --dir ../path # override the worktree directory # bin/worktree ls # list worktrees (branch = value for rm) # bin/worktree rm [-b] # remove a worktree (-b also deletes the branch) +# bin/worktree provision [dir] # (re)provision an existing worktree in place # -# `new` (aka create/add) provisions the worktree so mypy/pytest/pre-commit work -# out of the box (uv venv + `uv sync --all-groups`, `.envrc` symlink, direnv allow). +# `new` provisions the worktree so mypy/pytest/pre-commit work out of the box +# (uv venv + `uv sync --all-groups`, `.envrc` symlink, direnv allow) by calling +# `provision` — which is idempotent, so you can also run it on a bare tree +# (e.g. a plain `git worktree add`) to set it up after the fact. # # Examples: # bin/worktree new feat/ivan/foo # new branch off current HEAD -> ../dimos-foo @@ -18,10 +21,11 @@ # bin/worktree rm feat/ivan/foo -b # remove worktree and delete the branch set -euo pipefail -REPO_ROOT=$(git rev-parse --path-format=absolute --git-common-dir) +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +REPO_ROOT=$(git -C "$SCRIPT_DIR" rev-parse --path-format=absolute --git-common-dir) REPO_ROOT=$(cd "$(dirname "$REPO_ROOT")" && pwd) # main worktree root -usage() { sed -n '2,18p' "$0" | sed 's/^# \{0,1\}//'; } +usage() { sed -n '2,21p' "$0" | sed 's/^# \{0,1\}//'; } # Default worktree dir for a branch: ../dimos-, sanitized. default_dir() { @@ -125,6 +129,25 @@ cmd_add() { GIT_LFS_SKIP_SMUDGE=1 git worktree add -b "$BRANCH" "$DIR" "${BASE:-HEAD}" fi + cmd_provision "$DIR" +} + +cmd_provision() { + # Provision an EXISTING worktree in place so mypy/pytest/pre-commit work out + # of the box: .envrc symlink + uv venv + `uv sync --all-groups` + direnv + # allow. Idempotent, so it's safe to re-run and `new` calls it after adding + # the tree; you can also point it at a bare `git worktree add` dir to set it + # up after the fact. With no argument, provisions the worktree this script + # lives in (not the caller's cwd). + local DIR="${1:-}" resolved="" + [[ -n "$DIR" ]] || DIR=$(git -C "$SCRIPT_DIR/.." rev-parse --show-toplevel) + # realpath -m (resolve non-existent paths) is GNU-only; fall back for BSD/macOS. + if resolved=$(realpath -m "$DIR" 2>/dev/null); then + DIR=$resolved + else + DIR=$(cd "$(dirname "$DIR")" && echo "$PWD/$(basename "$DIR")") + fi + [[ -d "$DIR" ]] || { echo "not a directory: $DIR" >&2; exit 1; } cd "$DIR" # Per-worktree env (.envrc is gitignored, so each worktree needs its own). @@ -139,9 +162,9 @@ cmd_add() { # the system toolchain directly. echo ">>> uv venv + sync via $envrc (may take a few min on first run)" if [[ "$envrc" == .envrc.nix ]]; then - nix develop --command bash -c 'uv venv --python 3.12 && uv sync --all-groups' + nix develop --command bash -c 'uv venv --python 3.12 --allow-existing && uv sync --all-groups' else - uv venv --python 3.12 && uv sync --all-groups + uv venv --python 3.12 --allow-existing && uv sync --all-groups fi # Let direnv auto-activate it next time you cd in. @@ -161,5 +184,6 @@ case "${1:-}" in ls|list) shift; cmd_ls "$@" ;; rm|remove) shift; cmd_rm "$@" ;; new|create|add) shift; cmd_add "$@" ;; + provision) shift; cmd_provision "$@" ;; *) echo "unknown command: $1 (did you mean 'new'?)" >&2; echo >&2; usage >&2; exit 1 ;; esac