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
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env -S uv run --script
2
+ # /// script
3
+ # requires-python = ">=3.11"
4
+ # dependencies = []
5
+ # ///
6
+ """
7
+ statusline_agent_sync.py - Sync statusline data to agent registry for HUD
8
+
9
+ This script reads Claude Code JSON from stdin (same as statusline.sh) and
10
+ updates the global agent registry with model, context, and cost information.
11
+
12
+ Called by statusline.sh after processing its output.
13
+
14
+ IMPORTANT (ANV-176): This script serves as a HEARTBEAT mechanism.
15
+ Each call to update_agent() updates the agent's lastActivity timestamp,
16
+ keeping the agent marked as "active" in the registry. Agents that don't
17
+ call this regularly (within 5 minutes) are considered stale and filtered
18
+ out of the agent count.
19
+
20
+ ANV-176 FIX: Added ensure_agent_registered() as a self-healing mechanism.
21
+ If SessionStart failed to register the agent, this will auto-register it
22
+ on the first heartbeat, preventing orphaned agents.
23
+ """
24
+
25
+ import json
26
+ import os
27
+ import sys
28
+ from pathlib import Path
29
+
30
+ # Add global lib to path for agent_registry
31
+ _global_lib = Path(__file__).parent.parent / "lib"
32
+ if _global_lib.exists():
33
+ sys.path.insert(0, str(_global_lib))
34
+
35
+ try:
36
+ from agent_registry import update_agent, get_registry, register_agent
37
+ REGISTRY_AVAILABLE = True
38
+ except ImportError:
39
+ REGISTRY_AVAILABLE = False
40
+
41
+
42
+ def get_agent_id_from_state():
43
+ """Read agent ID from anvil-state.json."""
44
+ state_file = Path(".claude/anvil-state.json")
45
+ try:
46
+ if state_file.exists():
47
+ state = json.loads(state_file.read_text())
48
+ return state.get("session", {}).get("agentId")
49
+ except Exception:
50
+ pass
51
+ return None
52
+
53
+
54
+ def get_phase_from_state():
55
+ """Read workflow phase from anvil-state.json."""
56
+ state_file = Path(".claude/anvil-state.json")
57
+ try:
58
+ if state_file.exists():
59
+ state = json.loads(state_file.read_text())
60
+ return state.get("session", {}).get("phase")
61
+ except Exception:
62
+ pass
63
+ return None
64
+
65
+
66
+ def get_issue_from_state():
67
+ """Read active issue from anvil-state.json."""
68
+ state_file = Path(".claude/anvil-state.json")
69
+ try:
70
+ if state_file.exists():
71
+ state = json.loads(state_file.read_text())
72
+ return state.get("session", {}).get("activeIssue")
73
+ except Exception:
74
+ pass
75
+ return None
76
+
77
+
78
+ def find_agent_by_project():
79
+ """Find agent ID by matching project path in registry."""
80
+ if not REGISTRY_AVAILABLE:
81
+ return None
82
+
83
+ try:
84
+ registry = get_registry()
85
+ agents = registry.get_all()
86
+ cwd = os.getcwd()
87
+
88
+ for agent_id, agent in agents.items():
89
+ if agent.get("project") == cwd:
90
+ return agent_id
91
+ except Exception:
92
+ pass
93
+ return None
94
+
95
+
96
+ def ensure_agent_registered(agent_id: str, model: str = "Claude") -> bool:
97
+ """Ensure agent is registered in the global registry.
98
+
99
+ This is a self-healing mechanism (ANV-176 fix). If the SessionStart hook
100
+ failed to register the agent, this will auto-register it on the first
101
+ heartbeat, preventing the agent from being orphaned.
102
+
103
+ Args:
104
+ agent_id: The agent ID from anvil-state.json
105
+ model: The model name (Opus, Sonnet, Haiku)
106
+
107
+ Returns:
108
+ True if agent was registered (new or existing), False on error
109
+ """
110
+ if not REGISTRY_AVAILABLE:
111
+ return False
112
+
113
+ try:
114
+ registry = get_registry()
115
+ # Read raw registry without cleanup to check if agent exists
116
+ raw_registry = registry._read_registry()
117
+ agents = raw_registry.get("agents", {})
118
+
119
+ if agent_id in agents:
120
+ return True # Already registered
121
+
122
+ # Agent not in registry - auto-register it
123
+ # This happens when SessionStart registration failed or after context compaction
124
+ project_path = os.getcwd()
125
+
126
+ register_agent(
127
+ agent_id=agent_id,
128
+ project=project_path,
129
+ model=model
130
+ )
131
+ return True
132
+
133
+ except Exception:
134
+ return False
135
+
136
+
137
+ def main():
138
+ if not REGISTRY_AVAILABLE:
139
+ sys.exit(0)
140
+
141
+ try:
142
+ input_data = json.load(sys.stdin)
143
+
144
+ # Get agent ID (from state file or by matching project)
145
+ agent_id = get_agent_id_from_state() or find_agent_by_project()
146
+ if not agent_id:
147
+ sys.exit(0)
148
+
149
+ # Extract model name
150
+ model_info = input_data.get("model", {})
151
+ model_name = model_info.get("display_name", "Claude")
152
+ if "Opus" in model_name:
153
+ model_name = "Opus"
154
+ elif "Sonnet" in model_name:
155
+ model_name = "Sonnet"
156
+ elif "Haiku" in model_name:
157
+ model_name = "Haiku"
158
+
159
+ # Extract transcript path for tool activity (ANV-126)
160
+ transcript_path = input_data.get("transcript_path", "")
161
+
162
+ # Extract context info
163
+ context_window = input_data.get("context_window", {})
164
+ current_usage = context_window.get("current_usage", {})
165
+ context_limit = context_window.get("context_window_size", 200000)
166
+
167
+ # Extract detailed token breakdown (ANV-91)
168
+ tokens_input = current_usage.get("input_tokens", 0)
169
+ tokens_output = current_usage.get("output_tokens", 0)
170
+ tokens_cache_read = current_usage.get("cache_read_input_tokens", 0)
171
+ tokens_cache_write = current_usage.get("cache_creation_input_tokens", 0)
172
+
173
+ # Context usage = input + cache write (what counts toward limit)
174
+ context_usage = tokens_input + tokens_cache_write
175
+
176
+ # Extract legacy cost (for backward compat, will be replaced by token calc)
177
+ cost_info = input_data.get("cost", {})
178
+ session_cost = cost_info.get("total_cost_usd", 0.0)
179
+
180
+ # Check if we have token data for detailed tracking
181
+ has_token_data = any([tokens_input, tokens_output, tokens_cache_read, tokens_cache_write])
182
+
183
+ # Get phase and issue from local state for sync
184
+ phase = get_phase_from_state()
185
+ issue = get_issue_from_state()
186
+
187
+ # ANV-176 FIX: Ensure agent is registered before updating
188
+ # This is a self-healing mechanism - if SessionStart registration failed
189
+ # (e.g., after context compaction), auto-register on first heartbeat
190
+ ensure_agent_registered(agent_id, model_name)
191
+
192
+ # Update agent registry with token data for cost attribution (ANV-90/91)
193
+ update_kwargs = {
194
+ "agent_id": agent_id,
195
+ "model": model_name if model_name != "Claude" else None,
196
+ "context_usage": context_usage if context_usage > 0 else None,
197
+ "context_limit": context_limit if context_limit > 0 else None,
198
+ "transcript_path": transcript_path if transcript_path else None,
199
+ "phase": phase,
200
+ "issue": issue,
201
+ }
202
+
203
+ if has_token_data:
204
+ update_kwargs.update({
205
+ "tokens_input": tokens_input,
206
+ "tokens_output": tokens_output,
207
+ "tokens_cache_read": tokens_cache_read,
208
+ "tokens_cache_write": tokens_cache_write,
209
+ })
210
+ elif session_cost > 0:
211
+ update_kwargs["session_cost"] = session_cost
212
+
213
+ update_agent(**update_kwargs)
214
+
215
+ except json.JSONDecodeError:
216
+ pass
217
+ except Exception:
218
+ pass
219
+
220
+ sys.exit(0)
221
+
222
+
223
+ if __name__ == "__main__":
224
+ main()
@@ -0,0 +1,250 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # stop_gate.sh - Verification Gate for Stop Hook (ANV-152)
4
+ # =============================================================================
5
+ #
6
+ # Prevents Claude from exiting when verification fails. This ensures the AI
7
+ # cannot prematurely complete tasks with broken code.
8
+ #
9
+ # Usage: Called automatically by Claude Code on stop events
10
+ #
11
+ # Environment Variables:
12
+ # ANVIL_REQUIRE_VERIFICATION - Set to "false" to disable gate (default: true)
13
+ # ANVIL_FORCE_EXIT - Set to "true" to bypass gate
14
+ # ANVIL_STOP_GATE_LOG - Set to "true" to enable logging
15
+ #
16
+ # Exit Codes:
17
+ # 0 - Allow exit
18
+ # 1 - Block exit (verification failed)
19
+ #
20
+
21
+ set -euo pipefail
22
+
23
+ # =============================================================================
24
+ # Configuration
25
+ # =============================================================================
26
+
27
+ REQUIRE_VERIFY="${ANVIL_REQUIRE_VERIFICATION:-true}"
28
+ FORCE_EXIT="${ANVIL_FORCE_EXIT:-false}"
29
+ ENABLE_LOGGING="${ANVIL_STOP_GATE_LOG:-false}"
30
+ LOG_FILE=".claude/logs/stop_gate.log"
31
+
32
+ # Get script directory for relative imports
33
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
34
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
35
+
36
+ # =============================================================================
37
+ # Logging
38
+ # =============================================================================
39
+
40
+ log_event() {
41
+ local level="$1"
42
+ local message="$2"
43
+ local timestamp
44
+ timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
45
+
46
+ if [[ "$ENABLE_LOGGING" == "true" ]]; then
47
+ mkdir -p "$(dirname "$LOG_FILE")"
48
+ echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
49
+ fi
50
+
51
+ # Also output to stderr for visibility
52
+ case "$level" in
53
+ ERROR) echo "❌ $message" >&2 ;;
54
+ WARN) echo "⚠️ $message" >&2 ;;
55
+ INFO) echo "ℹ️ $message" >&2 ;;
56
+ OK) echo "✅ $message" >&2 ;;
57
+ esac
58
+ }
59
+
60
+ # =============================================================================
61
+ # Verification Functions
62
+ # =============================================================================
63
+
64
+ detect_project_type() {
65
+ if [[ -f "package.json" ]]; then
66
+ echo "nodejs"
67
+ elif [[ -f "pyproject.toml" ]] || [[ -f "setup.py" ]]; then
68
+ echo "python"
69
+ elif [[ -f "Cargo.toml" ]]; then
70
+ echo "rust"
71
+ elif [[ -f "go.mod" ]]; then
72
+ echo "go"
73
+ else
74
+ echo "unknown"
75
+ fi
76
+ }
77
+
78
+ run_tests() {
79
+ local project_type="$1"
80
+
81
+ case "$project_type" in
82
+ nodejs)
83
+ npm test --silent 2>/dev/null
84
+ ;;
85
+ python)
86
+ pytest --quiet 2>/dev/null || python -m pytest --quiet 2>/dev/null
87
+ ;;
88
+ rust)
89
+ cargo test --quiet 2>/dev/null
90
+ ;;
91
+ go)
92
+ go test ./... 2>/dev/null
93
+ ;;
94
+ *)
95
+ # Try common test commands
96
+ npm test --silent 2>/dev/null || pytest --quiet 2>/dev/null || true
97
+ ;;
98
+ esac
99
+ }
100
+
101
+ run_lint() {
102
+ local project_type="$1"
103
+
104
+ case "$project_type" in
105
+ nodejs)
106
+ npm run lint --silent 2>/dev/null
107
+ ;;
108
+ python)
109
+ ruff check . --quiet 2>/dev/null || true
110
+ ;;
111
+ rust)
112
+ cargo clippy --quiet 2>/dev/null || true
113
+ ;;
114
+ go)
115
+ golangci-lint run --quiet 2>/dev/null || true
116
+ ;;
117
+ *)
118
+ npm run lint --silent 2>/dev/null || true
119
+ ;;
120
+ esac
121
+ }
122
+
123
+ run_typecheck() {
124
+ local project_type="$1"
125
+
126
+ case "$project_type" in
127
+ nodejs)
128
+ npm run typecheck --silent 2>/dev/null || npm run type-check --silent 2>/dev/null || npm run tsc --silent 2>/dev/null
129
+ ;;
130
+ python)
131
+ mypy . --quiet 2>/dev/null || true
132
+ ;;
133
+ rust)
134
+ cargo check --quiet 2>/dev/null
135
+ ;;
136
+ *)
137
+ npm run typecheck --silent 2>/dev/null || true
138
+ ;;
139
+ esac
140
+ }
141
+
142
+ check_verification_state() {
143
+ # Check if verification state file exists and shows passing
144
+ local state_file=".claude/verification-state.json"
145
+
146
+ if [[ -f "$state_file" ]]; then
147
+ local status
148
+ status=$(python3 -c "import json; print(json.load(open('$state_file')).get('status', 'unknown'))" 2>/dev/null || echo "unknown")
149
+
150
+ if [[ "$status" == "passed" ]]; then
151
+ return 0
152
+ fi
153
+ fi
154
+
155
+ return 1
156
+ }
157
+
158
+ # =============================================================================
159
+ # Main Gate Logic
160
+ # =============================================================================
161
+
162
+ main() {
163
+ log_event "INFO" "Stop gate triggered"
164
+
165
+ # Check if verification is required
166
+ if [[ "$REQUIRE_VERIFY" != "true" ]]; then
167
+ log_event "INFO" "Verification disabled (ANVIL_REQUIRE_VERIFICATION=$REQUIRE_VERIFY)"
168
+ exit 0
169
+ fi
170
+
171
+ # Check for explicit force exit override
172
+ if [[ "$FORCE_EXIT" == "true" ]]; then
173
+ log_event "WARN" "Force exit requested - bypassing verification gate"
174
+ exit 0
175
+ fi
176
+
177
+ # Check for recent passing verification state
178
+ if check_verification_state; then
179
+ log_event "OK" "Recent verification passed - allowing exit"
180
+ exit 0
181
+ fi
182
+
183
+ log_event "INFO" "Running verification gate checks..."
184
+
185
+ # Detect project type
186
+ local project_type
187
+ project_type=$(detect_project_type)
188
+ log_event "INFO" "Detected project type: $project_type"
189
+
190
+ local failed=false
191
+ local failure_reasons=()
192
+
193
+ # Run tests
194
+ log_event "INFO" "Running tests..."
195
+ if ! run_tests "$project_type"; then
196
+ failed=true
197
+ failure_reasons+=("tests")
198
+ log_event "ERROR" "Tests failed"
199
+ else
200
+ log_event "OK" "Tests passed"
201
+ fi
202
+
203
+ # Run lint (continue even if tests failed)
204
+ log_event "INFO" "Running lint..."
205
+ if ! run_lint "$project_type"; then
206
+ failed=true
207
+ failure_reasons+=("lint")
208
+ log_event "ERROR" "Lint failed"
209
+ else
210
+ log_event "OK" "Lint passed"
211
+ fi
212
+
213
+ # Run typecheck (continue even if others failed)
214
+ log_event "INFO" "Running type check..."
215
+ if ! run_typecheck "$project_type"; then
216
+ failed=true
217
+ failure_reasons+=("types")
218
+ log_event "ERROR" "Type check failed"
219
+ else
220
+ log_event "OK" "Type check passed"
221
+ fi
222
+
223
+ # Final decision
224
+ if [[ "$failed" == "true" ]]; then
225
+ local reasons
226
+ reasons=$(IFS=", "; echo "${failure_reasons[*]}")
227
+ log_event "ERROR" "Verification gate BLOCKED - failures: $reasons"
228
+
229
+ echo ""
230
+ echo "🚫 Cannot exit - verification failed"
231
+ echo " Failing: $reasons"
232
+ echo ""
233
+ echo "Options:"
234
+ echo " 1. Fix the issues and try again"
235
+ echo " 2. Run /verify to see details"
236
+ echo " 3. Use /force-exit to bypass (not recommended)"
237
+ echo ""
238
+
239
+ exit 1
240
+ fi
241
+
242
+ log_event "OK" "Verification gate PASSED - allowing exit"
243
+ exit 0
244
+ }
245
+
246
+ # =============================================================================
247
+ # Entry Point
248
+ # =============================================================================
249
+
250
+ main "$@"
@@ -0,0 +1,21 @@
1
+ {
2
+ "version": "1.0",
3
+ "session": {
4
+ "docCoverage": {
5
+ "percent": 81.0,
6
+ "total": 268,
7
+ "documented": 217,
8
+ "status": "healthy",
9
+ "timestamp": "2026-01-11T12:05:20.361234+00:00"
10
+ },
11
+ "lastCommand": "/doc-coverage",
12
+ "lastCommandAt": "2026-01-11T12:05:20.361235+00:00"
13
+ },
14
+ "cache": {
15
+ "git": {}
16
+ },
17
+ "meta": {
18
+ "createdAt": "2026-01-11T12:05:20.361088+00:00",
19
+ "updatedAt": "2026-01-11T12:05:20.361237+00:00"
20
+ }
21
+ }