codeforge-dev 1.14.2 → 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.
Files changed (106) hide show
  1. package/{.devcontainer/config/defaults → .codeforge/config}/ccstatusline-settings.json +44 -6
  2. package/{.devcontainer/config/defaults → .codeforge/config}/main-system-prompt.md +14 -6
  3. package/.codeforge/config/orchestrator-system-prompt.md +333 -0
  4. package/{.devcontainer/config/defaults → .codeforge/config}/settings.json +3 -1
  5. package/{.devcontainer/config → .codeforge}/file-manifest.json +15 -9
  6. package/{.devcontainer → .codeforge/scripts}/connect-external-terminal.sh +3 -1
  7. package/.devcontainer/.env.example +5 -5
  8. package/.devcontainer/.secrets.example +3 -0
  9. package/.devcontainer/CHANGELOG.md +242 -0
  10. package/.devcontainer/CLAUDE.md +129 -22
  11. package/.devcontainer/README.md +34 -19
  12. package/.devcontainer/devcontainer.json +28 -10
  13. package/.devcontainer/features/agent-browser/install.sh +2 -0
  14. package/.devcontainer/features/ast-grep/install.sh +2 -0
  15. package/.devcontainer/features/biome/install.sh +2 -0
  16. package/.devcontainer/features/ccburn/install.sh +2 -0
  17. package/.devcontainer/features/ccms/install.sh +2 -0
  18. package/.devcontainer/features/ccstatusline/README.md +7 -6
  19. package/.devcontainer/features/ccstatusline/install.sh +9 -4
  20. package/.devcontainer/features/ccusage/install.sh +2 -0
  21. package/.devcontainer/features/chromaterm/chromaterm.yml +2 -2
  22. package/.devcontainer/features/chromaterm/install.sh +2 -0
  23. package/.devcontainer/features/claude-code-native/README.md +47 -0
  24. package/.devcontainer/features/claude-code-native/devcontainer-feature.json +29 -0
  25. package/.devcontainer/features/claude-code-native/install.sh +131 -0
  26. package/.devcontainer/features/claude-monitor/install.sh +2 -0
  27. package/.devcontainer/features/claude-session-dashboard/README.md +2 -2
  28. package/.devcontainer/features/claude-session-dashboard/install.sh +2 -0
  29. package/.devcontainer/features/dprint/install.sh +2 -0
  30. package/.devcontainer/features/hadolint/install.sh +2 -0
  31. package/.devcontainer/features/kitty-terminfo/README.md +3 -1
  32. package/.devcontainer/features/kitty-terminfo/install.sh +2 -0
  33. package/.devcontainer/features/lsp-servers/install.sh +2 -0
  34. package/.devcontainer/features/mcp-qdrant/CHANGES.md +3 -3
  35. package/.devcontainer/features/mcp-qdrant/README.md +1 -0
  36. package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +1 -1
  37. package/.devcontainer/features/mcp-qdrant/install.sh +9 -2
  38. package/.devcontainer/features/mcp-qdrant/poststart-hook.sh +9 -2
  39. package/.devcontainer/features/notify-hook/devcontainer-feature.json +1 -1
  40. package/.devcontainer/features/notify-hook/install.sh +2 -0
  41. package/.devcontainer/features/ruff/install.sh +2 -0
  42. package/.devcontainer/features/shellcheck/install.sh +2 -0
  43. package/.devcontainer/features/shfmt/install.sh +2 -0
  44. package/.devcontainer/features/tmux/README.md +3 -3
  45. package/.devcontainer/features/tmux/install.sh +3 -1
  46. package/.devcontainer/features/tree-sitter/install.sh +2 -0
  47. package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +27 -11
  48. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/README.md +23 -4
  49. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md +4 -4
  50. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/documenter.md +254 -0
  51. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/implementer.md +260 -0
  52. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/investigator.md +255 -0
  53. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/tester.md +304 -0
  54. package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md +1 -1
  55. package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/advisory-test-runner.py +4 -2
  56. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py +2 -2
  57. package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/.claude-plugin/plugin.json +7 -0
  58. package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/README.md +125 -0
  59. package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/pr-review/SKILL.md +325 -0
  60. package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/ship/SKILL.md +314 -0
  61. package/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/.claude-plugin/plugin.json +5 -0
  62. package/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/README.md +52 -0
  63. package/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/skills/ps/SKILL.md +37 -0
  64. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py +1 -1
  65. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +1 -1
  66. package/.devcontainer/plugins/devs-marketplace/plugins/session-context/README.md +30 -14
  67. package/.devcontainer/plugins/devs-marketplace/plugins/session-context/hooks/hooks.json +13 -1
  68. package/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/collect-session-edits.py +44 -0
  69. package/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py +89 -10
  70. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json +1 -1
  71. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/README.md +19 -11
  72. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py +476 -282
  73. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/SKILL.md +227 -0
  74. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/manual-worktree-commands.md +238 -0
  75. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/parallel-workflow-patterns.md +228 -0
  76. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/scripts/ticket-linker.py +2 -2
  77. package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md +1 -1
  78. package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py +3 -2
  79. package/.devcontainer/scripts/check-setup.sh +5 -3
  80. package/.devcontainer/scripts/preflight.sh +113 -0
  81. package/.devcontainer/scripts/setup-aliases.sh +13 -8
  82. package/.devcontainer/scripts/setup-auth.sh +46 -0
  83. package/.devcontainer/scripts/setup-config.sh +29 -10
  84. package/.devcontainer/scripts/setup-migrate-claude.sh +80 -0
  85. package/.devcontainer/scripts/setup-migrate-codeforge.sh +60 -0
  86. package/.devcontainer/scripts/setup-plugins.sh +3 -1
  87. package/.devcontainer/scripts/setup-projects.sh +3 -1
  88. package/.devcontainer/scripts/setup-terminal.sh +3 -1
  89. package/.devcontainer/scripts/setup-update-claude.sh +22 -27
  90. package/.devcontainer/scripts/setup.sh +57 -5
  91. package/LICENSE.txt +14 -0
  92. package/README.md +79 -5
  93. package/package.json +2 -1
  94. package/setup.js +392 -21
  95. package/.devcontainer/docs/configuration-reference.md +0 -93
  96. package/.devcontainer/docs/keybindings.md +0 -100
  97. package/.devcontainer/docs/optional-features.md +0 -64
  98. package/.devcontainer/docs/plugins.md +0 -176
  99. package/.devcontainer/docs/troubleshooting.md +0 -128
  100. package/.devcontainer/scripts/setup-symlink-claude.sh +0 -36
  101. /package/{.devcontainer/config/defaults → .codeforge/config}/keybindings.json +0 -0
  102. /package/{.devcontainer/config/defaults → .codeforge/config}/rules/session-search.md +0 -0
  103. /package/{.devcontainer/config/defaults → .codeforge/config}/rules/spec-workflow.md +0 -0
  104. /package/{.devcontainer/config/defaults → .codeforge/config}/rules/workspace-scope.md +0 -0
  105. /package/{.devcontainer/config/defaults → .codeforge/config}/writing-system-prompt.md +0 -0
  106. /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
