audit fixes: RTK hook, settings unification, graphifyy, statusline

- Add RTK PreToolUse hook (rtk-rewrite.sh) and fix missing config
- Unify settings.json: merge hooks, marketplaces, model into project file
  so link.sh symlink is the single source of truth
- Add statusline: model, folder, git branch, context % progress bar
- Add graphifyy support: detect, install (pipx), lock, doctor, session-start
- Clarify ctx7/ruflo as standalone CLI (not MCP servers)
- Fix install-plugins.sh step numbering (duplicate step 6)
- Add version check in session-start (local vs origin/master)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
bastien
2026-04-12 13:28:30 +02:00
co-authored by Claude Opus 4.6
parent 4c9ad86ee4
commit 35ea5c1a49
9 changed files with 255 additions and 4 deletions
+1
View File
@@ -0,0 +1 @@
ef0d630994fd7ef5f2b84fb66cd6249c493bb8736bcacd4734d7c798125018fb rtk-rewrite.sh
+98
View File
@@ -0,0 +1,98 @@
#!/usr/bin/env bash
# rtk-hook-version: 3
# RTK Claude Code hook — rewrites commands to use rtk for token savings.
# Requires: rtk >= 0.23.0, jq
#
# This is a thin delegating hook: all rewrite logic lives in `rtk rewrite`,
# which is the single source of truth (src/discover/registry.rs).
# To add or change rewrite rules, edit the Rust registry — not this file.
#
# Exit code protocol for `rtk rewrite`:
# 0 + stdout Rewrite found, no deny/ask rule matched → auto-allow
# 1 No RTK equivalent → pass through unchanged
# 2 Deny rule matched → pass through (Claude Code native deny handles it)
# 3 + stdout Ask rule matched → rewrite but let Claude Code prompt the user
if ! command -v jq &>/dev/null; then
echo "[rtk] WARNING: jq is not installed. Hook cannot rewrite commands. Install jq: https://jqlang.github.io/jq/download/" >&2
exit 0
fi
if ! command -v rtk &>/dev/null; then
echo "[rtk] WARNING: rtk is not installed or not in PATH. Hook cannot rewrite commands. Install: https://github.com/rtk-ai/rtk#installation" >&2
exit 0
fi
# Version guard: rtk rewrite was added in 0.23.0.
# Older binaries: warn once and exit cleanly (no silent failure).
RTK_VERSION=$(rtk --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
if [ -n "$RTK_VERSION" ]; then
MAJOR=$(echo "$RTK_VERSION" | cut -d. -f1)
MINOR=$(echo "$RTK_VERSION" | cut -d. -f2)
# Require >= 0.23.0
if [ "$MAJOR" -eq 0 ] && [ "$MINOR" -lt 23 ]; then
echo "[rtk] WARNING: rtk $RTK_VERSION is too old (need >= 0.23.0). Upgrade: cargo install rtk" >&2
exit 0
fi
fi
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if [ -z "$CMD" ]; then
exit 0
fi
# Delegate all rewrite + permission logic to the Rust binary.
REWRITTEN=$(rtk rewrite "$CMD" 2>/dev/null)
EXIT_CODE=$?
case $EXIT_CODE in
0)
# Rewrite found, no permission rules matched — safe to auto-allow.
# If the output is identical, the command was already using RTK.
[ "$CMD" = "$REWRITTEN" ] && exit 0
;;
1)
# No RTK equivalent — pass through unchanged.
exit 0
;;
2)
# Deny rule matched — let Claude Code's native deny rule handle it.
exit 0
;;
3)
# Ask rule matched — rewrite the command but do NOT auto-allow so that
# Claude Code prompts the user for confirmation.
;;
*)
exit 0
;;
esac
ORIGINAL_INPUT=$(echo "$INPUT" | jq -c '.tool_input')
UPDATED_INPUT=$(echo "$ORIGINAL_INPUT" | jq --arg cmd "$REWRITTEN" '.command = $cmd')
if [ "$EXIT_CODE" -eq 3 ]; then
# Ask: rewrite the command, omit permissionDecision so Claude Code prompts.
jq -n \
--argjson updated "$UPDATED_INPUT" \
'{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"updatedInput": $updated
}
}'
else
# Allow: rewrite the command and auto-allow.
jq -n \
--argjson updated "$UPDATED_INPUT" \
'{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "RTK auto-rewrite",
"updatedInput": $updated
}
}'
fi
+13 -1
View File
@@ -48,7 +48,7 @@ unset _lib
TOGGLE_ACTIVE=()
TOGGLE_INACTIVE=()
for plugin in gstack uiux_pro_max frontend_design plugin_dev context7 ruflo; do
for plugin in gstack uiux_pro_max frontend_design plugin_dev context7 ruflo graphifyy; do
# Map function name to display name
case "$plugin" in
uiux_pro_max) display="ui-ux-pro-max" ;;
@@ -83,6 +83,7 @@ if [ -n "$_claude_real" ]; then
else
CONFIG_VERSION="?"
fi
REPO_DIR="${_repo_dir:-}"
unset _claude_real _repo_dir
# Quick passive token cost estimate (Pro session budget = ~11k tokens)
@@ -94,6 +95,7 @@ detect_plugin_dev 2>/dev/null && _passive_t=$((_passive_t + 100))
detect_uiux_pro_max 2>/dev/null && _passive_t=$((_passive_t + 400))
detect_context7 2>/dev/null && _passive_t=$((_passive_t + 200))
detect_ruflo 2>/dev/null && _passive_t=$((_passive_t + 1000))
detect_graphifyy 2>/dev/null && _passive_t=$((_passive_t + 300))
_budget_pct=$((_passive_t * 100 / 11000))
if [ "$_budget_pct" -gt 50 ]; then
TOKEN_WARN="⚠️ ~${_passive_t}t passif (${_budget_pct}% budget)"
@@ -141,6 +143,16 @@ unset _active_count _inactive_count
printf "│ 🖥️ CLI : %-40s│\n" "$GSD_STATUS"
[ -n "$TOKEN_WARN" ] && printf "│ 💰 %-44s│\n" "${TOKEN_WARN:0:44}"
printf "│ 📦 v%-45s│\n" "$CONFIG_VERSION"
# Version check: compare local vs remote (non-blocking)
_remote_ver=""
if [ -n "$REPO_DIR" ] && [ -d "$REPO_DIR/.git" ]; then
_remote_ver=$(cd "$REPO_DIR" 2>/dev/null && git fetch origin --quiet 2>/dev/null && git show origin/master:version.txt 2>/dev/null || true)
fi
if [ -n "$_remote_ver" ] && [ "$_remote_ver" != "$CONFIG_VERSION" ]; then
printf "│ 🔄 update available: v%-27s│\n" "$_remote_ver"
fi
unset _remote_ver REPO_DIR
echo "│ 💡 /plugin-check before starting a new project │"
echo "│ 🩺 /health to run full diagnostic │"
echo "└───────────────────────────────────────────────────┘"
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# Claude Code statusline — folder, git branch, model, context %
# Receives JSON on stdin from Claude Code.
INPUT=$(cat)
MODEL=$(echo "$INPUT" | jq -r '.model.display_name // "?"')
DIR=$(echo "$INPUT" | jq -r '.cwd // "?"')
FOLDER="${DIR##*/}"
PCT=$(echo "$INPUT" | jq -r \
'.context_window.used_percentage // 0' \
| cut -d. -f1)
# Git branch (fast, no network)
BRANCH=""
if [ -d "$DIR" ]; then
BRANCH=$(git -C "$DIR" branch --show-current 2>/dev/null)
fi
BRANCH_STR="${BRANCH:+ ($BRANCH)}"
# Progress bar (20 chars wide)
WIDTH=20
FILLED=$((PCT * WIDTH / 100))
EMPTY=$((WIDTH - FILLED))
if [ "$FILLED" -gt 0 ]; then
printf -v FILL "%${FILLED}s"
else
FILL=""
fi
if [ "$EMPTY" -gt 0 ]; then
printf -v PAD "%${EMPTY}s"
else
PAD=""
fi
BAR="${FILL// /█}${PAD// /░}"
# Color: green <50%, yellow 50-79%, red >=80%
if [ "$PCT" -ge 80 ]; then
COLOR="\033[31m"
elif [ "$PCT" -ge 50 ]; then
COLOR="\033[33m"
else
COLOR="\033[32m"
fi
RESET="\033[0m"
# Output: single line
echo -e "$MODEL | $FOLDER${BRANCH_STR} | ${COLOR}${BAR}${RESET} ${PCT}%"