aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-18 20:08:56 -0500
committerCraig Jennings <c@cjennings.net>2026-07-18 20:08:56 -0500
commitd2b1bef686fd6c22a6303c13829f88bdd110fa7b (patch)
tree65254d95d52bcfb8eb26b54de96f18ff08c8284e
parent2cb7c1b3957193f74b1ecd1b5e0f437e13092eb1 (diff)
downloadrulesets-d2b1bef686fd6c22a6303c13829f88bdd110fa7b.tar.gz
rulesets-d2b1bef686fd6c22a6303c13829f88bdd110fa7b.zip
feat(bin): launch install-ai from PATH
Added claude-templates/bin/install-ai, a thin launcher that resolves its own path through the symlink chain and execs scripts/install-ai.sh. make install's bin loop symlinks it into ~/.local/bin/install-ai, the same mechanism that links ai and agent-page, so the fresh-project bootstrapper now runs as install-ai from anywhere. The symlink always points at the canonical, so no separate dotfiles copy is needed. Three launcher bats cover the wrapper, including invocation through a symlink from another directory (the case a naive dirname "$0"/.. would break).
-rwxr-xr-xclaude-templates/bin/install-ai23
-rw-r--r--scripts/tests/install-ai.bats28
-rw-r--r--todo.org5
3 files changed, 55 insertions, 1 deletions
diff --git a/claude-templates/bin/install-ai b/claude-templates/bin/install-ai
new file mode 100755
index 0000000..88cb8e5
--- /dev/null
+++ b/claude-templates/bin/install-ai
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+# install-ai — PATH-facing launcher for the fresh-project bootstrapper.
+#
+# make install symlinks this into ~/.local/bin/install-ai (same bin loop that
+# links `ai` and `agent-page`), so `install-ai [--track|--gitignore] [PROJECT]`
+# runs from anywhere. The real logic lives in scripts/install-ai.sh; this
+# resolves its own location through the ~/.local/bin symlink and execs that
+# script by its true repo path, so the script's own repo-root computation
+# (dirname "$0"/..) stays correct. dotfiles needs no copy — the symlink always
+# points at the canonical.
+set -euo pipefail
+
+# Resolve this file through any symlink chain to its real location in the repo.
+source="${BASH_SOURCE[0]}"
+while [ -L "$source" ]; do
+ dir="$(cd -P "$(dirname "$source")" && pwd)"
+ source="$(readlink "$source")"
+ [[ "$source" != /* ]] && source="$dir/$source"
+done
+bindir="$(cd -P "$(dirname "$source")" && pwd)" # <repo>/claude-templates/bin
+repo="$(cd -P "$bindir/../.." && pwd)" # <repo>
+
+exec "$repo/scripts/install-ai.sh" "$@"
diff --git a/scripts/tests/install-ai.bats b/scripts/tests/install-ai.bats
index a7eb3c0..9c6040c 100644
--- a/scripts/tests/install-ai.bats
+++ b/scripts/tests/install-ai.bats
@@ -172,3 +172,31 @@ EOF
grep -q "project-owned entry file" "$TEST_HOME/code/fresh/AGENTS.md"
! grep -q "protocols.org" "$TEST_HOME/code/fresh/AGENTS.md"
}
+
+# --- claude-templates/bin/install-ai launcher --------------------------------
+#
+# The launcher is the PATH-facing front door (make install symlinks it into
+# ~/.local/bin/install-ai, same loop as `ai` and `agent-page`). It resolves its
+# own real path through the symlink and execs scripts/install-ai.sh, so the
+# repo-root computation survives being invoked as a symlink from anywhere.
+
+LAUNCHER="$REAL_REPO/claude-templates/bin/install-ai"
+
+@test "install-ai launcher: exists and is executable" {
+ [ -x "$LAUNCHER" ]
+}
+
+@test "install-ai launcher: --help reaches install-ai.sh through the launcher" {
+ run bash "$LAUNCHER" --help
+ [ "$status" -eq 0 ]
+ [[ "$output" == *"Bootstrap .ai/"* ]]
+}
+
+@test "install-ai launcher: works when invoked as a symlink from another dir" {
+ # Reproduces the ~/.local/bin symlink: a link elsewhere must still resolve
+ # the repo root, not break on dirname of the link path.
+ ln -s "$LAUNCHER" "$TEST_HOME/install-ai"
+ run bash "$TEST_HOME/install-ai" --help
+ [ "$status" -eq 0 ]
+ [[ "$output" == *"Bootstrap .ai/"* ]]
+}
diff --git a/todo.org b/todo.org
index 2fb5fdd..f4b3a55 100644
--- a/todo.org
+++ b/todo.org
@@ -173,13 +173,16 @@ Open question from the roam inbox (2026-07-11): could the startup sequence for .
:END:
From the roam inbox (2026-07-11): work in progress in one project shouldn't stop the sync gate. Idea: keep all diffs/changes in a =working/= directory and exclude it (and its subdirectories) from the sync gate. Many projects run at once, so their WIP files need to be grouped. Also add a per-project count of when the gate tripped, tracked as a metric to investigate. Distinct from the 2026-07-02 policy (untracked/gitignored changes already pass — this is about *tracked* WIP under =working/=). Verify how the gate detects dirtiness today before designing.
-** TODO [#C] Put install-ai on PATH, launchable as =install-ai= :chore:quick:solo:
+** DONE [#C] Put install-ai on PATH, launchable as =install-ai= :chore:quick:solo:
+CLOSED: [2026-07-18 Sat]
:PROPERTIES:
:CREATED: [2026-07-11 Sat]
:LAST_REVIEWED: 2026-07-13
:END:
From the roam inbox (2026-07-11): make install-ai launchable as =install-ai= (no =.sh=) from PATH. dotfiles needs a copy that stays in sync with the rulesets canonical — decide whether the startup script-sync already covers it or a dedicated mechanism is needed.
+Resolved: added =claude-templates/bin/install-ai=, a thin launcher that resolves its own path through the symlink chain and execs =scripts/install-ai.sh=. =make install='s existing bin loop symlinks it into =~/.local/bin/install-ai= (same mechanism as =ai= and =agent-page=), so no dedicated sync and no dotfiles copy — the symlink always points at the canonical. 3 launcher bats added (incl. symlink-invocation resolution). Verified live: =install-ai --help= runs from PATH.
+
** TODO [#B] Document (and own) the Signal pager :feature:spec:
:PROPERTIES:
:CREATED: [2026-07-11 Sat]