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.
- package/README.md +719 -0
- package/VERSION +1 -0
- package/docs/ANVIL-REPO-IMPLEMENTATION-PLAN.md +441 -0
- package/docs/FIRST-SKILL-TUTORIAL.md +408 -0
- package/docs/INSTALLATION-RETRO-NOTES.md +458 -0
- package/docs/INSTALLATION.md +984 -0
- package/docs/anvil-hud.md +469 -0
- package/docs/anvil-init.md +255 -0
- package/docs/anvil-state.md +210 -0
- package/docs/boris-cherny-ralph-wiggum-insights.md +608 -0
- package/docs/command-reference.md +2022 -0
- package/docs/hooks-tts.md +368 -0
- package/docs/implementation-guide.md +810 -0
- package/docs/linear-github-integration.md +247 -0
- package/docs/local-issues.md +677 -0
- package/docs/patterns/README.md +419 -0
- package/docs/planning-responsibilities.md +139 -0
- package/docs/session-workflow.md +573 -0
- package/docs/simplification-plan-template.md +297 -0
- package/docs/simplification-principles.md +129 -0
- package/docs/specifications/CCS-RALPH-INTEGRATION-DESIGN.md +633 -0
- package/docs/specifications/CCS-RESEARCH-REPORT.md +169 -0
- package/docs/specifications/PLAN-ANV-verification-ralph-wiggum.md +403 -0
- package/docs/specifications/PLAN-parallel-tracks-anvil-memory-ccs.md +494 -0
- package/docs/specifications/SPEC-ANV-VRW/component-01-verify.md +208 -0
- package/docs/specifications/SPEC-ANV-VRW/component-02-stop-gate.md +226 -0
- package/docs/specifications/SPEC-ANV-VRW/component-03-posttooluse.md +209 -0
- package/docs/specifications/SPEC-ANV-VRW/component-04-ralph-wiggum.md +604 -0
- package/docs/specifications/SPEC-ANV-VRW/component-05-atomic-actions.md +311 -0
- package/docs/specifications/SPEC-ANV-VRW/component-06-verify-subagent.md +264 -0
- package/docs/specifications/SPEC-ANV-VRW/component-07-claude-md.md +363 -0
- package/docs/specifications/SPEC-ANV-VRW/index.md +182 -0
- package/docs/specifications/SPEC-ANV-anvil-memory.md +573 -0
- package/docs/specifications/SPEC-ANV-context-checkpoints.md +781 -0
- package/docs/specifications/SPEC-ANV-verification-ralph-wiggum.md +789 -0
- package/docs/sync.md +122 -0
- package/global/CLAUDE.md +140 -0
- package/global/agents/verify-app.md +164 -0
- package/global/commands/anvil-settings.md +527 -0
- package/global/commands/anvil-sync.md +121 -0
- package/global/commands/change.md +197 -0
- package/global/commands/clarify.md +252 -0
- package/global/commands/cleanup.md +292 -0
- package/global/commands/commit-push-pr.md +207 -0
- package/global/commands/decay-review.md +127 -0
- package/global/commands/discover.md +158 -0
- package/global/commands/doc-coverage.md +122 -0
- package/global/commands/evidence.md +307 -0
- package/global/commands/explore.md +121 -0
- package/global/commands/force-exit.md +135 -0
- package/global/commands/handoff.md +191 -0
- package/global/commands/healthcheck.md +302 -0
- package/global/commands/hud.md +84 -0
- package/global/commands/insights.md +319 -0
- package/global/commands/linear-setup.md +184 -0
- package/global/commands/lint-fix.md +198 -0
- package/global/commands/orient.md +510 -0
- package/global/commands/plan.md +228 -0
- package/global/commands/ralph.md +346 -0
- package/global/commands/ready.md +182 -0
- package/global/commands/release.md +305 -0
- package/global/commands/retro.md +96 -0
- package/global/commands/shard.md +166 -0
- package/global/commands/spec.md +227 -0
- package/global/commands/sprint.md +184 -0
- package/global/commands/tasks.md +228 -0
- package/global/commands/test-and-commit.md +151 -0
- package/global/commands/validate.md +132 -0
- package/global/commands/verify.md +251 -0
- package/global/commands/weekly-review.md +156 -0
- package/global/hooks/__pycache__/ralph_context_monitor.cpython-314.pyc +0 -0
- package/global/hooks/__pycache__/statusline_agent_sync.cpython-314.pyc +0 -0
- package/global/hooks/anvil_memory_observe.ts +322 -0
- package/global/hooks/anvil_memory_session.ts +166 -0
- package/global/hooks/anvil_memory_stop.ts +187 -0
- package/global/hooks/parse_transcript.py +116 -0
- package/global/hooks/post_merge_cleanup.sh +132 -0
- package/global/hooks/post_tool_format.sh +215 -0
- package/global/hooks/ralph_context_monitor.py +240 -0
- package/global/hooks/ralph_stop.sh +502 -0
- package/global/hooks/statusline.sh +1110 -0
- package/global/hooks/statusline_agent_sync.py +224 -0
- package/global/hooks/stop_gate.sh +250 -0
- package/global/lib/.claude/anvil-state.json +21 -0
- package/global/lib/__pycache__/agent_registry.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/claim_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/coderabbit_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/config_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/coordination_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/doc_coverage_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/gate_logger.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/github_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/hygiene_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/issue_models.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/issue_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/linear_data_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/linear_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/local_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/quality_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/ralph_state.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/state_manager.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/transcript_parser.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verification_runner.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verify_iteration.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verify_subagent.cpython-314.pyc +0 -0
- package/global/lib/agent_registry.py +995 -0
- package/global/lib/anvil-state.sh +435 -0
- package/global/lib/claim_service.py +515 -0
- package/global/lib/coderabbit_service.py +314 -0
- package/global/lib/config_service.py +423 -0
- package/global/lib/coordination_service.py +331 -0
- package/global/lib/doc_coverage_service.py +1305 -0
- package/global/lib/gate_logger.py +316 -0
- package/global/lib/github_service.py +310 -0
- package/global/lib/handoff_generator.py +775 -0
- package/global/lib/hygiene_service.py +712 -0
- package/global/lib/issue_models.py +257 -0
- package/global/lib/issue_provider.py +339 -0
- package/global/lib/linear_data_service.py +210 -0
- package/global/lib/linear_provider.py +987 -0
- package/global/lib/linear_provider.py.backup +671 -0
- package/global/lib/local_provider.py +486 -0
- package/global/lib/orient_fast.py +457 -0
- package/global/lib/quality_service.py +470 -0
- package/global/lib/ralph_prompt_generator.py +563 -0
- package/global/lib/ralph_state.py +1202 -0
- package/global/lib/state_manager.py +417 -0
- package/global/lib/transcript_parser.py +597 -0
- package/global/lib/verification_runner.py +557 -0
- package/global/lib/verify_iteration.py +490 -0
- package/global/lib/verify_subagent.py +250 -0
- package/global/skills/README.md +155 -0
- package/global/skills/quality-gates/SKILL.md +252 -0
- package/global/skills/skill-template/SKILL.md +109 -0
- package/global/skills/testing-strategies/SKILL.md +337 -0
- package/global/templates/CHANGE-template.md +105 -0
- package/global/templates/HANDOFF-template.md +63 -0
- package/global/templates/PLAN-template.md +111 -0
- package/global/templates/SPEC-template.md +93 -0
- package/global/templates/ralph/PROMPT.md.template +89 -0
- package/global/templates/ralph/fix_plan.md.template +31 -0
- package/global/templates/ralph/progress.txt.template +23 -0
- package/global/tests/__pycache__/test_doc_coverage.cpython-314.pyc +0 -0
- package/global/tests/test_doc_coverage.py +520 -0
- package/global/tests/test_issue_models.py +299 -0
- package/global/tests/test_local_provider.py +323 -0
- package/global/tools/README.md +178 -0
- package/global/tools/__pycache__/anvil-hud.cpython-314.pyc +0 -0
- package/global/tools/anvil-hud.py +3622 -0
- package/global/tools/anvil-hud.py.bak +3318 -0
- package/global/tools/anvil-issue.py +432 -0
- package/global/tools/anvil-memory/CLAUDE.md +49 -0
- package/global/tools/anvil-memory/README.md +42 -0
- package/global/tools/anvil-memory/bun.lock +25 -0
- package/global/tools/anvil-memory/bunfig.toml +9 -0
- package/global/tools/anvil-memory/package.json +23 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/context-monitor.test.ts +535 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/edge-cases.test.ts +645 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/fixtures.ts +363 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/index.ts +8 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/integration.test.ts +417 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/prompt-generator.test.ts +571 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/ralph-stop.test.ts +440 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/test-utils.ts +252 -0
- package/global/tools/anvil-memory/src/__tests__/commands.test.ts +657 -0
- package/global/tools/anvil-memory/src/__tests__/db.test.ts +641 -0
- package/global/tools/anvil-memory/src/__tests__/hooks.test.ts +272 -0
- package/global/tools/anvil-memory/src/__tests__/performance.test.ts +427 -0
- package/global/tools/anvil-memory/src/__tests__/test-utils.ts +113 -0
- package/global/tools/anvil-memory/src/commands/checkpoint.ts +197 -0
- package/global/tools/anvil-memory/src/commands/get.ts +115 -0
- package/global/tools/anvil-memory/src/commands/init.ts +94 -0
- package/global/tools/anvil-memory/src/commands/observe.ts +163 -0
- package/global/tools/anvil-memory/src/commands/search.ts +112 -0
- package/global/tools/anvil-memory/src/db.ts +638 -0
- package/global/tools/anvil-memory/src/index.ts +205 -0
- package/global/tools/anvil-memory/src/types.ts +122 -0
- package/global/tools/anvil-memory/tsconfig.json +29 -0
- package/global/tools/ralph-loop.sh +359 -0
- package/package.json +45 -0
- package/scripts/anvil +822 -0
- package/scripts/extract_patterns.py +222 -0
- package/scripts/init-project.sh +541 -0
- package/scripts/install.sh +229 -0
- package/scripts/postinstall.js +41 -0
- package/scripts/rollback.sh +188 -0
- package/scripts/sync.sh +623 -0
- package/scripts/test-statusline.sh +248 -0
- package/scripts/update_claude_md.py +224 -0
- 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
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|