anvil-dev-framework 0.1.6

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 (190) hide show
  1. package/README.md +719 -0
  2. package/VERSION +1 -0
  3. package/docs/ANVIL-REPO-IMPLEMENTATION-PLAN.md +441 -0
  4. package/docs/FIRST-SKILL-TUTORIAL.md +408 -0
  5. package/docs/INSTALLATION-RETRO-NOTES.md +458 -0
  6. package/docs/INSTALLATION.md +984 -0
  7. package/docs/anvil-hud.md +469 -0
  8. package/docs/anvil-init.md +255 -0
  9. package/docs/anvil-state.md +210 -0
  10. package/docs/boris-cherny-ralph-wiggum-insights.md +608 -0
  11. package/docs/command-reference.md +2022 -0
  12. package/docs/hooks-tts.md +368 -0
  13. package/docs/implementation-guide.md +810 -0
  14. package/docs/linear-github-integration.md +247 -0
  15. package/docs/local-issues.md +677 -0
  16. package/docs/patterns/README.md +419 -0
  17. package/docs/planning-responsibilities.md +139 -0
  18. package/docs/session-workflow.md +573 -0
  19. package/docs/simplification-plan-template.md +297 -0
  20. package/docs/simplification-principles.md +129 -0
  21. package/docs/specifications/CCS-RALPH-INTEGRATION-DESIGN.md +633 -0
  22. package/docs/specifications/CCS-RESEARCH-REPORT.md +169 -0
  23. package/docs/specifications/PLAN-ANV-verification-ralph-wiggum.md +403 -0
  24. package/docs/specifications/PLAN-parallel-tracks-anvil-memory-ccs.md +494 -0
  25. package/docs/specifications/SPEC-ANV-VRW/component-01-verify.md +208 -0
  26. package/docs/specifications/SPEC-ANV-VRW/component-02-stop-gate.md +226 -0
  27. package/docs/specifications/SPEC-ANV-VRW/component-03-posttooluse.md +209 -0
  28. package/docs/specifications/SPEC-ANV-VRW/component-04-ralph-wiggum.md +604 -0
  29. package/docs/specifications/SPEC-ANV-VRW/component-05-atomic-actions.md +311 -0
  30. package/docs/specifications/SPEC-ANV-VRW/component-06-verify-subagent.md +264 -0
  31. package/docs/specifications/SPEC-ANV-VRW/component-07-claude-md.md +363 -0
  32. package/docs/specifications/SPEC-ANV-VRW/index.md +182 -0
  33. package/docs/specifications/SPEC-ANV-anvil-memory.md +573 -0
  34. package/docs/specifications/SPEC-ANV-context-checkpoints.md +781 -0
  35. package/docs/specifications/SPEC-ANV-verification-ralph-wiggum.md +789 -0
  36. package/docs/sync.md +122 -0
  37. package/global/CLAUDE.md +140 -0
  38. package/global/agents/verify-app.md +164 -0
  39. package/global/commands/anvil-settings.md +527 -0
  40. package/global/commands/anvil-sync.md +121 -0
  41. package/global/commands/change.md +197 -0
  42. package/global/commands/clarify.md +252 -0
  43. package/global/commands/cleanup.md +292 -0
  44. package/global/commands/commit-push-pr.md +207 -0
  45. package/global/commands/decay-review.md +127 -0
  46. package/global/commands/discover.md +158 -0
  47. package/global/commands/doc-coverage.md +122 -0
  48. package/global/commands/evidence.md +307 -0
  49. package/global/commands/explore.md +121 -0
  50. package/global/commands/force-exit.md +135 -0
  51. package/global/commands/handoff.md +191 -0
  52. package/global/commands/healthcheck.md +302 -0
  53. package/global/commands/hud.md +84 -0
  54. package/global/commands/insights.md +319 -0
  55. package/global/commands/linear-setup.md +184 -0
  56. package/global/commands/lint-fix.md +198 -0
  57. package/global/commands/orient.md +510 -0
  58. package/global/commands/plan.md +228 -0
  59. package/global/commands/ralph.md +346 -0
  60. package/global/commands/ready.md +182 -0
  61. package/global/commands/release.md +305 -0
  62. package/global/commands/retro.md +96 -0
  63. package/global/commands/shard.md +166 -0
  64. package/global/commands/spec.md +227 -0
  65. package/global/commands/sprint.md +184 -0
  66. package/global/commands/tasks.md +228 -0
  67. package/global/commands/test-and-commit.md +151 -0
  68. package/global/commands/validate.md +132 -0
  69. package/global/commands/verify.md +251 -0
  70. package/global/commands/weekly-review.md +156 -0
  71. package/global/hooks/__pycache__/ralph_context_monitor.cpython-314.pyc +0 -0
  72. package/global/hooks/__pycache__/statusline_agent_sync.cpython-314.pyc +0 -0
  73. package/global/hooks/anvil_memory_observe.ts +322 -0
  74. package/global/hooks/anvil_memory_session.ts +166 -0
  75. package/global/hooks/anvil_memory_stop.ts +187 -0
  76. package/global/hooks/parse_transcript.py +116 -0
  77. package/global/hooks/post_merge_cleanup.sh +132 -0
  78. package/global/hooks/post_tool_format.sh +215 -0
  79. package/global/hooks/ralph_context_monitor.py +240 -0
  80. package/global/hooks/ralph_stop.sh +502 -0
  81. package/global/hooks/statusline.sh +1110 -0
  82. package/global/hooks/statusline_agent_sync.py +224 -0
  83. package/global/hooks/stop_gate.sh +250 -0
  84. package/global/lib/.claude/anvil-state.json +21 -0
  85. package/global/lib/__pycache__/agent_registry.cpython-314.pyc +0 -0
  86. package/global/lib/__pycache__/claim_service.cpython-314.pyc +0 -0
  87. package/global/lib/__pycache__/coderabbit_service.cpython-314.pyc +0 -0
  88. package/global/lib/__pycache__/config_service.cpython-314.pyc +0 -0
  89. package/global/lib/__pycache__/coordination_service.cpython-314.pyc +0 -0
  90. package/global/lib/__pycache__/doc_coverage_service.cpython-314.pyc +0 -0
  91. package/global/lib/__pycache__/gate_logger.cpython-314.pyc +0 -0
  92. package/global/lib/__pycache__/github_service.cpython-314.pyc +0 -0
  93. package/global/lib/__pycache__/hygiene_service.cpython-314.pyc +0 -0
  94. package/global/lib/__pycache__/issue_models.cpython-314.pyc +0 -0
  95. package/global/lib/__pycache__/issue_provider.cpython-314.pyc +0 -0
  96. package/global/lib/__pycache__/linear_data_service.cpython-314.pyc +0 -0
  97. package/global/lib/__pycache__/linear_provider.cpython-314.pyc +0 -0
  98. package/global/lib/__pycache__/local_provider.cpython-314.pyc +0 -0
  99. package/global/lib/__pycache__/quality_service.cpython-314.pyc +0 -0
  100. package/global/lib/__pycache__/ralph_state.cpython-314.pyc +0 -0
  101. package/global/lib/__pycache__/state_manager.cpython-314.pyc +0 -0
  102. package/global/lib/__pycache__/transcript_parser.cpython-314.pyc +0 -0
  103. package/global/lib/__pycache__/verification_runner.cpython-314.pyc +0 -0
  104. package/global/lib/__pycache__/verify_iteration.cpython-314.pyc +0 -0
  105. package/global/lib/__pycache__/verify_subagent.cpython-314.pyc +0 -0
  106. package/global/lib/agent_registry.py +995 -0
  107. package/global/lib/anvil-state.sh +435 -0
  108. package/global/lib/claim_service.py +515 -0
  109. package/global/lib/coderabbit_service.py +314 -0
  110. package/global/lib/config_service.py +423 -0
  111. package/global/lib/coordination_service.py +331 -0
  112. package/global/lib/doc_coverage_service.py +1305 -0
  113. package/global/lib/gate_logger.py +316 -0
  114. package/global/lib/github_service.py +310 -0
  115. package/global/lib/handoff_generator.py +775 -0
  116. package/global/lib/hygiene_service.py +712 -0
  117. package/global/lib/issue_models.py +257 -0
  118. package/global/lib/issue_provider.py +339 -0
  119. package/global/lib/linear_data_service.py +210 -0
  120. package/global/lib/linear_provider.py +987 -0
  121. package/global/lib/linear_provider.py.backup +671 -0
  122. package/global/lib/local_provider.py +486 -0
  123. package/global/lib/orient_fast.py +457 -0
  124. package/global/lib/quality_service.py +470 -0
  125. package/global/lib/ralph_prompt_generator.py +563 -0
  126. package/global/lib/ralph_state.py +1202 -0
  127. package/global/lib/state_manager.py +417 -0
  128. package/global/lib/transcript_parser.py +597 -0
  129. package/global/lib/verification_runner.py +557 -0
  130. package/global/lib/verify_iteration.py +490 -0
  131. package/global/lib/verify_subagent.py +250 -0
  132. package/global/skills/README.md +155 -0
  133. package/global/skills/quality-gates/SKILL.md +252 -0
  134. package/global/skills/skill-template/SKILL.md +109 -0
  135. package/global/skills/testing-strategies/SKILL.md +337 -0
  136. package/global/templates/CHANGE-template.md +105 -0
  137. package/global/templates/HANDOFF-template.md +63 -0
  138. package/global/templates/PLAN-template.md +111 -0
  139. package/global/templates/SPEC-template.md +93 -0
  140. package/global/templates/ralph/PROMPT.md.template +89 -0
  141. package/global/templates/ralph/fix_plan.md.template +31 -0
  142. package/global/templates/ralph/progress.txt.template +23 -0
  143. package/global/tests/__pycache__/test_doc_coverage.cpython-314.pyc +0 -0
  144. package/global/tests/test_doc_coverage.py +520 -0
  145. package/global/tests/test_issue_models.py +299 -0
  146. package/global/tests/test_local_provider.py +323 -0
  147. package/global/tools/README.md +178 -0
  148. package/global/tools/__pycache__/anvil-hud.cpython-314.pyc +0 -0
  149. package/global/tools/anvil-hud.py +3622 -0
  150. package/global/tools/anvil-hud.py.bak +3318 -0
  151. package/global/tools/anvil-issue.py +432 -0
  152. package/global/tools/anvil-memory/CLAUDE.md +49 -0
  153. package/global/tools/anvil-memory/README.md +42 -0
  154. package/global/tools/anvil-memory/bun.lock +25 -0
  155. package/global/tools/anvil-memory/bunfig.toml +9 -0
  156. package/global/tools/anvil-memory/package.json +23 -0
  157. package/global/tools/anvil-memory/src/__tests__/ccs/context-monitor.test.ts +535 -0
  158. package/global/tools/anvil-memory/src/__tests__/ccs/edge-cases.test.ts +645 -0
  159. package/global/tools/anvil-memory/src/__tests__/ccs/fixtures.ts +363 -0
  160. package/global/tools/anvil-memory/src/__tests__/ccs/index.ts +8 -0
  161. package/global/tools/anvil-memory/src/__tests__/ccs/integration.test.ts +417 -0
  162. package/global/tools/anvil-memory/src/__tests__/ccs/prompt-generator.test.ts +571 -0
  163. package/global/tools/anvil-memory/src/__tests__/ccs/ralph-stop.test.ts +440 -0
  164. package/global/tools/anvil-memory/src/__tests__/ccs/test-utils.ts +252 -0
  165. package/global/tools/anvil-memory/src/__tests__/commands.test.ts +657 -0
  166. package/global/tools/anvil-memory/src/__tests__/db.test.ts +641 -0
  167. package/global/tools/anvil-memory/src/__tests__/hooks.test.ts +272 -0
  168. package/global/tools/anvil-memory/src/__tests__/performance.test.ts +427 -0
  169. package/global/tools/anvil-memory/src/__tests__/test-utils.ts +113 -0
  170. package/global/tools/anvil-memory/src/commands/checkpoint.ts +197 -0
  171. package/global/tools/anvil-memory/src/commands/get.ts +115 -0
  172. package/global/tools/anvil-memory/src/commands/init.ts +94 -0
  173. package/global/tools/anvil-memory/src/commands/observe.ts +163 -0
  174. package/global/tools/anvil-memory/src/commands/search.ts +112 -0
  175. package/global/tools/anvil-memory/src/db.ts +638 -0
  176. package/global/tools/anvil-memory/src/index.ts +205 -0
  177. package/global/tools/anvil-memory/src/types.ts +122 -0
  178. package/global/tools/anvil-memory/tsconfig.json +29 -0
  179. package/global/tools/ralph-loop.sh +359 -0
  180. package/package.json +45 -0
  181. package/scripts/anvil +822 -0
  182. package/scripts/extract_patterns.py +222 -0
  183. package/scripts/init-project.sh +541 -0
  184. package/scripts/install.sh +229 -0
  185. package/scripts/postinstall.js +41 -0
  186. package/scripts/rollback.sh +188 -0
  187. package/scripts/sync.sh +623 -0
  188. package/scripts/test-statusline.sh +248 -0
  189. package/scripts/update_claude_md.py +224 -0
  190. package/scripts/verify.sh +255 -0
