feat(profiles): prune the gstack catalog, add max, honor a removed denylist

Nine gstack skills leave every profile (ship is trunk-based on Gitea,
land-and-deploy auto-merges and deploys, setup-deploy, autoplan reads
paths that do not exist here, context-save has no restore, learn is an
unused parallel store, careful and guard hooks never fired, design-shotgun
needs an absent OpenAI key). lib/gstack-removed.sh is the single denylist;
profile.sh gstack on and toggle-external.sh enable gstack skip it.

full now carries everything every other profile carries (user rule), minus
the parked make-pdf, diagram and 21st-ai/ui-explore/ui-review, which live
in the new max profile together with pr-review-toolkit. The 21st trio also
leaves web, web-full and design (redundant with impeccable + ui-ux-pro-max).

lib/tests/profile-census.test.sh asserts the invariants live and on a
baseline fixture plus one mutant per invariant; gstack-removed.test.sh
covers both restore paths.
This commit is contained in:
bastien
2026-09-28 11:48:44 +02:00
parent d91d8d820a
commit f83f8f755b
18 changed files with 457 additions and 67 deletions
+15 -2
View File
@@ -40,6 +40,12 @@ REPO="${TOGGLE_EXTERNAL_REPO_OVERRIDE:-$(cd -P "$(dirname "$0")/.." && pwd)}"
SKILLS_DIR="$REPO/skills"
DISABLED_DIR="$REPO/skills-disabled"
# GSTACK_REMOVED + gstack_is_removed() — single source, honored by the
# `enable gstack` loop below. Resolved from $0 like REPO above, not from
# $REPO/lib — gstack-removed.sh sits next to this file wherever it runs.
# shellcheck source=lib/gstack-removed.sh disable=SC1091
source "$(dirname "$0")/gstack-removed.sh"
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'
ok() { echo -e "${GREEN}✓${NC} $1"; }
warn() { echo -e "${YELLOW}⚠${NC} $1"; }
@@ -162,18 +168,25 @@ enable_tool() {
local tool="$1"
case "$tool" in
gstack)
local moved=0
local moved=0 skipped=0
if [ -d "$DISABLED_DIR" ]; then
for entry in "$DISABLED_DIR"/gstack__*; do
[ -e "$entry" ] || continue
local name
name="$(basename "$entry" | sed 's/^gstack__//')"
if gstack_is_removed "$name"; then
warn "skipped (removed by policy, lib/gstack-removed.sh): $name"
skipped=$((skipped + 1))
continue
fi
rm -rf "${SKILLS_DIR:?}/${name:?}"
mv "$entry" "$SKILLS_DIR/$name"
moved=$((moved + 1))
done
fi
if [ "$moved" -eq 0 ]; then
if [ "$moved" -eq 0 ] && [ "$skipped" -ge 1 ]; then
warn "only policy-removed skills remain parked (lib/gstack-removed.sh)"
elif [ "$moved" -eq 0 ]; then
warn "gstack was not disabled — re-run gstack setup to (re)create symlinks"
else
ok "gstack enabled ($moved symlinks restored)"