safeword 0.12.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{check-3X75X2JL.js → check-X7NR4WAM.js} +5 -4
- package/dist/check-X7NR4WAM.js.map +1 -0
- package/dist/{chunk-O4LAXZK3.js → chunk-XLOXGDJG.js} +103 -41
- package/dist/chunk-XLOXGDJG.js.map +1 -0
- package/dist/cli.js +5 -5
- package/dist/{diff-6HFT7BLG.js → diff-3USPMFT2.js} +2 -2
- package/dist/{reset-XFXLQXOC.js → reset-CM3BNT5S.js} +2 -2
- package/dist/{setup-C6NF3YJ5.js → setup-4PQV6EV2.js} +4 -17
- package/dist/setup-4PQV6EV2.js.map +1 -0
- package/dist/{upgrade-C2I22FAB.js → upgrade-BIMRJENC.js} +12 -6
- package/dist/upgrade-BIMRJENC.js.map +1 -0
- package/package.json +2 -2
- package/templates/hooks/cursor/after-file-edit.ts +47 -0
- package/templates/hooks/cursor/stop.ts +73 -0
- package/templates/hooks/lib/lint.ts +49 -0
- package/templates/hooks/lib/quality.ts +30 -0
- package/templates/hooks/post-tool-lint.ts +33 -0
- package/templates/hooks/prompt-questions.ts +32 -0
- package/templates/hooks/prompt-timestamp.ts +30 -0
- package/templates/hooks/session-lint-check.ts +62 -0
- package/templates/hooks/session-verify-agents.ts +32 -0
- package/templates/hooks/session-version.ts +18 -0
- package/templates/hooks/stop-quality.ts +171 -0
- package/dist/check-3X75X2JL.js.map +0 -1
- package/dist/chunk-O4LAXZK3.js.map +0 -1
- package/dist/setup-C6NF3YJ5.js.map +0 -1
- package/dist/upgrade-C2I22FAB.js.map +0 -1
- package/templates/hooks/cursor/after-file-edit.sh +0 -58
- package/templates/hooks/cursor/stop.sh +0 -50
- package/templates/hooks/post-tool-lint.sh +0 -51
- package/templates/hooks/prompt-questions.sh +0 -27
- package/templates/hooks/prompt-timestamp.sh +0 -13
- package/templates/hooks/session-lint-check.sh +0 -42
- package/templates/hooks/session-verify-agents.sh +0 -31
- package/templates/hooks/session-version.sh +0 -17
- package/templates/hooks/stop-quality.sh +0 -91
- package/templates/lib/common.sh +0 -26
- package/templates/lib/jq-fallback.sh +0 -20
- /package/dist/{diff-6HFT7BLG.js.map → diff-3USPMFT2.js.map} +0 -0
- /package/dist/{reset-XFXLQXOC.js.map → reset-CM3BNT5S.js.map} +0 -0
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Safeword: Question protocol guidance (UserPromptSubmit)
|
|
3
|
-
# Reminds Claude to ask 1-5 clarifying questions for ambiguous tasks
|
|
4
|
-
|
|
5
|
-
# Change to project directory if set
|
|
6
|
-
[ -n "$CLAUDE_PROJECT_DIR" ] && cd "$CLAUDE_PROJECT_DIR" || true
|
|
7
|
-
|
|
8
|
-
if [ ! -d ".safeword" ]; then
|
|
9
|
-
exit 0
|
|
10
|
-
fi
|
|
11
|
-
|
|
12
|
-
# Read the user prompt from stdin
|
|
13
|
-
input=$(cat)
|
|
14
|
-
|
|
15
|
-
# Only trigger on substantial prompts (more than 20 chars)
|
|
16
|
-
prompt_length=${#input}
|
|
17
|
-
if [ "$prompt_length" -lt 20 ]; then
|
|
18
|
-
exit 0
|
|
19
|
-
fi
|
|
20
|
-
|
|
21
|
-
# Output guidance
|
|
22
|
-
cat << 'EOF'
|
|
23
|
-
SAFEWORD Question Protocol: For ambiguous or complex requests, ask 1-5 clarifying questions before proceeding. Focus on:
|
|
24
|
-
- Scope boundaries (what's included/excluded)
|
|
25
|
-
- Technical constraints (frameworks, patterns, compatibility)
|
|
26
|
-
- Success criteria (how will we know it's done)
|
|
27
|
-
EOF
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Safeword: Inject timestamp (UserPromptSubmit)
|
|
3
|
-
# Outputs current timestamp for Claude's context awareness
|
|
4
|
-
# Helps with accurate ticket timestamps and time-based reasoning
|
|
5
|
-
|
|
6
|
-
# Natural language day/time in UTC
|
|
7
|
-
natural=$(date -u +"%A, %B %d, %Y at %H:%M UTC")
|
|
8
|
-
# ISO 8601 UTC
|
|
9
|
-
iso=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
10
|
-
# Local timezone
|
|
11
|
-
local_time=$(date +"%H:%M %Z")
|
|
12
|
-
|
|
13
|
-
echo "Current time: $natural ($iso) | Local: $local_time"
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Safeword: Lint configuration sync check (SessionStart)
|
|
3
|
-
# Warns if ESLint or Prettier configs are missing or out of sync
|
|
4
|
-
|
|
5
|
-
# Change to project directory if set
|
|
6
|
-
[ -n "$CLAUDE_PROJECT_DIR" ] && cd "$CLAUDE_PROJECT_DIR" || true
|
|
7
|
-
|
|
8
|
-
if [ ! -d ".safeword" ]; then
|
|
9
|
-
exit 0
|
|
10
|
-
fi
|
|
11
|
-
|
|
12
|
-
warnings=()
|
|
13
|
-
|
|
14
|
-
# Check for ESLint config
|
|
15
|
-
if [ ! -f "eslint.config.mjs" ] && [ ! -f "eslint.config.js" ] && [ ! -f ".eslintrc.json" ] && [ ! -f ".eslintrc.js" ]; then
|
|
16
|
-
warnings+=("ESLint config not found - run 'npm run lint' may fail")
|
|
17
|
-
fi
|
|
18
|
-
|
|
19
|
-
# Check for Prettier config
|
|
20
|
-
if [ ! -f ".prettierrc" ] && [ ! -f ".prettierrc.json" ] && [ ! -f "prettier.config.js" ]; then
|
|
21
|
-
warnings+=("Prettier config not found - formatting may be inconsistent")
|
|
22
|
-
fi
|
|
23
|
-
|
|
24
|
-
# Check for required dependencies
|
|
25
|
-
if [ -f "package.json" ]; then
|
|
26
|
-
if ! grep -q '"eslint"' package.json 2>/dev/null; then
|
|
27
|
-
warnings+=("ESLint not in package.json - run 'npm install -D eslint'")
|
|
28
|
-
fi
|
|
29
|
-
if ! grep -q '"prettier"' package.json 2>/dev/null; then
|
|
30
|
-
warnings+=("Prettier not in package.json - run 'npm install -D prettier'")
|
|
31
|
-
fi
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
# Output warnings if any
|
|
35
|
-
if [ ${#warnings[@]} -gt 0 ]; then
|
|
36
|
-
echo "SAFEWORD Lint Check:"
|
|
37
|
-
for warning in "${warnings[@]}"; do
|
|
38
|
-
echo " ⚠️ $warning"
|
|
39
|
-
done
|
|
40
|
-
fi
|
|
41
|
-
|
|
42
|
-
exit 0
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Safeword: Verify AGENTS.md link (SessionStart)
|
|
3
|
-
# Self-heals by restoring the link if removed
|
|
4
|
-
|
|
5
|
-
# shellcheck disable=SC2016 # Backticks are literal markdown, not command substitution
|
|
6
|
-
LINK='**⚠️ ALWAYS READ FIRST:** `.safeword/SAFEWORD.md`'
|
|
7
|
-
|
|
8
|
-
# Change to project directory if set
|
|
9
|
-
[ -n "$CLAUDE_PROJECT_DIR" ] && cd "$CLAUDE_PROJECT_DIR" || true
|
|
10
|
-
|
|
11
|
-
if [ ! -d ".safeword" ]; then
|
|
12
|
-
# Not a safeword project, skip silently
|
|
13
|
-
exit 0
|
|
14
|
-
fi
|
|
15
|
-
|
|
16
|
-
if [ ! -f "AGENTS.md" ]; then
|
|
17
|
-
# AGENTS.md doesn't exist, create it
|
|
18
|
-
echo "$LINK" > AGENTS.md
|
|
19
|
-
echo "SAFEWORD: Created AGENTS.md with safeword link"
|
|
20
|
-
exit 0
|
|
21
|
-
fi
|
|
22
|
-
|
|
23
|
-
# Check if link is present
|
|
24
|
-
if ! grep -q ".safeword/SAFEWORD.md" AGENTS.md; then
|
|
25
|
-
# Link missing, prepend it
|
|
26
|
-
CONTENT=$(cat AGENTS.md)
|
|
27
|
-
echo -e "$LINK\n\n$CONTENT" > AGENTS.md
|
|
28
|
-
echo "SAFEWORD: Restored AGENTS.md link (was removed)"
|
|
29
|
-
fi
|
|
30
|
-
|
|
31
|
-
exit 0
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Safeword: Display version on session start (SessionStart)
|
|
3
|
-
# Shows current safeword version and confirms hooks are active
|
|
4
|
-
|
|
5
|
-
# Change to project directory if set
|
|
6
|
-
[ -n "$CLAUDE_PROJECT_DIR" ] && cd "$CLAUDE_PROJECT_DIR" || true
|
|
7
|
-
|
|
8
|
-
if [ ! -d ".safeword" ]; then
|
|
9
|
-
exit 0
|
|
10
|
-
fi
|
|
11
|
-
|
|
12
|
-
VERSION="unknown"
|
|
13
|
-
if [ -f ".safeword/version" ]; then
|
|
14
|
-
VERSION=$(cat .safeword/version)
|
|
15
|
-
fi
|
|
16
|
-
|
|
17
|
-
echo "SAFE WORD Claude Config v${VERSION} installed - auto-linting and quality review active"
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Auto Quality Review Stop Hook
|
|
3
|
-
# Triggers quality review when changes are proposed or made
|
|
4
|
-
# Only runs for projects with .safeword/ directory
|
|
5
|
-
# Looks for {"proposedChanges": ..., "madeChanges": ...} JSON blob
|
|
6
|
-
|
|
7
|
-
# Change to project directory if set
|
|
8
|
-
[ -n "$CLAUDE_PROJECT_DIR" ] && cd "$CLAUDE_PROJECT_DIR" || true
|
|
9
|
-
|
|
10
|
-
# Check for .safeword directory
|
|
11
|
-
if [ ! -d ".safeword" ]; then
|
|
12
|
-
exit 0
|
|
13
|
-
fi
|
|
14
|
-
|
|
15
|
-
# Read hook input from stdin
|
|
16
|
-
input=$(cat)
|
|
17
|
-
|
|
18
|
-
# Require jq for JSON parsing
|
|
19
|
-
if ! command -v jq &> /dev/null; then
|
|
20
|
-
exit 0
|
|
21
|
-
fi
|
|
22
|
-
|
|
23
|
-
# Get transcript path
|
|
24
|
-
transcript_path=$(echo "$input" | jq -r '.transcript_path // empty' 2>/dev/null)
|
|
25
|
-
|
|
26
|
-
if [ -z "$transcript_path" ] || [ ! -f "$transcript_path" ]; then
|
|
27
|
-
exit 0
|
|
28
|
-
fi
|
|
29
|
-
|
|
30
|
-
# Extract last assistant message from transcript
|
|
31
|
-
# Transcript is JSONL format - each line is a message
|
|
32
|
-
last_assistant_msg=$(grep '"role":"assistant"' "$transcript_path" | tail -1)
|
|
33
|
-
|
|
34
|
-
if [ -z "$last_assistant_msg" ]; then
|
|
35
|
-
exit 0
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
# Extract the text content from the message
|
|
39
|
-
msg_text=$(echo "$last_assistant_msg" | jq -r '.message.content[]? | select(.type == "text") | .text' 2>/dev/null)
|
|
40
|
-
|
|
41
|
-
if [ -z "$msg_text" ]; then
|
|
42
|
-
exit 0
|
|
43
|
-
fi
|
|
44
|
-
|
|
45
|
-
# Extract JSON blob containing our required fields (order-independent)
|
|
46
|
-
# Strategy: Use jq to find and validate the response summary object
|
|
47
|
-
# Look for object with exactly our three boolean fields anywhere in the text
|
|
48
|
-
json_blob=$(echo "$msg_text" | grep -oE '\{[^}]+\}' | while IFS= read -r candidate; do
|
|
49
|
-
if echo "$candidate" | jq -e '
|
|
50
|
-
type == "object" and
|
|
51
|
-
(.proposedChanges | type) == "boolean" and
|
|
52
|
-
(.madeChanges | type) == "boolean" and
|
|
53
|
-
(.askedQuestion | type) == "boolean"
|
|
54
|
-
' >/dev/null 2>&1; then
|
|
55
|
-
echo "$candidate"
|
|
56
|
-
fi
|
|
57
|
-
done | tail -1)
|
|
58
|
-
|
|
59
|
-
if [ -z "$json_blob" ]; then
|
|
60
|
-
# No valid JSON blob found - remind about required format
|
|
61
|
-
echo "SAFEWORD: Response missing required JSON summary. Add to end of response:" >&2
|
|
62
|
-
echo '{"proposedChanges": boolean, "madeChanges": boolean, "askedQuestion": boolean}' >&2
|
|
63
|
-
exit 2
|
|
64
|
-
fi
|
|
65
|
-
|
|
66
|
-
# Parse the boolean values (already validated, safe to extract)
|
|
67
|
-
proposed_changes=$(echo "$json_blob" | jq -r '.proposedChanges')
|
|
68
|
-
made_changes=$(echo "$json_blob" | jq -r '.madeChanges')
|
|
69
|
-
asked_question=$(echo "$json_blob" | jq -r '.askedQuestion')
|
|
70
|
-
|
|
71
|
-
# If asked a question, don't trigger review (waiting for user input)
|
|
72
|
-
if [ "$asked_question" = "true" ]; then
|
|
73
|
-
exit 0
|
|
74
|
-
fi
|
|
75
|
-
|
|
76
|
-
# If either proposed or made changes, trigger quality review
|
|
77
|
-
if [ "$proposed_changes" = "true" ] || [ "$made_changes" = "true" ]; then
|
|
78
|
-
echo "SAFEWORD Quality Review:" >&2
|
|
79
|
-
echo "" >&2
|
|
80
|
-
echo "Double check and critique your work again just in case." >&2
|
|
81
|
-
echo "Assume you've never seen it before." >&2
|
|
82
|
-
echo "" >&2
|
|
83
|
-
echo "- Is it correct?" >&2
|
|
84
|
-
echo "- Is it elegant?" >&2
|
|
85
|
-
echo "- Does it follow latest docs/best practices?" >&2
|
|
86
|
-
echo "- Ask me any non-obvious questions." >&2
|
|
87
|
-
echo "- Avoid bloat." >&2
|
|
88
|
-
exit 2
|
|
89
|
-
fi
|
|
90
|
-
|
|
91
|
-
exit 0
|
package/templates/lib/common.sh
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Safeword common utilities for hook scripts
|
|
3
|
-
|
|
4
|
-
# Output JSON response for Claude Code hooks
|
|
5
|
-
# Usage: json_response '{"key": "value"}'
|
|
6
|
-
json_response() {
|
|
7
|
-
echo "$1"
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
# Check if running in a safeword project
|
|
11
|
-
is_safeword_project() {
|
|
12
|
-
[ -d ".safeword" ]
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
# Get project root (directory containing .safeword)
|
|
16
|
-
get_project_root() {
|
|
17
|
-
local dir="$PWD"
|
|
18
|
-
while [ "$dir" != "/" ]; do
|
|
19
|
-
if [ -d "$dir/.safeword" ]; then
|
|
20
|
-
echo "$dir"
|
|
21
|
-
return 0
|
|
22
|
-
fi
|
|
23
|
-
dir=$(dirname "$dir")
|
|
24
|
-
done
|
|
25
|
-
return 1
|
|
26
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Fallback JSON output when jq is not available
|
|
3
|
-
# Uses printf for safe JSON string escaping
|
|
4
|
-
|
|
5
|
-
# Escape a string for JSON output
|
|
6
|
-
json_escape() {
|
|
7
|
-
local str="$1"
|
|
8
|
-
str="${str//\\/\\\\}"
|
|
9
|
-
str="${str//\"/\\\"}"
|
|
10
|
-
str="${str//$'\n'/\\n}"
|
|
11
|
-
str="${str//$'\t'/\\t}"
|
|
12
|
-
echo "$str"
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
# Output a simple JSON object with one key-value pair
|
|
16
|
-
json_kv() {
|
|
17
|
-
local key="$1"
|
|
18
|
-
local value="$2"
|
|
19
|
-
printf '{"proposedChanges": false, "madeChanges": false, "askedQuestion": false, "%s": "%s"}\n' "$key" "$(json_escape "$value")"
|
|
20
|
-
}
|
|
File without changes
|
|
File without changes
|