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,222 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Extract patterns from recent git commits for CLAUDE.md updates.
|
|
4
|
+
|
|
5
|
+
Usage: python scripts/extract_patterns.py [--days N] [--min-occurrences N]
|
|
6
|
+
|
|
7
|
+
Output: JSON with extracted patterns to stdout
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import subprocess
|
|
11
|
+
import json
|
|
12
|
+
import re
|
|
13
|
+
import argparse
|
|
14
|
+
from collections import Counter, defaultdict
|
|
15
|
+
from datetime import datetime, timedelta
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_recent_commits(days: int = 7) -> list[dict]:
|
|
20
|
+
"""Get commits from the last N days."""
|
|
21
|
+
since = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d')
|
|
22
|
+
result = subprocess.run(
|
|
23
|
+
['git', 'log', f'--since={since}', '--pretty=format:%s||%h||%an||%ai'],
|
|
24
|
+
capture_output=True, text=True
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if result.returncode != 0:
|
|
28
|
+
return []
|
|
29
|
+
|
|
30
|
+
commits = []
|
|
31
|
+
for line in result.stdout.strip().split('\n'):
|
|
32
|
+
if not line or '||' not in line:
|
|
33
|
+
continue
|
|
34
|
+
parts = line.split('||')
|
|
35
|
+
if len(parts) >= 4:
|
|
36
|
+
commits.append({
|
|
37
|
+
'message': parts[0],
|
|
38
|
+
'hash': parts[1],
|
|
39
|
+
'author': parts[2],
|
|
40
|
+
'date': parts[3]
|
|
41
|
+
})
|
|
42
|
+
return commits
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_files_changed(commit_hash: str) -> list[str]:
|
|
46
|
+
"""Get list of files changed in a commit."""
|
|
47
|
+
result = subprocess.run(
|
|
48
|
+
['git', 'diff-tree', '--no-commit-id', '--name-only', '-r', commit_hash],
|
|
49
|
+
capture_output=True, text=True
|
|
50
|
+
)
|
|
51
|
+
if result.returncode != 0:
|
|
52
|
+
return []
|
|
53
|
+
return [f for f in result.stdout.strip().split('\n') if f]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def extract_commit_patterns(commits: list[dict], min_occurrences: int = 2) -> list[dict]:
|
|
57
|
+
"""Identify patterns in commit messages."""
|
|
58
|
+
patterns = []
|
|
59
|
+
|
|
60
|
+
# Track various patterns
|
|
61
|
+
prefix_counter = Counter()
|
|
62
|
+
scope_counter = Counter()
|
|
63
|
+
issue_refs = Counter()
|
|
64
|
+
file_dir_counter = Counter()
|
|
65
|
+
fix_patterns = defaultdict(list) # Pattern name -> list of issue refs
|
|
66
|
+
|
|
67
|
+
for commit in commits:
|
|
68
|
+
msg = commit['message']
|
|
69
|
+
commit_hash = commit['hash']
|
|
70
|
+
|
|
71
|
+
# Extract conventional commit prefix (feat, fix, docs, etc.)
|
|
72
|
+
prefix_match = re.match(r'^(\w+)(?:\(([^)]+)\))?[!:]', msg)
|
|
73
|
+
if prefix_match:
|
|
74
|
+
prefix = prefix_match.group(1).lower()
|
|
75
|
+
prefix_counter[prefix] += 1
|
|
76
|
+
|
|
77
|
+
# Extract scope if present
|
|
78
|
+
scope = prefix_match.group(2)
|
|
79
|
+
if scope:
|
|
80
|
+
scope_counter[scope.lower()] += 1
|
|
81
|
+
|
|
82
|
+
# Extract issue references (ANV-XX, ENG-XX, etc.)
|
|
83
|
+
issues = re.findall(r'([A-Z]+-\d+)', msg)
|
|
84
|
+
for issue in issues:
|
|
85
|
+
issue_refs[issue] += 1
|
|
86
|
+
|
|
87
|
+
# Track fix patterns for "Verify X Before Y" type learnings
|
|
88
|
+
if msg.startswith('fix'):
|
|
89
|
+
# Look for common fix categories
|
|
90
|
+
if 'branch' in msg.lower():
|
|
91
|
+
fix_patterns['Verify Branch Before Read'].extend(issues)
|
|
92
|
+
if 'path' in msg.lower() or 'file' in msg.lower():
|
|
93
|
+
fix_patterns['Verify File Location First'].extend(issues)
|
|
94
|
+
if 'config' in msg.lower():
|
|
95
|
+
fix_patterns['Verify Config Before Use'].extend(issues)
|
|
96
|
+
|
|
97
|
+
# Track directory patterns from changed files
|
|
98
|
+
files = get_files_changed(commit_hash)
|
|
99
|
+
for f in files:
|
|
100
|
+
parts = Path(f).parts
|
|
101
|
+
if len(parts) > 1:
|
|
102
|
+
file_dir_counter[parts[0]] += 1
|
|
103
|
+
|
|
104
|
+
# Generate patterns from collected data
|
|
105
|
+
|
|
106
|
+
# 1. Commit type patterns (if dominant type exists)
|
|
107
|
+
total_commits = len(commits)
|
|
108
|
+
for prefix, count in prefix_counter.most_common(3):
|
|
109
|
+
if count >= min_occurrences:
|
|
110
|
+
percentage = (count / total_commits) * 100
|
|
111
|
+
if percentage >= 20: # At least 20% of commits
|
|
112
|
+
patterns.append({
|
|
113
|
+
'name': f'{prefix.title()} Focus',
|
|
114
|
+
'description': f'{count} {prefix} commits ({percentage:.0f}% of activity)',
|
|
115
|
+
'evidence': ', '.join(list(issue_refs.keys())[:5]),
|
|
116
|
+
'type': 'activity'
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
# 2. Scope patterns (frequently touched areas)
|
|
120
|
+
for scope, count in scope_counter.most_common(5):
|
|
121
|
+
if count >= min_occurrences:
|
|
122
|
+
patterns.append({
|
|
123
|
+
'name': f'{scope.title()} Changes',
|
|
124
|
+
'description': f'Frequent modifications to {scope} ({count} commits)',
|
|
125
|
+
'evidence': ', '.join(list(issue_refs.keys())[:3]),
|
|
126
|
+
'type': 'scope'
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
# 3. Fix patterns (potential learnings)
|
|
130
|
+
for pattern_name, issues in fix_patterns.items():
|
|
131
|
+
if len(issues) >= min_occurrences:
|
|
132
|
+
unique_issues = list(set(issues))[:5]
|
|
133
|
+
patterns.append({
|
|
134
|
+
'name': pattern_name,
|
|
135
|
+
'description': f'Pattern emerged from {len(issues)} related fixes',
|
|
136
|
+
'evidence': ', '.join(unique_issues),
|
|
137
|
+
'type': 'learning'
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
# 4. Directory focus patterns
|
|
141
|
+
for directory, count in file_dir_counter.most_common(3):
|
|
142
|
+
if count >= min_occurrences * 2 and directory not in ['.github', 'node_modules']:
|
|
143
|
+
patterns.append({
|
|
144
|
+
'name': f'{directory}/ Development',
|
|
145
|
+
'description': f'Active development in {directory}/ ({count} file changes)',
|
|
146
|
+
'evidence': ', '.join(list(issue_refs.keys())[:3]),
|
|
147
|
+
'type': 'focus'
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
return patterns
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def extract_retro_patterns(days: int = 7) -> list[dict]:
|
|
154
|
+
"""Extract patterns from recent retrospective files."""
|
|
155
|
+
patterns = []
|
|
156
|
+
retros_dir = Path('.claude/retros')
|
|
157
|
+
|
|
158
|
+
if not retros_dir.exists():
|
|
159
|
+
return patterns
|
|
160
|
+
|
|
161
|
+
cutoff = datetime.now() - timedelta(days=days)
|
|
162
|
+
|
|
163
|
+
for retro_file in retros_dir.rglob('*.md'):
|
|
164
|
+
try:
|
|
165
|
+
# Check file modification time
|
|
166
|
+
mtime = datetime.fromtimestamp(retro_file.stat().st_mtime)
|
|
167
|
+
if mtime < cutoff:
|
|
168
|
+
continue
|
|
169
|
+
|
|
170
|
+
content = retro_file.read_text()
|
|
171
|
+
|
|
172
|
+
# Extract key learning
|
|
173
|
+
learning_match = re.search(r'## Key Learning\s*\n+(.+?)(?:\n\n|\n##|$)', content, re.DOTALL)
|
|
174
|
+
if learning_match:
|
|
175
|
+
learning = learning_match.group(1).strip()
|
|
176
|
+
# Clean up the learning text
|
|
177
|
+
learning = re.sub(r'\s+', ' ', learning)[:100]
|
|
178
|
+
|
|
179
|
+
# Extract issue references from filename or content
|
|
180
|
+
issue_match = re.search(r'([A-Z]+-\d+)', retro_file.name)
|
|
181
|
+
evidence = issue_match.group(1) if issue_match else retro_file.stem
|
|
182
|
+
|
|
183
|
+
patterns.append({
|
|
184
|
+
'name': learning[:50] + ('...' if len(learning) > 50 else ''),
|
|
185
|
+
'description': 'From retrospective analysis',
|
|
186
|
+
'evidence': evidence,
|
|
187
|
+
'type': 'retro',
|
|
188
|
+
'source': str(retro_file)
|
|
189
|
+
})
|
|
190
|
+
except Exception:
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
return patterns
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def main():
|
|
197
|
+
parser = argparse.ArgumentParser(description='Extract patterns from git commits')
|
|
198
|
+
parser.add_argument('--days', type=int, default=7, help='Number of days to analyze')
|
|
199
|
+
parser.add_argument('--min-occurrences', type=int, default=2, help='Minimum occurrences for a pattern')
|
|
200
|
+
parser.add_argument('--include-retros', action='store_true', help='Include retrospective patterns')
|
|
201
|
+
args = parser.parse_args()
|
|
202
|
+
|
|
203
|
+
commits = get_recent_commits(args.days)
|
|
204
|
+
patterns = extract_commit_patterns(commits, args.min_occurrences)
|
|
205
|
+
|
|
206
|
+
if args.include_retros:
|
|
207
|
+
retro_patterns = extract_retro_patterns(args.days)
|
|
208
|
+
patterns.extend(retro_patterns)
|
|
209
|
+
|
|
210
|
+
output = {
|
|
211
|
+
'extracted_at': datetime.now().isoformat(),
|
|
212
|
+
'analysis_period_days': args.days,
|
|
213
|
+
'commit_count': len(commits),
|
|
214
|
+
'pattern_count': len(patterns),
|
|
215
|
+
'patterns': patterns
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
print(json.dumps(output, indent=2))
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
if __name__ == '__main__':
|
|
222
|
+
main()
|