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
package/scripts/sync.sh
ADDED
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Anvil Development Framework - Sync Script
|
|
5
|
+
#
|
|
6
|
+
# Syncs Anvil framework updates to global config and/or projects
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# anvil sync --global # Sync to ~/.claude/
|
|
10
|
+
# anvil sync --project /path/to/project # Sync to a specific project
|
|
11
|
+
# anvil sync --project . # Sync to current directory
|
|
12
|
+
# anvil sync --all # Sync global + all registered projects
|
|
13
|
+
# anvil sync --dry-run --project . # Preview changes without applying
|
|
14
|
+
#
|
|
15
|
+
|
|
16
|
+
set -e
|
|
17
|
+
|
|
18
|
+
# Colors
|
|
19
|
+
RED='\033[0;31m'
|
|
20
|
+
GREEN='\033[0;32m'
|
|
21
|
+
YELLOW='\033[1;33m'
|
|
22
|
+
BLUE='\033[0;34m'
|
|
23
|
+
CYAN='\033[0;36m'
|
|
24
|
+
DIM='\033[2m'
|
|
25
|
+
NC='\033[0m' # No Color
|
|
26
|
+
|
|
27
|
+
# Symbols
|
|
28
|
+
CHECK="${GREEN}✓${NC}"
|
|
29
|
+
CROSS="${RED}✗${NC}"
|
|
30
|
+
ARROW="${BLUE}→${NC}"
|
|
31
|
+
SKIP="${YELLOW}⊘${NC}"
|
|
32
|
+
|
|
33
|
+
# Configuration
|
|
34
|
+
ANVIL_DIR=""
|
|
35
|
+
DRY_RUN=false
|
|
36
|
+
FORCE=false
|
|
37
|
+
VERBOSE=false
|
|
38
|
+
SYNC_GLOBAL=false
|
|
39
|
+
SYNC_PROJECT=""
|
|
40
|
+
SYNC_ALL=false
|
|
41
|
+
|
|
42
|
+
# Files that should NEVER be overwritten (user customizations)
|
|
43
|
+
PROTECTED_FILES=(
|
|
44
|
+
"CLAUDE.md"
|
|
45
|
+
"product.md"
|
|
46
|
+
"constitution.md"
|
|
47
|
+
"settings.local.json"
|
|
48
|
+
"coordination.md"
|
|
49
|
+
"linear.yaml"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Files that should ALWAYS be synced (framework updates)
|
|
53
|
+
SYNC_ALWAYS=(
|
|
54
|
+
"hooks/"
|
|
55
|
+
"examples/"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
#######################################
|
|
60
|
+
# Helper Functions
|
|
61
|
+
#######################################
|
|
62
|
+
|
|
63
|
+
print_banner() {
|
|
64
|
+
echo ""
|
|
65
|
+
echo -e "${BLUE}╔═══════════════════════════════════════╗${NC}"
|
|
66
|
+
echo -e "${BLUE}║${NC} Anvil Framework Sync Tool ${BLUE}║${NC}"
|
|
67
|
+
echo -e "${BLUE}╚═══════════════════════════════════════╝${NC}"
|
|
68
|
+
echo ""
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
log_info() {
|
|
72
|
+
echo -e "${BLUE}[INFO]${NC} $1"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
log_success() {
|
|
76
|
+
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
log_warning() {
|
|
80
|
+
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
log_error() {
|
|
84
|
+
echo -e "${RED}[ERROR]${NC} $1"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
log_verbose() {
|
|
88
|
+
if [ "$VERBOSE" = true ]; then
|
|
89
|
+
echo -e "${DIM}[DEBUG] $1${NC}"
|
|
90
|
+
fi
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
log_dry_run() {
|
|
94
|
+
if [ "$DRY_RUN" = true ]; then
|
|
95
|
+
echo -e "${CYAN}[DRY-RUN]${NC} $1"
|
|
96
|
+
fi
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# Check if file is protected
|
|
100
|
+
is_protected() {
|
|
101
|
+
local filename=$(basename "$1")
|
|
102
|
+
for protected in "${PROTECTED_FILES[@]}"; do
|
|
103
|
+
if [ "$filename" = "$protected" ]; then
|
|
104
|
+
return 0
|
|
105
|
+
fi
|
|
106
|
+
done
|
|
107
|
+
return 1
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# Find Anvil installation directory
|
|
111
|
+
find_anvil_dir() {
|
|
112
|
+
# Check ANVIL_HOME env var first
|
|
113
|
+
if [ -n "$ANVIL_HOME" ] && [ -d "$ANVIL_HOME" ]; then
|
|
114
|
+
echo "$ANVIL_HOME"
|
|
115
|
+
return 0
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# Check if we're in the anvil directory
|
|
119
|
+
if [ -f "./VERSION" ] && [ -d "./global" ]; then
|
|
120
|
+
echo "$(pwd)"
|
|
121
|
+
return 0
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
# Check common locations
|
|
125
|
+
local locations=(
|
|
126
|
+
"$HOME/Projects/anvil-dev-framework"
|
|
127
|
+
"$HOME/dev/anvil-dev-framework"
|
|
128
|
+
"$HOME/code/anvil-dev-framework"
|
|
129
|
+
"/opt/anvil-dev-framework"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
for loc in "${locations[@]}"; do
|
|
133
|
+
if [ -f "$loc/VERSION" ]; then
|
|
134
|
+
echo "$loc"
|
|
135
|
+
return 0
|
|
136
|
+
fi
|
|
137
|
+
done
|
|
138
|
+
|
|
139
|
+
return 1
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# Get current Anvil version
|
|
144
|
+
get_version() {
|
|
145
|
+
if [ -f "$ANVIL_DIR/VERSION" ]; then
|
|
146
|
+
cat "$ANVIL_DIR/VERSION"
|
|
147
|
+
else
|
|
148
|
+
echo "unknown"
|
|
149
|
+
fi
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# Compare files and return if different
|
|
153
|
+
files_differ() {
|
|
154
|
+
local src="$1"
|
|
155
|
+
local dst="$2"
|
|
156
|
+
|
|
157
|
+
if [ ! -f "$dst" ]; then
|
|
158
|
+
return 0 # Destination doesn't exist, so "different"
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
if ! diff -q "$src" "$dst" > /dev/null 2>&1; then
|
|
162
|
+
return 0 # Files differ
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
return 1 # Files are same
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
# Sync a single file
|
|
169
|
+
sync_file() {
|
|
170
|
+
local src="$1"
|
|
171
|
+
local dst="$2"
|
|
172
|
+
local filename=$(basename "$src")
|
|
173
|
+
|
|
174
|
+
# Check if file exists at destination
|
|
175
|
+
if [ ! -f "$dst" ]; then
|
|
176
|
+
if [ "$DRY_RUN" = true ]; then
|
|
177
|
+
log_dry_run "Would create: $dst"
|
|
178
|
+
else
|
|
179
|
+
mkdir -p "$(dirname "$dst")"
|
|
180
|
+
cp "$src" "$dst"
|
|
181
|
+
echo -e " ${CHECK} Created: ${filename}"
|
|
182
|
+
fi
|
|
183
|
+
return 0
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# Check if protected and not forcing
|
|
187
|
+
if is_protected "$filename" && [ "$FORCE" != true ]; then
|
|
188
|
+
if files_differ "$src" "$dst"; then
|
|
189
|
+
echo -e " ${SKIP} Protected (use --force): ${filename}"
|
|
190
|
+
else
|
|
191
|
+
log_verbose "Protected file unchanged: $filename"
|
|
192
|
+
fi
|
|
193
|
+
return 0
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
# Check if files differ
|
|
197
|
+
if files_differ "$src" "$dst"; then
|
|
198
|
+
if [ "$DRY_RUN" = true ]; then
|
|
199
|
+
log_dry_run "Would update: $dst"
|
|
200
|
+
if [ "$VERBOSE" = true ]; then
|
|
201
|
+
echo -e "${DIM}--- Diff ---${NC}"
|
|
202
|
+
diff "$dst" "$src" || true
|
|
203
|
+
echo -e "${DIM}------------${NC}"
|
|
204
|
+
fi
|
|
205
|
+
else
|
|
206
|
+
cp "$src" "$dst"
|
|
207
|
+
echo -e " ${CHECK} Updated: ${filename}"
|
|
208
|
+
fi
|
|
209
|
+
else
|
|
210
|
+
log_verbose "Unchanged: $filename"
|
|
211
|
+
fi
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
# Sync a directory recursively
|
|
215
|
+
sync_directory() {
|
|
216
|
+
local src_dir="${1%/}" # Remove trailing slash if present
|
|
217
|
+
local dst_dir="${2%/}" # Remove trailing slash if present
|
|
218
|
+
local dir_name=$(basename "$src_dir")
|
|
219
|
+
|
|
220
|
+
if [ ! -d "$src_dir" ]; then
|
|
221
|
+
log_warning "Source directory not found: $src_dir"
|
|
222
|
+
return 1
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
if [ "$DRY_RUN" = true ]; then
|
|
226
|
+
log_dry_run "Would sync directory: $dir_name/"
|
|
227
|
+
fi
|
|
228
|
+
|
|
229
|
+
mkdir -p "$dst_dir"
|
|
230
|
+
|
|
231
|
+
# Sync all files in directory
|
|
232
|
+
find "$src_dir" -type f | while read -r src_file; do
|
|
233
|
+
local rel_path="${src_file#$src_dir/}"
|
|
234
|
+
local dst_file="$dst_dir/$rel_path"
|
|
235
|
+
sync_file "$src_file" "$dst_file"
|
|
236
|
+
done
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
#######################################
|
|
241
|
+
# Sync Functions
|
|
242
|
+
#######################################
|
|
243
|
+
|
|
244
|
+
# Sync global config to ~/.claude/
|
|
245
|
+
sync_global() {
|
|
246
|
+
local global_dir="$HOME/.claude"
|
|
247
|
+
local version=$(get_version)
|
|
248
|
+
|
|
249
|
+
echo ""
|
|
250
|
+
echo -e "${BLUE}═══════════════════════════════════════${NC}"
|
|
251
|
+
echo -e "${BLUE} Syncing Global Config (v${version})${NC}"
|
|
252
|
+
echo -e "${BLUE}═══════════════════════════════════════${NC}"
|
|
253
|
+
echo ""
|
|
254
|
+
|
|
255
|
+
# Create directory structure
|
|
256
|
+
mkdir -p "$global_dir/commands"
|
|
257
|
+
mkdir -p "$global_dir/standards"
|
|
258
|
+
mkdir -p "$global_dir/templates"
|
|
259
|
+
mkdir -p "$global_dir/skills"
|
|
260
|
+
|
|
261
|
+
# Sync commands (always update)
|
|
262
|
+
echo -e "${ARROW} Syncing commands..."
|
|
263
|
+
if [ -d "$ANVIL_DIR/global/commands" ]; then
|
|
264
|
+
for cmd in "$ANVIL_DIR/global/commands"/*.md; do
|
|
265
|
+
if [ -f "$cmd" ]; then
|
|
266
|
+
sync_file "$cmd" "$global_dir/commands/$(basename "$cmd")"
|
|
267
|
+
fi
|
|
268
|
+
done
|
|
269
|
+
fi
|
|
270
|
+
|
|
271
|
+
# Sync standards
|
|
272
|
+
echo -e "${ARROW} Syncing standards..."
|
|
273
|
+
if [ -d "$ANVIL_DIR/global/standards" ]; then
|
|
274
|
+
for std in "$ANVIL_DIR/global/standards"/*.md; do
|
|
275
|
+
if [ -f "$std" ]; then
|
|
276
|
+
sync_file "$std" "$global_dir/standards/$(basename "$std")"
|
|
277
|
+
fi
|
|
278
|
+
done
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
# Sync templates (including subdirectories like ralph/)
|
|
282
|
+
echo -e "${ARROW} Syncing templates..."
|
|
283
|
+
if [ -d "$ANVIL_DIR/global/templates" ]; then
|
|
284
|
+
# Sync top-level template files
|
|
285
|
+
for tpl in "$ANVIL_DIR/global/templates"/*.md; do
|
|
286
|
+
if [ -f "$tpl" ]; then
|
|
287
|
+
sync_file "$tpl" "$global_dir/templates/$(basename "$tpl")"
|
|
288
|
+
fi
|
|
289
|
+
done
|
|
290
|
+
# Sync template subdirectories (e.g., ralph/)
|
|
291
|
+
for subdir in "$ANVIL_DIR/global/templates"/*/; do
|
|
292
|
+
if [ -d "$subdir" ]; then
|
|
293
|
+
local subdir_name=$(basename "$subdir")
|
|
294
|
+
mkdir -p "$global_dir/templates/$subdir_name"
|
|
295
|
+
sync_directory "$subdir" "$global_dir/templates/$subdir_name"
|
|
296
|
+
fi
|
|
297
|
+
done
|
|
298
|
+
fi
|
|
299
|
+
|
|
300
|
+
# Sync global hooks (ralph_stop.sh, etc.)
|
|
301
|
+
echo -e "${ARROW} Syncing global hooks..."
|
|
302
|
+
if [ -d "$ANVIL_DIR/global/hooks" ]; then
|
|
303
|
+
mkdir -p "$global_dir/hooks"
|
|
304
|
+
for hook in "$ANVIL_DIR/global/hooks"/*; do
|
|
305
|
+
if [ -f "$hook" ]; then
|
|
306
|
+
sync_file "$hook" "$global_dir/hooks/$(basename "$hook")"
|
|
307
|
+
fi
|
|
308
|
+
done
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
# Sync global lib (ralph_state.py, etc.)
|
|
312
|
+
echo -e "${ARROW} Syncing global lib..."
|
|
313
|
+
if [ -d "$ANVIL_DIR/global/lib" ]; then
|
|
314
|
+
mkdir -p "$global_dir/lib"
|
|
315
|
+
for lib in "$ANVIL_DIR/global/lib"/*; do
|
|
316
|
+
if [ -f "$lib" ]; then
|
|
317
|
+
sync_file "$lib" "$global_dir/lib/$(basename "$lib")"
|
|
318
|
+
fi
|
|
319
|
+
done
|
|
320
|
+
fi
|
|
321
|
+
|
|
322
|
+
# Sync global tools (ralph-loop.sh, etc.)
|
|
323
|
+
echo -e "${ARROW} Syncing global tools..."
|
|
324
|
+
if [ -d "$ANVIL_DIR/global/tools" ]; then
|
|
325
|
+
mkdir -p "$global_dir/tools"
|
|
326
|
+
for tool in "$ANVIL_DIR/global/tools"/*; do
|
|
327
|
+
if [ -f "$tool" ]; then
|
|
328
|
+
sync_file "$tool" "$global_dir/tools/$(basename "$tool")"
|
|
329
|
+
fi
|
|
330
|
+
done
|
|
331
|
+
fi
|
|
332
|
+
|
|
333
|
+
# Sync global CLAUDE.md (protected)
|
|
334
|
+
if [ -f "$ANVIL_DIR/global/CLAUDE.md" ]; then
|
|
335
|
+
echo -e "${ARROW} Syncing CLAUDE.md..."
|
|
336
|
+
sync_file "$ANVIL_DIR/global/CLAUDE.md" "$global_dir/CLAUDE.md"
|
|
337
|
+
fi
|
|
338
|
+
|
|
339
|
+
# Update version marker
|
|
340
|
+
if [ "$DRY_RUN" != true ]; then
|
|
341
|
+
echo "$version" > "$global_dir/.anvil-version"
|
|
342
|
+
echo -e " ${CHECK} Version marker updated: v${version}"
|
|
343
|
+
fi
|
|
344
|
+
|
|
345
|
+
echo ""
|
|
346
|
+
log_success "Global sync complete!"
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
# Sync to a specific project
|
|
351
|
+
sync_project() {
|
|
352
|
+
local project_dir="$1"
|
|
353
|
+
local claude_dir="$project_dir/.claude"
|
|
354
|
+
local version=$(get_version)
|
|
355
|
+
|
|
356
|
+
# Resolve to absolute path
|
|
357
|
+
project_dir=$(cd "$project_dir" && pwd)
|
|
358
|
+
claude_dir="$project_dir/.claude"
|
|
359
|
+
|
|
360
|
+
# Check if project has .claude directory
|
|
361
|
+
if [ ! -d "$claude_dir" ]; then
|
|
362
|
+
log_warning "Project does not have .claude/ directory: $project_dir"
|
|
363
|
+
echo "Run 'anvil init $project_dir' first, or create manually."
|
|
364
|
+
return 1
|
|
365
|
+
fi
|
|
366
|
+
|
|
367
|
+
local project_name=$(basename "$project_dir")
|
|
368
|
+
|
|
369
|
+
echo ""
|
|
370
|
+
echo -e "${BLUE}═══════════════════════════════════════${NC}"
|
|
371
|
+
echo -e "${BLUE} Syncing Project: ${project_name} (v${version})${NC}"
|
|
372
|
+
echo -e "${BLUE}═══════════════════════════════════════${NC}"
|
|
373
|
+
echo ""
|
|
374
|
+
|
|
375
|
+
# Load project config if exists
|
|
376
|
+
local config_file="$claude_dir/.anvil-project.yaml"
|
|
377
|
+
|
|
378
|
+
# Sync hooks (always - critical for functionality)
|
|
379
|
+
echo -e "${ARROW} Syncing hooks..."
|
|
380
|
+
if [ -d "$ANVIL_DIR/project/hooks" ]; then
|
|
381
|
+
sync_directory "$ANVIL_DIR/project/hooks" "$claude_dir/hooks"
|
|
382
|
+
fi
|
|
383
|
+
|
|
384
|
+
# Sync examples (always - reference implementations)
|
|
385
|
+
echo -e "${ARROW} Syncing examples..."
|
|
386
|
+
if [ -d "$ANVIL_DIR/project/examples" ]; then
|
|
387
|
+
sync_directory "$ANVIL_DIR/project/examples" "$claude_dir/examples"
|
|
388
|
+
fi
|
|
389
|
+
|
|
390
|
+
# Sync agents (always - sub-agent definitions)
|
|
391
|
+
echo -e "${ARROW} Syncing agents..."
|
|
392
|
+
if [ -d "$ANVIL_DIR/project/agents" ]; then
|
|
393
|
+
sync_directory "$ANVIL_DIR/project/agents" "$claude_dir/agents"
|
|
394
|
+
fi
|
|
395
|
+
|
|
396
|
+
# Sync retro templates
|
|
397
|
+
echo -e "${ARROW} Syncing retro templates..."
|
|
398
|
+
if [ -d "$ANVIL_DIR/project/retros" ]; then
|
|
399
|
+
mkdir -p "$claude_dir/retros/templates"
|
|
400
|
+
if [ -f "$ANVIL_DIR/project/retros/README.md" ]; then
|
|
401
|
+
sync_file "$ANVIL_DIR/project/retros/README.md" "$claude_dir/retros/README.md"
|
|
402
|
+
fi
|
|
403
|
+
if [ -d "$ANVIL_DIR/project/retros/templates" ]; then
|
|
404
|
+
sync_directory "$ANVIL_DIR/project/retros/templates" "$claude_dir/retros/templates"
|
|
405
|
+
fi
|
|
406
|
+
fi
|
|
407
|
+
|
|
408
|
+
# Sync protected files (only if --force or doesn't exist)
|
|
409
|
+
echo -e "${ARROW} Checking core files..."
|
|
410
|
+
|
|
411
|
+
# CLAUDE.md
|
|
412
|
+
if [ -f "$ANVIL_DIR/project/CLAUDE.md.template" ]; then
|
|
413
|
+
if [ ! -f "$claude_dir/CLAUDE.md" ]; then
|
|
414
|
+
if [ "$DRY_RUN" = true ]; then
|
|
415
|
+
log_dry_run "Would create: CLAUDE.md"
|
|
416
|
+
else
|
|
417
|
+
cp "$ANVIL_DIR/project/CLAUDE.md.template" "$claude_dir/CLAUDE.md"
|
|
418
|
+
echo -e " ${CHECK} Created: CLAUDE.md"
|
|
419
|
+
fi
|
|
420
|
+
else
|
|
421
|
+
echo -e " ${SKIP} Protected: CLAUDE.md (exists)"
|
|
422
|
+
fi
|
|
423
|
+
fi
|
|
424
|
+
|
|
425
|
+
# constitution.md
|
|
426
|
+
if [ -f "$ANVIL_DIR/project/constitution.md.template" ]; then
|
|
427
|
+
if [ ! -f "$claude_dir/constitution.md" ]; then
|
|
428
|
+
if [ "$DRY_RUN" = true ]; then
|
|
429
|
+
log_dry_run "Would create: constitution.md"
|
|
430
|
+
else
|
|
431
|
+
cp "$ANVIL_DIR/project/constitution.md.template" "$claude_dir/constitution.md"
|
|
432
|
+
echo -e " ${CHECK} Created: constitution.md"
|
|
433
|
+
fi
|
|
434
|
+
else
|
|
435
|
+
echo -e " ${SKIP} Protected: constitution.md (exists)"
|
|
436
|
+
fi
|
|
437
|
+
fi
|
|
438
|
+
|
|
439
|
+
# product.md
|
|
440
|
+
if [ -f "$ANVIL_DIR/project/product.md.template" ]; then
|
|
441
|
+
if [ ! -f "$claude_dir/product.md" ]; then
|
|
442
|
+
if [ "$DRY_RUN" = true ]; then
|
|
443
|
+
log_dry_run "Would create: product.md"
|
|
444
|
+
else
|
|
445
|
+
cp "$ANVIL_DIR/project/product.md.template" "$claude_dir/product.md"
|
|
446
|
+
echo -e " ${CHECK} Created: product.md"
|
|
447
|
+
fi
|
|
448
|
+
else
|
|
449
|
+
echo -e " ${SKIP} Protected: product.md (exists)"
|
|
450
|
+
fi
|
|
451
|
+
fi
|
|
452
|
+
|
|
453
|
+
# coordination.md (less protected, sync if newer)
|
|
454
|
+
if [ -f "$ANVIL_DIR/project/coordination.md" ]; then
|
|
455
|
+
sync_file "$ANVIL_DIR/project/coordination.md" "$claude_dir/coordination.md"
|
|
456
|
+
fi
|
|
457
|
+
|
|
458
|
+
# Sync global commands to project (optional - projects can use global)
|
|
459
|
+
echo -e "${ARROW} Syncing commands..."
|
|
460
|
+
if [ -d "$ANVIL_DIR/global/commands" ]; then
|
|
461
|
+
mkdir -p "$claude_dir/commands"
|
|
462
|
+
for cmd in "$ANVIL_DIR/global/commands"/*.md; do
|
|
463
|
+
if [ -f "$cmd" ]; then
|
|
464
|
+
sync_file "$cmd" "$claude_dir/commands/$(basename "$cmd")"
|
|
465
|
+
fi
|
|
466
|
+
done
|
|
467
|
+
fi
|
|
468
|
+
|
|
469
|
+
# Update version marker
|
|
470
|
+
if [ "$DRY_RUN" != true ]; then
|
|
471
|
+
echo "$version" > "$claude_dir/.anvil-version"
|
|
472
|
+
echo -e " ${CHECK} Version marker: v${version}"
|
|
473
|
+
fi
|
|
474
|
+
|
|
475
|
+
echo ""
|
|
476
|
+
log_success "Project sync complete: $project_name"
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
#######################################
|
|
481
|
+
# Argument Parsing
|
|
482
|
+
#######################################
|
|
483
|
+
|
|
484
|
+
show_help() {
|
|
485
|
+
echo "Anvil Sync - Sync framework updates to global config and projects"
|
|
486
|
+
echo ""
|
|
487
|
+
echo "Usage: $0 [OPTIONS]"
|
|
488
|
+
echo ""
|
|
489
|
+
echo "Targets:"
|
|
490
|
+
echo " --global Sync to ~/.claude/ (global config)"
|
|
491
|
+
echo " --project PATH Sync to a specific project"
|
|
492
|
+
echo " --all Sync global + all registered projects"
|
|
493
|
+
echo ""
|
|
494
|
+
echo "Options:"
|
|
495
|
+
echo " --force, -f Overwrite protected files"
|
|
496
|
+
echo " --dry-run, -n Preview changes without applying"
|
|
497
|
+
echo " --verbose, -v Show detailed output"
|
|
498
|
+
echo " --help, -h Show this help message"
|
|
499
|
+
echo ""
|
|
500
|
+
echo "Examples:"
|
|
501
|
+
echo " $0 --global # Sync global commands"
|
|
502
|
+
echo " $0 --project /path/to/myproject # Sync to specific project"
|
|
503
|
+
echo " $0 --project . --dry-run # Preview project sync"
|
|
504
|
+
echo " $0 --global --project . --force # Full sync with overwrites"
|
|
505
|
+
echo ""
|
|
506
|
+
echo "Protected files (not overwritten without --force):"
|
|
507
|
+
echo " - CLAUDE.md"
|
|
508
|
+
echo " - product.md"
|
|
509
|
+
echo " - constitution.md"
|
|
510
|
+
echo " - settings.local.json"
|
|
511
|
+
echo ""
|
|
512
|
+
echo "Environment:"
|
|
513
|
+
echo " ANVIL_HOME Path to anvil-dev-framework (auto-detected if not set)"
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
parse_args() {
|
|
517
|
+
while [[ $# -gt 0 ]]; do
|
|
518
|
+
case $1 in
|
|
519
|
+
--global)
|
|
520
|
+
SYNC_GLOBAL=true
|
|
521
|
+
shift
|
|
522
|
+
;;
|
|
523
|
+
--project)
|
|
524
|
+
SYNC_PROJECT="$2"
|
|
525
|
+
shift 2
|
|
526
|
+
;;
|
|
527
|
+
--all)
|
|
528
|
+
SYNC_ALL=true
|
|
529
|
+
shift
|
|
530
|
+
;;
|
|
531
|
+
--force|-f)
|
|
532
|
+
FORCE=true
|
|
533
|
+
shift
|
|
534
|
+
;;
|
|
535
|
+
--dry-run|-n)
|
|
536
|
+
DRY_RUN=true
|
|
537
|
+
shift
|
|
538
|
+
;;
|
|
539
|
+
--verbose|-v)
|
|
540
|
+
VERBOSE=true
|
|
541
|
+
shift
|
|
542
|
+
;;
|
|
543
|
+
--help|-h)
|
|
544
|
+
show_help
|
|
545
|
+
exit 0
|
|
546
|
+
;;
|
|
547
|
+
*)
|
|
548
|
+
log_error "Unknown option: $1"
|
|
549
|
+
show_help
|
|
550
|
+
exit 1
|
|
551
|
+
;;
|
|
552
|
+
esac
|
|
553
|
+
done
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
#######################################
|
|
558
|
+
# Main
|
|
559
|
+
#######################################
|
|
560
|
+
|
|
561
|
+
main() {
|
|
562
|
+
parse_args "$@"
|
|
563
|
+
|
|
564
|
+
# Find Anvil directory
|
|
565
|
+
ANVIL_DIR=$(find_anvil_dir)
|
|
566
|
+
if [ -z "$ANVIL_DIR" ]; then
|
|
567
|
+
log_error "Could not find Anvil installation."
|
|
568
|
+
echo "Set ANVIL_HOME environment variable or run from anvil-dev-framework directory."
|
|
569
|
+
exit 1
|
|
570
|
+
fi
|
|
571
|
+
|
|
572
|
+
log_verbose "Anvil directory: $ANVIL_DIR"
|
|
573
|
+
|
|
574
|
+
# Print banner
|
|
575
|
+
print_banner
|
|
576
|
+
|
|
577
|
+
local version=$(get_version)
|
|
578
|
+
echo -e "Anvil Framework v${version}"
|
|
579
|
+
echo -e "Source: ${DIM}${ANVIL_DIR}${NC}"
|
|
580
|
+
|
|
581
|
+
if [ "$DRY_RUN" = true ]; then
|
|
582
|
+
echo -e "${CYAN}Mode: DRY RUN (no changes will be made)${NC}"
|
|
583
|
+
fi
|
|
584
|
+
if [ "$FORCE" = true ]; then
|
|
585
|
+
echo -e "${YELLOW}Mode: FORCE (protected files will be overwritten)${NC}"
|
|
586
|
+
fi
|
|
587
|
+
|
|
588
|
+
# Check if any target specified
|
|
589
|
+
if [ "$SYNC_GLOBAL" != true ] && [ -z "$SYNC_PROJECT" ] && [ "$SYNC_ALL" != true ]; then
|
|
590
|
+
log_error "No sync target specified."
|
|
591
|
+
echo ""
|
|
592
|
+
echo "Use --global, --project PATH, or --all"
|
|
593
|
+
echo "Run '$0 --help' for usage information."
|
|
594
|
+
exit 1
|
|
595
|
+
fi
|
|
596
|
+
|
|
597
|
+
# Execute syncs
|
|
598
|
+
if [ "$SYNC_GLOBAL" = true ] || [ "$SYNC_ALL" = true ]; then
|
|
599
|
+
sync_global
|
|
600
|
+
fi
|
|
601
|
+
|
|
602
|
+
if [ -n "$SYNC_PROJECT" ]; then
|
|
603
|
+
if [ ! -d "$SYNC_PROJECT" ]; then
|
|
604
|
+
log_error "Project directory not found: $SYNC_PROJECT"
|
|
605
|
+
exit 1
|
|
606
|
+
fi
|
|
607
|
+
sync_project "$SYNC_PROJECT"
|
|
608
|
+
fi
|
|
609
|
+
|
|
610
|
+
if [ "$SYNC_ALL" = true ]; then
|
|
611
|
+
# TODO: Read registered projects from ~/.claude/.anvil-projects
|
|
612
|
+
log_warning "--all currently only syncs global. Project registry coming soon."
|
|
613
|
+
fi
|
|
614
|
+
|
|
615
|
+
echo ""
|
|
616
|
+
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
617
|
+
echo -e "${GREEN} Sync Complete!${NC}"
|
|
618
|
+
echo -e "${GREEN}════════════════════════════════════════${NC}"
|
|
619
|
+
echo ""
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
# Run main
|
|
623
|
+
main "$@"
|