package/scripts/anvil ADDED
@@ -0,0 +1,822 @@
1
+ #!/bin/bash
2
+
3
+ #
4
+ # Anvil Development Framework CLI
5
+ #
6
+ # Usage: anvil init [options]
7
+ #
8
+ # Scaffolds new projects with Anvil Framework configuration.
9
+ #
10
+
11
+ set -e
12
+
13
+ # Version
14
+ VERSION="1.3.0"
15
+
16
+ # Colors
17
+ RED='\033[0;31m'
18
+ GREEN='\033[0;32m'
19
+ YELLOW='\033[1;33m'
20
+ BLUE='\033[0;34m'
21
+ CYAN='\033[0;36m'
22
+ BOLD='\033[1m'
23
+ NC='\033[0m' # No Color
24
+
25
+ # Determine anvil home directory
26
+ if [ -n "$ANVIL_HOME" ]; then
27
+ ANVIL_DIR="$ANVIL_HOME"
28
+ else
29
+ # Default to where this script lives
30
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
31
+ ANVIL_DIR="$(dirname "$SCRIPT_DIR")"
32
+ fi
33
+
34
+ # Default options
35
+ DRY_RUN=false
36
+ FORCE=false
37
+ TEMPLATE="generic"
38
+ WITH_LINEAR=false
39
+ WITH_CODE_REVIEW=false
40
+ NO_HOOKS=false
41
+ NO_TTS=false
42
+ NO_MEMORY=false
43
+ NO_STATUSLINE=false
44
+ TARGET_DIR="$(pwd)"
45
+
46
+ # Show help
47
+ show_help() {
48
+ cat << EOF
49
+ ${BOLD}Anvil Development Framework CLI${NC}
50
+
51
+ ${BOLD}USAGE${NC}
52
+ anvil init [options]
53
+ anvil --help
54
+ anvil --version
55
+
56
+ ${BOLD}COMMANDS${NC}
57
+ init Initialize Anvil in the current directory
58
+
59
+ ${BOLD}OPTIONS${NC}
60
+ --template <type> Project template: saas, api-python, generic (default: generic)
61
+ --with-linear Run /linear-setup after initialization
62
+ --with-code-review Enable code review integration in config
63
+ --no-tts Disable TTS voice announcements
64
+ --no-memory Disable claude-mem context loading
65
+ --no-statusline Disable custom statusline display
66
+ --no-hooks Skip ALL hooks (includes safety hooks - use with caution)
67
+ --force Overwrite existing files (backs up first)
68
+ --dry-run Show what would be created without making changes
69
+ --help, -h Show this help message
70
+ --version, -v Show version number
71
+
72
+ ${BOLD}HOOK OPTIONS${NC}
73
+ By default, all hooks and statusline are installed including:
74
+ - Safety hooks (rm -rf protection, .env protection)
75
+ - TTS voice announcements
76
+ - Claude-mem context loading
77
+ - Custom statusline (model, context%, branch, issue, phase, cost)
78
+
79
+ Use --no-tts, --no-memory, or --no-statusline to selectively disable
80
+ features while keeping safety hooks active. Only use --no-hooks if you
81
+ want a completely minimal setup without any hook protections.
82
+
83
+ ${BOLD}EXAMPLES${NC}
84
+ anvil init # Full init with all features
85
+ anvil init --template saas # SaaS stack (Next.js + Supabase + Vercel)
86
+ anvil init --template api-python # Python API (FastAPI + PostgreSQL)
87
+ anvil init --with-linear # Full setup with Linear integration
88
+ anvil init --with-code-review # Enable code review in config
89
+ anvil init --no-tts # Without voice announcements
90
+ anvil init --no-memory # Without context loading
91
+ anvil init --no-statusline # Without custom statusline
92
+ anvil init --no-tts --no-memory # Safety hooks only
93
+ anvil init --no-hooks # Minimal setup (no safety hooks!)
94
+ anvil init --dry-run # Preview changes
95
+
96
+ ${BOLD}ENVIRONMENT${NC}
97
+ ANVIL_HOME Path to Anvil framework installation (auto-detected if not set)
98
+
99
+ EOF
100
+ }
101
+
102
+ # Show version
103
+ show_version() {
104
+ echo "anvil version $VERSION"
105
+ }
106
+
107
+ # Log functions
108
+ log_info() {
109
+ echo -e "${BLUE}$1${NC}"
110
+ }
111
+
112
+ log_success() {
113
+ echo -e " ${GREEN}✓${NC} $1"
114
+ }
115
+
116
+ log_skip() {
117
+ echo -e " ${YELLOW}⊘${NC} $1"
118
+ }
119
+
120
+ log_warn() {
121
+ echo -e " ${YELLOW}⚠${NC} $1"
122
+ }
123
+
124
+ log_error() {
125
+ echo -e "${RED}Error: $1${NC}" >&2
126
+ }
127
+
128
+ log_dry() {
129
+ echo -e " ${CYAN}[DRY RUN]${NC} Would create: $1"
130
+ }
131
+
132
+ # Create directory (respects dry-run)
133
+ create_dir() {
134
+ local dir="$1"
135
+ if [ "$DRY_RUN" = true ]; then
136
+ log_dry "$dir/"
137
+ else
138
+ mkdir -p "$dir"
139
+ log_success "$dir/"
140
+ fi
141
+ }
142
+
143
+ # Copy file (respects dry-run and force)
144
+ copy_file() {
145
+ local src="$1"
146
+ local dest="$2"
147
+ local display_name="$3"
148
+
149
+ if [ "$DRY_RUN" = true ]; then
150
+ if [ -f "$dest" ] && [ "$FORCE" = false ]; then
151
+ log_dry "$display_name (exists, would skip)"
152
+ else
153
+ log_dry "$display_name"
154
+ fi
155
+ return
156
+ fi
157
+
158
+ if [ -f "$dest" ] && [ "$FORCE" = false ]; then
159
+ log_skip "$display_name (exists, use --force to overwrite)"
160
+ return
161
+ fi
162
+
163
+ # Backup if force and file exists
164
+ if [ -f "$dest" ] && [ "$FORCE" = true ]; then
165
+ backup_dir="$TARGET_DIR/.claude/.backup"
166
+ mkdir -p "$backup_dir"
167
+ cp "$dest" "$backup_dir/$(basename "$dest").$(date +%Y%m%d%H%M%S)"
168
+ log_info "Backed up existing $display_name"
169
+ fi
170
+
171
+ cp "$src" "$dest"
172
+ log_success "$display_name"
173
+ }
174
+
175
+ # Create symlink (respects dry-run)
176
+ create_symlink() {
177
+ local src="$1"
178
+ local dest="$2"
179
+ local display_name="$3"
180
+
181
+ if [ "$DRY_RUN" = true ]; then
182
+ log_dry "$display_name → $src"
183
+ return
184
+ fi
185
+
186
+ # Remove existing symlink or file
187
+ if [ -L "$dest" ] || [ -f "$dest" ]; then
188
+ if [ "$FORCE" = true ]; then
189
+ rm -f "$dest"
190
+ else
191
+ log_skip "$display_name (exists)"
192
+ return
193
+ fi
194
+ fi
195
+
196
+ ln -s "$src" "$dest"
197
+ log_success "$display_name → $(basename "$src")"
198
+ }
199
+
200
+ # Check if Anvil is already initialized
201
+ check_existing() {
202
+ if [ -d "$TARGET_DIR/.claude" ]; then
203
+ if [ "$FORCE" = false ]; then
204
+ log_error "Existing Anvil configuration found at $TARGET_DIR/.claude/"
205
+ echo ""
206
+ echo "Use --force to overwrite (existing files will be backed up to .claude/.backup/)"
207
+ echo "Use --dry-run to preview what would change"
208
+ exit 1
209
+ fi
210
+ fi
211
+ }
212
+
213
+ # Generate settings.local.json based on flags
214
+ generate_settings() {
215
+ local settings_file="$TARGET_DIR/.claude/settings.local.json"
216
+
217
+ if [ "$DRY_RUN" = true ]; then
218
+ if [ "$NO_HOOKS" = true ]; then
219
+ log_dry "settings.local.json (minimal - no hooks)"
220
+ elif [ "$NO_TTS" = true ] && [ "$NO_MEMORY" = true ]; then
221
+ log_dry "settings.local.json (safety hooks only)"
222
+ elif [ "$NO_TTS" = true ]; then
223
+ log_dry "settings.local.json (no TTS)"
224
+ elif [ "$NO_MEMORY" = true ]; then
225
+ log_dry "settings.local.json (no memory)"
226
+ else
227
+ log_dry "settings.local.json (full)"
228
+ fi
229
+ return
230
+ fi
231
+
232
+ if [ -f "$settings_file" ] && [ "$FORCE" = false ]; then
233
+ log_skip "settings.local.json (exists)"
234
+ return
235
+ fi
236
+
237
+ # Build session_start command based on flags
238
+ local session_cmd="uv run .claude/hooks/session_start.py"
239
+ if [ "$NO_MEMORY" = false ]; then
240
+ session_cmd="$session_cmd --load-context"
241
+ fi
242
+ session_cmd="$session_cmd --register-agent"
243
+ if [ "$NO_TTS" = false ]; then
244
+ session_cmd="$session_cmd --announce"
245
+ fi
246
+
247
+ # Build pre_tool_use command based on flags
248
+ local pre_cmd="uv run .claude/hooks/pre_tool_use.py"
249
+ if [ "$NO_TTS" = false ]; then
250
+ pre_cmd="$pre_cmd --announce"
251
+ fi
252
+
253
+ # Build post_tool_use command based on flags
254
+ local post_cmd="uv run .claude/hooks/post_tool_use.py"
255
+ if [ "$NO_TTS" = false ]; then
256
+ post_cmd="$post_cmd --announce"
257
+ fi
258
+
259
+ # Build subagent_start command
260
+ local subagent_start_cmd="uv run .claude/hooks/subagent_start.py --log --inject-context"
261
+
262
+ # Build subagent_stop command based on flags
263
+ local subagent_stop_cmd="uv run .claude/hooks/subagent_stop.py"
264
+ if [ "$NO_TTS" = false ]; then
265
+ subagent_stop_cmd="$subagent_stop_cmd --announce"
266
+ fi
267
+
268
+ # Build permission_request command (always log, safety checks always active)
269
+ local permission_cmd="uv run .claude/hooks/permission_request.py --log"
270
+
271
+ # Build statusLine section (unless disabled)
272
+ local statusline_section=""
273
+ if [ "$NO_STATUSLINE" = false ]; then
274
+ statusline_section=',
275
+ "statusLine": {
276
+ "type": "command",
277
+ "command": "bash .claude/hooks/statusline.sh"
278
+ }'
279
+ fi
280
+
281
+ # Write the settings file
282
+ cat > "$settings_file" << SETTINGS_EOF
283
+ {
284
+ "permissions": {
285
+ "allow": [],
286
+ "deny": []
287
+ },
288
+ "hooks": {
289
+ "SessionStart": [
290
+ {
291
+ "matcher": "",
292
+ "hooks": [
293
+ {
294
+ "type": "command",
295
+ "command": "$session_cmd"
296
+ }
297
+ ]
298
+ }
299
+ ],
300
+ "PreToolUse": [
301
+ {
302
+ "matcher": "",
303
+ "hooks": [
304
+ {
305
+ "type": "command",
306
+ "command": "$pre_cmd"
307
+ }
308
+ ]
309
+ }
310
+ ],
311
+ "PostToolUse": [
312
+ {
313
+ "matcher": "",
314
+ "hooks": [
315
+ {
316
+ "type": "command",
317
+ "command": "$post_cmd"
318
+ }
319
+ ]
320
+ }
321
+ ],
322
+ "SubagentStart": [
323
+ {
324
+ "matcher": "",
325
+ "hooks": [
326
+ {
327
+ "type": "command",
328
+ "command": "$subagent_start_cmd"
329
+ }
330
+ ]
331
+ }
332
+ ],
333
+ "SubagentStop": [
334
+ {
335
+ "matcher": "",
336
+ "hooks": [
337
+ {
338
+ "type": "command",
339
+ "command": "$subagent_stop_cmd"
340
+ }
341
+ ]
342
+ }
343
+ ],
344
+ "PermissionRequest": [
345
+ {
346
+ "matcher": "",
347
+ "hooks": [
348
+ {
349
+ "type": "command",
350
+ "command": "$permission_cmd"
351
+ }
352
+ ]
353
+ }
354
+ ]
355
+ }$statusline_section
356
+ }
357
+ SETTINGS_EOF
358
+
359
+ # Determine what mode was used
360
+ if [ "$NO_TTS" = true ] && [ "$NO_MEMORY" = true ]; then
361
+ log_success "settings.local.json (safety hooks only)"
362
+ elif [ "$NO_TTS" = true ]; then
363
+ log_success "settings.local.json (no TTS)"
364
+ elif [ "$NO_MEMORY" = true ]; then
365
+ log_success "settings.local.json (no memory)"
366
+ else
367
+ log_success "settings.local.json (full)"
368
+ fi
369
+ }
370
+
371
+ # Generate minimal settings (no hooks at all)
372
+ generate_minimal_settings() {
373
+ local settings_file="$TARGET_DIR/.claude/settings.local.json"
374
+
375
+ if [ "$DRY_RUN" = true ]; then
376
+ log_dry "settings.local.json (minimal - no hooks)"
377
+ return
378
+ fi
379
+
380
+ if [ -f "$settings_file" ] && [ "$FORCE" = false ]; then
381
+ log_skip "settings.local.json (exists)"
382
+ return
383
+ fi
384
+
385
+ cat > "$settings_file" << 'SETTINGS_EOF'
386
+ {
387
+ "permissions": {
388
+ "allow": [],
389
+ "deny": []
390
+ }
391
+ }
392
+ SETTINGS_EOF
393
+ log_success "settings.local.json (minimal)"
394
+ }
395
+
396
+ # Generate anvil.config.json for framework settings
397
+ generate_anvil_config() {
398
+ local config_file="$TARGET_DIR/.claude/anvil.config.json"
399
+
400
+ if [ "$DRY_RUN" = true ]; then
401
+ if [ "$WITH_CODE_REVIEW" = true ]; then
402
+ log_dry "anvil.config.json (with code review)"
403
+ else
404
+ log_dry "anvil.config.json (framework settings)"
405
+ fi
406
+ return
407
+ fi
408
+
409
+ if [ -f "$config_file" ] && [ "$FORCE" = false ]; then
410
+ log_skip "anvil.config.json (exists)"
411
+ return
412
+ fi
413
+
414
+ if [ "$WITH_CODE_REVIEW" = true ]; then
415
+ cat > "$config_file" << 'CONFIG_EOF'
416
+ {
417
+ "version": "1.1",
418
+ "autoRetro": false,
419
+ "autoHealthcheck": false,
420
+ "codeReview": {
421
+ "enabled": true,
422
+ "tool": "coderabbit",
423
+ "command": "coderabbit --prompt-only",
424
+ "enforcement": "soft"
425
+ }
426
+ }
427
+ CONFIG_EOF
428
+ log_success "anvil.config.json (with code review)"
429
+ else
430
+ cat > "$config_file" << 'CONFIG_EOF'
431
+ {
432
+ "version": "1.1",
433
+ "autoRetro": false,
434
+ "autoHealthcheck": false,
435
+ "codeReview": {
436
+ "enabled": false,
437
+ "tool": "coderabbit",
438
+ "command": "coderabbit --prompt-only",
439
+ "enforcement": "soft"
440
+ }
441
+ }
442
+ CONFIG_EOF
443
+ log_success "anvil.config.json"
444
+ fi
445
+ }
446
+
447
+ # Update .gitignore with Anvil-specific exclusions
448
+ update_gitignore() {
449
+ local gitignore_file="$TARGET_DIR/.gitignore"
450
+ local anvil_marker="# Anvil Framework"
451
+ local anvil_state_entry=".claude/anvil-state.json"
452
+
453
+ if [ "$DRY_RUN" = true ]; then
454
+ if [ -f "$gitignore_file" ]; then
455
+ log_dry ".gitignore (add anvil-state.json)"
456
+ else
457
+ log_dry ".gitignore (create with anvil-state.json)"
458
+ fi
459
+ return
460
+ fi
461
+
462
+ # Check if entry already exists
463
+ if [ -f "$gitignore_file" ] && grep -q "$anvil_state_entry" "$gitignore_file" 2>/dev/null; then
464
+ log_skip ".gitignore (anvil-state.json already present)"
465
+ return
466
+ fi
467
+
468
+ # Add the Anvil section to .gitignore
469
+ if [ -f "$gitignore_file" ]; then
470
+ # Append to existing file
471
+ echo "" >> "$gitignore_file"
472
+ echo "$anvil_marker" >> "$gitignore_file"
473
+ echo "$anvil_state_entry" >> "$gitignore_file"
474
+ log_success ".gitignore (added anvil-state.json)"
475
+ else
476
+ # Create new file
477
+ cat > "$gitignore_file" << GITIGNORE_EOF
478
+ $anvil_marker
479
+ $anvil_state_entry
480
+ GITIGNORE_EOF
481
+ log_success ".gitignore (created with anvil-state.json)"
482
+ fi
483
+ }
484
+
485
+ # Main init function
486
+ do_init() {
487
+ echo ""
488
+ echo -e "${BOLD}${BLUE}Anvil Development Framework${NC} ${CYAN}v${VERSION}${NC}"
489
+ echo "══════════════════════════════════════"
490
+ echo ""
491
+
492
+ if [ "$DRY_RUN" = true ]; then
493
+ echo -e "${CYAN}[DRY RUN MODE] No changes will be made${NC}"
494
+ echo ""
495
+ fi
496
+
497
+ # Validate Anvil installation
498
+ if [ ! -d "$ANVIL_DIR/project" ]; then
499
+ log_error "Anvil framework not found at $ANVIL_DIR"
500
+ echo "Set ANVIL_HOME environment variable to your Anvil installation path"
501
+ exit 1
502
+ fi
503
+
504
+ # Show configuration
505
+ log_info "Template: $TEMPLATE"
506
+ log_info "Target: $TARGET_DIR"
507
+
508
+ # Show hooks status
509
+ if [ "$NO_HOOKS" = true ]; then
510
+ echo -e "${YELLOW}Hooks: DISABLED (no safety protections!)${NC}"
511
+ else
512
+ local hooks_status="enabled"
513
+ local hooks_details=""
514
+ if [ "$NO_TTS" = true ]; then
515
+ hooks_details="${hooks_details}, no TTS"
516
+ fi
517
+ if [ "$NO_MEMORY" = true ]; then
518
+ hooks_details="${hooks_details}, no memory"
519
+ fi
520
+ if [ -n "$hooks_details" ]; then
521
+ hooks_status="enabled (${hooks_details:2})" # Remove leading ", "
522
+ fi
523
+ log_info "Hooks: $hooks_status"
524
+ fi
525
+ echo ""
526
+
527
+ # Safety warning for --no-hooks
528
+ if [ "$NO_HOOKS" = true ]; then
529
+ echo -e "${YELLOW}════════════════════════════════════════${NC}"
530
+ echo -e "${YELLOW} ⚠️ WARNING: Safety hooks disabled!${NC}"
531
+ echo -e "${YELLOW}════════════════════════════════════════${NC}"
532
+ echo ""
533
+ echo "Without hooks, you lose these protections:"
534
+ echo " • rm -rf command blocking"
535
+ echo " • .env file access prevention"
536
+ echo " • TTS voice announcements"
537
+ echo " • Claude-mem context loading"
538
+ echo ""
539
+ echo "Consider using --no-tts or --no-memory instead"
540
+ echo "to keep safety protections while disabling features."
541
+ echo ""
542
+ fi
543
+
544
+ # Check for existing installation (unless force)
545
+ check_existing
546
+
547
+ # Create directory structure
548
+ log_info "Creating directory structure..."
549
+ create_dir "$TARGET_DIR/.claude"
550
+ create_dir "$TARGET_DIR/.claude/specs/current"
551
+ create_dir "$TARGET_DIR/.claude/specs/archive"
552
+ create_dir "$TARGET_DIR/.claude/changes"
553
+ create_dir "$TARGET_DIR/.claude/handoffs"
554
+ create_dir "$TARGET_DIR/.claude/docs"
555
+ create_dir "$TARGET_DIR/.claude/examples"
556
+ create_dir "$TARGET_DIR/.claude/commands"
557
+ create_dir "$TARGET_DIR/.claude/agents"
558
+ create_dir "$TARGET_DIR/.claude/retros"
559
+ create_dir "$TARGET_DIR/.claude/rules"
560
+
561
+ # Create hooks directory only if hooks enabled
562
+ if [ "$NO_HOOKS" = false ]; then
563
+ create_dir "$TARGET_DIR/.claude/hooks"
564
+ create_dir "$TARGET_DIR/.claude/hooks/utils"
565
+ create_dir "$TARGET_DIR/.claude/hooks/utils/tts"
566
+ fi
567
+
568
+ echo ""
569
+
570
+ # Copy templates
571
+ log_info "Installing templates..."
572
+
573
+ # CLAUDE.md - use template-specific version if available
574
+ TEMPLATE_DIR="$ANVIL_DIR/project/templates/$TEMPLATE"
575
+ if [ -f "$TEMPLATE_DIR/CLAUDE.md" ]; then
576
+ copy_file "$TEMPLATE_DIR/CLAUDE.md" "$TARGET_DIR/.claude/CLAUDE.md" "CLAUDE.md ($TEMPLATE template)"
577
+ elif [ -f "$ANVIL_DIR/project/CLAUDE.md.template" ]; then
578
+ copy_file "$ANVIL_DIR/project/CLAUDE.md.template" "$TARGET_DIR/.claude/CLAUDE.md" "CLAUDE.md (generic)"
579
+ fi
580
+
581
+ # Shared templates
582
+ if [ -f "$ANVIL_DIR/project/constitution.md.template" ]; then
583
+ copy_file "$ANVIL_DIR/project/constitution.md.template" "$TARGET_DIR/.claude/constitution.md" "constitution.md"
584
+ fi
585
+
586
+ if [ -f "$ANVIL_DIR/project/product.md.template" ]; then
587
+ copy_file "$ANVIL_DIR/project/product.md.template" "$TARGET_DIR/.claude/product.md" "product.md"
588
+ fi
589
+
590
+ if [ -f "$ANVIL_DIR/project/coordination.md.template" ]; then
591
+ copy_file "$ANVIL_DIR/project/coordination.md.template" "$TARGET_DIR/.claude/coordination.md" "coordination.md"
592
+ fi
593
+
594
+ # Copy rules templates
595
+ if [ -d "$ANVIL_DIR/project/rules" ]; then
596
+ for rules_file in "$ANVIL_DIR/project/rules"/*.md; do
597
+ if [ -f "$rules_file" ]; then
598
+ BASENAME=$(basename "$rules_file")
599
+ copy_file "$rules_file" "$TARGET_DIR/.claude/rules/$BASENAME" "rules/$BASENAME"
600
+ fi
601
+ done
602
+ fi
603
+
604
+ echo ""
605
+
606
+ # Install hooks via symlinks (unless --no-hooks)
607
+ if [ "$NO_HOOKS" = false ]; then
608
+ log_info "Installing hooks (symlinks)..."
609
+
610
+ # Symlink hook files
611
+ if [ -d "$ANVIL_DIR/project/hooks" ]; then
612
+ for hook_file in "$ANVIL_DIR/project/hooks"/*.py; do
613
+ if [ -f "$hook_file" ]; then
614
+ BASENAME=$(basename "$hook_file")
615
+ create_symlink "$hook_file" "$TARGET_DIR/.claude/hooks/$BASENAME" "hooks/$BASENAME"
616
+ fi
617
+ done
618
+
619
+ # Symlink TTS utilities (even if --no-tts, in case user changes mind later)
620
+ if [ -d "$ANVIL_DIR/project/hooks/utils/tts" ]; then
621
+ for tts_file in "$ANVIL_DIR/project/hooks/utils/tts"/*.py; do
622
+ if [ -f "$tts_file" ]; then
623
+ BASENAME=$(basename "$tts_file")
624
+ create_symlink "$tts_file" "$TARGET_DIR/.claude/hooks/utils/tts/$BASENAME" "hooks/utils/tts/$BASENAME"
625
+ fi
626
+ done
627
+ fi
628
+ fi
629
+
630
+ echo ""
631
+
632
+ # Install test harnesses (symlinks)
633
+ log_info "Installing test harnesses (symlinks)..."
634
+ create_dir "$TARGET_DIR/.claude/tests"
635
+ if [ -d "$ANVIL_DIR/project/tests" ]; then
636
+ for test_file in "$ANVIL_DIR/project/tests"/*.sh; do
637
+ if [ -f "$test_file" ]; then
638
+ BASENAME=$(basename "$test_file")
639
+ create_symlink "$test_file" "$TARGET_DIR/.claude/tests/$BASENAME" "tests/$BASENAME"
640
+ fi
641
+ done
642
+ # Also symlink the README
643
+ if [ -f "$ANVIL_DIR/project/tests/README.md" ]; then
644
+ create_symlink "$ANVIL_DIR/project/tests/README.md" "$TARGET_DIR/.claude/tests/README.md" "tests/README.md"
645
+ fi
646
+ fi
647
+
648
+ echo ""
649
+
650
+ # Create settings.local.json with hook configurations
651
+ log_info "Creating settings.local.json..."
652
+ generate_settings
653
+ else
654
+ # Minimal settings without hooks
655
+ log_info "Creating minimal settings.local.json (no hooks)..."
656
+ generate_minimal_settings
657
+ fi
658
+
659
+ # Create anvil.config.json for framework settings
660
+ log_info "Creating anvil.config.json..."
661
+ generate_anvil_config
662
+
663
+ # Update .gitignore with Anvil exclusions
664
+ log_info "Updating .gitignore..."
665
+ update_gitignore
666
+
667
+ echo ""
668
+
669
+ # Install commands via symlinks (from global/commands/)
670
+ log_info "Installing commands (symlinks)..."
671
+ if [ -d "$ANVIL_DIR/global/commands" ]; then
672
+ for cmd_file in "$ANVIL_DIR/global/commands"/*.md; do
673
+ if [ -f "$cmd_file" ]; then
674
+ BASENAME=$(basename "$cmd_file")
675
+ create_symlink "$cmd_file" "$TARGET_DIR/.claude/commands/$BASENAME" "commands/$BASENAME"
676
+ fi
677
+ done
678
+ fi
679
+
680
+ echo ""
681
+
682
+ # Summary
683
+ if [ "$DRY_RUN" = true ]; then
684
+ echo ""
685
+ echo -e "${CYAN}[DRY RUN] No changes made${NC}"
686
+ echo "Run without --dry-run to apply these changes"
687
+ else
688
+ echo ""
689
+ echo -e "${GREEN}════════════════════════════════════════${NC}"
690
+ echo -e "${GREEN} Anvil initialized successfully!${NC}"
691
+ echo -e "${GREEN}════════════════════════════════════════${NC}"
692
+ echo ""
693
+ echo "Project configured at: $TARGET_DIR/.claude/"
694
+ echo ""
695
+ echo -e "${BOLD}Next steps:${NC}"
696
+ echo " 1. Edit .claude/CLAUDE.md with your project details"
697
+ echo " 2. Review .claude/constitution.md principles"
698
+ echo " 3. Fill out .claude/product.md"
699
+ if [ "$NO_HOOKS" = false ]; then
700
+ if [ "$NO_TTS" = true ] && [ "$NO_MEMORY" = true ]; then
701
+ echo " 4. Safety hooks active (TTS and memory disabled)"
702
+ elif [ "$NO_TTS" = true ]; then
703
+ echo " 4. Hooks ready (TTS disabled, memory enabled)"
704
+ elif [ "$NO_MEMORY" = true ]; then
705
+ echo " 4. Hooks ready (TTS enabled, memory disabled)"
706
+ else
707
+ echo " 4. Hooks ready - TTS and memory enabled"
708
+ fi
709
+ else
710
+ echo -e " 4. ${YELLOW}No hooks installed - consider adding safety hooks${NC}"
711
+ fi
712
+ echo ""
713
+ echo "To start using Anvil, open Claude Code and run /orient"
714
+
715
+ # Linear setup if requested
716
+ if [ "$WITH_LINEAR" = true ]; then
717
+ echo ""
718
+ log_info "Linear integration requested..."
719
+ if [ -f "$ANVIL_DIR/project/linear.yaml.template" ]; then
720
+ copy_file "$ANVIL_DIR/project/linear.yaml.template" "$TARGET_DIR/.claude/linear.yaml" "linear.yaml"
721
+ fi
722
+ echo ""
723
+ echo -e "${YELLOW}Run /linear-setup in Claude Code to configure your Linear team${NC}"
724
+ fi
725
+ fi
726
+
727
+ echo ""
728
+ }
729
+
730
+ # Parse arguments
731
+ parse_args() {
732
+ while [[ $# -gt 0 ]]; do
733
+ case $1 in
734
+ init)
735
+ COMMAND="init"
736
+ shift
737
+ ;;
738
+ --template)
739
+ if [[ -z "$2" || "$2" == -* ]]; then
740
+ log_error "Option --template requires an argument"
741
+ echo "Valid templates: saas, api-python, generic"
742
+ echo "Usage: anvil init --template <template>"
743
+ exit 1
744
+ fi
745
+ TEMPLATE="$2"
746
+ if [[ ! "$TEMPLATE" =~ ^(saas|api-python|generic)$ ]]; then
747
+ log_error "Invalid template: $TEMPLATE"
748
+ echo "Valid templates: saas, api-python, generic"
749
+ exit 1
750
+ fi
751
+ shift 2
752
+ ;;
753
+ --with-linear)
754
+ WITH_LINEAR=true
755
+ shift
756
+ ;;
757
+ --with-code-review)
758
+ WITH_CODE_REVIEW=true
759
+ shift
760
+ ;;
761
+ --no-hooks)
762
+ NO_HOOKS=true
763
+ shift
764
+ ;;
765
+ --no-tts)
766
+ NO_TTS=true
767
+ shift
768
+ ;;
769
+ --no-memory)
770
+ NO_MEMORY=true
771
+ shift
772
+ ;;
773
+ --no-statusline)
774
+ NO_STATUSLINE=true
775
+ shift
776
+ ;;
777
+ --force)
778
+ FORCE=true
779
+ shift
780
+ ;;
781
+ --dry-run)
782
+ DRY_RUN=true
783
+ shift
784
+ ;;
785
+ --help|-h)
786
+ show_help
787
+ exit 0
788
+ ;;
789
+ --version|-v)
790
+ show_version
791
+ exit 0
792
+ ;;
793
+ *)
794
+ log_error "Unknown option: $1"
795
+ echo "Run 'anvil --help' for usage"
796
+ exit 1
797
+ ;;
798
+ esac
799
+ done
800
+ }
801
+
802
+ # Main
803
+ main() {
804
+ if [ $# -eq 0 ]; then
805
+ show_help
806
+ exit 0
807
+ fi
808
+
809
+ parse_args "$@"
810
+
811
+ case $COMMAND in
812
+ init)
813
+ do_init
814
+ ;;
815
+ *)
816
+ show_help
817
+ exit 0
818
+ ;;
819
+ esac
820
+ }
821
+
822
+ main "$@"