safeword 0.2.2 → 0.2.4
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/.claude/commands/arch-review.md +32 -0
- package/.claude/commands/lint.md +6 -0
- package/.claude/commands/quality-review.md +13 -0
- package/.claude/commands/setup-linting.md +6 -0
- package/.claude/hooks/auto-lint.sh +6 -0
- package/.claude/hooks/auto-quality-review.sh +170 -0
- package/.claude/hooks/check-linting-sync.sh +17 -0
- package/.claude/hooks/inject-timestamp.sh +6 -0
- package/.claude/hooks/question-protocol.sh +12 -0
- package/.claude/hooks/run-linters.sh +8 -0
- package/.claude/hooks/run-quality-review.sh +76 -0
- package/.claude/hooks/version-check.sh +10 -0
- package/.claude/mcp/README.md +96 -0
- package/.claude/mcp/arcade.sample.json +9 -0
- package/.claude/mcp/context7.sample.json +7 -0
- package/.claude/mcp/playwright.sample.json +7 -0
- package/.claude/settings.json +62 -0
- package/.claude/skills/quality-reviewer/SKILL.md +190 -0
- package/.claude/skills/safeword-quality-reviewer/SKILL.md +13 -0
- package/.env.arcade.example +4 -0
- package/.env.example +11 -0
- package/.gitmodules +4 -0
- package/.safeword/SAFEWORD.md +33 -0
- package/.safeword/eslint/eslint-base.mjs +101 -0
- package/.safeword/guides/architecture-guide.md +404 -0
- package/.safeword/guides/code-philosophy.md +174 -0
- package/.safeword/guides/context-files-guide.md +405 -0
- package/.safeword/guides/data-architecture-guide.md +183 -0
- package/.safeword/guides/design-doc-guide.md +165 -0
- package/.safeword/guides/learning-extraction.md +515 -0
- package/.safeword/guides/llm-instruction-design.md +239 -0
- package/.safeword/guides/llm-prompting.md +95 -0
- package/.safeword/guides/tdd-best-practices.md +570 -0
- package/.safeword/guides/test-definitions-guide.md +243 -0
- package/.safeword/guides/testing-methodology.md +573 -0
- package/.safeword/guides/user-story-guide.md +237 -0
- package/.safeword/guides/zombie-process-cleanup.md +214 -0
- package/{templates → .safeword}/hooks/agents-md-check.sh +0 -0
- package/{templates → .safeword}/hooks/post-tool.sh +0 -0
- package/{templates → .safeword}/hooks/pre-commit.sh +0 -0
- package/.safeword/planning/002-user-story-quality-evaluation.md +1840 -0
- package/.safeword/planning/003-langsmith-eval-setup-prompt.md +363 -0
- package/.safeword/planning/004-llm-eval-test-cases.md +3226 -0
- package/.safeword/planning/005-architecture-enforcement-system.md +169 -0
- package/.safeword/planning/006-reactive-fix-prevention-research.md +135 -0
- package/.safeword/planning/011-cli-ux-vision.md +330 -0
- package/.safeword/planning/012-project-structure-cleanup.md +154 -0
- package/.safeword/planning/README.md +39 -0
- package/.safeword/planning/automation-plan-v2.md +1225 -0
- package/.safeword/planning/automation-plan-v3.md +1291 -0
- package/.safeword/planning/automation-plan.md +3058 -0
- package/.safeword/planning/design/005-cli-implementation.md +343 -0
- package/.safeword/planning/design/013-cli-self-contained-templates.md +596 -0
- package/.safeword/planning/design/013a-eslint-plugin-suite.md +256 -0
- package/.safeword/planning/design/013b-implementation-snippets.md +385 -0
- package/.safeword/planning/design/013c-config-isolation-strategy.md +242 -0
- package/.safeword/planning/design/code-philosophy-improvements.md +60 -0
- package/.safeword/planning/mcp-analysis.md +545 -0
- package/.safeword/planning/phase2-subagents-vs-skills-analysis.md +451 -0
- package/.safeword/planning/settings-improvements.md +970 -0
- package/.safeword/planning/test-definitions/005-cli-implementation.md +1301 -0
- package/.safeword/planning/test-definitions/cli-self-contained-templates.md +205 -0
- package/.safeword/planning/user-stories/001-guides-review-user-stories.md +1381 -0
- package/.safeword/planning/user-stories/003-reactive-fix-prevention.md +132 -0
- package/.safeword/planning/user-stories/004-technical-constraints.md +86 -0
- package/.safeword/planning/user-stories/005-cli-implementation.md +311 -0
- package/.safeword/planning/user-stories/cli-self-contained-templates.md +172 -0
- package/.safeword/planning/versioned-distribution.md +740 -0
- package/.safeword/prompts/arch-review.md +43 -0
- package/.safeword/prompts/quality-review.md +11 -0
- package/.safeword/scripts/arch-review.sh +235 -0
- package/.safeword/scripts/check-linting-sync.sh +58 -0
- package/.safeword/scripts/setup-linting.sh +559 -0
- package/.safeword/templates/architecture-template.md +136 -0
- package/.safeword/templates/ci/architecture-check.yml +79 -0
- package/.safeword/templates/design-doc-template.md +127 -0
- package/.safeword/templates/test-definitions-feature.md +100 -0
- package/.safeword/templates/ticket-template.md +74 -0
- package/.safeword/templates/user-stories-template.md +82 -0
- package/.safeword/tickets/001-guides-review-user-stories.md +83 -0
- package/.safeword/tickets/002-architecture-enforcement.md +211 -0
- package/.safeword/tickets/003-reactive-fix-prevention.md +57 -0
- package/.safeword/tickets/004-technical-constraints-in-user-stories.md +39 -0
- package/.safeword/tickets/005-cli-implementation.md +248 -0
- package/.safeword/tickets/006-flesh-out-skills.md +43 -0
- package/.safeword/tickets/007-flesh-out-questioning.md +44 -0
- package/.safeword/tickets/008-upgrade-questioning.md +58 -0
- package/.safeword/tickets/009-naming-conventions.md +41 -0
- package/.safeword/tickets/010-safeword-md-cleanup.md +34 -0
- package/.safeword/tickets/011-cursor-setup.md +86 -0
- package/.safeword/tickets/README.md +73 -0
- package/.safeword/version +1 -0
- package/AGENTS.md +59 -0
- package/CLAUDE.md +12 -0
- package/README.md +347 -0
- package/docs/001-cli-implementation-plan.md +856 -0
- package/docs/elite-dx-implementation-plan.md +1034 -0
- package/framework/README.md +131 -0
- package/framework/mcp/README.md +96 -0
- package/framework/mcp/arcade.sample.json +8 -0
- package/framework/mcp/context7.sample.json +6 -0
- package/framework/mcp/playwright.sample.json +6 -0
- package/framework/scripts/arch-review.sh +235 -0
- package/framework/scripts/check-linting-sync.sh +58 -0
- package/framework/scripts/load-env.sh +49 -0
- package/framework/scripts/setup-claude.sh +223 -0
- package/framework/scripts/setup-linting.sh +559 -0
- package/framework/scripts/setup-quality.sh +477 -0
- package/framework/scripts/setup-safeword.sh +550 -0
- package/framework/templates/ci/architecture-check.yml +78 -0
- package/learnings/ai-sdk-v5-breaking-changes.md +178 -0
- package/learnings/e2e-test-zombie-processes.md +231 -0
- package/learnings/milkdown-crepe-editor-property.md +96 -0
- package/learnings/prosemirror-fragment-traversal.md +119 -0
- package/package.json +19 -43
- package/packages/cli/AGENTS.md +1 -0
- package/packages/cli/ARCHITECTURE.md +279 -0
- package/packages/cli/package.json +51 -0
- package/packages/cli/src/cli.ts +63 -0
- package/packages/cli/src/commands/check.ts +166 -0
- package/packages/cli/src/commands/diff.ts +209 -0
- package/packages/cli/src/commands/reset.ts +190 -0
- package/packages/cli/src/commands/setup.ts +325 -0
- package/packages/cli/src/commands/upgrade.ts +163 -0
- package/packages/cli/src/index.ts +3 -0
- package/packages/cli/src/templates/config.ts +58 -0
- package/packages/cli/src/templates/content.ts +18 -0
- package/packages/cli/src/templates/index.ts +12 -0
- package/packages/cli/src/utils/agents-md.ts +66 -0
- package/packages/cli/src/utils/fs.ts +179 -0
- package/packages/cli/src/utils/git.ts +124 -0
- package/packages/cli/src/utils/hooks.ts +29 -0
- package/packages/cli/src/utils/output.ts +60 -0
- package/packages/cli/src/utils/project-detector.test.ts +185 -0
- package/packages/cli/src/utils/project-detector.ts +44 -0
- package/packages/cli/src/utils/version.ts +28 -0
- package/packages/cli/src/version.ts +6 -0
- package/packages/cli/templates/SAFEWORD.md +776 -0
- package/packages/cli/templates/doc-templates/architecture-template.md +136 -0
- package/packages/cli/templates/doc-templates/design-doc-template.md +134 -0
- package/packages/cli/templates/doc-templates/test-definitions-feature.md +131 -0
- package/packages/cli/templates/doc-templates/ticket-template.md +82 -0
- package/packages/cli/templates/doc-templates/user-stories-template.md +92 -0
- package/packages/cli/templates/guides/architecture-guide.md +423 -0
- package/packages/cli/templates/guides/code-philosophy.md +195 -0
- package/packages/cli/templates/guides/context-files-guide.md +457 -0
- package/packages/cli/templates/guides/data-architecture-guide.md +200 -0
- package/packages/cli/templates/guides/design-doc-guide.md +171 -0
- package/packages/cli/templates/guides/learning-extraction.md +552 -0
- package/packages/cli/templates/guides/llm-instruction-design.md +248 -0
- package/packages/cli/templates/guides/llm-prompting.md +102 -0
- package/packages/cli/templates/guides/tdd-best-practices.md +615 -0
- package/packages/cli/templates/guides/test-definitions-guide.md +334 -0
- package/packages/cli/templates/guides/testing-methodology.md +618 -0
- package/packages/cli/templates/guides/user-story-guide.md +256 -0
- package/packages/cli/templates/guides/zombie-process-cleanup.md +219 -0
- package/packages/cli/templates/hooks/agents-md-check.sh +27 -0
- package/packages/cli/templates/hooks/post-tool.sh +4 -0
- package/packages/cli/templates/hooks/pre-commit.sh +10 -0
- package/packages/cli/templates/prompts/arch-review.md +43 -0
- package/packages/cli/templates/prompts/quality-review.md +10 -0
- package/packages/cli/templates/skills/safeword-quality-reviewer/SKILL.md +207 -0
- package/packages/cli/tests/commands/check.test.ts +129 -0
- package/packages/cli/tests/commands/cli.test.ts +89 -0
- package/packages/cli/tests/commands/diff.test.ts +115 -0
- package/packages/cli/tests/commands/reset.test.ts +310 -0
- package/packages/cli/tests/commands/self-healing.test.ts +170 -0
- package/packages/cli/tests/commands/setup-blocking.test.ts +71 -0
- package/packages/cli/tests/commands/setup-core.test.ts +135 -0
- package/packages/cli/tests/commands/setup-git.test.ts +139 -0
- package/packages/cli/tests/commands/setup-hooks.test.ts +334 -0
- package/packages/cli/tests/commands/setup-linting.test.ts +189 -0
- package/packages/cli/tests/commands/setup-noninteractive.test.ts +80 -0
- package/packages/cli/tests/commands/setup-templates.test.ts +181 -0
- package/packages/cli/tests/commands/upgrade.test.ts +215 -0
- package/packages/cli/tests/helpers.ts +243 -0
- package/packages/cli/tests/npm-package.test.ts +83 -0
- package/packages/cli/tests/technical-constraints.test.ts +96 -0
- package/packages/cli/tsconfig.json +25 -0
- package/packages/cli/tsup.config.ts +11 -0
- package/packages/cli/vitest.config.ts +23 -0
- package/promptfoo.yaml +3270 -0
- package/dist/check-M73LGONJ.js +0 -129
- package/dist/check-M73LGONJ.js.map +0 -1
- package/dist/chunk-2XWIUEQK.js +0 -190
- package/dist/chunk-2XWIUEQK.js.map +0 -1
- package/dist/chunk-GZRQL3SX.js +0 -146
- package/dist/chunk-GZRQL3SX.js.map +0 -1
- package/dist/chunk-V5G6BGOK.js +0 -26
- package/dist/chunk-V5G6BGOK.js.map +0 -1
- package/dist/chunk-W66Z3C5H.js +0 -21
- package/dist/chunk-W66Z3C5H.js.map +0 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -34
- package/dist/cli.js.map +0 -1
- package/dist/diff-FSFDCBL5.js +0 -166
- package/dist/diff-FSFDCBL5.js.map +0 -1
- package/dist/index.d.ts +0 -11
- package/dist/index.js +0 -7
- package/dist/index.js.map +0 -1
- package/dist/reset-3ACTIYYE.js +0 -143
- package/dist/reset-3ACTIYYE.js.map +0 -1
- package/dist/setup-MKVVQTVA.js +0 -266
- package/dist/setup-MKVVQTVA.js.map +0 -1
- package/dist/upgrade-FQOL6AF5.js +0 -134
- package/dist/upgrade-FQOL6AF5.js.map +0 -1
- /package/{templates → framework}/SAFEWORD.md +0 -0
- /package/{templates → framework}/guides/architecture-guide.md +0 -0
- /package/{templates → framework}/guides/code-philosophy.md +0 -0
- /package/{templates → framework}/guides/context-files-guide.md +0 -0
- /package/{templates → framework}/guides/data-architecture-guide.md +0 -0
- /package/{templates → framework}/guides/design-doc-guide.md +0 -0
- /package/{templates → framework}/guides/learning-extraction.md +0 -0
- /package/{templates → framework}/guides/llm-instruction-design.md +0 -0
- /package/{templates → framework}/guides/llm-prompting.md +0 -0
- /package/{templates → framework}/guides/tdd-best-practices.md +0 -0
- /package/{templates → framework}/guides/test-definitions-guide.md +0 -0
- /package/{templates → framework}/guides/testing-methodology.md +0 -0
- /package/{templates → framework}/guides/user-story-guide.md +0 -0
- /package/{templates → framework}/guides/zombie-process-cleanup.md +0 -0
- /package/{templates → framework}/prompts/arch-review.md +0 -0
- /package/{templates → framework}/prompts/quality-review.md +0 -0
- /package/{templates/skills/safeword-quality-reviewer → framework/skills/quality-reviewer}/SKILL.md +0 -0
- /package/{templates/doc-templates → framework/templates}/architecture-template.md +0 -0
- /package/{templates/doc-templates → framework/templates}/design-doc-template.md +0 -0
- /package/{templates/doc-templates → framework/templates}/test-definitions-feature.md +0 -0
- /package/{templates/doc-templates → framework/templates}/ticket-template.md +0 -0
- /package/{templates/doc-templates → framework/templates}/user-stories-template.md +0 -0
- /package/{templates → packages/cli/templates}/commands/arch-review.md +0 -0
- /package/{templates → packages/cli/templates}/commands/lint.md +0 -0
- /package/{templates → packages/cli/templates}/commands/quality-review.md +0 -0
- /package/{templates → packages/cli/templates}/hooks/inject-timestamp.sh +0 -0
- /package/{templates → packages/cli/templates}/lib/common.sh +0 -0
- /package/{templates → packages/cli/templates}/lib/jq-fallback.sh +0 -0
- /package/{templates → packages/cli/templates}/markdownlint.jsonc +0 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
################################################################################
|
|
3
|
+
# Claude Code Quality Review Hook Setup Script
|
|
4
|
+
#
|
|
5
|
+
# This script configures quality review hooks to automatically trigger
|
|
6
|
+
# when Claude Code makes or proposes changes to your project files.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# bash setup-quality.sh
|
|
10
|
+
#
|
|
11
|
+
# What it does:
|
|
12
|
+
# - Creates auto-quality-review.sh (Stop hook - detects changes)
|
|
13
|
+
# - Creates run-quality-review.sh (quality review prompt)
|
|
14
|
+
# - Creates /quality-review slash command
|
|
15
|
+
# - Updates .claude/settings.json with Stop hook
|
|
16
|
+
#
|
|
17
|
+
# All files are created project-local (no global dependencies).
|
|
18
|
+
################################################################################
|
|
19
|
+
|
|
20
|
+
set -e # Exit on error
|
|
21
|
+
|
|
22
|
+
VERSION="v1.0.0"
|
|
23
|
+
|
|
24
|
+
# Version header for generated files
|
|
25
|
+
# Note: This script is project-local; the header records the generator name.
|
|
26
|
+
VERSION_HEADER="# Generated by setup-quality.sh $VERSION
|
|
27
|
+
# To upgrade: Re-run setup script in this project
|
|
28
|
+
#"
|
|
29
|
+
|
|
30
|
+
echo "================================="
|
|
31
|
+
echo "Claude Code Quality Review Setup"
|
|
32
|
+
echo "Version: $VERSION"
|
|
33
|
+
echo "================================="
|
|
34
|
+
echo ""
|
|
35
|
+
|
|
36
|
+
# Check if we're in a directory where we can write
|
|
37
|
+
if [ ! -w "." ]; then
|
|
38
|
+
echo "ERROR: Cannot write to current directory. Please cd to your project root."
|
|
39
|
+
exit 1
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
PROJECT_ROOT="$(pwd)"
|
|
43
|
+
echo "Setting up quality review in: $PROJECT_ROOT"
|
|
44
|
+
echo ""
|
|
45
|
+
|
|
46
|
+
# ============================================================================
|
|
47
|
+
# Step 1: Check dependencies
|
|
48
|
+
# ============================================================================
|
|
49
|
+
echo "[1/4] Checking dependencies..."
|
|
50
|
+
|
|
51
|
+
# Check if jq is available (REQUIRED for settings.json merging)
|
|
52
|
+
if ! command -v jq &> /dev/null; then
|
|
53
|
+
echo "ERROR: jq is required for settings.json merging."
|
|
54
|
+
echo "Install it:"
|
|
55
|
+
echo " • macOS: brew install jq"
|
|
56
|
+
echo " • Ubuntu/Debian: sudo apt-get install jq"
|
|
57
|
+
echo " • Other: https://jqlang.github.io/jq/download/"
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
echo " ✓ Dependencies checked"
|
|
62
|
+
echo ""
|
|
63
|
+
|
|
64
|
+
# ============================================================================
|
|
65
|
+
# Step 2: Create .claude directory structure
|
|
66
|
+
# ============================================================================
|
|
67
|
+
echo "[2/4] Creating directory structure..."
|
|
68
|
+
|
|
69
|
+
mkdir -p .claude/hooks
|
|
70
|
+
mkdir -p .claude/commands
|
|
71
|
+
|
|
72
|
+
echo " ✓ Created .claude/hooks and .claude/commands"
|
|
73
|
+
echo ""
|
|
74
|
+
|
|
75
|
+
# ============================================================================
|
|
76
|
+
# Step 3: Generate hook files
|
|
77
|
+
# ============================================================================
|
|
78
|
+
echo "[3/4] Generating quality review hooks..."
|
|
79
|
+
|
|
80
|
+
# Create auto-quality-review.sh (Stop hook)
|
|
81
|
+
{
|
|
82
|
+
echo '#!/bin/bash'
|
|
83
|
+
echo "$VERSION_HEADER"
|
|
84
|
+
cat << 'EOF'
|
|
85
|
+
# Auto Quality Review Stop Hook
|
|
86
|
+
# Triggers quality review when changes are proposed or made
|
|
87
|
+
# Only runs for projects with SAFEWORD.md or CLAUDE.md (searches upward)
|
|
88
|
+
# Looks for {"proposedChanges": ..., "madeChanges": ...} JSON blob
|
|
89
|
+
|
|
90
|
+
# Debug logging (persistent across reboots, user-specific)
|
|
91
|
+
DEBUG_DIR="$HOME/.cache/claude-hooks"
|
|
92
|
+
mkdir -p "$DEBUG_DIR" 2>/dev/null || DEBUG_DIR="/tmp"
|
|
93
|
+
DEBUG_LOG="$DEBUG_DIR/auto-quality-review-debug.log"
|
|
94
|
+
echo "=== Hook triggered at $(date) ===" >> "$DEBUG_LOG"
|
|
95
|
+
echo "PWD: $PWD" >> "$DEBUG_LOG"
|
|
96
|
+
|
|
97
|
+
# Read hook input from stdin
|
|
98
|
+
input=$(cat)
|
|
99
|
+
echo "Input: $input" >> "$DEBUG_LOG"
|
|
100
|
+
|
|
101
|
+
# Check for project-level SAFEWORD.md or CLAUDE.md
|
|
102
|
+
# Search upward from current directory (supports monorepos)
|
|
103
|
+
current_dir="$PWD"
|
|
104
|
+
echo "Searching for SAFEWORD.md or CLAUDE.md..." >> "$DEBUG_LOG"
|
|
105
|
+
while true; do
|
|
106
|
+
if [ -f "$current_dir/SAFEWORD.md" ] || [ -f "$current_dir/CLAUDE.md" ]; then
|
|
107
|
+
echo "Found context file in: $current_dir" >> "$DEBUG_LOG"
|
|
108
|
+
break # Found context file
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# Reached root without finding context file
|
|
112
|
+
if [ "$current_dir" = "/" ]; then
|
|
113
|
+
echo "No SAFEWORD.md or CLAUDE.md found, exiting" >> "$DEBUG_LOG"
|
|
114
|
+
exit 0
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
current_dir=$(dirname "$current_dir")
|
|
118
|
+
done
|
|
119
|
+
|
|
120
|
+
# Read project config (defaults: enabled=true, ask_questions=true)
|
|
121
|
+
config_file="$current_dir/.auto-quality-review.config"
|
|
122
|
+
enabled=true
|
|
123
|
+
ask_questions=true
|
|
124
|
+
|
|
125
|
+
if [ -f "$config_file" ]; then
|
|
126
|
+
while IFS='=' read -r key value; do
|
|
127
|
+
# Skip empty lines and comments
|
|
128
|
+
[[ -z "$key" || "$key" =~ ^# ]] && continue
|
|
129
|
+
# Trim whitespace
|
|
130
|
+
key=$(echo "$key" | xargs)
|
|
131
|
+
value=$(echo "$value" | xargs)
|
|
132
|
+
|
|
133
|
+
case "$key" in
|
|
134
|
+
enabled)
|
|
135
|
+
enabled="$value"
|
|
136
|
+
;;
|
|
137
|
+
ask_questions)
|
|
138
|
+
ask_questions="$value"
|
|
139
|
+
;;
|
|
140
|
+
esac
|
|
141
|
+
done < "$config_file"
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
# Exit if disabled for this project
|
|
145
|
+
echo "Config: enabled=$enabled, ask_questions=$ask_questions" >> "$DEBUG_LOG"
|
|
146
|
+
if [ "$enabled" != "true" ]; then
|
|
147
|
+
echo "Hook disabled for this project, exiting" >> "$DEBUG_LOG"
|
|
148
|
+
exit 0
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
# Get transcript path
|
|
152
|
+
if ! transcript_path=$(echo "$input" | jq -r '.transcript_path // empty' 2>/dev/null); then
|
|
153
|
+
echo "ERROR: jq failed to extract transcript_path from hook input." >&2
|
|
154
|
+
echo "ERROR: jq failed to parse input" >> "$DEBUG_LOG"
|
|
155
|
+
exit 2
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
echo "Transcript path: $transcript_path" >> "$DEBUG_LOG"
|
|
159
|
+
if [ -z "$transcript_path" ] || [ ! -f "$transcript_path" ]; then
|
|
160
|
+
echo "No transcript or file doesn't exist, exiting" >> "$DEBUG_LOG"
|
|
161
|
+
exit 0
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# Extract last assistant message from transcript
|
|
165
|
+
# Transcript is JSONL format - each line is a message
|
|
166
|
+
last_assistant_msg=$(grep '"role":"assistant"' "$transcript_path" | tail -1)
|
|
167
|
+
|
|
168
|
+
echo "Last assistant message length: ${#last_assistant_msg}" >> "$DEBUG_LOG"
|
|
169
|
+
if [ -z "$last_assistant_msg" ]; then
|
|
170
|
+
# Normal case: No assistant messages yet
|
|
171
|
+
echo "No assistant messages found, exiting" >> "$DEBUG_LOG"
|
|
172
|
+
exit 0
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# Extract the text content from the message
|
|
176
|
+
# Looking for the final JSON blob: {"proposedChanges": ..., "madeChanges": ...}
|
|
177
|
+
if ! msg_text=$(echo "$last_assistant_msg" | jq -r '.message.content[]? | select(.type == "text") | .text' 2>/dev/null); then
|
|
178
|
+
echo "ERROR: jq failed to parse assistant message structure. Transcript format may have changed." >&2
|
|
179
|
+
echo "DEBUG: Message structure: $(echo "$last_assistant_msg" | jq -r '.message.content[].type' 2>/dev/null)" >> "$DEBUG_LOG"
|
|
180
|
+
exit 2
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
if [ -z "$msg_text" ]; then
|
|
184
|
+
# Normal case: Message has no text content
|
|
185
|
+
exit 0
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
# Extract the JSON blob from the end of the message
|
|
189
|
+
# Pattern: {"proposedChanges": boolean, "madeChanges": boolean, "askedQuestion": boolean}
|
|
190
|
+
json_blob=$(echo "$msg_text" | grep -oE '\{"proposedChanges":\s*(true|false)\s*,\s*"madeChanges":\s*(true|false)\s*,\s*"askedQuestion":\s*(true|false)\s*\}' | tail -1)
|
|
191
|
+
|
|
192
|
+
echo "JSON blob: $json_blob" >> "$DEBUG_LOG"
|
|
193
|
+
if [ -z "$json_blob" ]; then
|
|
194
|
+
# Check if this looks like a substantive response (>100 chars)
|
|
195
|
+
# If so, remind agent to include JSON payload
|
|
196
|
+
msg_length=${#msg_text}
|
|
197
|
+
echo "No JSON blob found, message length: $msg_length" >> "$DEBUG_LOG"
|
|
198
|
+
|
|
199
|
+
if [ "$msg_length" -gt 100 ]; then
|
|
200
|
+
echo "Substantive response without JSON payload, sending reminder" >> "$DEBUG_LOG"
|
|
201
|
+
echo "Missing required JSON response format. Please include: {\"proposedChanges\": bool, \"madeChanges\": bool, \"askedQuestion\": bool}" >&2
|
|
202
|
+
exit 2
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
# Short message without JSON is fine (acknowledgments, etc)
|
|
206
|
+
echo "Short message without JSON, exiting" >> "$DEBUG_LOG"
|
|
207
|
+
exit 0
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
# Parse the boolean values
|
|
211
|
+
if ! proposed_changes=$(echo "$json_blob" | jq -r '.proposedChanges // false' 2>/dev/null); then
|
|
212
|
+
echo "ERROR: jq failed to parse proposedChanges field from JSON blob." >&2
|
|
213
|
+
exit 2
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
if ! made_changes=$(echo "$json_blob" | jq -r '.madeChanges // false' 2>/dev/null); then
|
|
217
|
+
echo "ERROR: jq failed to parse madeChanges field from JSON blob." >&2
|
|
218
|
+
exit 2
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
if ! asked_question=$(echo "$json_blob" | jq -r '.askedQuestion // false' 2>/dev/null); then
|
|
222
|
+
echo "ERROR: jq failed to parse askedQuestion field from JSON blob." >&2
|
|
223
|
+
exit 2
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
# If asked question, skip quality review (waiting for user response)
|
|
227
|
+
echo "proposedChanges=$proposed_changes, madeChanges=$made_changes, askedQuestion=$asked_question" >> "$DEBUG_LOG"
|
|
228
|
+
if [ "$asked_question" = "true" ]; then
|
|
229
|
+
echo "Agent asked question, skipping quality review" >> "$DEBUG_LOG"
|
|
230
|
+
exit 0
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
# If either proposed or made changes, trigger quality review
|
|
234
|
+
if [ "$proposed_changes" = "true" ] || [ "$made_changes" = "true" ]; then
|
|
235
|
+
# Block and request quality review
|
|
236
|
+
echo "TRIGGERING QUALITY REVIEW" >> "$DEBUG_LOG"
|
|
237
|
+
|
|
238
|
+
# Find the shared quality review script (same directory as this hook)
|
|
239
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
240
|
+
|
|
241
|
+
# Call shared quality review script
|
|
242
|
+
if [ "$ask_questions" = "true" ]; then
|
|
243
|
+
exec "$SCRIPT_DIR/run-quality-review.sh"
|
|
244
|
+
else
|
|
245
|
+
exec "$SCRIPT_DIR/run-quality-review.sh" --no-questions
|
|
246
|
+
fi
|
|
247
|
+
fi
|
|
248
|
+
|
|
249
|
+
# No changes proposed or made - allow continuation
|
|
250
|
+
exit 0
|
|
251
|
+
EOF
|
|
252
|
+
} > .claude/hooks/auto-quality-review.sh
|
|
253
|
+
|
|
254
|
+
chmod +x .claude/hooks/auto-quality-review.sh
|
|
255
|
+
|
|
256
|
+
# Create run-quality-review.sh (quality review prompt)
|
|
257
|
+
{
|
|
258
|
+
echo '#!/bin/bash'
|
|
259
|
+
echo "$VERSION_HEADER"
|
|
260
|
+
cat << 'EOF'
|
|
261
|
+
# Shared Quality Review Script
|
|
262
|
+
#
|
|
263
|
+
# Prompts for a quality review with configurable questions.
|
|
264
|
+
# Used by both Stop hooks and manual /quality-review command.
|
|
265
|
+
#
|
|
266
|
+
# Usage:
|
|
267
|
+
# run-quality-review.sh # With questions enabled
|
|
268
|
+
# run-quality-review.sh --no-questions # Without "ask me" prompt
|
|
269
|
+
#
|
|
270
|
+
# Environment variables:
|
|
271
|
+
# CLAUDE_PROJECT_DIR - Project root (for finding config and prompts)
|
|
272
|
+
|
|
273
|
+
# Default: ask questions enabled
|
|
274
|
+
ask_questions=true
|
|
275
|
+
|
|
276
|
+
# Parse command line arguments
|
|
277
|
+
for arg in "$@"; do
|
|
278
|
+
case "$arg" in
|
|
279
|
+
--no-questions)
|
|
280
|
+
ask_questions=false
|
|
281
|
+
;;
|
|
282
|
+
esac
|
|
283
|
+
done
|
|
284
|
+
|
|
285
|
+
# Try to read project config if CLAUDE_PROJECT_DIR is set
|
|
286
|
+
if [ -n "$CLAUDE_PROJECT_DIR" ]; then
|
|
287
|
+
config_file="$CLAUDE_PROJECT_DIR/.auto-quality-review.config"
|
|
288
|
+
|
|
289
|
+
if [ -f "$config_file" ]; then
|
|
290
|
+
while IFS='=' read -r key value; do
|
|
291
|
+
# Skip empty lines and comments
|
|
292
|
+
[[ -z "$key" || "$key" =~ ^# ]] && continue
|
|
293
|
+
# Trim whitespace
|
|
294
|
+
key=$(echo "$key" | xargs)
|
|
295
|
+
value=$(echo "$value" | xargs)
|
|
296
|
+
|
|
297
|
+
case "$key" in
|
|
298
|
+
ask_questions)
|
|
299
|
+
ask_questions="$value"
|
|
300
|
+
;;
|
|
301
|
+
esac
|
|
302
|
+
done < "$config_file"
|
|
303
|
+
fi
|
|
304
|
+
fi
|
|
305
|
+
|
|
306
|
+
# Try to read prompt from .safeword/prompts/quality-review.md
|
|
307
|
+
PROMPT_FILE=""
|
|
308
|
+
if [ -n "$CLAUDE_PROJECT_DIR" ] && [ -f "$CLAUDE_PROJECT_DIR/.safeword/prompts/quality-review.md" ]; then
|
|
309
|
+
PROMPT_FILE="$CLAUDE_PROJECT_DIR/.safeword/prompts/quality-review.md"
|
|
310
|
+
fi
|
|
311
|
+
|
|
312
|
+
if [ -n "$PROMPT_FILE" ]; then
|
|
313
|
+
# Read prompt from file, skip the header line
|
|
314
|
+
tail -n +3 "$PROMPT_FILE" >&2
|
|
315
|
+
else
|
|
316
|
+
# Fallback to hardcoded prompt
|
|
317
|
+
echo "Double check and critique your work just in case." >&2
|
|
318
|
+
echo "" >&2
|
|
319
|
+
echo "- Is it correct?" >&2
|
|
320
|
+
echo "- Is it elegant?" >&2
|
|
321
|
+
echo "- Does it adhere to the latest documentation and best practices for the relevant stack items, UX principles, domain requirements, and testing practices?" >&2
|
|
322
|
+
|
|
323
|
+
if [ "$ask_questions" = "true" ]; then
|
|
324
|
+
echo "- Ask me any non-obvious questions you can't research yourself in the codebase or online." >&2
|
|
325
|
+
fi
|
|
326
|
+
|
|
327
|
+
echo "- Think hard." >&2
|
|
328
|
+
echo "- Avoid bloat." >&2
|
|
329
|
+
fi
|
|
330
|
+
|
|
331
|
+
# Exit with code 2 to block (Stop hook behavior)
|
|
332
|
+
exit 2
|
|
333
|
+
EOF
|
|
334
|
+
} > .claude/hooks/run-quality-review.sh
|
|
335
|
+
|
|
336
|
+
chmod +x .claude/hooks/run-quality-review.sh
|
|
337
|
+
|
|
338
|
+
echo " ✓ Created auto-quality-review.sh (Stop hook)"
|
|
339
|
+
echo " ✓ Created run-quality-review.sh (quality review prompt)"
|
|
340
|
+
|
|
341
|
+
# Create quality-review slash command
|
|
342
|
+
cat > .claude/commands/quality-review.md << 'EOF'
|
|
343
|
+
Trigger a quality review to double-check your work.
|
|
344
|
+
|
|
345
|
+
Execute:
|
|
346
|
+
```bash
|
|
347
|
+
bash .claude/hooks/run-quality-review.sh
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
This will prompt you to:
|
|
351
|
+
- Verify correctness
|
|
352
|
+
- Check elegance
|
|
353
|
+
- Validate against latest docs and best practices
|
|
354
|
+
- Consider asking clarifying questions
|
|
355
|
+
- Think deeply and avoid bloat
|
|
356
|
+
EOF
|
|
357
|
+
|
|
358
|
+
echo " ✓ Created quality-review slash command"
|
|
359
|
+
|
|
360
|
+
# Create version-check SessionStart hook
|
|
361
|
+
{
|
|
362
|
+
echo '#!/bin/bash'
|
|
363
|
+
echo "$VERSION_HEADER"
|
|
364
|
+
cat << 'EOF'
|
|
365
|
+
# SAFE WORD Claude Config - SessionStart Hook
|
|
366
|
+
# Injects version and status into Claude's context (silent - not displayed to user)
|
|
367
|
+
|
|
368
|
+
echo "SAFE WORD Claude Config v1.0.0 installed - auto-linting and quality review active"
|
|
369
|
+
|
|
370
|
+
exit 0
|
|
371
|
+
EOF
|
|
372
|
+
} > .claude/hooks/version-check.sh
|
|
373
|
+
|
|
374
|
+
chmod +x .claude/hooks/version-check.sh
|
|
375
|
+
|
|
376
|
+
echo " ✓ Created version-check SessionStart hook"
|
|
377
|
+
|
|
378
|
+
# Create inject-timestamp UserPromptSubmit hook
|
|
379
|
+
{
|
|
380
|
+
echo '#!/bin/bash'
|
|
381
|
+
echo "$VERSION_HEADER"
|
|
382
|
+
cat << 'EOF'
|
|
383
|
+
# Inject Timestamp - UserPromptSubmit Hook
|
|
384
|
+
# Outputs current Unix timestamp for Claude's context awareness
|
|
385
|
+
# Helps with accurate ticket timestamps and time-based reasoning
|
|
386
|
+
|
|
387
|
+
echo "Current time: $(date +%s) ($(date -u +%Y-%m-%dT%H:%M:%SZ))"
|
|
388
|
+
EOF
|
|
389
|
+
} > .claude/hooks/inject-timestamp.sh
|
|
390
|
+
|
|
391
|
+
chmod +x .claude/hooks/inject-timestamp.sh
|
|
392
|
+
|
|
393
|
+
echo " ✓ Created inject-timestamp UserPromptSubmit hook"
|
|
394
|
+
echo ""
|
|
395
|
+
|
|
396
|
+
# ============================================================================
|
|
397
|
+
# Step 4: Update settings.json
|
|
398
|
+
# ============================================================================
|
|
399
|
+
echo "[4/4] Updating .claude/settings.json..."
|
|
400
|
+
|
|
401
|
+
# Create base settings.json if doesn't exist
|
|
402
|
+
if [ ! -f .claude/settings.json ]; then
|
|
403
|
+
echo '{"hooks": {}}' > .claude/settings.json
|
|
404
|
+
echo " ✓ Created .claude/settings.json"
|
|
405
|
+
fi
|
|
406
|
+
|
|
407
|
+
# Check if auto-quality-review hook already exists in Stop
|
|
408
|
+
if jq -e '.hooks.Stop[]?.hooks[]? | select(.command == "$CLAUDE_PROJECT_DIR/.claude/hooks/auto-quality-review.sh")' .claude/settings.json > /dev/null 2>&1; then
|
|
409
|
+
echo " ✓ Stop hook already exists (skipped)"
|
|
410
|
+
else
|
|
411
|
+
# Append to Stop array (or create if doesn't exist)
|
|
412
|
+
jq '.hooks.Stop = (.hooks.Stop // []) + [{"hooks": [{"type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/auto-quality-review.sh"}]}]' \
|
|
413
|
+
.claude/settings.json > .claude/settings.json.tmp
|
|
414
|
+
mv .claude/settings.json.tmp .claude/settings.json
|
|
415
|
+
echo " ✓ Added Stop hook to settings.json"
|
|
416
|
+
fi
|
|
417
|
+
|
|
418
|
+
# Check if version-check hook already exists in SessionStart
|
|
419
|
+
if jq -e '.hooks.SessionStart[]?.hooks[]? | select(.command == "$CLAUDE_PROJECT_DIR/.claude/hooks/version-check.sh")' .claude/settings.json > /dev/null 2>&1; then
|
|
420
|
+
echo " ✓ SessionStart hook already exists (skipped)"
|
|
421
|
+
else
|
|
422
|
+
# Append to SessionStart array (or create if doesn't exist)
|
|
423
|
+
jq '.hooks.SessionStart = (.hooks.SessionStart // []) + [{"hooks": [{"type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/version-check.sh"}]}]' \
|
|
424
|
+
.claude/settings.json > .claude/settings.json.tmp
|
|
425
|
+
mv .claude/settings.json.tmp .claude/settings.json
|
|
426
|
+
echo " ✓ Added SessionStart hook to settings.json"
|
|
427
|
+
fi
|
|
428
|
+
|
|
429
|
+
# Check if inject-timestamp hook already exists in UserPromptSubmit
|
|
430
|
+
if jq -e '.hooks.UserPromptSubmit[]?.hooks[]? | select(.command == "$CLAUDE_PROJECT_DIR/.claude/hooks/inject-timestamp.sh")' .claude/settings.json > /dev/null 2>&1; then
|
|
431
|
+
echo " ✓ UserPromptSubmit hook already exists (skipped)"
|
|
432
|
+
else
|
|
433
|
+
# Append to UserPromptSubmit array (or create if doesn't exist)
|
|
434
|
+
jq '.hooks.UserPromptSubmit = (.hooks.UserPromptSubmit // []) + [{"hooks": [{"type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/inject-timestamp.sh"}]}]' \
|
|
435
|
+
.claude/settings.json > .claude/settings.json.tmp
|
|
436
|
+
mv .claude/settings.json.tmp .claude/settings.json
|
|
437
|
+
echo " ✓ Added UserPromptSubmit hook to settings.json"
|
|
438
|
+
fi
|
|
439
|
+
echo ""
|
|
440
|
+
|
|
441
|
+
echo "================================="
|
|
442
|
+
echo "✓ Setup Complete!"
|
|
443
|
+
echo "================================="
|
|
444
|
+
echo ""
|
|
445
|
+
echo "What was configured:"
|
|
446
|
+
echo " • Quality review hooks installed (triggers on changes)"
|
|
447
|
+
echo " • Version display on session start"
|
|
448
|
+
echo " • Timestamp injection on every prompt"
|
|
449
|
+
echo " • /quality-review command available"
|
|
450
|
+
echo " • All files are project-local and standalone"
|
|
451
|
+
echo ""
|
|
452
|
+
echo "⚠ IMPORTANT:"
|
|
453
|
+
echo " • COMMIT .safeword/ and .claude/ to your repo for team consistency"
|
|
454
|
+
echo " • Claude Code will read @./.safeword/SAFEWORD.md via SAFEWORD.md or CLAUDE.md reference"
|
|
455
|
+
echo " • No external dependencies - project is fully portable"
|
|
456
|
+
echo ""
|
|
457
|
+
echo "Hook behavior:"
|
|
458
|
+
echo " • Only runs in projects with SAFEWORD.md or CLAUDE.md"
|
|
459
|
+
echo " • Triggers when Claude proposes or makes changes"
|
|
460
|
+
echo " • Skips if Claude asks a question (waits for your answer)"
|
|
461
|
+
echo " • Can be disabled per-project: create .auto-quality-review.config"
|
|
462
|
+
echo ""
|
|
463
|
+
echo "To customize:"
|
|
464
|
+
echo " • Edit .claude/hooks/run-quality-review.sh for review prompt"
|
|
465
|
+
echo " • Edit .claude/hooks/auto-quality-review.sh for trigger logic"
|
|
466
|
+
echo " • Edit .claude/settings.json for hook configuration"
|
|
467
|
+
echo ""
|
|
468
|
+
echo "Test it:"
|
|
469
|
+
echo " • Ask Claude to create or modify a file"
|
|
470
|
+
echo " • Watch for quality review prompt after the change"
|
|
471
|
+
echo ""
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
|