feat(install): optional --activate-hooks jq-merge into settings.json
Adds --activate-hooks flag for non-interactive hook activation, plus a TTY prompt at end-of-install. Merge is jq-based, groups by matcher, and de-dupes hook entries by command — idempotent across re-runs. Existing user hooks on the same matcher are preserved. Non-TTY without the flag keeps the manual instructions.
This commit is contained in:
parent
5f51822214
commit
3c9e89e8c8
1 changed files with 116 additions and 14 deletions
130
install.sh
130
install.sh
|
|
@ -10,6 +10,24 @@ AGENTS_DIR="$HOME_DIR/.claude/agents"
|
|||
HOOKS_DIR="$HOME_DIR/.claude/hooks"
|
||||
SKILLS_DIR="$HOME_DIR/.claude/skills"
|
||||
|
||||
# --- flag parsing ----------------------------------------------------------
|
||||
ACTIVATE_HOOKS=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--activate-hooks) ACTIVATE_HOOKS=1 ;;
|
||||
--help|-h)
|
||||
cat <<EOF
|
||||
Usage: ./install.sh [--activate-hooks]
|
||||
|
||||
--activate-hooks jq-merge settings-snippet.json into ~/.claude/settings.json
|
||||
non-interactively. Without this flag, a TTY prompt asks
|
||||
at the end; non-TTY runs print manual instructions.
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
say() { printf '\033[1;36m[install]\033[0m %s\n' "$*"; }
|
||||
warn() { printf '\033[1;33m[warn]\033[0m %s\n' "$*"; }
|
||||
err() { printf '\033[1;31m[error]\033[0m %s\n' "$*" >&2; }
|
||||
|
|
@ -62,6 +80,57 @@ backup_dir() {
|
|||
say "backed up existing $target to $backup"
|
||||
}
|
||||
|
||||
# Activate KeiSeiKit hooks by merging settings-snippet.json into the user's
|
||||
# settings.json. Idempotent:
|
||||
# - If settings.json is absent, copy snippet verbatim (minus _comment key).
|
||||
# - If present, concatenate the snippet's PostToolUse / PreToolUse entries
|
||||
# onto existing arrays, then de-dupe by the nested hooks[].command field
|
||||
# so re-runs do not stack duplicate entries.
|
||||
# - .hooks itself (the root object key) is merged with `*` — snippet wins on
|
||||
# scalar keys, arrays are unioned then de-duped.
|
||||
# Requires jq (already checked earlier in prerequisites). Writes atomically
|
||||
# via a tmpfile in the same dir.
|
||||
activate_hooks() {
|
||||
local snippet="$KIT_DIR/settings-snippet.json"
|
||||
local target="$HOME_DIR/.claude/settings.json"
|
||||
local tmp
|
||||
[ -f "$snippet" ] || { warn "no snippet at $snippet"; return 0; }
|
||||
if [ ! -f "$target" ]; then
|
||||
# Strip _comment, keep the rest. Create atomically.
|
||||
tmp="$(mktemp "$target.XXXXXX")"
|
||||
jq 'del(._comment)' "$snippet" > "$tmp"
|
||||
mv "$tmp" "$target"
|
||||
say "created $target from snippet (no prior settings.json)"
|
||||
return 0
|
||||
fi
|
||||
# Merge: walk each matcher-group in PostToolUse / PreToolUse, append hooks,
|
||||
# unique_by command. jq filter is written for readability, not golf.
|
||||
tmp="$(mktemp "$target.XXXXXX")"
|
||||
jq --slurpfile snip "$snippet" '
|
||||
. as $orig
|
||||
| ($snip[0] | del(._comment)) as $add
|
||||
| reduce ($add.hooks | keys[]) as $phase ($orig;
|
||||
.hooks[$phase] = (
|
||||
((.hooks[$phase] // []) + ($add.hooks[$phase] // []))
|
||||
| group_by(.matcher)
|
||||
| map({
|
||||
matcher: .[0].matcher,
|
||||
hooks: (map(.hooks // []) | add | unique_by(.command))
|
||||
})
|
||||
)
|
||||
)
|
||||
' "$target" > "$tmp"
|
||||
# Only replace if jq produced non-empty valid JSON
|
||||
if [ -s "$tmp" ] && jq -e . "$tmp" >/dev/null 2>&1; then
|
||||
mv "$tmp" "$target"
|
||||
say "merged hooks into $target (idempotent)"
|
||||
else
|
||||
rm -f "$tmp"
|
||||
err "jq-merge produced invalid output; $target unchanged"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# --- prerequisites ----------------------------------------------------------
|
||||
say "checking prerequisites"
|
||||
if ! command -v cargo >/dev/null 2>&1; then
|
||||
|
|
@ -176,27 +245,35 @@ fi
|
|||
say "generating agent .md files (--in-place)"
|
||||
AGENT_ROOT="$AGENTS_DIR" "$AGENTS_DIR/_assembler/target/release/assemble" --in-place
|
||||
|
||||
# --- activate hooks (flag, or interactive prompt on TTY) -------------------
|
||||
SETTINGS_FILE="$HOME_DIR/.claude/settings.json"
|
||||
DID_ACTIVATE=0
|
||||
if [ "$ACTIVATE_HOOKS" = "1" ]; then
|
||||
say "activating hooks (--activate-hooks)"
|
||||
activate_hooks && DID_ACTIVATE=1
|
||||
elif [ ! -f "$SETTINGS_FILE" ]; then
|
||||
# No existing settings — merge is trivial, do it unconditionally.
|
||||
say "no existing settings.json; installing snippet"
|
||||
activate_hooks && DID_ACTIVATE=1
|
||||
elif [ -t 0 ] && [ -t 1 ]; then
|
||||
printf '\033[1;36m[install]\033[0m activate hooks now? [y/N] '
|
||||
read -r reply
|
||||
case "$reply" in
|
||||
y|Y|yes|YES) activate_hooks && DID_ACTIVATE=1 ;;
|
||||
*) say "skipping hook activation" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# --- done -----------------------------------------------------------------
|
||||
echo
|
||||
say "install complete"
|
||||
echo
|
||||
cat <<EOF
|
||||
if [ "$DID_ACTIVATE" = "1" ]; then
|
||||
cat <<EOF
|
||||
==========================================================================
|
||||
NEXT STEP: merge settings-snippet.json into ~/.claude/settings.json
|
||||
Hooks activated. Settings merged into $SETTINGS_FILE
|
||||
==========================================================================
|
||||
|
||||
KeiSeiKit ships 3 hooks (assemble-agents, assemble-validate, no-hand-edit).
|
||||
To activate them, merge entries from:
|
||||
$KIT_DIR/settings-snippet.json
|
||||
into your:
|
||||
$HOME_DIR/.claude/settings.json
|
||||
|
||||
If you have no settings.json yet, you can simply copy the snippet:
|
||||
cp $KIT_DIR/settings-snippet.json $HOME_DIR/.claude/settings.json
|
||||
|
||||
Otherwise, open both files and append the PostToolUse / PreToolUse
|
||||
entries to the matching arrays in your existing settings.json.
|
||||
|
||||
To verify install:
|
||||
ls $AGENTS_DIR/*.md # should show ~14 generated agents
|
||||
$AGENTS_DIR/_assembler/target/release/assemble --validate
|
||||
|
|
@ -206,3 +283,28 @@ cat <<EOF
|
|||
|
||||
==========================================================================
|
||||
EOF
|
||||
else
|
||||
cat <<EOF
|
||||
==========================================================================
|
||||
NEXT STEP: merge settings-snippet.json into ~/.claude/settings.json
|
||||
==========================================================================
|
||||
|
||||
KeiSeiKit ships 3 hooks (assemble-agents, assemble-validate, no-hand-edit).
|
||||
To activate them, merge entries from:
|
||||
$KIT_DIR/settings-snippet.json
|
||||
into your:
|
||||
$SETTINGS_FILE
|
||||
|
||||
Or re-run with automatic activation:
|
||||
./install.sh --activate-hooks
|
||||
|
||||
To verify install:
|
||||
ls $AGENTS_DIR/*.md # should show ~14 generated agents
|
||||
$AGENTS_DIR/_assembler/target/release/assemble --validate
|
||||
|
||||
To create a new project-specialist agent:
|
||||
/new-agent
|
||||
|
||||
==========================================================================
|
||||
EOF
|
||||
fi
|
||||
|
|
|
|||
Loading…
Reference in a new issue