- | `/workspaces/.claude/` | Claude config, plans, rules |
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
- "/workspaces/.claude/", # Claude config, plans, rules
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 ] || [ -x /usr/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:-/workspaces/.claude}' ]"
40
- check "Settings file exists" "[ -f '${CLAUDE_CONFIG_DIR:-/workspaces/.claude}/settings.json' ]"
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
- # Prefer native binary over npm-installed version
78
- if [ -x "\$HOME/.local/bin/claude" ]; then
79
- _CLAUDE_BIN="\$HOME/.local/bin/claude"
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 defaults/settings.json defaults/keybindings.json defaults/main-system-prompt.md; do
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
- log "Copied $src → $dest_path (always)"
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
- log "Copied $src → $dest_path (new)"
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
- log "Copied $src → $dest_path (changed)"
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 &>/dev/null; then
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 &>/dev/null; then
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
- # === VERIFY CLAUDE IS INSTALLED ===
35
- if ! command -v claude &>/dev/null; then
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
- # === 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=""
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
- 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"
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: 'claude install' failed, skipping"
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
- if "$NATIVE_BIN" update 2>&1 | tee -a "$LOG_FILE"; then
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, skipping"
70
+ log "WARNING: 'claude update' failed or timed out (exit ${UPDATE_STATUS})"
76
71
  fi