codeforge-dev 1.14.1 → 2.0.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/config/defaults → .codeforge/config}/ccstatusline-settings.json +44 -6
- package/{.devcontainer/config/defaults → .codeforge/config}/main-system-prompt.md +14 -6
- package/.codeforge/config/orchestrator-system-prompt.md +333 -0
- package/{.devcontainer/config/defaults → .codeforge/config}/settings.json +3 -1
- package/{.devcontainer/config → .codeforge}/file-manifest.json +15 -9
- package/{.devcontainer → .codeforge/scripts}/connect-external-terminal.sh +3 -1
- package/.devcontainer/.env.example +5 -5
- package/.devcontainer/.secrets.example +3 -0
- package/.devcontainer/CHANGELOG.md +251 -3
- package/.devcontainer/CLAUDE.md +129 -22
- package/.devcontainer/README.md +34 -19
- package/.devcontainer/devcontainer.json +28 -10
- package/.devcontainer/features/agent-browser/install.sh +2 -0
- package/.devcontainer/features/ast-grep/install.sh +2 -0
- package/.devcontainer/features/biome/install.sh +2 -0
- package/.devcontainer/features/ccburn/devcontainer-feature.json +0 -5
- package/.devcontainer/features/ccburn/install.sh +2 -0
- package/.devcontainer/features/ccms/install.sh +2 -0
- package/.devcontainer/features/ccstatusline/README.md +7 -6
- package/.devcontainer/features/ccstatusline/install.sh +9 -4
- package/.devcontainer/features/ccusage/devcontainer-feature.json +0 -5
- package/.devcontainer/features/ccusage/install.sh +2 -0
- package/.devcontainer/features/chromaterm/chromaterm.yml +2 -2
- package/.devcontainer/features/chromaterm/install.sh +2 -0
- package/.devcontainer/features/claude-code-native/README.md +47 -0
- package/.devcontainer/features/claude-code-native/devcontainer-feature.json +29 -0
- package/.devcontainer/features/claude-code-native/install.sh +131 -0
- package/.devcontainer/features/claude-monitor/devcontainer-feature.json +0 -5
- package/.devcontainer/features/claude-monitor/install.sh +2 -0
- package/.devcontainer/features/claude-session-dashboard/README.md +2 -2
- package/.devcontainer/features/claude-session-dashboard/devcontainer-feature.json +1 -2
- package/.devcontainer/features/claude-session-dashboard/install.sh +2 -0
- package/.devcontainer/features/dprint/install.sh +2 -0
- package/.devcontainer/features/hadolint/install.sh +2 -0
- package/.devcontainer/features/kitty-terminfo/README.md +3 -1
- package/.devcontainer/features/kitty-terminfo/install.sh +2 -0
- package/.devcontainer/features/lsp-servers/install.sh +2 -0
- package/.devcontainer/features/mcp-qdrant/CHANGES.md +3 -3
- package/.devcontainer/features/mcp-qdrant/README.md +1 -0
- package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +1 -7
- package/.devcontainer/features/mcp-qdrant/install.sh +9 -2
- package/.devcontainer/features/mcp-qdrant/poststart-hook.sh +9 -2
- package/.devcontainer/features/notify-hook/devcontainer-feature.json +1 -1
- package/.devcontainer/features/notify-hook/install.sh +2 -0
- package/.devcontainer/features/ruff/install.sh +2 -0
- package/.devcontainer/features/shellcheck/install.sh +2 -0
- package/.devcontainer/features/shfmt/install.sh +2 -0
- package/.devcontainer/features/tmux/README.md +3 -3
- package/.devcontainer/features/tmux/install.sh +3 -1
- package/.devcontainer/features/tree-sitter/devcontainer-feature.json +0 -6
- package/.devcontainer/features/tree-sitter/install.sh +2 -0
- package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +27 -11
- package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/README.md +23 -4
- package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md +4 -4
- package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/documenter.md +254 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/implementer.md +260 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/investigator.md +255 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/tester.md +304 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/advisory-test-runner.py +4 -2
- package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py +2 -2
- package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/README.md +125 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/pr-review/SKILL.md +325 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/ship/SKILL.md +314 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/.claude-plugin/plugin.json +5 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/README.md +52 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/skills/ps/SKILL.md +37 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/session-context/README.md +30 -14
- package/.devcontainer/plugins/devs-marketplace/plugins/session-context/hooks/hooks.json +13 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/collect-session-edits.py +44 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py +89 -10
- package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/README.md +19 -11
- package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py +476 -282
- package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/SKILL.md +227 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/manual-worktree-commands.md +238 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/parallel-workflow-patterns.md +228 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/scripts/ticket-linker.py +2 -2
- package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py +3 -2
- package/.devcontainer/scripts/check-setup.sh +5 -3
- package/.devcontainer/scripts/preflight.sh +113 -0
- package/.devcontainer/scripts/setup-aliases.sh +13 -8
- package/.devcontainer/scripts/setup-auth.sh +46 -0
- package/.devcontainer/scripts/setup-config.sh +29 -10
- package/.devcontainer/scripts/setup-migrate-claude.sh +80 -0
- package/.devcontainer/scripts/setup-migrate-codeforge.sh +60 -0
- package/.devcontainer/scripts/setup-plugins.sh +3 -1
- package/.devcontainer/scripts/setup-projects.sh +3 -1
- package/.devcontainer/scripts/setup-terminal.sh +3 -1
- package/.devcontainer/scripts/setup-update-claude.sh +22 -27
- package/.devcontainer/scripts/setup.sh +57 -5
- package/LICENSE.txt +14 -0
- package/README.md +79 -5
- package/package.json +2 -1
- package/setup.js +392 -21
- package/.devcontainer/docs/configuration-reference.md +0 -93
- package/.devcontainer/docs/keybindings.md +0 -100
- package/.devcontainer/docs/optional-features.md +0 -64
- package/.devcontainer/docs/plugins.md +0 -176
- package/.devcontainer/docs/troubleshooting.md +0 -128
- package/.devcontainer/scripts/setup-symlink-claude.sh +0 -36
- /package/{.devcontainer/config/defaults → .codeforge/config}/keybindings.json +0 -0
- /package/{.devcontainer/config/defaults → .codeforge/config}/rules/session-search.md +0 -0
- /package/{.devcontainer/config/defaults → .codeforge/config}/rules/spec-workflow.md +0 -0
- /package/{.devcontainer/config/defaults → .codeforge/config}/rules/workspace-scope.md +0 -0
- /package/{.devcontainer/config/defaults → .codeforge/config}/writing-system-prompt.md +0 -0
- /package/{.devcontainer → .codeforge/scripts}/connect-external-terminal.ps1 +0 -0
|
@@ -27,7 +27,7 @@ These paths are always permitted regardless of working directory:
|
|
|
27
27
|
|
|
28
28
|
| Path | Reason |
|
|
29
29
|
|------|--------|
|
|
30
|
-
|
|
|
30
|
+
| `~/.claude/` | Claude config, plans, rules |
|
|
31
31
|
| `/tmp/` | System temp directory |
|
|
32
32
|
|
|
33
33
|
### CWD Context Injection
|
|
@@ -28,8 +28,9 @@ BLACKLISTED_PREFIXES = [
|
|
|
28
28
|
]
|
|
29
29
|
|
|
30
30
|
# Paths always allowed regardless of working directory
|
|
31
|
+
_home = os.environ.get("HOME", "/home/vscode")
|
|
31
32
|
ALLOWED_PREFIXES = [
|
|
32
|
-
"
|
|
33
|
+
f"{_home}/.claude/", # Claude config, plans, rules
|
|
33
34
|
"/tmp/", # System scratch
|
|
34
35
|
]
|
|
35
36
|
|
|
@@ -156,7 +157,7 @@ def extract_primary_command(command: str) -> str:
|
|
|
156
157
|
while i < len(tokens):
|
|
157
158
|
tok = tokens[i]
|
|
158
159
|
# Skip inline variable assignments: VAR=value
|
|
159
|
-
if "=" in tok and not tok.startswith("-") and tok.split("=")[0].isidentifier():
|
|
160
|
+
if "=" in tok and not tok.startswith("-") and tok.split("=", 1)[0].isidentifier():
|
|
160
161
|
i += 1
|
|
161
162
|
continue
|
|
162
163
|
# Skip sudo and its flags
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
3
|
+
# Copyright (c) 2026 Marcus Krueger
|
|
2
4
|
# Verify CodeForge setup is working correctly
|
|
3
5
|
# Run anytime with: check-setup
|
|
4
6
|
|
|
@@ -34,10 +36,10 @@ warn_check() {
|
|
|
34
36
|
echo ""
|
|
35
37
|
echo "Core:"
|
|
36
38
|
check "Claude Code installed" "command -v claude"
|
|
37
|
-
warn_check "Claude native binary" "[ -x ~/.local/bin/claude ]
|
|
39
|
+
warn_check "Claude native binary" "[ -x ~/.local/bin/claude ]"
|
|
38
40
|
check "cc alias configured" "grep -q 'alias cc=' ~/.bashrc 2>/dev/null || grep -q 'alias cc=' ~/.zshrc 2>/dev/null"
|
|
39
|
-
check "Config directory exists" "[ -d '${CLAUDE_CONFIG_DIR
|
|
40
|
-
check "Settings file exists" "[ -f '${CLAUDE_CONFIG_DIR
|
|
41
|
+
check "Config directory exists" "[ -d '${CLAUDE_CONFIG_DIR:-$HOME/.claude}' ]"
|
|
42
|
+
check "Settings file exists" "[ -f '${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json' ]"
|
|
41
43
|
|
|
42
44
|
echo ""
|
|
43
45
|
echo "Authentication:"
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
3
|
+
# Copyright (c) 2026 Marcus Krueger
|
|
4
|
+
# Pre-flight check: validates a container runtime is available on the host.
|
|
5
|
+
# Runs via initializeCommand BEFORE any container build/pull/start.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
# --- OS detection ---
|
|
10
|
+
|
|
11
|
+
detect_os() {
|
|
12
|
+
if [[ -f /proc/version ]] && grep -qi 'microsoft\|wsl' /proc/version 2>/dev/null; then
|
|
13
|
+
echo "wsl"
|
|
14
|
+
elif [[ "$(uname -s)" == "Darwin" ]]; then
|
|
15
|
+
echo "macos"
|
|
16
|
+
else
|
|
17
|
+
echo "linux"
|
|
18
|
+
fi
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# --- Timeout wrapper (macOS lacks coreutils timeout) ---
|
|
22
|
+
|
|
23
|
+
run_with_timeout() {
|
|
24
|
+
local seconds="$1"
|
|
25
|
+
shift
|
|
26
|
+
if command -v timeout &>/dev/null; then
|
|
27
|
+
timeout "$seconds" "$@" &>/dev/null 2>&1
|
|
28
|
+
else
|
|
29
|
+
# Fallback for macOS: background + kill
|
|
30
|
+
"$@" &>/dev/null 2>&1 &
|
|
31
|
+
local pid=$!
|
|
32
|
+
(sleep "$seconds" && kill "$pid" 2>/dev/null) &
|
|
33
|
+
local watchdog=$!
|
|
34
|
+
if wait "$pid" 2>/dev/null; then
|
|
35
|
+
kill "$watchdog" 2>/dev/null
|
|
36
|
+
wait "$watchdog" 2>/dev/null
|
|
37
|
+
return 0
|
|
38
|
+
else
|
|
39
|
+
kill "$watchdog" 2>/dev/null
|
|
40
|
+
wait "$watchdog" 2>/dev/null
|
|
41
|
+
return 1
|
|
42
|
+
fi
|
|
43
|
+
fi
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# --- Runtime detection ---
|
|
47
|
+
|
|
48
|
+
check_runtime() {
|
|
49
|
+
local runtime="$1"
|
|
50
|
+
if ! command -v "$runtime" &>/dev/null; then
|
|
51
|
+
return 1
|
|
52
|
+
fi
|
|
53
|
+
if run_with_timeout 5 "$runtime" info; then
|
|
54
|
+
return 0
|
|
55
|
+
fi
|
|
56
|
+
return 1
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# --- Main ---
|
|
60
|
+
|
|
61
|
+
for runtime in docker podman; do
|
|
62
|
+
if check_runtime "$runtime"; then
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
done
|
|
66
|
+
|
|
67
|
+
# No working runtime found — determine why and advise
|
|
68
|
+
|
|
69
|
+
found_binary=""
|
|
70
|
+
for runtime in docker podman; do
|
|
71
|
+
if command -v "$runtime" &>/dev/null; then
|
|
72
|
+
found_binary="$runtime"
|
|
73
|
+
break
|
|
74
|
+
fi
|
|
75
|
+
done
|
|
76
|
+
|
|
77
|
+
HOST_OS="$(detect_os)"
|
|
78
|
+
|
|
79
|
+
echo ""
|
|
80
|
+
echo "╔══════════════════════════════════════════════════════════════╗"
|
|
81
|
+
echo "║ CodeForge: Container runtime not available ║"
|
|
82
|
+
echo "╚══════════════════════════════════════════════════════════════╝"
|
|
83
|
+
echo ""
|
|
84
|
+
|
|
85
|
+
if [[ -n "$found_binary" ]]; then
|
|
86
|
+
echo " Found '$found_binary' but the daemon is not responding."
|
|
87
|
+
echo ""
|
|
88
|
+
case "$HOST_OS" in
|
|
89
|
+
wsl)
|
|
90
|
+
echo " Fix: Start Docker Desktop and enable WSL 2 integration:"
|
|
91
|
+
echo " Settings → Resources → WSL Integration"
|
|
92
|
+
;;
|
|
93
|
+
macos)
|
|
94
|
+
echo " Fix: Start Docker Desktop:"
|
|
95
|
+
echo " open -a Docker"
|
|
96
|
+
;;
|
|
97
|
+
linux)
|
|
98
|
+
echo " Fix: Start the Docker daemon:"
|
|
99
|
+
echo " sudo systemctl start docker"
|
|
100
|
+
;;
|
|
101
|
+
esac
|
|
102
|
+
else
|
|
103
|
+
echo " No container runtime (docker or podman) found in PATH."
|
|
104
|
+
echo ""
|
|
105
|
+
echo " Install Docker Desktop:"
|
|
106
|
+
echo " https://www.docker.com/products/docker-desktop/"
|
|
107
|
+
echo ""
|
|
108
|
+
echo " Or install Podman:"
|
|
109
|
+
echo " https://podman.io/getting-started/installation"
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
echo ""
|
|
113
|
+
exit 1
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
3
|
+
# Copyright (c) 2026 Marcus Krueger
|
|
2
4
|
# Setup cc/claude/ccraw aliases for claude with local system prompt support
|
|
3
5
|
#
|
|
4
6
|
# Idempotent: removes the entire managed block then re-writes it fresh.
|
|
@@ -74,14 +76,14 @@ export GH_CONFIG_DIR="${GH_CONFIG_DIR:-/workspaces/.gh}"
|
|
|
74
76
|
export LANG=en_US.UTF-8
|
|
75
77
|
export LC_ALL=en_US.UTF-8
|
|
76
78
|
|
|
77
|
-
#
|
|
78
|
-
if [ -
|
|
79
|
-
|
|
80
|
-
elif [ -x /usr/local/bin/claude ]; then
|
|
81
|
-
_CLAUDE_BIN=/usr/local/bin/claude
|
|
82
|
-
else
|
|
83
|
-
_CLAUDE_BIN=claude
|
|
79
|
+
# Terminal color defaults — Docker sets TERM=xterm (8 colors); upgrade to 256-color
|
|
80
|
+
if [ "\$TERM" = "xterm" ] || [ -z "\$TERM" ]; then
|
|
81
|
+
export TERM=xterm-256color
|
|
84
82
|
fi
|
|
83
|
+
export COLORTERM="\${COLORTERM:-truecolor}"
|
|
84
|
+
|
|
85
|
+
# Native binary (installed by claude-code-native feature)
|
|
86
|
+
_CLAUDE_BIN="\$HOME/.local/bin/claude"
|
|
85
87
|
|
|
86
88
|
# ChromaTerm wrapper (if ct is installed, wrap claude through it)
|
|
87
89
|
if command -v ct >/dev/null 2>&1; then
|
|
@@ -94,13 +96,14 @@ alias cc='CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 "\$_CLAUDE_WRAP" "\$_CL
|
|
|
94
96
|
alias claude='CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 "\$_CLAUDE_WRAP" "\$_CLAUDE_BIN" --system-prompt-file "\$CLAUDE_CONFIG_DIR/main-system-prompt.md" --permission-mode plan --allow-dangerously-skip-permissions'
|
|
95
97
|
alias ccraw='command "\$_CLAUDE_BIN"'
|
|
96
98
|
alias ccw='CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 "\$_CLAUDE_WRAP" "\$_CLAUDE_BIN" --system-prompt-file "\$CLAUDE_CONFIG_DIR/writing-system-prompt.md" --permission-mode plan --allow-dangerously-skip-permissions'
|
|
99
|
+
alias cc-orc='CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 "\$_CLAUDE_WRAP" "\$_CLAUDE_BIN" --system-prompt-file "\$CLAUDE_CONFIG_DIR/orchestrator-system-prompt.md" --permission-mode plan --allow-dangerously-skip-permissions'
|
|
97
100
|
|
|
98
101
|
cc-tools() {
|
|
99
102
|
echo "CodeForge Available Tools"
|
|
100
103
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
101
104
|
printf " %-20s %s\n" "COMMAND" "STATUS"
|
|
102
105
|
echo " ────────────────────────────────────"
|
|
103
|
-
for cmd in claude cc ccw ccraw ccusage ccburn claude-monitor \\
|
|
106
|
+
for cmd in claude cc ccw ccraw cc-orc ccusage ccburn claude-monitor \\
|
|
104
107
|
ccms ct cargo ruff biome dprint shfmt shellcheck hadolint \\
|
|
105
108
|
ast-grep tree-sitter pyright typescript-language-server \\
|
|
106
109
|
agent-browser gh docker git jq tmux bun go infocmp; do
|
|
@@ -114,6 +117,7 @@ cc-tools() {
|
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
alias check-setup='bash ${DEVCONTAINER_SCRIPTS}/check-setup.sh'
|
|
120
|
+
alias codeforge='node \${WORKSPACE_ROOT}/setup.js'
|
|
117
121
|
${BLOCK_END}
|
|
118
122
|
BLOCK_EOF
|
|
119
123
|
|
|
@@ -126,5 +130,6 @@ echo " cc -> claude with \$CLAUDE_CONFIG_DIR/main-system-prompt.md"
|
|
|
126
130
|
echo " claude -> claude with \$CLAUDE_CONFIG_DIR/main-system-prompt.md"
|
|
127
131
|
echo " ccraw -> vanilla claude without any config"
|
|
128
132
|
echo " ccw -> claude with \$CLAUDE_CONFIG_DIR/writing-system-prompt.md"
|
|
133
|
+
echo " cc-orc -> claude with \$CLAUDE_CONFIG_DIR/orchestrator-system-prompt.md (delegation mode)"
|
|
129
134
|
echo " cc-tools -> list all available CodeForge tools"
|
|
130
135
|
echo " check-setup -> verify CodeForge setup health"
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
3
|
+
# Copyright (c) 2026 Marcus Krueger
|
|
2
4
|
# Configure Git (GitHub CLI) and NPM authentication from .secrets file or environment variables.
|
|
3
5
|
# Environment variables override .secrets values, supporting Codespaces secrets and localEnv.
|
|
4
6
|
# Auth failure should not block other setup steps, so set -e is intentionally omitted.
|
|
@@ -65,6 +67,50 @@ else
|
|
|
65
67
|
echo "[setup-auth] NPM_TOKEN not set, skipping NPM auth"
|
|
66
68
|
fi
|
|
67
69
|
|
|
70
|
+
# --- Claude auth token (from 'claude setup-token') ---
|
|
71
|
+
# Long-lived tokens only — generated via: claude setup-token
|
|
72
|
+
# Note: After unset, the token remains visible in /proc/<pid>/environ for the
|
|
73
|
+
# lifetime of this process. This is a platform limitation of environment variables.
|
|
74
|
+
_USERNAME="${SUDO_USER:-${USER:-vscode}}"
|
|
75
|
+
_USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6)
|
|
76
|
+
_USER_HOME="${_USER_HOME:-/home/$_USERNAME}"
|
|
77
|
+
CLAUDE_CRED_DIR="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}"
|
|
78
|
+
CLAUDE_CRED_FILE="$CLAUDE_CRED_DIR/.credentials.json"
|
|
79
|
+
if [ -n "$CLAUDE_AUTH_TOKEN" ]; then
|
|
80
|
+
# Validate token format (claude setup-token produces sk-ant-* tokens)
|
|
81
|
+
if [[ ! "$CLAUDE_AUTH_TOKEN" =~ ^sk-ant- ]]; then
|
|
82
|
+
echo "[setup-auth] WARNING: CLAUDE_AUTH_TOKEN doesn't match expected format (sk-ant-*), skipping"
|
|
83
|
+
elif [ -f "$CLAUDE_CRED_FILE" ]; then
|
|
84
|
+
echo "[setup-auth] .credentials.json already exists, skipping token injection"
|
|
85
|
+
# Verify permissions haven't been tampered with
|
|
86
|
+
perms=$(stat -c %a "$CLAUDE_CRED_FILE" 2>/dev/null)
|
|
87
|
+
if [ -n "$perms" ] && [ "$perms" != "600" ]; then
|
|
88
|
+
echo "[setup-auth] WARNING: .credentials.json has permissions $perms (expected 600), fixing"
|
|
89
|
+
chmod 600 "$CLAUDE_CRED_FILE"
|
|
90
|
+
fi
|
|
91
|
+
AUTH_CONFIGURED=true
|
|
92
|
+
else
|
|
93
|
+
echo "[setup-auth] Creating .credentials.json from CLAUDE_AUTH_TOKEN..."
|
|
94
|
+
# Create directory with restrictive permissions (matches credential file at 600)
|
|
95
|
+
( umask 077; mkdir -p "$CLAUDE_CRED_DIR" )
|
|
96
|
+
# Escape JSON-special characters in token value (defense against malformed JSON
|
|
97
|
+
# if a token ever contains " or \ — unlikely with sk-ant-* but closes the gap)
|
|
98
|
+
ESCAPED_TOKEN=$(printf '%s' "$CLAUDE_AUTH_TOKEN" | sed 's/\\/\\\\/g; s/"/\\"/g')
|
|
99
|
+
# Write credentials with restrictive permissions from the start (no race window).
|
|
100
|
+
# Uses printf '%s' to avoid shell expansion of token value (defense against
|
|
101
|
+
# metacharacters in the token string — backticks, $(), quotes).
|
|
102
|
+
if ( umask 077; printf '{\n "claudeAiOauth": {\n "accessToken": "%s",\n "refreshToken": "%s",\n "expiresAt": 9999999999999,\n "scopes": ["user:inference", "user:profile"]\n }\n}\n' "$ESCAPED_TOKEN" "$ESCAPED_TOKEN" > "$CLAUDE_CRED_FILE" ); then
|
|
103
|
+
echo "[setup-auth] Claude auth token configured"
|
|
104
|
+
AUTH_CONFIGURED=true
|
|
105
|
+
else
|
|
106
|
+
echo "[setup-auth] WARNING: Failed to write .credentials.json — check permissions on $CLAUDE_CRED_DIR"
|
|
107
|
+
fi
|
|
108
|
+
fi
|
|
109
|
+
unset CLAUDE_AUTH_TOKEN
|
|
110
|
+
else
|
|
111
|
+
echo "[setup-auth] CLAUDE_AUTH_TOKEN not set, skipping Claude auth"
|
|
112
|
+
fi
|
|
113
|
+
|
|
68
114
|
# --- Summary ---
|
|
69
115
|
if [ "$AUTH_CONFIGURED" = true ]; then
|
|
70
116
|
echo "[setup-auth] Auth configuration complete"
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
3
|
+
# Copyright (c) 2026 Marcus Krueger
|
|
2
4
|
# Copy configuration files to workspace based on file-manifest.json
|
|
3
5
|
|
|
4
|
-
CONFIG_DIR="${CONFIG_SOURCE_DIR:?CONFIG_SOURCE_DIR not set}"
|
|
5
|
-
MANIFEST="$CONFIG_DIR/file-manifest.json"
|
|
6
|
-
|
|
7
6
|
log() { echo "[setup-config] $*"; }
|
|
8
7
|
warn() { echo "[setup-config] WARNING: $*"; }
|
|
9
8
|
err() { echo "[setup-config] ERROR: $*" >&2; }
|
|
10
9
|
|
|
10
|
+
CODEFORGE_DIR="${CODEFORGE_DIR:-${WORKSPACE_ROOT:?}/.codeforge}"
|
|
11
|
+
CONFIG_DIR="$CODEFORGE_DIR"
|
|
12
|
+
MANIFEST="$CODEFORGE_DIR/file-manifest.json"
|
|
13
|
+
|
|
14
|
+
# Legacy fallback: if .codeforge/ doesn't exist but old config dir does, warn and use old path
|
|
15
|
+
if [ ! -d "$CODEFORGE_DIR" ] && [ -d "${WORKSPACE_ROOT}/.devcontainer/config/defaults" ]; then
|
|
16
|
+
warn ".codeforge/ not found — falling back to .devcontainer/config/defaults (deprecated)"
|
|
17
|
+
CONFIG_DIR="${WORKSPACE_ROOT}/.devcontainer/config"
|
|
18
|
+
MANIFEST="$CONFIG_DIR/file-manifest.json"
|
|
19
|
+
fi
|
|
20
|
+
|
|
11
21
|
# Deprecation notice if legacy OVERWRITE_CONFIG is still set
|
|
12
22
|
if [ -n "${OVERWRITE_CONFIG+x}" ]; then
|
|
13
23
|
warn "OVERWRITE_CONFIG is deprecated. Use per-file 'overwrite' in config/file-manifest.json instead."
|
|
@@ -18,7 +28,7 @@ legacy_copy() {
|
|
|
18
28
|
local target_dir="${CLAUDE_CONFIG_DIR:?CLAUDE_CONFIG_DIR not set}"
|
|
19
29
|
warn "file-manifest.json not found, falling back to legacy copy"
|
|
20
30
|
mkdir -p "$target_dir"
|
|
21
|
-
for file in
|
|
31
|
+
for file in config/settings.json config/keybindings.json config/main-system-prompt.md; do
|
|
22
32
|
if [ -f "$CONFIG_DIR/$file" ]; then
|
|
23
33
|
local basename="${file##*/}"
|
|
24
34
|
cp "$CONFIG_DIR/$file" "$target_dir/$basename"
|
|
@@ -96,21 +106,30 @@ jq -r '.[] | [.src, .dest, (.destFilename // "__NONE__"), (.enabled // true | to
|
|
|
96
106
|
# Apply overwrite strategy
|
|
97
107
|
case "$overwrite" in
|
|
98
108
|
always)
|
|
99
|
-
cp "$src_path" "$dest_path"
|
|
100
|
-
|
|
109
|
+
if cp "$src_path" "$dest_path" 2>/dev/null; then
|
|
110
|
+
log "Copied $src → $dest_path (always)"
|
|
111
|
+
else
|
|
112
|
+
warn "Failed to copy $src → $dest_path (permission denied?)"
|
|
113
|
+
fi
|
|
101
114
|
;;
|
|
102
115
|
never)
|
|
103
116
|
if [ ! -f "$dest_path" ]; then
|
|
104
|
-
cp "$src_path" "$dest_path"
|
|
105
|
-
|
|
117
|
+
if cp "$src_path" "$dest_path" 2>/dev/null; then
|
|
118
|
+
log "Copied $src → $dest_path (new)"
|
|
119
|
+
else
|
|
120
|
+
warn "Failed to copy $src → $dest_path (permission denied?)"
|
|
121
|
+
fi
|
|
106
122
|
else
|
|
107
123
|
log "Skipping $src (exists, overwrite=never)"
|
|
108
124
|
fi
|
|
109
125
|
;;
|
|
110
126
|
if-changed | *)
|
|
111
127
|
if should_copy "$src_path" "$dest_path"; then
|
|
112
|
-
cp "$src_path" "$dest_path"
|
|
113
|
-
|
|
128
|
+
if cp "$src_path" "$dest_path" 2>/dev/null; then
|
|
129
|
+
log "Copied $src → $dest_path (changed)"
|
|
130
|
+
else
|
|
131
|
+
warn "Failed to copy $src → $dest_path (permission denied?)"
|
|
132
|
+
fi
|
|
114
133
|
else
|
|
115
134
|
log "Skipping $src (unchanged)"
|
|
116
135
|
fi
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
3
|
+
# Copyright (c) 2026 Marcus Krueger
|
|
4
|
+
# One-time migration: /workspaces/.claude → $HOME/.claude
|
|
5
|
+
# Migrates config, credentials, and rules from the old bind-mount location
|
|
6
|
+
# to the new home directory (Docker named volume).
|
|
7
|
+
#
|
|
8
|
+
# Uses cp -a (archive) for a faithful copy that preserves permissions,
|
|
9
|
+
# timestamps, symlinks, and directory structure. Migration is one-time
|
|
10
|
+
# (marker-gated), so overwrite is safe — the old directory is authoritative.
|
|
11
|
+
|
|
12
|
+
OLD_DIR="/workspaces/.claude"
|
|
13
|
+
_USERNAME="${SUDO_USER:-${USER:-vscode}}"
|
|
14
|
+
_USER_HOME=$(getent passwd "$_USERNAME" 2>/dev/null | cut -d: -f6)
|
|
15
|
+
_USER_HOME="${_USER_HOME:-/home/$_USERNAME}"
|
|
16
|
+
NEW_DIR="${CLAUDE_CONFIG_DIR:-${_USER_HOME}/.claude}"
|
|
17
|
+
MARKER="$NEW_DIR/.migrated-from-workspaces"
|
|
18
|
+
CODEFORGE_MARKER="${CODEFORGE_DIR:-${WORKSPACE_ROOT:-/workspaces}/.codeforge}/.markers/v2-migrated"
|
|
19
|
+
|
|
20
|
+
# Nothing to migrate if old directory doesn't exist
|
|
21
|
+
if [ ! -d "$OLD_DIR" ]; then
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Skip if old directory is empty (nothing worth migrating)
|
|
26
|
+
if [ -z "$(ls -A "$OLD_DIR" 2>/dev/null)" ]; then
|
|
27
|
+
exit 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Idempotency: skip if migration already completed (check both old and new markers)
|
|
31
|
+
if [ -f "$MARKER" ] || [ -f "$CODEFORGE_MARKER" ]; then
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Symlink protection: verify OLD_DIR itself is a real directory, not a symlink
|
|
36
|
+
if [ -L "$OLD_DIR" ]; then
|
|
37
|
+
echo "[setup-migrate] WARNING: /workspaces/.claude is a symlink, skipping migration for safety"
|
|
38
|
+
exit 0
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
echo "[setup-migrate] Migrating /workspaces/.claude → $NEW_DIR ..."
|
|
42
|
+
mkdir -p "$NEW_DIR"
|
|
43
|
+
|
|
44
|
+
# -a: archive mode (-dR --preserve=all) — preserves permissions, timestamps,
|
|
45
|
+
# symlinks, ownership, and directory structure faithfully.
|
|
46
|
+
# Errors logged explicitly (no 2>/dev/null) so failures are visible.
|
|
47
|
+
if cp -a "$OLD_DIR/." "$NEW_DIR/"; then
|
|
48
|
+
# Fix ownership — source files may be owned by a different uid from a
|
|
49
|
+
# previous container lifecycle. chown everything to the current user.
|
|
50
|
+
chown -R "$(id -un):$(id -gn)" "$NEW_DIR/" 2>/dev/null || true
|
|
51
|
+
|
|
52
|
+
# Verify critical files arrived
|
|
53
|
+
MISSING=""
|
|
54
|
+
[ ! -f "$NEW_DIR/.claude.json" ] && [ -f "$OLD_DIR/.claude.json" ] && MISSING="$MISSING .claude.json"
|
|
55
|
+
[ ! -d "$NEW_DIR/plugins" ] && [ -d "$OLD_DIR/plugins" ] && MISSING="$MISSING plugins/"
|
|
56
|
+
[ ! -f "$NEW_DIR/.credentials.json" ] && [ -f "$OLD_DIR/.credentials.json" ] && MISSING="$MISSING .credentials.json"
|
|
57
|
+
if [ -n "$MISSING" ]; then
|
|
58
|
+
echo "[setup-migrate] WARNING: Migration incomplete — missing:$MISSING"
|
|
59
|
+
echo "[setup-migrate] Old directory preserved at $OLD_DIR for manual recovery"
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Mark migration complete (write to both old and new marker locations)
|
|
64
|
+
date -Iseconds > "$MARKER"
|
|
65
|
+
_codeforge_markers_dir="$(dirname "$CODEFORGE_MARKER")"
|
|
66
|
+
if [ -d "$_codeforge_markers_dir" ] || mkdir -p "$_codeforge_markers_dir" 2>/dev/null; then
|
|
67
|
+
date -Iseconds > "$CODEFORGE_MARKER"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# Rename old directory to .bak
|
|
71
|
+
if mv "$OLD_DIR" "${OLD_DIR}.bak" 2>/dev/null; then
|
|
72
|
+
echo "[setup-migrate] Migration complete. Old directory moved to ${OLD_DIR}.bak"
|
|
73
|
+
else
|
|
74
|
+
echo "[setup-migrate] Migration complete. Could not rename old directory — remove /workspaces/.claude/ manually"
|
|
75
|
+
fi
|
|
76
|
+
else
|
|
77
|
+
echo "[setup-migrate] ERROR: cp failed — check output above for details"
|
|
78
|
+
echo "[setup-migrate] Old directory preserved at $OLD_DIR"
|
|
79
|
+
exit 1
|
|
80
|
+
fi
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
3
|
+
# Copyright (c) 2026 Marcus Krueger
|
|
4
|
+
# One-time migration: .devcontainer/config/ → .codeforge/
|
|
5
|
+
# Migrates config files, manifest, and terminal scripts from the legacy
|
|
6
|
+
# .devcontainer/config/ layout to the new .codeforge/ directory structure.
|
|
7
|
+
|
|
8
|
+
WORKSPACE_ROOT="${WORKSPACE_ROOT:?WORKSPACE_ROOT not set}"
|
|
9
|
+
CODEFORGE_DIR="${CODEFORGE_DIR:-${WORKSPACE_ROOT}/.codeforge}"
|
|
10
|
+
OLD_CONFIG_DIR="${WORKSPACE_ROOT}/.devcontainer/config"
|
|
11
|
+
OLD_DEFAULTS_DIR="${OLD_CONFIG_DIR}/defaults"
|
|
12
|
+
MARKER="$CODEFORGE_DIR/.markers/v2-migrated"
|
|
13
|
+
|
|
14
|
+
log() { echo "[setup-migrate-codeforge] $*"; }
|
|
15
|
+
warn() { echo "[setup-migrate-codeforge] WARNING: $*"; }
|
|
16
|
+
|
|
17
|
+
# Already migrated — skip
|
|
18
|
+
if [ -d "$CODEFORGE_DIR" ]; then
|
|
19
|
+
log ".codeforge/ already exists — skipping migration"
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# Nothing to migrate if old defaults dir doesn't exist
|
|
24
|
+
if [ ! -d "$OLD_DEFAULTS_DIR" ]; then
|
|
25
|
+
log "No legacy .devcontainer/config/defaults/ found — skipping migration"
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
log "Migrating .devcontainer/config/ → .codeforge/ ..."
|
|
30
|
+
|
|
31
|
+
# Create directory structure
|
|
32
|
+
mkdir -p "$CODEFORGE_DIR/config/rules" \
|
|
33
|
+
"$CODEFORGE_DIR/scripts" \
|
|
34
|
+
"$CODEFORGE_DIR/.markers" \
|
|
35
|
+
"$CODEFORGE_DIR/.checksums"
|
|
36
|
+
|
|
37
|
+
# Copy config files from .devcontainer/config/defaults/ → .codeforge/config/
|
|
38
|
+
if [ -d "$OLD_DEFAULTS_DIR" ]; then
|
|
39
|
+
cp -a "$OLD_DEFAULTS_DIR/." "$CODEFORGE_DIR/config/"
|
|
40
|
+
log "Copied config files from defaults/ → .codeforge/config/"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Copy file-manifest.json, rewriting src paths from defaults/ to config/
|
|
44
|
+
if [ -f "$OLD_CONFIG_DIR/file-manifest.json" ]; then
|
|
45
|
+
sed 's|"defaults/|"config/|g' "$OLD_CONFIG_DIR/file-manifest.json" > "$CODEFORGE_DIR/file-manifest.json"
|
|
46
|
+
log "Copied file-manifest.json (rewrote defaults/ → config/)"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Copy terminal scripts from .devcontainer/ → .codeforge/scripts/
|
|
50
|
+
for script in connect-external-terminal.sh connect-external-terminal.ps1; do
|
|
51
|
+
if [ -f "${WORKSPACE_ROOT}/.devcontainer/${script}" ]; then
|
|
52
|
+
cp "${WORKSPACE_ROOT}/.devcontainer/${script}" "$CODEFORGE_DIR/scripts/${script}"
|
|
53
|
+
log "Copied ${script} → .codeforge/scripts/"
|
|
54
|
+
fi
|
|
55
|
+
done
|
|
56
|
+
|
|
57
|
+
# Write migration marker
|
|
58
|
+
date -Iseconds > "$MARKER"
|
|
59
|
+
|
|
60
|
+
log "Migration complete — .codeforge/ is ready"
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
3
|
+
# Copyright (c) 2026 Marcus Krueger
|
|
2
4
|
# Install plugins: official Anthropic + local devs-marketplace registration
|
|
3
5
|
#
|
|
4
6
|
# Individual marketplace plugins are enabled via enabledPlugins in settings.json.
|
|
@@ -57,7 +59,7 @@ if [ -d "$MARKETPLACE_PATH/plugins" ]; then
|
|
|
57
59
|
plugin_name=$(basename "$plugin_dir")
|
|
58
60
|
|
|
59
61
|
# Skip blacklisted plugins
|
|
60
|
-
if echo ",$PLUGIN_BLACKLIST," | grep -q ",$plugin_name,"; then
|
|
62
|
+
if echo ",${PLUGIN_BLACKLIST}," | grep -q ",$plugin_name,"; then
|
|
61
63
|
echo "[setup-plugins] Skipping $plugin_name (blacklisted)"
|
|
62
64
|
continue
|
|
63
65
|
fi
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
3
|
+
# Copyright (c) 2026 Marcus Krueger
|
|
2
4
|
# Auto-detect projects under /workspaces/ and register them in Project Manager's projects.json.
|
|
3
5
|
# Runs an initial scan synchronously, then starts an inotifywait background watcher
|
|
4
6
|
# that updates the project list instantly when directories are created or removed.
|
|
@@ -176,7 +178,7 @@ start_watcher() {
|
|
|
176
178
|
fi
|
|
177
179
|
|
|
178
180
|
# Check if inotifywait is available (installed by tmux feature at build time)
|
|
179
|
-
if ! command -v inotifywait
|
|
181
|
+
if ! command -v inotifywait >/dev/null 2>&1; then
|
|
180
182
|
echo "$LOG_PREFIX WARNING: inotify-tools not installed, watcher disabled"
|
|
181
183
|
return 1
|
|
182
184
|
fi
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
3
|
+
# Copyright (c) 2026 Marcus Krueger
|
|
2
4
|
# Configure VS Code Shift+Enter → newline for Claude Code terminal input
|
|
3
5
|
# Writes to ~/.config/Code/User/keybindings.json (same path /terminal-setup uses)
|
|
4
6
|
|
|
@@ -19,7 +21,7 @@ fi
|
|
|
19
21
|
# === Merge or create keybindings ===
|
|
20
22
|
BINDING='{"key":"shift+enter","command":"workbench.action.terminal.sendSequence","args":{"text":"\\u001b\\r"},"when":"terminalFocus"}'
|
|
21
23
|
|
|
22
|
-
if [ -f "$KEYBINDINGS_FILE" ] && command -v jq
|
|
24
|
+
if [ -f "$KEYBINDINGS_FILE" ] && command -v jq >/dev/null 2>&1; then
|
|
23
25
|
# Merge into existing keybindings
|
|
24
26
|
if jq empty "$KEYBINDINGS_FILE" 2>/dev/null; then
|
|
25
27
|
jq ". + [$BINDING]" "$KEYBINDINGS_FILE" >"$KEYBINDINGS_FILE.tmp" &&
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-only
|
|
3
|
+
# Copyright (c) 2026 Marcus Krueger
|
|
2
4
|
# Update Claude Code CLI to the latest version (native binary only)
|
|
3
5
|
# Runs non-blocking in background by default via setup.sh
|
|
4
6
|
# All failures are warnings — this script never blocks container startup
|
|
@@ -25,46 +27,39 @@ fi
|
|
|
25
27
|
|
|
26
28
|
# === CLEANUP TRAP ===
|
|
27
29
|
cleanup() {
|
|
28
|
-
rm -f "${_TMPDIR}/claude-update" 2>/dev/null || true
|
|
29
|
-
rm -f "${_TMPDIR}/claude-update-manifest.json" 2>/dev/null || true
|
|
30
30
|
rm -rf "$LOCK_FILE" 2>/dev/null || true
|
|
31
31
|
}
|
|
32
32
|
trap cleanup EXIT
|
|
33
33
|
|
|
34
|
-
# ===
|
|
35
|
-
|
|
36
|
-
log "Claude Code not found, skipping update"
|
|
37
|
-
exit 0
|
|
38
|
-
fi
|
|
34
|
+
# === NATIVE BINARY ===
|
|
35
|
+
NATIVE_BIN="$HOME/.local/bin/claude"
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
elif [ -x "/usr/local/bin/claude" ]; then
|
|
46
|
-
NATIVE_BIN="/usr/local/bin/claude"
|
|
47
|
-
else
|
|
48
|
-
NATIVE_BIN=""
|
|
37
|
+
if [ ! -x "$NATIVE_BIN" ]; then
|
|
38
|
+
log "ERROR: Native binary not found at ${NATIVE_BIN}"
|
|
39
|
+
log " The claude-code-native feature should install this during container build."
|
|
40
|
+
log " Try rebuilding the container or running: curl -fsSL https://claude.ai/install.sh | sh"
|
|
41
|
+
exit 1
|
|
49
42
|
fi
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
|
|
44
|
+
# === TRANSITIONAL: Remove leftover npm installation ===
|
|
45
|
+
NPM_CLAUDE="$(npm config get prefix 2>/dev/null)/lib/node_modules/@anthropic-ai/claude-code"
|
|
46
|
+
if [ -d "$NPM_CLAUDE" ]; then
|
|
47
|
+
log "Removing leftover npm installation at ${NPM_CLAUDE}..."
|
|
48
|
+
if sudo npm uninstall -g @anthropic-ai/claude-code 2>/dev/null; then
|
|
49
|
+
log "Removed leftover npm installation"
|
|
54
50
|
else
|
|
55
|
-
log "WARNING:
|
|
56
|
-
exit 0
|
|
51
|
+
log "WARNING: Could not remove npm installation (non-blocking)"
|
|
57
52
|
fi
|
|
58
|
-
# Skip update check on first install — next start will handle it
|
|
59
|
-
exit 0
|
|
60
53
|
fi
|
|
61
54
|
|
|
62
55
|
# === CHECK FOR UPDATES ===
|
|
63
56
|
CURRENT_VERSION=$("$NATIVE_BIN" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown")
|
|
64
57
|
log "Current version: ${CURRENT_VERSION}"
|
|
65
58
|
|
|
66
|
-
# Use the official update command (handles download, verification, and versioned install)
|
|
67
|
-
|
|
59
|
+
# Use the official update command with timeout (handles download, verification, and versioned install)
|
|
60
|
+
timeout 60 "$NATIVE_BIN" update 2>&1 | tee -a "$LOG_FILE"
|
|
61
|
+
UPDATE_STATUS=${PIPESTATUS[0]}
|
|
62
|
+
if [ "$UPDATE_STATUS" -eq 0 ]; then
|
|
68
63
|
UPDATED_VERSION=$("$NATIVE_BIN" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "unknown")
|
|
69
64
|
if [ "$CURRENT_VERSION" != "$UPDATED_VERSION" ]; then
|
|
70
65
|
log "Updated Claude Code: ${CURRENT_VERSION} → ${UPDATED_VERSION}"
|
|
@@ -72,5 +67,5 @@ if "$NATIVE_BIN" update 2>&1 | tee -a "$LOG_FILE"; then
|
|
|
72
67
|
log "Already up to date (${CURRENT_VERSION})"
|
|
73
68
|
fi
|
|
74
69
|
else
|
|
75
|
-
log "WARNING: 'claude update' failed
|
|
70
|
+
log "WARNING: 'claude update' failed or timed out (exit ${UPDATE_STATUS})"
|
|
76
71
|
fi
|