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.
Files changed (235) hide show
  1. package/.claude/commands/arch-review.md +32 -0
  2. package/.claude/commands/lint.md +6 -0
  3. package/.claude/commands/quality-review.md +13 -0
  4. package/.claude/commands/setup-linting.md +6 -0
  5. package/.claude/hooks/auto-lint.sh +6 -0
  6. package/.claude/hooks/auto-quality-review.sh +170 -0
  7. package/.claude/hooks/check-linting-sync.sh +17 -0
  8. package/.claude/hooks/inject-timestamp.sh +6 -0
  9. package/.claude/hooks/question-protocol.sh +12 -0
  10. package/.claude/hooks/run-linters.sh +8 -0
  11. package/.claude/hooks/run-quality-review.sh +76 -0
  12. package/.claude/hooks/version-check.sh +10 -0
  13. package/.claude/mcp/README.md +96 -0
  14. package/.claude/mcp/arcade.sample.json +9 -0
  15. package/.claude/mcp/context7.sample.json +7 -0
  16. package/.claude/mcp/playwright.sample.json +7 -0
  17. package/.claude/settings.json +62 -0
  18. package/.claude/skills/quality-reviewer/SKILL.md +190 -0
  19. package/.claude/skills/safeword-quality-reviewer/SKILL.md +13 -0
  20. package/.env.arcade.example +4 -0
  21. package/.env.example +11 -0
  22. package/.gitmodules +4 -0
  23. package/.safeword/SAFEWORD.md +33 -0
  24. package/.safeword/eslint/eslint-base.mjs +101 -0
  25. package/.safeword/guides/architecture-guide.md +404 -0
  26. package/.safeword/guides/code-philosophy.md +174 -0
  27. package/.safeword/guides/context-files-guide.md +405 -0
  28. package/.safeword/guides/data-architecture-guide.md +183 -0
  29. package/.safeword/guides/design-doc-guide.md +165 -0
  30. package/.safeword/guides/learning-extraction.md +515 -0
  31. package/.safeword/guides/llm-instruction-design.md +239 -0
  32. package/.safeword/guides/llm-prompting.md +95 -0
  33. package/.safeword/guides/tdd-best-practices.md +570 -0
  34. package/.safeword/guides/test-definitions-guide.md +243 -0
  35. package/.safeword/guides/testing-methodology.md +573 -0
  36. package/.safeword/guides/user-story-guide.md +237 -0
  37. package/.safeword/guides/zombie-process-cleanup.md +214 -0
  38. package/{templates → .safeword}/hooks/agents-md-check.sh +0 -0
  39. package/{templates → .safeword}/hooks/post-tool.sh +0 -0
  40. package/{templates → .safeword}/hooks/pre-commit.sh +0 -0
  41. package/.safeword/planning/002-user-story-quality-evaluation.md +1840 -0
  42. package/.safeword/planning/003-langsmith-eval-setup-prompt.md +363 -0
  43. package/.safeword/planning/004-llm-eval-test-cases.md +3226 -0
  44. package/.safeword/planning/005-architecture-enforcement-system.md +169 -0
  45. package/.safeword/planning/006-reactive-fix-prevention-research.md +135 -0
  46. package/.safeword/planning/011-cli-ux-vision.md +330 -0
  47. package/.safeword/planning/012-project-structure-cleanup.md +154 -0
  48. package/.safeword/planning/README.md +39 -0
  49. package/.safeword/planning/automation-plan-v2.md +1225 -0
  50. package/.safeword/planning/automation-plan-v3.md +1291 -0
  51. package/.safeword/planning/automation-plan.md +3058 -0
  52. package/.safeword/planning/design/005-cli-implementation.md +343 -0
  53. package/.safeword/planning/design/013-cli-self-contained-templates.md +596 -0
  54. package/.safeword/planning/design/013a-eslint-plugin-suite.md +256 -0
  55. package/.safeword/planning/design/013b-implementation-snippets.md +385 -0
  56. package/.safeword/planning/design/013c-config-isolation-strategy.md +242 -0
  57. package/.safeword/planning/design/code-philosophy-improvements.md +60 -0
  58. package/.safeword/planning/mcp-analysis.md +545 -0
  59. package/.safeword/planning/phase2-subagents-vs-skills-analysis.md +451 -0
  60. package/.safeword/planning/settings-improvements.md +970 -0
  61. package/.safeword/planning/test-definitions/005-cli-implementation.md +1301 -0
  62. package/.safeword/planning/test-definitions/cli-self-contained-templates.md +205 -0
  63. package/.safeword/planning/user-stories/001-guides-review-user-stories.md +1381 -0
  64. package/.safeword/planning/user-stories/003-reactive-fix-prevention.md +132 -0
  65. package/.safeword/planning/user-stories/004-technical-constraints.md +86 -0
  66. package/.safeword/planning/user-stories/005-cli-implementation.md +311 -0
  67. package/.safeword/planning/user-stories/cli-self-contained-templates.md +172 -0
  68. package/.safeword/planning/versioned-distribution.md +740 -0
  69. package/.safeword/prompts/arch-review.md +43 -0
  70. package/.safeword/prompts/quality-review.md +11 -0
  71. package/.safeword/scripts/arch-review.sh +235 -0
  72. package/.safeword/scripts/check-linting-sync.sh +58 -0
  73. package/.safeword/scripts/setup-linting.sh +559 -0
  74. package/.safeword/templates/architecture-template.md +136 -0
  75. package/.safeword/templates/ci/architecture-check.yml +79 -0
  76. package/.safeword/templates/design-doc-template.md +127 -0
  77. package/.safeword/templates/test-definitions-feature.md +100 -0
  78. package/.safeword/templates/ticket-template.md +74 -0
  79. package/.safeword/templates/user-stories-template.md +82 -0
  80. package/.safeword/tickets/001-guides-review-user-stories.md +83 -0
  81. package/.safeword/tickets/002-architecture-enforcement.md +211 -0
  82. package/.safeword/tickets/003-reactive-fix-prevention.md +57 -0
  83. package/.safeword/tickets/004-technical-constraints-in-user-stories.md +39 -0
  84. package/.safeword/tickets/005-cli-implementation.md +248 -0
  85. package/.safeword/tickets/006-flesh-out-skills.md +43 -0
  86. package/.safeword/tickets/007-flesh-out-questioning.md +44 -0
  87. package/.safeword/tickets/008-upgrade-questioning.md +58 -0
  88. package/.safeword/tickets/009-naming-conventions.md +41 -0
  89. package/.safeword/tickets/010-safeword-md-cleanup.md +34 -0
  90. package/.safeword/tickets/011-cursor-setup.md +86 -0
  91. package/.safeword/tickets/README.md +73 -0
  92. package/.safeword/version +1 -0
  93. package/AGENTS.md +59 -0
  94. package/CLAUDE.md +12 -0
  95. package/README.md +347 -0
  96. package/docs/001-cli-implementation-plan.md +856 -0
  97. package/docs/elite-dx-implementation-plan.md +1034 -0
  98. package/framework/README.md +131 -0
  99. package/framework/mcp/README.md +96 -0
  100. package/framework/mcp/arcade.sample.json +8 -0
  101. package/framework/mcp/context7.sample.json +6 -0
  102. package/framework/mcp/playwright.sample.json +6 -0
  103. package/framework/scripts/arch-review.sh +235 -0
  104. package/framework/scripts/check-linting-sync.sh +58 -0
  105. package/framework/scripts/load-env.sh +49 -0
  106. package/framework/scripts/setup-claude.sh +223 -0
  107. package/framework/scripts/setup-linting.sh +559 -0
  108. package/framework/scripts/setup-quality.sh +477 -0
  109. package/framework/scripts/setup-safeword.sh +550 -0
  110. package/framework/templates/ci/architecture-check.yml +78 -0
  111. package/learnings/ai-sdk-v5-breaking-changes.md +178 -0
  112. package/learnings/e2e-test-zombie-processes.md +231 -0
  113. package/learnings/milkdown-crepe-editor-property.md +96 -0
  114. package/learnings/prosemirror-fragment-traversal.md +119 -0
  115. package/package.json +19 -43
  116. package/packages/cli/AGENTS.md +1 -0
  117. package/packages/cli/ARCHITECTURE.md +279 -0
  118. package/packages/cli/package.json +51 -0
  119. package/packages/cli/src/cli.ts +63 -0
  120. package/packages/cli/src/commands/check.ts +166 -0
  121. package/packages/cli/src/commands/diff.ts +209 -0
  122. package/packages/cli/src/commands/reset.ts +190 -0
  123. package/packages/cli/src/commands/setup.ts +325 -0
  124. package/packages/cli/src/commands/upgrade.ts +163 -0
  125. package/packages/cli/src/index.ts +3 -0
  126. package/packages/cli/src/templates/config.ts +58 -0
  127. package/packages/cli/src/templates/content.ts +18 -0
  128. package/packages/cli/src/templates/index.ts +12 -0
  129. package/packages/cli/src/utils/agents-md.ts +66 -0
  130. package/packages/cli/src/utils/fs.ts +179 -0
  131. package/packages/cli/src/utils/git.ts +124 -0
  132. package/packages/cli/src/utils/hooks.ts +29 -0
  133. package/packages/cli/src/utils/output.ts +60 -0
  134. package/packages/cli/src/utils/project-detector.test.ts +185 -0
  135. package/packages/cli/src/utils/project-detector.ts +44 -0
  136. package/packages/cli/src/utils/version.ts +28 -0
  137. package/packages/cli/src/version.ts +6 -0
  138. package/packages/cli/templates/SAFEWORD.md +776 -0
  139. package/packages/cli/templates/doc-templates/architecture-template.md +136 -0
  140. package/packages/cli/templates/doc-templates/design-doc-template.md +134 -0
  141. package/packages/cli/templates/doc-templates/test-definitions-feature.md +131 -0
  142. package/packages/cli/templates/doc-templates/ticket-template.md +82 -0
  143. package/packages/cli/templates/doc-templates/user-stories-template.md +92 -0
  144. package/packages/cli/templates/guides/architecture-guide.md +423 -0
  145. package/packages/cli/templates/guides/code-philosophy.md +195 -0
  146. package/packages/cli/templates/guides/context-files-guide.md +457 -0
  147. package/packages/cli/templates/guides/data-architecture-guide.md +200 -0
  148. package/packages/cli/templates/guides/design-doc-guide.md +171 -0
  149. package/packages/cli/templates/guides/learning-extraction.md +552 -0
  150. package/packages/cli/templates/guides/llm-instruction-design.md +248 -0
  151. package/packages/cli/templates/guides/llm-prompting.md +102 -0
  152. package/packages/cli/templates/guides/tdd-best-practices.md +615 -0
  153. package/packages/cli/templates/guides/test-definitions-guide.md +334 -0
  154. package/packages/cli/templates/guides/testing-methodology.md +618 -0
  155. package/packages/cli/templates/guides/user-story-guide.md +256 -0
  156. package/packages/cli/templates/guides/zombie-process-cleanup.md +219 -0
  157. package/packages/cli/templates/hooks/agents-md-check.sh +27 -0
  158. package/packages/cli/templates/hooks/post-tool.sh +4 -0
  159. package/packages/cli/templates/hooks/pre-commit.sh +10 -0
  160. package/packages/cli/templates/prompts/arch-review.md +43 -0
  161. package/packages/cli/templates/prompts/quality-review.md +10 -0
  162. package/packages/cli/templates/skills/safeword-quality-reviewer/SKILL.md +207 -0
  163. package/packages/cli/tests/commands/check.test.ts +129 -0
  164. package/packages/cli/tests/commands/cli.test.ts +89 -0
  165. package/packages/cli/tests/commands/diff.test.ts +115 -0
  166. package/packages/cli/tests/commands/reset.test.ts +310 -0
  167. package/packages/cli/tests/commands/self-healing.test.ts +170 -0
  168. package/packages/cli/tests/commands/setup-blocking.test.ts +71 -0
  169. package/packages/cli/tests/commands/setup-core.test.ts +135 -0
  170. package/packages/cli/tests/commands/setup-git.test.ts +139 -0
  171. package/packages/cli/tests/commands/setup-hooks.test.ts +334 -0
  172. package/packages/cli/tests/commands/setup-linting.test.ts +189 -0
  173. package/packages/cli/tests/commands/setup-noninteractive.test.ts +80 -0
  174. package/packages/cli/tests/commands/setup-templates.test.ts +181 -0
  175. package/packages/cli/tests/commands/upgrade.test.ts +215 -0
  176. package/packages/cli/tests/helpers.ts +243 -0
  177. package/packages/cli/tests/npm-package.test.ts +83 -0
  178. package/packages/cli/tests/technical-constraints.test.ts +96 -0
  179. package/packages/cli/tsconfig.json +25 -0
  180. package/packages/cli/tsup.config.ts +11 -0
  181. package/packages/cli/vitest.config.ts +23 -0
  182. package/promptfoo.yaml +3270 -0
  183. package/dist/check-M73LGONJ.js +0 -129
  184. package/dist/check-M73LGONJ.js.map +0 -1
  185. package/dist/chunk-2XWIUEQK.js +0 -190
  186. package/dist/chunk-2XWIUEQK.js.map +0 -1
  187. package/dist/chunk-GZRQL3SX.js +0 -146
  188. package/dist/chunk-GZRQL3SX.js.map +0 -1
  189. package/dist/chunk-V5G6BGOK.js +0 -26
  190. package/dist/chunk-V5G6BGOK.js.map +0 -1
  191. package/dist/chunk-W66Z3C5H.js +0 -21
  192. package/dist/chunk-W66Z3C5H.js.map +0 -1
  193. package/dist/cli.d.ts +0 -1
  194. package/dist/cli.js +0 -34
  195. package/dist/cli.js.map +0 -1
  196. package/dist/diff-FSFDCBL5.js +0 -166
  197. package/dist/diff-FSFDCBL5.js.map +0 -1
  198. package/dist/index.d.ts +0 -11
  199. package/dist/index.js +0 -7
  200. package/dist/index.js.map +0 -1
  201. package/dist/reset-3ACTIYYE.js +0 -143
  202. package/dist/reset-3ACTIYYE.js.map +0 -1
  203. package/dist/setup-MKVVQTVA.js +0 -266
  204. package/dist/setup-MKVVQTVA.js.map +0 -1
  205. package/dist/upgrade-FQOL6AF5.js +0 -134
  206. package/dist/upgrade-FQOL6AF5.js.map +0 -1
  207. /package/{templates → framework}/SAFEWORD.md +0 -0
  208. /package/{templates → framework}/guides/architecture-guide.md +0 -0
  209. /package/{templates → framework}/guides/code-philosophy.md +0 -0
  210. /package/{templates → framework}/guides/context-files-guide.md +0 -0
  211. /package/{templates → framework}/guides/data-architecture-guide.md +0 -0
  212. /package/{templates → framework}/guides/design-doc-guide.md +0 -0
  213. /package/{templates → framework}/guides/learning-extraction.md +0 -0
  214. /package/{templates → framework}/guides/llm-instruction-design.md +0 -0
  215. /package/{templates → framework}/guides/llm-prompting.md +0 -0
  216. /package/{templates → framework}/guides/tdd-best-practices.md +0 -0
  217. /package/{templates → framework}/guides/test-definitions-guide.md +0 -0
  218. /package/{templates → framework}/guides/testing-methodology.md +0 -0
  219. /package/{templates → framework}/guides/user-story-guide.md +0 -0
  220. /package/{templates → framework}/guides/zombie-process-cleanup.md +0 -0
  221. /package/{templates → framework}/prompts/arch-review.md +0 -0
  222. /package/{templates → framework}/prompts/quality-review.md +0 -0
  223. /package/{templates/skills/safeword-quality-reviewer → framework/skills/quality-reviewer}/SKILL.md +0 -0
  224. /package/{templates/doc-templates → framework/templates}/architecture-template.md +0 -0
  225. /package/{templates/doc-templates → framework/templates}/design-doc-template.md +0 -0
  226. /package/{templates/doc-templates → framework/templates}/test-definitions-feature.md +0 -0
  227. /package/{templates/doc-templates → framework/templates}/ticket-template.md +0 -0
  228. /package/{templates/doc-templates → framework/templates}/user-stories-template.md +0 -0
  229. /package/{templates → packages/cli/templates}/commands/arch-review.md +0 -0
  230. /package/{templates → packages/cli/templates}/commands/lint.md +0 -0
  231. /package/{templates → packages/cli/templates}/commands/quality-review.md +0 -0
  232. /package/{templates → packages/cli/templates}/hooks/inject-timestamp.sh +0 -0
  233. /package/{templates → packages/cli/templates}/lib/common.sh +0 -0
  234. /package/{templates → packages/cli/templates}/lib/jq-fallback.sh +0 -0
  235. /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
+