feat(profile): add gstack on|off verb to lib/profile.sh
Centralize gstack toggling in the `profile` command without losing the
active-profile label.
- `gstack on` re-enables ALL parked gstack skills (moves
skills-disabled/gstack__* back) but does NOT touch .active-profile,
so the user layers full gstack on top of their current profile and
the statusline label is preserved. Unlike `reset`, which clears the
label to "none".
- `gstack off` disables gstack skills not listed in the active profile;
errors cleanly when no profile is active (needs one to know what to
keep).
Refactor (behavior-preserving): extract three shared helpers
`enable_all_gstack`, `disable_gstack_not_in`, `parked_gstack_count` and
rewire `cmd_reset` + `cmd_set` to reuse them instead of duplicating the
symlink-toggle loops. Wire `gstack` into main() dispatch, usage(), and the
header usage block.
Docs: SKILL.md argument-hint, examples, and output-policy updated. The
generic `make profile cmd="gstack on"` target already covers Make usage.
Verified: shellcheck CLEAN, `bash -n` OK, 6-case test (help, bad-action,
off-with-no-profile, on, off-trim, on-cycle) with final assertion that the
live symlink state was restored exactly to its pre-test value.
Memory: capitalize BDR-018 (decision), LRN-024 (DRY helper-extraction
pattern), BLK-007 (6 gstack source skills ios-*/spec unlinked post
submodule bump — open follow-up), EVAL-002 (self-eval, false "full.profile
bug" flag corrected pre-edit). Backfill index drift: BDR-017, BLK-005/006.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0d9f3d41eb
commit
da4e6b9590
@ -24,6 +24,9 @@ rules:
|
||||
| BLK-002 | 2026-04-23 | `rmdir` denied in sandbox on empty directory | resolved |
|
||||
| BLK-003 | 2026-05-12 | `scripts/screenshot.mjs` hardcoded macOS path blocks PNG cards on Linux | upstream |
|
||||
| BLK-004 | 2026-05-20 | `/ship-feature` wrapper at `~/.claude/commands/` points to deleted agent files post-refactor | resolved |
|
||||
| BLK-005 | 2026-05-21 | gstack submodule rename (checkpoint→context-save) breaks profile entries | resolved |
|
||||
| BLK-006 | 2026-05-21 | `profile.sh current` false-negative via `~/.claude` symlink (`cd` not `cd -P`) | resolved |
|
||||
| BLK-007 | 2026-06-02 | 6 gstack source skills (ios-*, spec) unlinked post-bump — invisible to profiles + `gstack on` | open |
|
||||
|
||||
---
|
||||
|
||||
@ -82,3 +85,11 @@ rules:
|
||||
- **Real cause**: `lib/profile.sh:43` set `REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"`. Default bash `cd` preserves symlinks (logical pathname mode, `set -P` off). When the script is invoked via the `~/.claude/lib/profile.sh` symlink (link.sh wires `~/.claude/lib -> <repo>/lib`), `$BASH_SOURCE[0]` is the symlinked path, `dirname` returns `~/.claude/lib`, `cd ..` lands at `~/.claude`, and `pwd` returns the logical path `/home/bchanot-ubuntu/.claude`. `$SKILLS_DIR="$REPO/skills"` still works because `~/.claude/skills` happens to be a symlink to the repo's `skills/`. But `$DISABLED_DIR="$REPO/skills-disabled"` resolves to `~/.claude/skills-disabled` — a real sibling directory created at some earlier point containing only 2 stale npx-skill symlinks (`darwin-skill`, `find-skills`). `cmd_current` scans this near-empty dir, finds 0 `gstack__*` entries, returns the "none" sentinel.
|
||||
- **Solution**: `REPO="$(cd -P "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"` (commit `a4558ee`). `-P` forces physical-path resolution so `$REPO` is always the real repo path regardless of how the script is invoked. Verify: `bash "$HOME/.claude/lib/profile.sh" current` now returns `full (100% match, 14 gstack skills disabled)`.
|
||||
- **Status**: resolved. Follow-up: `~/.claude/skills-disabled/` (real dir with only `darwin-skill`/`find-skills` symlinks) is orphaned — these npx skills are already symlinked into `<repo>/skills/` by link.sh, so the disabled-side copies serve no purpose. Could be deleted to remove confusion, but harmless as-is.
|
||||
|
||||
## BLK-007 — 6 gstack source skills (ios-*, spec) unlinked — invisible to profile system + `gstack on`
|
||||
|
||||
- **Date**: 2026-06-02
|
||||
- **Friction**: `skills-external/gstack/` has 53 source skills; 6 (`ios-clean`, `ios-design-review`, `ios-fix`, `ios-qa`, `ios-sync`, `spec`) exist ONLY as source — NOT symlinked into `skills/` (enabled) nor `skills-disabled/gstack__*` (parked). So invisible to Claude AND untouched by `reset`/`gstack on` (both operate on parked `gstack__*` only). Surfaced while adding `gstack on|off`: `comm` of gstack source vs `full.profile`.
|
||||
- **Real cause**: gstack submodule bump added new skills; gstack's own `./setup` (source of truth for per-skill symlinks per link.sh) not re-run → symlinks never created. Same lifecycle gap class as [[toggle-external-source-only-state]] (LRN-007). NOT a `full.profile` bug — full curated by design (BDR-017 caveat: "full excludes rarely-used gstack skills"). Initial "full omits ios = bug" flag was WRONG, self-corrected (see EVAL-002).
|
||||
- **Solution**: re-run gstack setup to link new skills, then reconcile profiles (decide if iOS skills belong in web/dev profiles — likely NOT). Per [[gstack-rename-profile-audit]] (LRN-022): diff `skills-external/gstack/` vs `lib/profiles/*.profile` after every submodule bump. NOT auto-fixed — gstack installer domain + iOS-in-web judgment call.
|
||||
- **Status**: open. Low impact (skills unused today, never linked). Next: run gstack `./setup`, audit profile membership.
|
||||
|
||||
@ -38,6 +38,8 @@ rules:
|
||||
| BDR-014 | 2026-05-11 | Personal SKILL.md descriptions: "Use when [triggers]…" pattern + 1024-char spec limit | accepted |
|
||||
| BDR-015 | 2026-05-12 | Exclude broken gstack symlinks from /darwin-skill scope (external ownership) | accepted |
|
||||
| BDR-016 | 2026-05-15 | doc-syncer: README AUTO+unconditional, DEPLOY.md prod-only + 14-section VPS template | accepted |
|
||||
| BDR-017 | 2026-05-18 | `full` profile = web-full + plan + dev superset for /init-project MVP | accepted |
|
||||
| BDR-018 | 2026-06-02 | `profile gstack on/off` verb — toggle gstack keeping active-profile label | accepted |
|
||||
|
||||
---
|
||||
|
||||
@ -321,3 +323,17 @@ rules:
|
||||
- `full` excludes a few rarely-used gstack skills (devex-review, pair-agent, gstack-upgrade, skills-perso). `set full` will disable those; user can `apply <profile>` after to add back.
|
||||
- Sentinel rename "full" → "none" is breaking for any tooling that grepped `cmd_current` output for literal "full". No known consumers in this repo.
|
||||
- **Reference**: commit message references `lib/profiles/full.profile` (new), `lib/profile.sh:421` sentinel, `skills/profile/SKILL.md` table row. Linked to [[profile-sentinel-collision]] (LRN-020).
|
||||
|
||||
---
|
||||
|
||||
## BDR-018 — `profile gstack on|off` verb keeps active-profile label
|
||||
|
||||
- **Date**: 2026-06-02
|
||||
- **Status**: accepted
|
||||
- **Decision**: New `cmd_gstack()` in `lib/profile.sh`. `gstack on` = re-enable all parked gstack (move `skills-disabled/gstack__*` back), DON'T touch `.active-profile`. `gstack off` = disable gstack skills not in active profile (errors if active=none). Wired into `main()` dispatch + `usage()` + header block + `skills/profile/SKILL.md` (argument-hint + examples + output-policy).
|
||||
- **Why**: User wanted central command for "enable all gstack" + "disable gstack not needed by profile". Both ops existed (`reset`, `set`) but `reset` clobbers `.active-profile` to "none" — loses profile context in statusline. New verb does same skill-toggle WITHOUT clearing label, so user layers full gstack on top of current profile (e.g. `dev`) and statusline still reads `dev`.
|
||||
- **Alternatives rejected**:
|
||||
- 3 new profiles (current+gstack, current+gsd, current+gsd+gstack) — rejected: `gsd` = standalone CLI (not profile-toggleable, always-on, 0 passive token), so 2 of 3 meaningless. `full` already = current+gstack+gsd advisory. `apply` already additive.
|
||||
- Just document `reset`/`set` — rejected: user wanted clearer centralized verb + label preservation.
|
||||
- **Impl note**: extracted 3 shared helpers (`enable_all_gstack`, `disable_gstack_not_in`, `parked_gstack_count`); `cmd_reset`+`cmd_set` refactored to reuse (behavior preserved exact, verified by test). See [[dry-helper-extract-sibling-command]] (LRN-024).
|
||||
- **Reference**: `lib/profile.sh` cmd_gstack + helpers, `skills/profile/SKILL.md`. Linked to [[full-profile-superset-init-project]] (BDR-017), [[gstack-source-only-skills-unlinked]] (BLK-007).
|
||||
|
||||
@ -22,6 +22,7 @@ rules:
|
||||
| ID | Date | Output | Action |
|
||||
|----|------|--------|--------|
|
||||
| EVAL-001 | 2026-04-23 | `.claude/` restructure plan (ship-feature STEP 2) | keep |
|
||||
| EVAL-002 | 2026-06-02 | `profile gstack on/off` verb implementation | keep |
|
||||
|
||||
---
|
||||
|
||||
@ -31,4 +32,14 @@ rules:
|
||||
- **Output**: 21-task plan migrate `tasks/` to `.claude/tasks/` + create `.claude/memory/` + `.claude/audits/` + integrate CAPITALIZE across 5 skills + add `/close` skill.
|
||||
- **Method**: manual review of 5 impacted skills/agents; verified `rtk` path-agnostic; confirmed `~/.claude/CLAUDE.md` symlinks to project (single file edit). Radical-honesty check on session-close ritual: confirmed aspirational without skill integration → scope expanded to Option D.
|
||||
- **Anomalies**: none blocking. Note: `tasks/LESSONS.md` empty (101B, header only) — migration to `learnings.md` symbolic.
|
||||
- **Action**: keep — plan validated, ready for execution.
|
||||
- **Action**: keep — plan validated, ready for execution.
|
||||
|
||||
---
|
||||
|
||||
## EVAL-002 — `profile gstack on|off` verb implementation
|
||||
|
||||
- **Date**: 2026-06-02
|
||||
- **Output**: `cmd_gstack()` + 3 extracted helpers in `lib/profile.sh`; `cmd_reset`/`cmd_set` refactored to reuse; `skills/profile/SKILL.md` doc updated.
|
||||
- **Method**: shellcheck 0.10.0 (CLEAN) + `bash -n`; 6-case live test (help; bad-action exit 1; `off` with active=none → exit 1 zero-mutation; `on` restores 14 + label `full` preserved NOT cleared; `off` trim; `on` cycle) with saved manifest + final assertion final-state == original (PASS, live env untouched).
|
||||
- **Anomalies**: (1) Initial flag "full.profile omits ios/spec = bug" WRONG — full curated by design, confirmed by BDR-017 caveat. Self-corrected BEFORE any edit, no bad change shipped. Lesson: verify profile INTENT vs source completeness before calling omission a bug. (2) Surfaced real source-only gap → BLK-007 (open).
|
||||
- **Action**: keep — verb works, tested, documented; false bug-flag caught pre-edit.
|
||||
@ -129,3 +129,11 @@ rules:
|
||||
- `/hotfix` follow-up — `bash "$HOME/.claude/lib/profile.sh" current` falsely reported `none (all gstack skills enabled — no profile set)` even with profile applied + 14 gstack__* entries in repo's `skills-disabled/`. Root cause: `lib/profile.sh:43` used `cd "$(dirname $BASH_SOURCE)/.."` — default bash `cd` preserves symlinks, so `$REPO` resolved to `/home/bchanot-ubuntu/.claude` (symlink dir) instead of real repo path. `$DISABLED_DIR` then pointed at near-empty `~/.claude/skills-disabled/` (2 stale npx symlinks only). Fixed by adding `-P` to `cd` (commit `a4558ee`). `cmd_current` now correctly reports `full (100% match, 14 gstack skills disabled)`.
|
||||
- BLK-006 capitalized — `cmd_current` false-negative when invoked via `~/.claude/lib/profile.sh` symlink; status: resolved.
|
||||
- LRN-023 capitalized — `$REPO="$(cd -P "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"` mandatory pattern for any script meant to be invoked via a symlink into the install location.
|
||||
|
||||
## 2026-06-02
|
||||
|
||||
- Added `profile gstack on|off` verb to `lib/profile.sh`. `on` = re-enable all parked gstack keeping `.active-profile` label intact (vs `reset` which clears to "none"); `off` = disable gstack not in active profile (errors if none). User wanted centralized toggle without losing profile context.
|
||||
- Extracted 3 helpers (`enable_all_gstack`/`disable_gstack_not_in`/`parked_gstack_count`); refactored `cmd_reset`+`cmd_set` to reuse — behavior preserved, 6-case test + exact state-restore assertion PASS, shellcheck CLEAN. Doc: SKILL.md argument-hint + examples + output-policy. Makefile generic `make profile cmd="gstack on"` already covers it.
|
||||
- Corrected own false flag: full.profile omitting ios-*/spec is curation by design (BDR-017 caveat), NOT a bug — caught before any edit. Surfaced real gap: 6 gstack source skills unlinked post-submodule-bump → BLK-007 (open, gstack ./setup domain, not auto-fixed).
|
||||
- Backfilled index drift: decisions (BDR-017), blockers (BLK-005/006).
|
||||
- BDR-018 + LRN-024 + BLK-007 + EVAL-002 capitalized.
|
||||
|
||||
@ -40,6 +40,7 @@ rules:
|
||||
| LRN-017 | 2026-05-12 | Thin-dispatcher SKILL.md round-1 win = fallback + frontmatter triggers (+15 to +30) | any `/darwin-skill` round-1 on a dispatcher SKILL.md |
|
||||
| LRN-018 | 2026-05-12 | Darwin eval subagents drift on total math — recompute in main thread | any subagent-driven SKILL.md rescore |
|
||||
| LRN-019 | 2026-05-15 | Deployable-project doc split: README dev-quickstart + DEPLOY 14-section prod-VPS topology | any onboard/doc-syncer/scaffold producing docs for a deployable project |
|
||||
| LRN-024 | 2026-06-02 | New sibling command sharing logic → extract helper + refactor existing caller, never copy-paste; assert pre/post state equality | adding a subcommand/branch reusing logic inline in a peer command |
|
||||
|
||||
---
|
||||
|
||||
@ -359,3 +360,14 @@ rules:
|
||||
- Lint via: `grep -n 'cd "$(dirname "${BASH_SOURCE' <script>` — every match should also contain `cd -P` (or be followed by an explicit `realpath` call).
|
||||
- **Cost when missed**: state lands in two parallel directories. Reads from one, writes from the other. False-negative status reports. Worst case: silent data loss when one dir is cleaned by a tool that thinks the other is canonical.
|
||||
- **Reference**: BLK-006, commit `a4558ee`. Linked to [[gstack-rename-profile-audit]] (LRN-022) — both bugs surfaced from the same `/profile set full` invocation, but root causes are independent.
|
||||
|
||||
---
|
||||
|
||||
## LRN-024 — New sibling command sharing logic → extract helper + refactor caller, never copy-paste
|
||||
|
||||
- **Date**: 2026-06-02
|
||||
- **Pattern**: New `gstack on|off` needed same skill-toggle loops already inline in `cmd_reset` (enable-all-parked) + `cmd_set` (disable-not-in-profile). Copy-paste = divergence risk (gstack__ prefix logic, mktemp keep-file). Instead extracted `enable_all_gstack()` + `disable_gstack_not_in()` + `parked_gstack_count()`; refactored `cmd_reset`/`cmd_set` to call them, then added `cmd_gstack` as 3rd caller. Behavior preserved exact (code MOVED not changed).
|
||||
- **Why matters**: CLAUDE.md "more elegant solution exists?" — slight scope expansion (touch existing fns) beats duplication. Risk contained by test: snapshot original symlink state → run on/off cycle → re-park exact original → assert final == original. PASS, live env untouched.
|
||||
- **Key trick**: when mutating shared resource (symlinks, files, db), verify refactor by asserting `final_state == original_state` after a round-trip, not just "command exited 0".
|
||||
- **Applies to**: any new subcommand/branch reusing logic inline in a peer command — extract first, refactor existing caller, then add new caller. shellcheck after.
|
||||
- **Reference**: BDR-018, `lib/profile.sh` enable_all_gstack/disable_gstack_not_in/parked_gstack_count. Linked to [[gstack-on-off-verb]] (BDR-018).
|
||||
|
||||
@ -1,5 +1,18 @@
|
||||
# TODO
|
||||
|
||||
## profile.sh — verbe `gstack on|off`
|
||||
- [x] Extraire helper `enable_all_gstack()` (boucle de cmd_reset) — anti-duplication
|
||||
- [x] Extraire helper `disable_gstack_not_in(prof)` (boucle gstack de cmd_set) — anti-duplication
|
||||
- [x] Extraire helper `parked_gstack_count()` (réutilise pattern cmd_current)
|
||||
- [x] Refactor cmd_reset + cmd_set pour utiliser les helpers (comportement préservé)
|
||||
- [x] `cmd_gstack()` : `on` = enable tout gstack (garde label active-profile), `off` = disable gstack hors profil actif
|
||||
- [x] Wire main() dispatch `gstack)` + usage() + bloc header
|
||||
- [x] Doc : SKILL.md argument-hint + exemples + output-policy (Makefile générique suffit)
|
||||
- [x] shellcheck propre + tests (help/bad-action/none-error/on/off cycle) — état live restauré exact
|
||||
- [x] Investigué "fix" full.profile : PAS un bug — curation par design (BDR-017 caveat). Aucun fix code.
|
||||
- [ ] FOLLOW-UP (BLK-007) : 6 skills gstack source (ios-*, spec) unlinkés post-bump → re-run gstack ./setup + reconcilier profils (iOS dans profils web ? probablement non)
|
||||
- [x] Capitalize : BDR-018, LRN-024, BLK-007, EVAL-002, journal 2026-06-02 + backfill index (BDR-017, BLK-005/006)
|
||||
|
||||
## README.md overhaul
|
||||
- [x] Plan
|
||||
- [x] Corriger section install ctx7 (retirer MCP, clarifier CLI + API key)
|
||||
|
||||
122
lib/profile.sh
122
lib/profile.sh
@ -26,6 +26,7 @@
|
||||
# profile.sh apply <name> enable items in profile (additive)
|
||||
# profile.sh set <name> enable only profile (disables rest)
|
||||
# profile.sh reset re-enable all gstack skills + managed plugins
|
||||
# profile.sh gstack on|off toggle gstack, keeping active-profile label
|
||||
# profile.sh diff <a> <b> compare two profiles
|
||||
#
|
||||
# Profile file format (lib/profiles/<name>.profile):
|
||||
@ -321,6 +322,43 @@ disable_skill() {
|
||||
esac
|
||||
}
|
||||
|
||||
# ── Shared gstack operations ──────────────────────────────
|
||||
|
||||
# Re-enable every gstack skill parked in skills-disabled/ (move gstack__*
|
||||
# back into skills/). Shared by cmd_reset and `gstack on`. Side effects
|
||||
# only; prints one confirmation per restored skill.
|
||||
enable_all_gstack() {
|
||||
local entry name
|
||||
[ -d "$DISABLED_DIR" ] || return 0
|
||||
for entry in "$DISABLED_DIR"/gstack__*; do
|
||||
[ -e "$entry" ] || continue
|
||||
name="$(basename "$entry" | sed 's/^gstack__//')"
|
||||
rm -rf "${SKILLS_DIR:?}/${name:?}"
|
||||
mv "$entry" "$SKILLS_DIR/$name"
|
||||
ok "re-enabled: $name"
|
||||
done
|
||||
}
|
||||
|
||||
# Disable gstack-origin skills not listed in the given profile. Shared by
|
||||
# cmd_set and `gstack off`. Caller is responsible for validating the profile.
|
||||
disable_gstack_not_in() {
|
||||
local prof="$1"
|
||||
local keep_file name
|
||||
keep_file="$(mktemp)"
|
||||
read_profile "$prof" | cut -f1 | sort -u > "$keep_file"
|
||||
while read -r name; do
|
||||
[ -n "$name" ] || continue
|
||||
grep -qx "$name" "$keep_file" || disable_skill "$name" gstack
|
||||
done < <(gstack_skills | sort -u)
|
||||
rm -f "$keep_file"
|
||||
}
|
||||
|
||||
# Count gstack skills currently parked in skills-disabled/.
|
||||
parked_gstack_count() {
|
||||
[ -d "$DISABLED_DIR" ] || { echo 0; return 0; }
|
||||
find "$DISABLED_DIR" -maxdepth 1 -name 'gstack__*' 2>/dev/null | wc -l | tr -d ' '
|
||||
}
|
||||
|
||||
# ── Commands ──────────────────────────────────────────────
|
||||
|
||||
cmd_list() {
|
||||
@ -367,28 +405,14 @@ cmd_set() {
|
||||
local prof="$1"
|
||||
info "Setting profile: $prof (exclusive — disables non-listed gstack skills + managed plugins)"
|
||||
|
||||
# Index of items in profile (skill names + plugin keys "name@marketplace").
|
||||
local keep_file
|
||||
keep_file="$(mktemp)"
|
||||
# Skill names (col 1) — used to keep gstack skills.
|
||||
read_profile "$prof" | cut -f1 | sort -u > "$keep_file"
|
||||
# Plugin keys "name@marketplace" — used to keep managed plugins.
|
||||
local plugin_keep_file
|
||||
plugin_keep_file="$(mktemp)"
|
||||
read_profile "$prof" | awk -F'\t' '$2 ~ /^plugin@/ { sub(/^plugin@/, "", $2); print $1"@"$2 }' | sort -u > "$plugin_keep_file"
|
||||
|
||||
# Disable gstack-origin skills not in profile.
|
||||
local name
|
||||
while read -r name; do
|
||||
[ -n "$name" ] || continue
|
||||
if ! grep -qx "$name" "$keep_file"; then
|
||||
disable_skill "$name" gstack
|
||||
fi
|
||||
done < <(gstack_skills | sort -u)
|
||||
disable_gstack_not_in "$prof"
|
||||
|
||||
# Disable managed plugins not in profile (PROTECTED_PLUGINS are excluded
|
||||
# by disable_skill itself — belt and suspenders).
|
||||
local p key plugin_name marketplace
|
||||
local plugin_keep_file p plugin_name marketplace
|
||||
plugin_keep_file="$(mktemp)"
|
||||
read_profile "$prof" | awk -F'\t' '$2 ~ /^plugin@/ { sub(/^plugin@/, "", $2); print $1"@"$2 }' | sort -u > "$plugin_keep_file"
|
||||
for p in "${MANAGED_PLUGINS[@]}"; do
|
||||
if ! grep -qx "$p" "$plugin_keep_file"; then
|
||||
plugin_name="${p%@*}"
|
||||
@ -396,29 +420,67 @@ cmd_set() {
|
||||
disable_skill "$plugin_name" "plugin@${marketplace}"
|
||||
fi
|
||||
done
|
||||
rm -f "$plugin_keep_file"
|
||||
|
||||
rm -f "$keep_file" "$plugin_keep_file"
|
||||
# Enable everything listed in the profile.
|
||||
cmd_apply "$prof"
|
||||
}
|
||||
|
||||
cmd_reset() {
|
||||
info "Re-enabling all gstack skills (move skills-disabled/gstack__* back)"
|
||||
local entry name
|
||||
if [ -d "$DISABLED_DIR" ]; then
|
||||
for entry in "$DISABLED_DIR"/gstack__*; do
|
||||
[ -e "$entry" ] || continue
|
||||
name="$(basename "$entry" | sed 's/^gstack__//')"
|
||||
rm -rf "${SKILLS_DIR:?}/${name:?}"
|
||||
mv "$entry" "$SKILLS_DIR/$name"
|
||||
ok "re-enabled: $name"
|
||||
done
|
||||
fi
|
||||
enable_all_gstack
|
||||
info "Plugin state NOT touched. To re-enable a managed plugin disabled by 'set',"
|
||||
info "run: claude plugin enable <name>@<marketplace> (or: profile apply <profile>)"
|
||||
write_active "none"
|
||||
}
|
||||
|
||||
# gstack on|off — focused gstack-only toggle that keeps the active-profile
|
||||
# label intact (unlike reset, which clears it to "none"). Lets the user
|
||||
# layer all gstack on top of their current profile, or trim it back down
|
||||
# to just what the active profile needs.
|
||||
cmd_gstack() {
|
||||
local action="${1:-}"
|
||||
case "$action" in
|
||||
on)
|
||||
# Re-enable ALL gstack skills, but DON'T touch active-profile — the
|
||||
# user is adding gstack on top of their current profile, not clearing it.
|
||||
local parked
|
||||
parked="$(parked_gstack_count)"
|
||||
if [ "$parked" -eq 0 ]; then
|
||||
info "all gstack skills already enabled"
|
||||
else
|
||||
enable_all_gstack
|
||||
ok "all gstack enabled ($parked skills restored)"
|
||||
fi
|
||||
;;
|
||||
off)
|
||||
# Disable gstack skills not needed by the active profile. Needs a real
|
||||
# active profile to know what to keep.
|
||||
local active
|
||||
active="$(head -n1 "$ACTIVE_CACHE" 2>/dev/null || echo none)"
|
||||
[ -z "$active" ] && active="none"
|
||||
if [ "$active" = "none" ] || [ ! -f "$PROFILES_DIR/$active.profile" ]; then
|
||||
err "no active profile — 'gstack off' needs one to know what to keep"
|
||||
info "run: bash lib/profile.sh set <name> then: gstack off"
|
||||
return 1
|
||||
fi
|
||||
info "Disabling gstack skills not in active profile: $active"
|
||||
disable_gstack_not_in "$active"
|
||||
ok "gstack trimmed to profile: $active"
|
||||
;;
|
||||
""|-h|--help|help)
|
||||
cat <<'EOF'
|
||||
profile gstack on|off — toggle gstack without losing the active-profile label
|
||||
|
||||
on re-enable ALL gstack skills (keeps active-profile label)
|
||||
off disable gstack skills not in the active profile
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
err "Unknown gstack action: '$action' (use: on | off)"; return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
cmd_current() {
|
||||
# A profile is "active" only if (a) most of its skills are enabled AND
|
||||
# (b) at least one non-listed gstack skill is currently disabled (i.e. a
|
||||
@ -491,6 +553,7 @@ USAGE:
|
||||
profile apply <name> enable skills in profile (additive)
|
||||
profile set <name> enable only listed skills (disables rest of gstack)
|
||||
profile reset re-enable all gstack skills
|
||||
profile gstack on|off toggle gstack only, keep active-profile label
|
||||
profile diff <a> <b> compare two profiles
|
||||
|
||||
PROFILES (in $PROFILES_DIR):
|
||||
@ -527,6 +590,7 @@ main() {
|
||||
apply) [ $# -ge 2 ] || { usage; exit 1; }; cmd_apply "$2" ;;
|
||||
set) [ $# -ge 2 ] || { usage; exit 1; }; cmd_set "$2" ;;
|
||||
reset) cmd_reset ;;
|
||||
gstack) cmd_gstack "${2:-}" ;;
|
||||
diff) [ $# -ge 3 ] || { usage; exit 1; }; cmd_diff "$2" "$3" ;;
|
||||
""|-h|--help|help) usage ;;
|
||||
*) err "Unknown command: $cmd"; usage; exit 1 ;;
|
||||
|
||||
@ -8,7 +8,7 @@ description: |
|
||||
"switch to design", "set profile", "active profile", "quel profil",
|
||||
"profil design", "active les skills design", "désactive gstack",
|
||||
"réduire le bruit gstack".
|
||||
argument-hint: list | show <name> | current | apply <name> | set <name> | reset | diff <a> <b>
|
||||
argument-hint: list | show <name> | current | apply <name> | set <name> | reset | gstack on|off | diff <a> <b>
|
||||
disable-model-invocation: false
|
||||
allowed-tools:
|
||||
- Bash
|
||||
@ -81,9 +81,13 @@ bash "$HOME/.claude/lib/profile.sh" apply <name>
|
||||
# Enable only skills in profile (disables non-listed gstack skills)
|
||||
bash "$HOME/.claude/lib/profile.sh" set <name>
|
||||
|
||||
# Re-enable every gstack skill (undo any set/apply)
|
||||
# Re-enable every gstack skill (undo any set/apply) — resets active label to "none"
|
||||
bash "$HOME/.claude/lib/profile.sh" reset
|
||||
|
||||
# Toggle gstack only, keeping the active-profile label intact
|
||||
bash "$HOME/.claude/lib/profile.sh" gstack on # re-enable ALL gstack on top of current profile
|
||||
bash "$HOME/.claude/lib/profile.sh" gstack off # disable gstack skills not in the active profile
|
||||
|
||||
# Compare two profiles
|
||||
bash "$HOME/.claude/lib/profile.sh" diff <a> <b>
|
||||
```
|
||||
@ -101,9 +105,9 @@ bash "$HOME/.claude/lib/profile.sh" $ARGUMENTS
|
||||
|
||||
## Output policy
|
||||
|
||||
- After `set` / `apply` / `reset`: show the count of skills moved + tell the
|
||||
user to start a new Claude session to pick up the changes (Claude scans
|
||||
`skills/` at session start).
|
||||
- After `set` / `apply` / `reset` / `gstack on|off`: show the count of skills
|
||||
moved + tell the user to start a new Claude session to pick up the changes
|
||||
(Claude scans `skills/` at session start).
|
||||
- After `current`: report the active profile + match percentage.
|
||||
- After `show`: render the table directly — no extra commentary unless the user
|
||||
asks.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user