codeforge-dev 1.9.0 → 1.10.0
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/.devcontainer/.env +3 -0
- package/.devcontainer/CHANGELOG.md +56 -0
- package/.devcontainer/CLAUDE.md +29 -8
- package/.devcontainer/README.md +61 -2
- package/.devcontainer/config/defaults/main-system-prompt.md +162 -128
- package/.devcontainer/config/defaults/rules/spec-workflow.md +10 -2
- package/.devcontainer/connect-external-terminal.sh +17 -17
- package/.devcontainer/devcontainer.json +143 -144
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/architect.md +4 -3
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/doc-writer.md +3 -3
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/spec-writer.md +21 -11
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/hooks/hooks.json +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/advisory-test-runner.py +186 -13
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/spec-reminder.py +2 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/SKILL.md +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-check/SKILL.md +22 -10
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/SKILL.md +7 -5
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/backlog-template.md +19 -3
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/roadmap-template.md +28 -8
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-new/SKILL.md +15 -6
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-new/references/template.md +24 -5
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-refine/SKILL.md +194 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-update/SKILL.md +19 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/SKILL.md +19 -12
- package/.devcontainer/scripts/check-setup.sh +24 -25
- package/.devcontainer/scripts/setup-aliases.sh +88 -76
- package/.devcontainer/scripts/setup-projects.sh +172 -131
- package/.devcontainer/scripts/setup-terminal.sh +48 -0
- package/.devcontainer/scripts/setup-update-claude.sh +49 -107
- package/.devcontainer/scripts/setup.sh +4 -17
- package/README.md +2 -2
- package/package.json +1 -1
|
@@ -17,146 +17,187 @@ EXCLUDED_DIRS=".claude .gh .tmp .devcontainer .config node_modules .git"
|
|
|
17
17
|
# --- Helpers ---
|
|
18
18
|
|
|
19
19
|
is_excluded() {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
local name="$1"
|
|
21
|
+
# Skip hidden directories (start with .)
|
|
22
|
+
[[ "$name" == .* ]] && return 0
|
|
23
|
+
# Skip explicitly excluded names
|
|
24
|
+
for excluded in $EXCLUDED_DIRS; do
|
|
25
|
+
[[ "$name" == "$excluded" ]] && return 0
|
|
26
|
+
done
|
|
27
|
+
return 1
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
has_project_markers() {
|
|
31
|
+
local dir="$1"
|
|
32
|
+
[ -d "$dir/.git" ] || [ -f "$dir/package.json" ] || [ -f "$dir/pyproject.toml" ] ||
|
|
33
|
+
[ -f "$dir/Cargo.toml" ] || [ -f "$dir/go.mod" ] || [ -f "$dir/deno.json" ] ||
|
|
34
|
+
[ -f "$dir/Makefile" ] || [ -f "$dir/CLAUDE.md" ]
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
detect_tags() {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
38
|
+
local dir="$1"
|
|
39
|
+
local tags=()
|
|
40
|
+
|
|
41
|
+
[ -d "$dir/.git" ] && tags+=("git")
|
|
42
|
+
[ -f "$dir/package.json" ] && tags+=("node")
|
|
43
|
+
[ -f "$dir/pyproject.toml" ] && tags+=("python")
|
|
44
|
+
[ -f "$dir/Cargo.toml" ] && tags+=("rust")
|
|
45
|
+
[ -f "$dir/go.mod" ] && tags+=("go")
|
|
46
|
+
[ -f "$dir/deno.json" ] && tags+=("deno")
|
|
47
|
+
[ -f "$dir/Makefile" ] && tags+=("make")
|
|
48
|
+
[ -f "$dir/CLAUDE.md" ] && tags+=("claude")
|
|
49
|
+
|
|
50
|
+
# Always add "auto" tag to mark as script-managed
|
|
51
|
+
tags+=("auto")
|
|
52
|
+
|
|
53
|
+
# If no markers found (only "auto"), add "folder" tag
|
|
54
|
+
if [ ${#tags[@]} -eq 1 ]; then
|
|
55
|
+
tags=("folder" "${tags[@]}")
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Output as JSON array
|
|
59
|
+
printf '%s\n' "${tags[@]}" | jq -R . | jq -s .
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
register_project() {
|
|
63
|
+
local projects_json="$1"
|
|
64
|
+
local name="$2"
|
|
65
|
+
local dir="$3"
|
|
66
|
+
local tags
|
|
67
|
+
tags=$(detect_tags "$dir")
|
|
68
|
+
echo "$projects_json" | jq \
|
|
69
|
+
--arg name "$name" \
|
|
70
|
+
--arg path "$dir" \
|
|
71
|
+
--argjson tags "$tags" \
|
|
72
|
+
'. += [{"name": $name, "rootPath": ($path | rtrimstr("/")), "tags": $tags, "enabled": true}]'
|
|
53
73
|
}
|
|
54
74
|
|
|
55
75
|
scan_and_update() {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
76
|
+
# Build the new auto-detected projects array (two-pass: depth 1 + depth 2)
|
|
77
|
+
local new_projects="[]"
|
|
78
|
+
|
|
79
|
+
for dir in "$WORKSPACE_ROOT"/*/; do
|
|
80
|
+
[ -d "$dir" ] || continue
|
|
81
|
+
local name
|
|
82
|
+
name=$(basename "$dir")
|
|
83
|
+
|
|
84
|
+
is_excluded "$name" && continue
|
|
85
|
+
|
|
86
|
+
if has_project_markers "$dir"; then
|
|
87
|
+
# Depth 1: directory has project markers — register it directly
|
|
88
|
+
new_projects=$(register_project "$new_projects" "$name" "$dir")
|
|
89
|
+
else
|
|
90
|
+
# Depth 2: no markers — treat as container dir, scan its children
|
|
91
|
+
for subdir in "${dir%/}"/*/; do
|
|
92
|
+
[ -d "$subdir" ] || continue
|
|
93
|
+
local subname
|
|
94
|
+
subname=$(basename "$subdir")
|
|
95
|
+
is_excluded "$subname" && continue
|
|
96
|
+
new_projects=$(register_project "$new_projects" "$subname" "$subdir")
|
|
97
|
+
done
|
|
98
|
+
fi
|
|
99
|
+
done
|
|
100
|
+
|
|
101
|
+
# Read existing projects.json (or empty array if missing/invalid)
|
|
102
|
+
local existing="[]"
|
|
103
|
+
if [ -f "$PROJECTS_FILE" ] && jq empty "$PROJECTS_FILE" 2>/dev/null; then
|
|
104
|
+
existing=$(cat "$PROJECTS_FILE")
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
# Separate user entries (no "auto" tag) from auto entries
|
|
108
|
+
local user_entries
|
|
109
|
+
user_entries=$(echo "$existing" | jq '[.[] | select(.tags | index("auto") | not)]')
|
|
110
|
+
|
|
111
|
+
# Merge: user entries + new auto-detected entries
|
|
112
|
+
local merged
|
|
113
|
+
merged=$(jq -n --argjson user "$user_entries" --argjson auto "$new_projects" '$user + $auto')
|
|
114
|
+
|
|
115
|
+
# Only write if content changed (avoid unnecessary file writes that trigger Project Manager reloads)
|
|
116
|
+
local current_hash new_hash
|
|
117
|
+
current_hash=""
|
|
118
|
+
[ -f "$PROJECTS_FILE" ] && current_hash=$(md5sum "$PROJECTS_FILE" 2>/dev/null | cut -d' ' -f1)
|
|
119
|
+
|
|
120
|
+
local tmp_file
|
|
121
|
+
tmp_file=$(mktemp)
|
|
122
|
+
echo "$merged" | jq '.' >"$tmp_file"
|
|
123
|
+
new_hash=$(md5sum "$tmp_file" | cut -d' ' -f1)
|
|
124
|
+
|
|
125
|
+
if [ "$current_hash" != "$new_hash" ]; then
|
|
126
|
+
mv "$tmp_file" "$PROJECTS_FILE"
|
|
127
|
+
chmod 644 "$PROJECTS_FILE"
|
|
128
|
+
local count
|
|
129
|
+
count=$(echo "$merged" | jq 'length')
|
|
130
|
+
echo "$LOG_PREFIX Updated projects.json ($count projects)"
|
|
131
|
+
else
|
|
132
|
+
rm -f "$tmp_file"
|
|
133
|
+
fi
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
stop_watcher() {
|
|
137
|
+
if [ -f "$PID_FILE" ]; then
|
|
138
|
+
local old_pid
|
|
139
|
+
old_pid=$(cat "$PID_FILE" 2>/dev/null)
|
|
140
|
+
if [ -n "$old_pid" ]; then
|
|
141
|
+
# Kill the process group to stop both the subshell and inotifywait pipeline
|
|
142
|
+
kill -- -"$old_pid" 2>/dev/null || kill "$old_pid" 2>/dev/null || true
|
|
143
|
+
fi
|
|
144
|
+
rm -f "$PID_FILE"
|
|
145
|
+
fi
|
|
109
146
|
}
|
|
110
147
|
|
|
111
148
|
start_watcher() {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
149
|
+
# Guard: don't start if already running
|
|
150
|
+
if [ -f "$PID_FILE" ]; then
|
|
151
|
+
local old_pid
|
|
152
|
+
old_pid=$(cat "$PID_FILE" 2>/dev/null)
|
|
153
|
+
if [ -n "$old_pid" ] && kill -0 "$old_pid" 2>/dev/null; then
|
|
154
|
+
echo "$LOG_PREFIX Watcher already running (PID $old_pid), skipping"
|
|
155
|
+
return 0
|
|
156
|
+
fi
|
|
157
|
+
# Stale PID file — clean up
|
|
158
|
+
stop_watcher
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# Check if inotifywait is available
|
|
162
|
+
if ! command -v inotifywait &>/dev/null; then
|
|
163
|
+
echo "$LOG_PREFIX Installing inotify-tools..."
|
|
164
|
+
if command -v sudo &>/dev/null; then
|
|
165
|
+
sudo apt-get update -qq && sudo apt-get install -y -qq inotify-tools >/dev/null 2>&1
|
|
166
|
+
else
|
|
167
|
+
apt-get update -qq && apt-get install -y -qq inotify-tools >/dev/null 2>&1
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
if ! command -v inotifywait &>/dev/null; then
|
|
171
|
+
echo "$LOG_PREFIX WARNING: Could not install inotify-tools, watcher disabled"
|
|
172
|
+
return 1
|
|
173
|
+
fi
|
|
174
|
+
echo "$LOG_PREFIX inotify-tools installed"
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
# Fork background watcher in its own process group for clean shutdown
|
|
178
|
+
set -m
|
|
179
|
+
(
|
|
180
|
+
# Watch for directory create/delete/move events recursively under /workspaces/
|
|
181
|
+
# -r watches subdirectories (catches events inside container dirs like projects/)
|
|
182
|
+
# --exclude filters noisy dirs that generate frequent irrelevant events
|
|
183
|
+
inotifywait -m -r -q -e create,delete,moved_to,moved_from \
|
|
184
|
+
--exclude '(node_modules|\.git/|\.tmp|__pycache__|\.venv)' \
|
|
185
|
+
--format '%w%f %e' "$WORKSPACE_ROOT" 2>/dev/null |
|
|
186
|
+
while read -r _path event; do
|
|
187
|
+
# Small delay to let filesystem settle (e.g., move operations)
|
|
188
|
+
sleep 1
|
|
189
|
+
scan_and_update
|
|
190
|
+
done
|
|
191
|
+
|
|
192
|
+
# Cleanup on exit
|
|
193
|
+
rm -f "$PID_FILE"
|
|
194
|
+
) &
|
|
195
|
+
local watcher_pid=$!
|
|
196
|
+
set +m
|
|
197
|
+
echo "$watcher_pid" >"$PID_FILE"
|
|
198
|
+
disown
|
|
199
|
+
|
|
200
|
+
echo "$LOG_PREFIX Background watcher started (PID $watcher_pid)"
|
|
160
201
|
}
|
|
161
202
|
|
|
162
203
|
# --- Main ---
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Configure VS Code Shift+Enter → newline for Claude Code terminal input
|
|
3
|
+
# Writes to ~/.config/Code/User/keybindings.json (same path /terminal-setup uses)
|
|
4
|
+
|
|
5
|
+
echo "[setup-terminal] Configuring Shift+Enter keybinding for Claude Code..."
|
|
6
|
+
|
|
7
|
+
KEYBINDINGS_DIR="$HOME/.config/Code/User"
|
|
8
|
+
KEYBINDINGS_FILE="$KEYBINDINGS_DIR/keybindings.json"
|
|
9
|
+
|
|
10
|
+
# === Create directory if needed ===
|
|
11
|
+
mkdir -p "$KEYBINDINGS_DIR"
|
|
12
|
+
|
|
13
|
+
# === Check if already configured ===
|
|
14
|
+
if [ -f "$KEYBINDINGS_FILE" ] && grep -q "workbench.action.terminal.sendSequence" "$KEYBINDINGS_FILE" 2>/dev/null; then
|
|
15
|
+
echo "[setup-terminal] Shift+Enter binding already present, skipping"
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# === Merge or create keybindings ===
|
|
20
|
+
BINDING='{"key":"shift+enter","command":"workbench.action.terminal.sendSequence","args":{"text":"\\u001b\\r"},"when":"terminalFocus"}'
|
|
21
|
+
|
|
22
|
+
if [ -f "$KEYBINDINGS_FILE" ] && command -v jq &>/dev/null; then
|
|
23
|
+
# Merge into existing keybindings
|
|
24
|
+
if jq empty "$KEYBINDINGS_FILE" 2>/dev/null; then
|
|
25
|
+
jq ". + [$BINDING]" "$KEYBINDINGS_FILE" >"$KEYBINDINGS_FILE.tmp" &&
|
|
26
|
+
mv "$KEYBINDINGS_FILE.tmp" "$KEYBINDINGS_FILE"
|
|
27
|
+
echo "[setup-terminal] Merged binding into existing keybindings"
|
|
28
|
+
else
|
|
29
|
+
# Invalid JSON — overwrite
|
|
30
|
+
echo "[$BINDING]" | jq '.' >"$KEYBINDINGS_FILE"
|
|
31
|
+
echo "[setup-terminal] Replaced invalid keybindings file"
|
|
32
|
+
fi
|
|
33
|
+
else
|
|
34
|
+
# No existing file — write fresh
|
|
35
|
+
cat >"$KEYBINDINGS_FILE" <<'EOF'
|
|
36
|
+
[
|
|
37
|
+
{
|
|
38
|
+
"key": "shift+enter",
|
|
39
|
+
"command": "workbench.action.terminal.sendSequence",
|
|
40
|
+
"args": {
|
|
41
|
+
"text": "\u001b\r"
|
|
42
|
+
},
|
|
43
|
+
"when": "terminalFocus"
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
EOF
|
|
47
|
+
echo "[setup-terminal] Created keybindings file at $KEYBINDINGS_FILE"
|
|
48
|
+
fi
|
|
@@ -3,7 +3,15 @@
|
|
|
3
3
|
# Runs non-blocking in background by default via setup.sh
|
|
4
4
|
# All failures are warnings — this script never blocks container startup
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
# Log to file (simple append — process substitution breaks under disown)
|
|
7
|
+
LOG_FILE="${TMPDIR:-/tmp}/claude-update.log"
|
|
8
|
+
|
|
9
|
+
log() { echo "[update-claude] $(date '+%Y-%m-%d %H:%M:%S') $*" | tee -a "$LOG_FILE"; }
|
|
10
|
+
|
|
11
|
+
log "Checking for Claude Code updates..."
|
|
12
|
+
|
|
13
|
+
# === Clear nesting guard (postStartCommand may inherit from VS Code extension) ===
|
|
14
|
+
unset CLAUDECODE
|
|
7
15
|
|
|
8
16
|
# === TMPDIR ===
|
|
9
17
|
_TMPDIR="${TMPDIR:-/tmp}"
|
|
@@ -11,124 +19,58 @@ _TMPDIR="${TMPDIR:-/tmp}"
|
|
|
11
19
|
# === LOCK FILE (prevent concurrent updates) ===
|
|
12
20
|
LOCK_FILE="${_TMPDIR}/claude-update.lock"
|
|
13
21
|
if ! mkdir "$LOCK_FILE" 2>/dev/null; then
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
log "Another update is already running, skipping"
|
|
23
|
+
exit 0
|
|
16
24
|
fi
|
|
17
25
|
|
|
18
26
|
# === CLEANUP TRAP ===
|
|
19
27
|
cleanup() {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
rm -f "${_TMPDIR}/claude-update" 2>/dev/null || true
|
|
29
|
+
rm -f "${_TMPDIR}/claude-update-manifest.json" 2>/dev/null || true
|
|
30
|
+
rm -rf "$LOCK_FILE" 2>/dev/null || true
|
|
23
31
|
}
|
|
24
32
|
trap cleanup EXIT
|
|
25
33
|
|
|
26
34
|
# === VERIFY CLAUDE IS INSTALLED ===
|
|
27
35
|
if ! command -v claude &>/dev/null; then
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
log "Claude Code not found, skipping update"
|
|
37
|
+
exit 0
|
|
30
38
|
fi
|
|
31
39
|
|
|
32
|
-
# ===
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if ! command -v "$dep" &>/dev/null; then
|
|
42
|
-
echo "[update-claude] WARNING: $dep not available, skipping update"
|
|
43
|
-
exit 0
|
|
44
|
-
fi
|
|
45
|
-
done
|
|
46
|
-
|
|
47
|
-
# === GET CURRENT VERSION ===
|
|
48
|
-
CURRENT_VERSION=$(claude --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown")
|
|
49
|
-
echo "[update-claude] Current version: ${CURRENT_VERSION}"
|
|
50
|
-
|
|
51
|
-
# === FETCH LATEST VERSION ===
|
|
52
|
-
BASE_URL="https://storage.googleapis.com/claude-code-dist-86c565f3-f756-42ad-8dfa-d59b1c096819/claude-code-releases"
|
|
53
|
-
|
|
54
|
-
LATEST_VERSION=$(curl -fsSL "${BASE_URL}/stable" 2>/dev/null)
|
|
55
|
-
if [ -z "$LATEST_VERSION" ]; then
|
|
56
|
-
echo "[update-claude] WARNING: Failed to fetch latest version, skipping"
|
|
57
|
-
exit 0
|
|
40
|
+
# === ENSURE NATIVE BINARY EXISTS ===
|
|
41
|
+
# 'claude install' puts the binary at ~/.local/bin/claude (symlink to ~/.local/share/claude/versions/*)
|
|
42
|
+
# Legacy manual installs used /usr/local/bin/claude — check both, prefer ~/.local
|
|
43
|
+
if [ -x "$HOME/.local/bin/claude" ]; then
|
|
44
|
+
NATIVE_BIN="$HOME/.local/bin/claude"
|
|
45
|
+
elif [ -x "/usr/local/bin/claude" ]; then
|
|
46
|
+
NATIVE_BIN="/usr/local/bin/claude"
|
|
47
|
+
else
|
|
48
|
+
NATIVE_BIN=""
|
|
58
49
|
fi
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
50
|
+
if [ -z "$NATIVE_BIN" ]; then
|
|
51
|
+
log "Native binary not found, installing..."
|
|
52
|
+
if claude install 2>&1 | tee -a "$LOG_FILE"; then
|
|
53
|
+
log "Native binary installed successfully"
|
|
54
|
+
else
|
|
55
|
+
log "WARNING: 'claude install' failed, skipping"
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
58
|
+
# Skip update check on first install — next start will handle it
|
|
59
|
+
exit 0
|
|
66
60
|
fi
|
|
67
61
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
;;
|
|
83
|
-
esac
|
|
84
|
-
|
|
85
|
-
# Detect musl (Alpine Linux)
|
|
86
|
-
if ldd --version 2>&1 | grep -qi musl; then
|
|
87
|
-
PLATFORM="${PLATFORM}-musl"
|
|
62
|
+
# === CHECK FOR UPDATES ===
|
|
63
|
+
CURRENT_VERSION=$("$NATIVE_BIN" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown")
|
|
64
|
+
log "Current version: ${CURRENT_VERSION}"
|
|
65
|
+
|
|
66
|
+
# Use the official update command (handles download, verification, and versioned install)
|
|
67
|
+
if "$NATIVE_BIN" update 2>&1 | tee -a "$LOG_FILE"; then
|
|
68
|
+
UPDATED_VERSION=$("$NATIVE_BIN" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown")
|
|
69
|
+
if [ "$CURRENT_VERSION" != "$UPDATED_VERSION" ]; then
|
|
70
|
+
log "Updated Claude Code: ${CURRENT_VERSION} → ${UPDATED_VERSION}"
|
|
71
|
+
else
|
|
72
|
+
log "Already up to date (${CURRENT_VERSION})"
|
|
73
|
+
fi
|
|
74
|
+
else
|
|
75
|
+
log "WARNING: 'claude update' failed, skipping"
|
|
88
76
|
fi
|
|
89
|
-
|
|
90
|
-
echo "[update-claude] Platform: ${PLATFORM}"
|
|
91
|
-
|
|
92
|
-
# === DOWNLOAD MANIFEST ===
|
|
93
|
-
MANIFEST_URL="${BASE_URL}/${LATEST_VERSION}/manifest.json"
|
|
94
|
-
|
|
95
|
-
if ! curl -fsSL "${MANIFEST_URL}" -o ${_TMPDIR}/claude-update-manifest.json 2>/dev/null; then
|
|
96
|
-
echo "[update-claude] WARNING: Failed to download manifest, skipping"
|
|
97
|
-
exit 0
|
|
98
|
-
fi
|
|
99
|
-
|
|
100
|
-
# === EXTRACT AND VERIFY CHECKSUM ===
|
|
101
|
-
EXPECTED_CHECKSUM=$(jq -r ".platforms.\"${PLATFORM}\".checksum" ${_TMPDIR}/claude-update-manifest.json)
|
|
102
|
-
if [ -z "${EXPECTED_CHECKSUM}" ] || [ "${EXPECTED_CHECKSUM}" = "null" ]; then
|
|
103
|
-
echo "[update-claude] WARNING: Platform ${PLATFORM} not found in manifest, skipping"
|
|
104
|
-
exit 0
|
|
105
|
-
fi
|
|
106
|
-
|
|
107
|
-
# === DOWNLOAD BINARY ===
|
|
108
|
-
BINARY_URL="${BASE_URL}/${LATEST_VERSION}/${PLATFORM}/claude"
|
|
109
|
-
|
|
110
|
-
if ! curl -fsSL "${BINARY_URL}" -o ${_TMPDIR}/claude-update 2>/dev/null; then
|
|
111
|
-
echo "[update-claude] WARNING: Failed to download binary, skipping"
|
|
112
|
-
exit 0
|
|
113
|
-
fi
|
|
114
|
-
|
|
115
|
-
# === VERIFY CHECKSUM ===
|
|
116
|
-
ACTUAL_CHECKSUM=$(sha256sum ${_TMPDIR}/claude-update | cut -d' ' -f1)
|
|
117
|
-
|
|
118
|
-
if [ "${ACTUAL_CHECKSUM}" != "${EXPECTED_CHECKSUM}" ]; then
|
|
119
|
-
echo "[update-claude] WARNING: Checksum verification failed, skipping"
|
|
120
|
-
echo "[update-claude] Expected: ${EXPECTED_CHECKSUM}"
|
|
121
|
-
echo "[update-claude] Actual: ${ACTUAL_CHECKSUM}"
|
|
122
|
-
exit 0
|
|
123
|
-
fi
|
|
124
|
-
|
|
125
|
-
# === INSTALL (atomic replace) ===
|
|
126
|
-
chmod +x ${_TMPDIR}/claude-update
|
|
127
|
-
if ! sudo mv ${_TMPDIR}/claude-update /usr/local/bin/claude; then
|
|
128
|
-
echo "[update-claude] WARNING: Failed to install update (sudo mv failed), skipping"
|
|
129
|
-
exit 0
|
|
130
|
-
fi
|
|
131
|
-
|
|
132
|
-
# === VERIFY UPDATE ===
|
|
133
|
-
INSTALLED_VERSION=$(claude --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown")
|
|
134
|
-
echo "[update-claude] Updated Claude Code: ${CURRENT_VERSION} → ${INSTALLED_VERSION}"
|
|
@@ -21,8 +21,9 @@ fi
|
|
|
21
21
|
: "${SETUP_PLUGINS:=true}"
|
|
22
22
|
: "${SETUP_UPDATE_CLAUDE:=true}"
|
|
23
23
|
: "${SETUP_PROJECTS:=true}"
|
|
24
|
+
: "${SETUP_TERMINAL:=true}"
|
|
24
25
|
|
|
25
|
-
export CLAUDE_CONFIG_DIR CONFIG_SOURCE_DIR SETUP_CONFIG SETUP_ALIASES SETUP_AUTH SETUP_PLUGINS SETUP_UPDATE_CLAUDE SETUP_PROJECTS
|
|
26
|
+
export CLAUDE_CONFIG_DIR CONFIG_SOURCE_DIR SETUP_CONFIG SETUP_ALIASES SETUP_AUTH SETUP_PLUGINS SETUP_UPDATE_CLAUDE SETUP_PROJECTS SETUP_TERMINAL
|
|
26
27
|
|
|
27
28
|
SETUP_START=$(date +%s)
|
|
28
29
|
SETUP_RESULTS=()
|
|
@@ -64,22 +65,8 @@ run_script "$SCRIPT_DIR/setup-config.sh" "$SETUP_CONFIG"
|
|
|
64
65
|
run_script "$SCRIPT_DIR/setup-aliases.sh" "$SETUP_ALIASES"
|
|
65
66
|
run_script "$SCRIPT_DIR/setup-plugins.sh" "$SETUP_PLUGINS"
|
|
66
67
|
run_script "$SCRIPT_DIR/setup-projects.sh" "$SETUP_PROJECTS"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if [ "$SETUP_UPDATE_CLAUDE" = "true" ]; then
|
|
70
|
-
if [ -f "$SCRIPT_DIR/setup-update-claude.sh" ]; then
|
|
71
|
-
echo " Claude Code update checking in background..."
|
|
72
|
-
echo " (If 'claude' fails, wait a moment and retry)"
|
|
73
|
-
bash "$SCRIPT_DIR/setup-update-claude.sh" &
|
|
74
|
-
disown
|
|
75
|
-
SETUP_RESULTS+=("setup-update-claude:ok")
|
|
76
|
-
else
|
|
77
|
-
SETUP_RESULTS+=("setup-update-claude:missing")
|
|
78
|
-
fi
|
|
79
|
-
else
|
|
80
|
-
echo " setup-update-claude... skipped (disabled)"
|
|
81
|
-
SETUP_RESULTS+=("setup-update-claude:disabled")
|
|
82
|
-
fi
|
|
68
|
+
run_script "$SCRIPT_DIR/setup-terminal.sh" "$SETUP_TERMINAL"
|
|
69
|
+
run_script "$SCRIPT_DIR/setup-update-claude.sh" "$SETUP_UPDATE_CLAUDE"
|
|
83
70
|
|
|
84
71
|
echo ""
|
|
85
72
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
package/README.md
CHANGED
|
@@ -65,9 +65,9 @@ tree-sitter (JS/TS/Python), ast-grep, Pyright, TypeScript LSP
|
|
|
65
65
|
|
|
66
66
|
tmux, agent-browser, claude-monitor, ccusage, ccburn, ccstatusline, ast-grep, tree-sitter, lsp-servers, biome, notify-hook, mcp-qdrant, mcp-reasoner, splitrail
|
|
67
67
|
|
|
68
|
-
### Agents (17) & Skills (
|
|
68
|
+
### Agents (17) & Skills (17)
|
|
69
69
|
|
|
70
|
-
The `code-directive` plugin includes specialized agents (architect, explorer, test-writer, security-auditor, etc.) and domain-specific coding reference skills (fastapi, svelte5, docker, testing, etc.).
|
|
70
|
+
The `code-directive` plugin includes 17 specialized agents (architect, explorer, test-writer, security-auditor, etc.) and 17 domain-specific coding reference skills (fastapi, svelte5, docker, testing, spec-refine, etc.).
|
|
71
71
|
|
|
72
72
|
## Quick Start
|
|
73
73
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeforge-dev",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Complete development container that sets up Claude Code with modular devcontainer features, modern dev tools, and persistent configurations. Drop it into any project and get a production-ready AI development environment in minutes.",
|
|
5
5
|
"main": "setup.js",
|
|
6
6
|
"bin": {
|