merge: sync Kilo runtime branch with latest main
This commit is contained in:
27
hooks/gsd-phase-boundary.sh
Executable file
27
hooks/gsd-phase-boundary.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
# gsd-phase-boundary.sh — PostToolUse hook: detect .planning/ file writes
|
||||
# Outputs a reminder when planning files are modified outside normal workflow.
|
||||
# Uses Node.js for JSON parsing (always available in GSD projects, no jq dependency).
|
||||
#
|
||||
# OPT-IN: This hook is a no-op unless config.json has hooks.community: true.
|
||||
# Enable with: "hooks": { "community": true } in .planning/config.json
|
||||
|
||||
# Check opt-in config — exit silently if not enabled
|
||||
if [ -f .planning/config.json ]; then
|
||||
ENABLED=$(node -e "try{const c=require('./.planning/config.json');process.stdout.write(c.hooks?.community===true?'1':'0')}catch{process.stdout.write('0')}" 2>/dev/null)
|
||||
if [ "$ENABLED" != "1" ]; then exit 0; fi
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
|
||||
INPUT=$(cat)
|
||||
|
||||
# Extract file_path from JSON using Node (handles escaping correctly)
|
||||
FILE=$(echo "$INPUT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{process.stdout.write(JSON.parse(d).tool_input?.file_path||'')}catch{}})" 2>/dev/null)
|
||||
|
||||
if [[ "$FILE" == *.planning/* ]] || [[ "$FILE" == .planning/* ]]; then
|
||||
echo ".planning/ file modified: $FILE"
|
||||
echo "Check: Should STATE.md be updated to reflect this change?"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
33
hooks/gsd-session-state.sh
Executable file
33
hooks/gsd-session-state.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
# gsd-session-state.sh — SessionStart hook: inject project state reminder
|
||||
# Outputs STATE.md head on every session start for orientation.
|
||||
#
|
||||
# OPT-IN: This hook is a no-op unless config.json has hooks.community: true.
|
||||
# Enable with: "hooks": { "community": true } in .planning/config.json
|
||||
|
||||
# Check opt-in config — exit silently if not enabled
|
||||
if [ -f .planning/config.json ]; then
|
||||
ENABLED=$(node -e "try{const c=require('./.planning/config.json');process.stdout.write(c.hooks?.community===true?'1':'0')}catch{process.stdout.write('0')}" 2>/dev/null)
|
||||
if [ "$ENABLED" != "1" ]; then exit 0; fi
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo '## Project State Reminder'
|
||||
echo ''
|
||||
|
||||
if [ -f .planning/STATE.md ]; then
|
||||
echo 'STATE.md exists - check for blockers and current phase.'
|
||||
head -20 .planning/STATE.md
|
||||
else
|
||||
echo 'No .planning/ found - suggest /gsd:new-project if starting new work.'
|
||||
fi
|
||||
|
||||
echo ''
|
||||
|
||||
if [ -f .planning/config.json ]; then
|
||||
MODE=$(grep -o '"mode"[[:space:]]*:[[:space:]]*"[^"]*"' .planning/config.json 2>/dev/null || echo '"mode": "unknown"')
|
||||
echo "Config: $MODE"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
47
hooks/gsd-validate-commit.sh
Executable file
47
hooks/gsd-validate-commit.sh
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
# gsd-validate-commit.sh — PreToolUse hook: enforce Conventional Commits format
|
||||
# Blocks git commit commands with non-conforming messages (exit 2).
|
||||
# Allows conforming messages and all non-commit commands (exit 0).
|
||||
# Uses Node.js for JSON parsing (always available in GSD projects, no jq dependency).
|
||||
#
|
||||
# OPT-IN: This hook is a no-op unless config.json has hooks.community: true.
|
||||
# Enable with: "hooks": { "community": true } in .planning/config.json
|
||||
|
||||
# Check opt-in config — exit silently if not enabled
|
||||
if [ -f .planning/config.json ]; then
|
||||
ENABLED=$(node -e "try{const c=require('./.planning/config.json');process.stdout.write(c.hooks?.community===true?'1':'0')}catch{process.stdout.write('0')}" 2>/dev/null)
|
||||
if [ "$ENABLED" != "1" ]; then exit 0; fi
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
|
||||
INPUT=$(cat)
|
||||
|
||||
# Extract command from JSON using Node (handles escaping correctly, no jq needed)
|
||||
CMD=$(echo "$INPUT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{process.stdout.write(JSON.parse(d).tool_input?.command||'')}catch{}})" 2>/dev/null)
|
||||
|
||||
# Only check git commit commands
|
||||
if [[ "$CMD" =~ ^git[[:space:]]+commit ]]; then
|
||||
# Extract message from -m flag
|
||||
MSG=""
|
||||
if [[ "$CMD" =~ -m[[:space:]]+\"([^\"]+)\" ]]; then
|
||||
MSG="${BASH_REMATCH[1]}"
|
||||
elif [[ "$CMD" =~ -m[[:space:]]+\'([^\']+)\' ]]; then
|
||||
MSG="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
|
||||
if [ -n "$MSG" ]; then
|
||||
SUBJECT=$(echo "$MSG" | head -1)
|
||||
# Validate Conventional Commits format
|
||||
if ! [[ "$SUBJECT" =~ ^(feat|fix|docs|style|refactor|perf|test|build|ci|chore)(\(.+\))?:[[:space:]].+ ]]; then
|
||||
echo '{"decision": "block", "reason": "Commit message must follow Conventional Commits: <type>(<scope>): <subject>. Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore. Subject must be <=72 chars, lowercase, imperative mood, no trailing period."}'
|
||||
exit 2
|
||||
fi
|
||||
if [ ${#SUBJECT} -gt 72 ]; then
|
||||
echo '{"decision": "block", "reason": "Commit subject must be 72 characters or less."}'
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user