codeforge-dev 1.5.8 → 1.8.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 (176) hide show
  1. package/.devcontainer/.env +4 -5
  2. package/.devcontainer/.env.example +29 -0
  3. package/.devcontainer/.gitignore +8 -0
  4. package/.devcontainer/.secrets.example +12 -0
  5. package/.devcontainer/CHANGELOG.md +186 -0
  6. package/.devcontainer/CLAUDE.md +108 -21
  7. package/.devcontainer/README.md +173 -57
  8. package/.devcontainer/config/defaults/keybindings.json +5 -0
  9. package/.devcontainer/config/{main-system-prompt.md → defaults/main-system-prompt.md} +135 -2
  10. package/.devcontainer/config/{settings.json → defaults/settings.json} +25 -6
  11. package/.devcontainer/config/file-manifest.json +20 -0
  12. package/.devcontainer/devcontainer.json +38 -2
  13. package/.devcontainer/docs/configuration-reference.md +90 -0
  14. package/.devcontainer/docs/keybindings.md +100 -0
  15. package/.devcontainer/docs/optional-features.md +129 -0
  16. package/.devcontainer/docs/plugins.md +154 -0
  17. package/.devcontainer/docs/troubleshooting.md +128 -0
  18. package/.devcontainer/features/README.md +21 -7
  19. package/.devcontainer/features/agent-browser/install.sh +6 -0
  20. package/.devcontainer/features/ast-grep/install.sh +6 -0
  21. package/.devcontainer/features/biome/README.md +27 -0
  22. package/.devcontainer/features/biome/install.sh +6 -0
  23. package/.devcontainer/features/ccburn/README.md +60 -0
  24. package/.devcontainer/features/ccburn/devcontainer-feature.json +38 -0
  25. package/.devcontainer/features/ccburn/install.sh +180 -0
  26. package/.devcontainer/features/ccstatusline/README.md +22 -21
  27. package/.devcontainer/features/ccstatusline/devcontainer-feature.json +6 -1
  28. package/.devcontainer/features/ccstatusline/install.sh +55 -16
  29. package/.devcontainer/features/ccusage/install.sh +6 -0
  30. package/.devcontainer/features/claude-monitor/install.sh +6 -0
  31. package/.devcontainer/features/dprint/README.md +30 -0
  32. package/.devcontainer/features/dprint/devcontainer-feature.json +18 -0
  33. package/.devcontainer/features/dprint/install.sh +131 -0
  34. package/.devcontainer/features/hadolint/README.md +35 -0
  35. package/.devcontainer/features/hadolint/devcontainer-feature.json +13 -0
  36. package/.devcontainer/features/hadolint/install.sh +86 -0
  37. package/.devcontainer/features/lsp-servers/devcontainer-feature.json +5 -0
  38. package/.devcontainer/features/lsp-servers/install.sh +7 -0
  39. package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +6 -1
  40. package/.devcontainer/features/mcp-qdrant/install.sh +13 -6
  41. package/.devcontainer/features/mcp-reasoner/devcontainer-feature.json +6 -1
  42. package/.devcontainer/features/mcp-reasoner/install.sh +8 -1
  43. package/.devcontainer/features/notify-hook/devcontainer-feature.json +5 -0
  44. package/.devcontainer/features/notify-hook/install.sh +7 -0
  45. package/.devcontainer/features/ruff/README.md +26 -0
  46. package/.devcontainer/features/ruff/devcontainer-feature.json +21 -0
  47. package/.devcontainer/features/ruff/install.sh +74 -0
  48. package/.devcontainer/features/shellcheck/README.md +38 -0
  49. package/.devcontainer/features/shellcheck/devcontainer-feature.json +13 -0
  50. package/.devcontainer/features/shellcheck/install.sh +24 -0
  51. package/.devcontainer/features/shfmt/README.md +37 -0
  52. package/.devcontainer/features/shfmt/devcontainer-feature.json +13 -0
  53. package/.devcontainer/features/shfmt/install.sh +85 -0
  54. package/.devcontainer/features/splitrail/devcontainer-feature.json +5 -0
  55. package/.devcontainer/features/splitrail/install.sh +7 -0
  56. package/.devcontainer/features/tmux/install.sh +8 -0
  57. package/.devcontainer/features/tree-sitter/install.sh +6 -0
  58. package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +3 -10
  59. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json +1 -1
  60. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/__pycache__/format-on-stop.cpython-314.pyc +0 -0
  61. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-on-stop.py +133 -13
  62. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json +1 -1
  63. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json +4 -5
  64. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/__pycache__/lint-file.cpython-314.pyc +0 -0
  65. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py +477 -78
  66. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/.claude-plugin/plugin.json +1 -1
  67. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/AGENT-REDIRECTION.md +226 -0
  68. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/REVIEW-RUBRIC.md +440 -0
  69. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/architect.md +207 -0
  70. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/bash-exec.md +173 -0
  71. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/claude-guide.md +146 -0
  72. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/debug-logs.md +2 -0
  73. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/dependency-analyst.md +250 -0
  74. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/doc-writer.md +246 -0
  75. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/explorer.md +237 -0
  76. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/generalist.md +134 -0
  77. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/git-archaeologist.md +242 -0
  78. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/migrator.md +201 -0
  79. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/perf-profiler.md +265 -0
  80. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/refactorer.md +213 -0
  81. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/researcher.md +195 -0
  82. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/security-auditor.md +289 -0
  83. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/spec-writer.md +297 -0
  84. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/statusline-config.md +188 -0
  85. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/test-writer.md +248 -0
  86. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/hooks/hooks.json +51 -0
  87. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/advisory-test-runner.cpython-314.pyc +0 -0
  88. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/collect-edited-files.cpython-314.pyc +0 -0
  89. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/commit-reminder.cpython-314.pyc +0 -0
  90. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/git-state-injector.cpython-314.pyc +0 -0
  91. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/guard-readonly-bash.cpython-314.pyc +0 -0
  92. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/redirect-builtin-agents.cpython-314.pyc +0 -0
  93. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/skill-suggester.cpython-314.pyc +0 -0
  94. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/syntax-validator.cpython-314.pyc +0 -0
  95. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/ticket-linker.cpython-314.pyc +0 -0
  96. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/todo-harvester.cpython-314.pyc +0 -0
  97. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/verify-no-regression.cpython-314.pyc +0 -0
  98. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/__pycache__/verify-tests-pass.cpython-314.pyc +0 -0
  99. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/advisory-test-runner.py +174 -0
  100. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/collect-edited-files.py +8 -6
  101. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/commit-reminder.py +90 -0
  102. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/git-state-injector.py +114 -0
  103. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/guard-readonly-bash.py +611 -0
  104. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/redirect-builtin-agents.py +83 -0
  105. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/skill-suggester.py +146 -2
  106. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/syntax-validator.py +9 -4
  107. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/ticket-linker.py +137 -0
  108. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/todo-harvester.py +130 -0
  109. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/verify-no-regression.py +221 -0
  110. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/verify-tests-pass.py +176 -0
  111. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/SKILL.md +224 -0
  112. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/references/error-handling.md +166 -0
  113. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/api-design/references/rest-conventions.md +215 -0
  114. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/ast-grep-patterns/SKILL.md +211 -0
  115. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/ast-grep-patterns/references/language-patterns.md +327 -0
  116. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/claude-agent-sdk/SKILL.md +599 -0
  117. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/claude-agent-sdk/references/sdk-typescript-reference.md +954 -0
  118. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/SKILL.md +134 -0
  119. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/references/ecosystem-commands.md +264 -0
  120. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/dependency-management/references/license-compliance.md +80 -0
  121. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/SKILL.md +153 -0
  122. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/references/api-doc-templates.md +221 -0
  123. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/references/docstring-formats.md +296 -0
  124. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/git-forensics/SKILL.md +276 -0
  125. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/git-forensics/references/advanced-commands.md +332 -0
  126. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/git-forensics/references/investigation-playbooks.md +319 -0
  127. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/SKILL.md +150 -0
  128. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/references/javascript-migrations.md +179 -0
  129. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/migration-patterns/references/python-migrations.md +141 -0
  130. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/performance-profiling/SKILL.md +341 -0
  131. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/performance-profiling/references/interpreting-results.md +235 -0
  132. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/performance-profiling/references/tool-commands.md +395 -0
  133. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/refactoring-patterns/SKILL.md +344 -0
  134. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/refactoring-patterns/references/safe-transformations.md +247 -0
  135. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/refactoring-patterns/references/smell-catalog.md +332 -0
  136. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/security-checklist/SKILL.md +277 -0
  137. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/security-checklist/references/owasp-patterns.md +269 -0
  138. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/security-checklist/references/secrets-patterns.md +253 -0
  139. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/SKILL.md +320 -0
  140. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/references/criteria-patterns.md +245 -0
  141. package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/references/ears-templates.md +239 -0
  142. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/__pycache__/block-dangerous.cpython-314.pyc +0 -0
  143. package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json +1 -1
  144. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/__pycache__/guard-protected.cpython-314.pyc +0 -0
  145. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +40 -39
  146. package/.devcontainer/scripts/check-setup.sh +72 -0
  147. package/.devcontainer/scripts/setup-aliases.sh +51 -6
  148. package/.devcontainer/scripts/setup-auth.sh +74 -0
  149. package/.devcontainer/scripts/setup-config.sh +112 -20
  150. package/.devcontainer/scripts/setup-plugins.sh +38 -46
  151. package/.devcontainer/scripts/setup-projects.sh +175 -0
  152. package/.devcontainer/scripts/setup-symlink-claude.sh +36 -0
  153. package/.devcontainer/scripts/setup-update-claude.sh +19 -8
  154. package/.devcontainer/scripts/setup.sh +49 -14
  155. package/README.md +23 -190
  156. package/package.json +1 -1
  157. package/setup.js +245 -71
  158. package/.devcontainer/features/claude-code/README.md +0 -498
  159. package/.devcontainer/features/claude-code/config/settings.json +0 -36
  160. package/.devcontainer/features/claude-code/config/system-prompt.md +0 -118
  161. package/.devcontainer/features/claude-code/config/world-building-sp.md +0 -1432
  162. package/.devcontainer/features/claude-code/devcontainer-feature.json +0 -42
  163. package/.devcontainer/features/claude-code/install.sh +0 -466
  164. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/.claude-plugin/plugin.json +0 -7
  165. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/hooks/hooks.json +0 -17
  166. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/.claude-plugin/plugin.json +0 -6
  167. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/config/planning-instructions.md +0 -14
  168. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/functional-conjuring-map.md +0 -989
  169. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/hooks/hooks.json +0 -33
  170. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/__pycache__/post-enhance-task.cpython-314.pyc +0 -0
  171. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhance-planning.py +0 -71
  172. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-plan.sh +0 -68
  173. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/enhancers/enhance-task.sh +0 -120
  174. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-plan.py +0 -133
  175. package/.devcontainer/plugins/devs-marketplace/plugins/workflow-enhancer/scripts/post-enhance-task.py +0 -253
  176. package/.devcontainer/scripts/setup-irie-claude.sh +0 -32
@@ -1,28 +1,120 @@
1
1
  #!/bin/bash
2
- # Copy Claude configuration files to workspace
2
+ # Copy configuration files to workspace based on file-manifest.json
3
3
 
4
4
  CONFIG_DIR="${CONFIG_SOURCE_DIR:?CONFIG_SOURCE_DIR not set}"
5
- TARGET_DIR="${CLAUDE_CONFIG_DIR:?CLAUDE_CONFIG_DIR not set}"
6
- OVERWRITE="${OVERWRITE_CONFIG:-false}"
7
-
8
- echo "[setup-config] Copying configuration files..."
9
- [ "$OVERWRITE" = "true" ] && echo "[setup-config] Overwrite mode enabled"
10
-
11
- mkdir -p "$TARGET_DIR"
12
-
13
- copy_file() {
14
- local file="$1"
15
- if [ -f "$CONFIG_DIR/$file" ]; then
16
- if [ "$OVERWRITE" = "true" ] || [ ! -f "$TARGET_DIR/$file" ]; then
17
- cp "$CONFIG_DIR/$file" "$TARGET_DIR/$file"
18
- echo "[setup-config] Copied $file"
19
- else
20
- echo "[setup-config] $file already exists, skipping"
5
+ MANIFEST="$CONFIG_DIR/file-manifest.json"
6
+
7
+ log() { echo "[setup-config] $*"; }
8
+ warn() { echo "[setup-config] WARNING: $*"; }
9
+ err() { echo "[setup-config] ERROR: $*" >&2; }
10
+
11
+ # Deprecation notice if legacy OVERWRITE_CONFIG is still set
12
+ if [ -n "${OVERWRITE_CONFIG+x}" ]; then
13
+ warn "OVERWRITE_CONFIG is deprecated. Use per-file 'overwrite' in config/file-manifest.json instead."
14
+ fi
15
+
16
+ # ── Legacy fallback ──────────────────────────────────────────────
17
+ legacy_copy() {
18
+ local target_dir="${CLAUDE_CONFIG_DIR:?CLAUDE_CONFIG_DIR not set}"
19
+ warn "file-manifest.json not found, falling back to legacy copy"
20
+ mkdir -p "$target_dir"
21
+ for file in defaults/settings.json defaults/keybindings.json defaults/main-system-prompt.md; do
22
+ if [ -f "$CONFIG_DIR/$file" ]; then
23
+ local basename="${file##*/}"
24
+ cp "$CONFIG_DIR/$file" "$target_dir/$basename"
25
+ chown "$(id -un):$(id -gn)" "$target_dir/$basename" 2>/dev/null || true
26
+ log "Copied $basename (legacy)"
21
27
  fi
28
+ done
29
+ log "Configuration complete (legacy)"
30
+ }
31
+
32
+ if [ ! -f "$MANIFEST" ]; then
33
+ legacy_copy
34
+ exit 0
35
+ fi
36
+
37
+ # ── Validate manifest JSON ──────────────────────────────────────
38
+ if ! jq empty "$MANIFEST" 2>/dev/null; then
39
+ err "Invalid JSON in file-manifest.json"
40
+ exit 1
41
+ fi
42
+
43
+ # ── Variable expansion ───────────────────────────────────────────
44
+ expand_vars() {
45
+ local val="$1"
46
+ val="${val//\$\{CLAUDE_CONFIG_DIR\}/$CLAUDE_CONFIG_DIR}"
47
+ val="${val//\$\{WORKSPACE_ROOT\}/$WORKSPACE_ROOT}"
48
+ # Warn on any remaining unresolved ${...} tokens
49
+ if [[ "$val" =~ \$\{[^}]+\} ]]; then
50
+ warn "Unresolved variable in: $val"
22
51
  fi
52
+ echo "$val"
53
+ }
54
+
55
+ # ── Change detection ─────────────────────────────────────────────
56
+ should_copy() {
57
+ local src="$1" dest="$2"
58
+ [ ! -f "$dest" ] && return 0
59
+ local src_hash dest_hash
60
+ src_hash=$(sha256sum "$src" | cut -d' ' -f1)
61
+ dest_hash=$(sha256sum "$dest" | cut -d' ' -f1)
62
+ [ "$src_hash" != "$dest_hash" ]
23
63
  }
24
64
 
25
- copy_file "settings.json"
26
- copy_file "main-system-prompt.md"
65
+ # ── Process manifest ─────────────────────────────────────────────
66
+ log "Copying configuration files..."
67
+
68
+ # Single jq invocation to extract all fields (reduces N×5 subprocess calls to 1)
69
+ jq -r '.[] | [.src, .dest, (.destFilename // ""), (.enabled // true | tostring), (.overwrite // "if-changed")] | @tsv' "$MANIFEST" |
70
+ while IFS=$'\t' read -r src dest dest_filename enabled overwrite; do
71
+ # Skip disabled entries
72
+ if [ "$enabled" = "false" ]; then
73
+ log "Skipping $src (disabled)"
74
+ continue
75
+ fi
76
+
77
+ # Resolve paths
78
+ src_path="$CONFIG_DIR/$src"
79
+ dest_dir=$(expand_vars "$dest")
80
+ filename="${dest_filename:-${src##*/}}"
81
+ dest_path="$dest_dir/$filename"
82
+
83
+ # Validate source exists
84
+ if [ ! -f "$src_path" ]; then
85
+ warn "$src not found in config dir, skipping"
86
+ continue
87
+ fi
88
+
89
+ # Ensure destination directory exists
90
+ mkdir -p "$dest_dir"
91
+
92
+ # Apply overwrite strategy
93
+ case "$overwrite" in
94
+ always)
95
+ cp "$src_path" "$dest_path"
96
+ log "Copied $src → $dest_path (always)"
97
+ ;;
98
+ never)
99
+ if [ ! -f "$dest_path" ]; then
100
+ cp "$src_path" "$dest_path"
101
+ log "Copied $src → $dest_path (new)"
102
+ else
103
+ log "Skipping $src (exists, overwrite=never)"
104
+ fi
105
+ ;;
106
+ if-changed|*)
107
+ if should_copy "$src_path" "$dest_path"; then
108
+ cp "$src_path" "$dest_path"
109
+ log "Copied $src → $dest_path (changed)"
110
+ else
111
+ log "Skipping $src (unchanged)"
112
+ fi
113
+ ;;
114
+ esac
115
+
116
+ # Fix ownership
117
+ chown "$(id -un):$(id -gn)" "$dest_path" 2>/dev/null || true
118
+ done
27
119
 
28
- echo "[setup-config] Configuration complete"
120
+ log "Configuration complete"
@@ -1,5 +1,21 @@
1
1
  #!/bin/bash
2
- # Install plugins: official Anthropic + local devs-marketplace
2
+ # Install plugins: official Anthropic + local devs-marketplace registration
3
+ #
4
+ # Individual marketplace plugins are enabled via enabledPlugins in settings.json.
5
+ # This script only handles:
6
+ # 1. Official Anthropic plugin installs (not managed by enabledPlugins)
7
+ # 2. Local marketplace registration (so Claude Code can discover devs-marketplace)
8
+
9
+ # Source .env for PLUGIN_BLACKLIST if not already set (e.g., standalone invocation)
10
+ if [ -z "${PLUGIN_BLACKLIST+x}" ]; then
11
+ SCRIPT_DIR_INIT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
+ ENV_FILE="${SCRIPT_DIR_INIT}/../.env"
13
+ if [ -f "$ENV_FILE" ]; then
14
+ set -a
15
+ source "$ENV_FILE"
16
+ set +a
17
+ fi
18
+ fi
3
19
 
4
20
  echo "[setup-plugins] Installing plugins..."
5
21
 
@@ -18,33 +34,13 @@ for plugin in "${OFFICIAL_PLUGINS[@]}"; do
18
34
  fi
19
35
  done
20
36
 
21
- # --- Local Marketplace Plugins ---
37
+ # --- Local Marketplace Registration ---
22
38
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
23
39
  MARKETPLACE_PATH="${SCRIPT_DIR}/../plugins/devs-marketplace"
24
40
 
25
- # Load PLUGIN_BLACKLIST from .env if not already set in environment
26
- ENV_FILE="${SCRIPT_DIR}/../.env"
27
- if [ -z "${PLUGIN_BLACKLIST+x}" ] && [ -f "$ENV_FILE" ]; then
28
- PLUGIN_BLACKLIST=$(grep -E '^PLUGIN_BLACKLIST=' "$ENV_FILE" | cut -d'=' -f2- | tr -d '"')
29
- fi
30
-
31
- # Parse blacklist into array
32
- IFS=',' read -ra BLACKLIST <<< "${PLUGIN_BLACKLIST:-}"
33
-
34
- # Helper function to check if plugin is blacklisted
35
- is_blacklisted() {
36
- local plugin_name="$1"
37
- for blocked in "${BLACKLIST[@]}"; do
38
- # Trim whitespace and compare
39
- blocked="${blocked// /}"
40
- if [ "$plugin_name" = "$blocked" ]; then
41
- return 0
42
- fi
43
- done
44
- return 1
45
- }
46
-
47
41
  # Add local marketplace (if not already added)
42
+ # This is still needed so Claude Code can discover devs-marketplace plugins
43
+ # listed in enabledPlugins in settings.json
48
44
  if ! claude plugin marketplace list 2>/dev/null | grep -q "devs-marketplace"; then
49
45
  echo "[setup-plugins] Adding devs-marketplace..."
50
46
  claude plugin marketplace add "$MARKETPLACE_PATH" 2>/dev/null || {
@@ -52,29 +48,25 @@ if ! claude plugin marketplace list 2>/dev/null | grep -q "devs-marketplace"; th
52
48
  }
53
49
  fi
54
50
 
55
- # Install ALL plugins from marketplace.json (dynamic discovery)
56
- MARKETPLACE_JSON="$MARKETPLACE_PATH/.claude-plugin/marketplace.json"
57
- if [ -f "$MARKETPLACE_JSON" ]; then
58
- PLUGINS=$(jq -r '.plugins[].name' "$MARKETPLACE_JSON" 2>/dev/null)
51
+ # --- Install/Update devs-marketplace plugins (syncs cache from source) ---
52
+ # enabledPlugins in settings.json controls enabled/disabled state, but plugins
53
+ # must be explicitly installed to populate the cache. This loop ensures the
54
+ # cache stays in sync with the source directory on every container start.
55
+ if [ -d "$MARKETPLACE_PATH/plugins" ]; then
56
+ for plugin_dir in "$MARKETPLACE_PATH"/plugins/*/; do
57
+ plugin_name=$(basename "$plugin_dir")
59
58
 
60
- if [ -z "$PLUGINS" ]; then
61
- echo "[setup-plugins] WARNING: No plugins found in marketplace.json"
62
- else
63
- for plugin in $PLUGINS; do
64
- if is_blacklisted "$plugin"; then
65
- echo "[setup-plugins] Skipping $plugin (blacklisted)"
66
- continue
67
- fi
68
- echo "[setup-plugins] Installing $plugin from devs-marketplace..."
69
- if claude plugin install "${plugin}@devs-marketplace" 2>/dev/null; then
70
- echo "[setup-plugins] Installed: $plugin"
71
- else
72
- echo "[setup-plugins] WARNING: Failed to install $plugin (may already be installed)"
73
- fi
74
- done
75
- fi
76
- else
77
- echo "[setup-plugins] WARNING: marketplace.json not found at $MARKETPLACE_JSON"
59
+ # Skip blacklisted plugins
60
+ if echo ",$PLUGIN_BLACKLIST," | grep -q ",$plugin_name,"; then
61
+ echo "[setup-plugins] Skipping $plugin_name (blacklisted)"
62
+ continue
63
+ fi
64
+
65
+ echo "[setup-plugins] Installing $plugin_name@devs-marketplace..."
66
+ claude plugin install "$plugin_name@devs-marketplace" 2>/dev/null || {
67
+ echo "[setup-plugins] Warning: Failed to install $plugin_name"
68
+ }
69
+ done
78
70
  fi
79
71
 
80
72
  echo "[setup-plugins] Plugin installation complete"
@@ -0,0 +1,175 @@
1
+ #!/bin/bash
2
+ # Auto-detect projects under /workspaces/ and register them in Project Manager's projects.json.
3
+ # Runs an initial scan synchronously, then starts an inotifywait background watcher
4
+ # that updates the project list instantly when directories are created or removed.
5
+
6
+ set -euo pipefail
7
+
8
+ WORKSPACE_ROOT="${WORKSPACE_ROOT:-/workspaces}"
9
+ PROJECTS_DIR="${WORKSPACE_ROOT}/.config/project-manager"
10
+ PROJECTS_FILE="${PROJECTS_DIR}/projects.json"
11
+ PID_FILE="/tmp/project-watcher.pid"
12
+ LOG_PREFIX="[setup-projects]"
13
+
14
+ # Directories to exclude from project detection (hidden/config dirs)
15
+ EXCLUDED_DIRS=".claude .gh .tmp .devcontainer .config node_modules .git"
16
+
17
+ # --- Helpers ---
18
+
19
+ is_excluded() {
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
+ detect_tags() {
31
+ local dir="$1"
32
+ local tags=()
33
+
34
+ [ -d "$dir/.git" ] && tags+=("git")
35
+ [ -f "$dir/package.json" ] && tags+=("node")
36
+ [ -f "$dir/pyproject.toml" ] && tags+=("python")
37
+ [ -f "$dir/Cargo.toml" ] && tags+=("rust")
38
+ [ -f "$dir/go.mod" ] && tags+=("go")
39
+ [ -f "$dir/deno.json" ] && tags+=("deno")
40
+ [ -f "$dir/Makefile" ] && tags+=("make")
41
+ [ -f "$dir/CLAUDE.md" ] && tags+=("claude")
42
+
43
+ # Always add "auto" tag to mark as script-managed
44
+ tags+=("auto")
45
+
46
+ # If no markers found (only "auto"), add "folder" tag
47
+ if [ ${#tags[@]} -eq 1 ]; then
48
+ tags=("folder" "${tags[@]}")
49
+ fi
50
+
51
+ # Output as JSON array
52
+ printf '%s\n' "${tags[@]}" | jq -R . | jq -s .
53
+ }
54
+
55
+ scan_and_update() {
56
+ # Build the new auto-detected projects array
57
+ local new_projects="[]"
58
+
59
+ for dir in "$WORKSPACE_ROOT"/*/; do
60
+ [ -d "$dir" ] || continue
61
+ local name
62
+ name=$(basename "$dir")
63
+
64
+ is_excluded "$name" && continue
65
+
66
+ local tags
67
+ tags=$(detect_tags "$dir")
68
+
69
+ new_projects=$(echo "$new_projects" | jq \
70
+ --arg name "$name" \
71
+ --arg path "$dir" \
72
+ --argjson tags "$tags" \
73
+ '. += [{"name": $name, "rootPath": ($path | rtrimstr("/")), "tags": $tags, "enabled": true}]')
74
+ done
75
+
76
+ # Read existing projects.json (or empty array if missing/invalid)
77
+ local existing="[]"
78
+ if [ -f "$PROJECTS_FILE" ] && jq empty "$PROJECTS_FILE" 2>/dev/null; then
79
+ existing=$(cat "$PROJECTS_FILE")
80
+ fi
81
+
82
+ # Separate user entries (no "auto" tag) from auto entries
83
+ local user_entries
84
+ user_entries=$(echo "$existing" | jq '[.[] | select(.tags | index("auto") | not)]')
85
+
86
+ # Merge: user entries + new auto-detected entries
87
+ local merged
88
+ merged=$(jq -n --argjson user "$user_entries" --argjson auto "$new_projects" '$user + $auto')
89
+
90
+ # Only write if content changed (avoid unnecessary file writes that trigger Project Manager reloads)
91
+ local current_hash new_hash
92
+ current_hash=""
93
+ [ -f "$PROJECTS_FILE" ] && current_hash=$(md5sum "$PROJECTS_FILE" 2>/dev/null | cut -d' ' -f1)
94
+
95
+ local tmp_file
96
+ tmp_file=$(mktemp)
97
+ echo "$merged" | jq '.' > "$tmp_file"
98
+ new_hash=$(md5sum "$tmp_file" | cut -d' ' -f1)
99
+
100
+ if [ "$current_hash" != "$new_hash" ]; then
101
+ mv "$tmp_file" "$PROJECTS_FILE"
102
+ chmod 644 "$PROJECTS_FILE"
103
+ local count
104
+ count=$(echo "$merged" | jq 'length')
105
+ echo "$LOG_PREFIX Updated projects.json ($count projects)"
106
+ else
107
+ rm -f "$tmp_file"
108
+ fi
109
+ }
110
+
111
+ start_watcher() {
112
+ # Guard: don't start if already running
113
+ if [ -f "$PID_FILE" ]; then
114
+ local old_pid
115
+ old_pid=$(cat "$PID_FILE" 2>/dev/null)
116
+ if [ -n "$old_pid" ] && kill -0 "$old_pid" 2>/dev/null; then
117
+ echo "$LOG_PREFIX Watcher already running (PID $old_pid), skipping"
118
+ return 0
119
+ fi
120
+ # Stale PID file — clean up
121
+ rm -f "$PID_FILE"
122
+ fi
123
+
124
+ # Check if inotifywait is available
125
+ if ! command -v inotifywait &>/dev/null; then
126
+ echo "$LOG_PREFIX Installing inotify-tools..."
127
+ if command -v sudo &>/dev/null; then
128
+ sudo apt-get update -qq && sudo apt-get install -y -qq inotify-tools >/dev/null 2>&1
129
+ else
130
+ apt-get update -qq && apt-get install -y -qq inotify-tools >/dev/null 2>&1
131
+ fi
132
+
133
+ if ! command -v inotifywait &>/dev/null; then
134
+ echo "$LOG_PREFIX WARNING: Could not install inotify-tools, watcher disabled"
135
+ return 1
136
+ fi
137
+ echo "$LOG_PREFIX inotify-tools installed"
138
+ fi
139
+
140
+ # Fork background watcher
141
+ (
142
+ # Watch for directory create/delete/move events in /workspaces/
143
+ # --quiet suppresses header, --monitor keeps watching indefinitely
144
+ inotifywait -m -q -e create,delete,moved_to,moved_from \
145
+ --format '%f %e' "$WORKSPACE_ROOT" 2>/dev/null |
146
+ while read -r name event; do
147
+ # Small delay to let filesystem settle (e.g., move operations)
148
+ sleep 1
149
+ scan_and_update
150
+ done
151
+
152
+ # Cleanup on exit
153
+ rm -f "$PID_FILE"
154
+ ) &
155
+ local watcher_pid=$!
156
+ echo "$watcher_pid" > "$PID_FILE"
157
+ disown
158
+
159
+ echo "$LOG_PREFIX Background watcher started (PID $watcher_pid)"
160
+ }
161
+
162
+ # --- Main ---
163
+
164
+ echo "$LOG_PREFIX Configuring Project Manager auto-detection..."
165
+
166
+ # Ensure projects directory exists
167
+ mkdir -p "$PROJECTS_DIR"
168
+
169
+ # Initial scan (synchronous — runs in <1 second)
170
+ scan_and_update
171
+
172
+ # Start background watcher
173
+ start_watcher
174
+
175
+ echo "$LOG_PREFIX Done"
@@ -0,0 +1,36 @@
1
+ #!/bin/bash
2
+ # Symlink $HOME/.claude → $CLAUDE_CONFIG_DIR so third-party tools
3
+ # (ccburn, ccusage, etc.) that hardcode ~/.claude can find auth and config.
4
+
5
+ CLAUDE_DIR="${CLAUDE_CONFIG_DIR:=/workspaces/.claude}"
6
+ HOME_CLAUDE="$HOME/.claude"
7
+
8
+ echo "[setup-symlink] Ensuring $HOME_CLAUDE → $CLAUDE_DIR ..."
9
+
10
+ # Already a correct symlink — nothing to do
11
+ if [ -L "$HOME_CLAUDE" ]; then
12
+ CURRENT_TARGET="$(readlink "$HOME_CLAUDE")"
13
+ if [ "$CURRENT_TARGET" = "$CLAUDE_DIR" ]; then
14
+ echo "[setup-symlink] Symlink already correct, skipping"
15
+ exit 0
16
+ fi
17
+ # Points somewhere else — remove stale symlink
18
+ echo "[setup-symlink] Removing stale symlink ($CURRENT_TARGET)"
19
+ rm "$HOME_CLAUDE"
20
+ fi
21
+
22
+ # Real directory exists — merge contents into target, then remove
23
+ if [ -d "$HOME_CLAUDE" ]; then
24
+ echo "[setup-symlink] Moving existing $HOME_CLAUDE contents into $CLAUDE_DIR"
25
+ mkdir -p "$CLAUDE_DIR"
26
+ # Copy contents preserving attributes; skip files that already exist in target
27
+ cp -rn "$HOME_CLAUDE/." "$CLAUDE_DIR/" 2>/dev/null || true
28
+ rm -rf "$HOME_CLAUDE"
29
+ fi
30
+
31
+ # Ensure target exists
32
+ mkdir -p "$CLAUDE_DIR"
33
+
34
+ # Create symlink
35
+ ln -s "$CLAUDE_DIR" "$HOME_CLAUDE"
36
+ echo "[setup-symlink] Created symlink: $HOME_CLAUDE → $CLAUDE_DIR"
@@ -5,10 +5,21 @@
5
5
 
6
6
  echo "[update-claude] Checking for Claude Code updates..."
7
7
 
8
+ # === TMPDIR ===
9
+ _TMPDIR="${TMPDIR:-/tmp}"
10
+
11
+ # === LOCK FILE (prevent concurrent updates) ===
12
+ LOCK_FILE="${_TMPDIR}/claude-update.lock"
13
+ if ! mkdir "$LOCK_FILE" 2>/dev/null; then
14
+ echo "[update-claude] Another update is already running, skipping"
15
+ exit 0
16
+ fi
17
+
8
18
  # === CLEANUP TRAP ===
9
19
  cleanup() {
10
- rm -f /tmp/claude-update 2>/dev/null || true
11
- rm -f /tmp/claude-update-manifest.json 2>/dev/null || true
20
+ rm -f "${_TMPDIR}/claude-update" 2>/dev/null || true
21
+ rm -f "${_TMPDIR}/claude-update-manifest.json" 2>/dev/null || true
22
+ rm -rf "$LOCK_FILE" 2>/dev/null || true
12
23
  }
13
24
  trap cleanup EXIT
14
25
 
@@ -81,13 +92,13 @@ echo "[update-claude] Platform: ${PLATFORM}"
81
92
  # === DOWNLOAD MANIFEST ===
82
93
  MANIFEST_URL="${BASE_URL}/${LATEST_VERSION}/manifest.json"
83
94
 
84
- if ! curl -fsSL "${MANIFEST_URL}" -o /tmp/claude-update-manifest.json 2>/dev/null; then
95
+ if ! curl -fsSL "${MANIFEST_URL}" -o ${_TMPDIR}/claude-update-manifest.json 2>/dev/null; then
85
96
  echo "[update-claude] WARNING: Failed to download manifest, skipping"
86
97
  exit 0
87
98
  fi
88
99
 
89
100
  # === EXTRACT AND VERIFY CHECKSUM ===
90
- EXPECTED_CHECKSUM=$(jq -r ".platforms.\"${PLATFORM}\".checksum" /tmp/claude-update-manifest.json)
101
+ EXPECTED_CHECKSUM=$(jq -r ".platforms.\"${PLATFORM}\".checksum" ${_TMPDIR}/claude-update-manifest.json)
91
102
  if [ -z "${EXPECTED_CHECKSUM}" ] || [ "${EXPECTED_CHECKSUM}" = "null" ]; then
92
103
  echo "[update-claude] WARNING: Platform ${PLATFORM} not found in manifest, skipping"
93
104
  exit 0
@@ -96,13 +107,13 @@ fi
96
107
  # === DOWNLOAD BINARY ===
97
108
  BINARY_URL="${BASE_URL}/${LATEST_VERSION}/${PLATFORM}/claude"
98
109
 
99
- if ! curl -fsSL "${BINARY_URL}" -o /tmp/claude-update 2>/dev/null; then
110
+ if ! curl -fsSL "${BINARY_URL}" -o ${_TMPDIR}/claude-update 2>/dev/null; then
100
111
  echo "[update-claude] WARNING: Failed to download binary, skipping"
101
112
  exit 0
102
113
  fi
103
114
 
104
115
  # === VERIFY CHECKSUM ===
105
- ACTUAL_CHECKSUM=$(sha256sum /tmp/claude-update | cut -d' ' -f1)
116
+ ACTUAL_CHECKSUM=$(sha256sum ${_TMPDIR}/claude-update | cut -d' ' -f1)
106
117
 
107
118
  if [ "${ACTUAL_CHECKSUM}" != "${EXPECTED_CHECKSUM}" ]; then
108
119
  echo "[update-claude] WARNING: Checksum verification failed, skipping"
@@ -112,8 +123,8 @@ if [ "${ACTUAL_CHECKSUM}" != "${EXPECTED_CHECKSUM}" ]; then
112
123
  fi
113
124
 
114
125
  # === INSTALL (atomic replace) ===
115
- chmod +x /tmp/claude-update
116
- if ! sudo mv /tmp/claude-update /usr/local/bin/claude; then
126
+ chmod +x ${_TMPDIR}/claude-update
127
+ if ! sudo mv ${_TMPDIR}/claude-update /usr/local/bin/claude; then
117
128
  echo "[update-claude] WARNING: Failed to install update (sudo mv failed), skipping"
118
129
  exit 0
119
130
  fi
@@ -1,8 +1,6 @@
1
1
  #!/bin/bash
2
2
  # Master setup script for CodeForge devcontainer
3
3
 
4
- set -e
5
-
6
4
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
5
  DEVCONTAINER_DIR="$(dirname "$SCRIPT_DIR")"
8
6
  ENV_FILE="$DEVCONTAINER_DIR/.env"
@@ -19,12 +17,15 @@ fi
19
17
  : "${CONFIG_SOURCE_DIR:=$DEVCONTAINER_DIR/config}"
20
18
  : "${SETUP_CONFIG:=true}"
21
19
  : "${SETUP_ALIASES:=true}"
22
- : "${OVERWRITE_CONFIG:=false}"
20
+ : "${SETUP_AUTH:=true}"
23
21
  : "${SETUP_PLUGINS:=true}"
24
22
  : "${SETUP_UPDATE_CLAUDE:=true}"
23
+ : "${SETUP_PROJECTS:=true}"
25
24
 
26
- export CLAUDE_CONFIG_DIR CONFIG_SOURCE_DIR SETUP_CONFIG SETUP_ALIASES OVERWRITE_CONFIG SETUP_PLUGINS SETUP_UPDATE_CLAUDE
25
+ export CLAUDE_CONFIG_DIR CONFIG_SOURCE_DIR SETUP_CONFIG SETUP_ALIASES SETUP_AUTH SETUP_PLUGINS SETUP_UPDATE_CLAUDE SETUP_PROJECTS
27
26
 
27
+ SETUP_START=$(date +%s)
28
+ SETUP_RESULTS=()
28
29
 
29
30
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
30
31
  echo " CodeForge Setup"
@@ -34,37 +35,71 @@ echo ""
34
35
  run_script() {
35
36
  local script="$1"
36
37
  local enabled="$2"
37
- local name="$(basename $script .sh)"
38
+ local name
39
+ name="$(basename "$script" .sh)"
38
40
 
39
41
  if [ "$enabled" = "true" ]; then
40
42
  if [ -f "$script" ]; then
41
- echo "Running $name..."
42
- bash "$script"
43
- echo ""
43
+ printf " %-30s" "$name..."
44
+ if bash "$script" 2>&1; then
45
+ echo "done"
46
+ SETUP_RESULTS+=("$name:ok")
47
+ else
48
+ echo "FAILED (exit $?)"
49
+ SETUP_RESULTS+=("$name:failed")
50
+ fi
44
51
  else
45
- echo "WARNING: $script not found, skipping"
52
+ echo " $name... not found, skipping"
53
+ SETUP_RESULTS+=("$name:missing")
46
54
  fi
47
55
  else
48
- echo "Skipping $name (disabled)"
56
+ echo " $name... skipped (disabled)"
57
+ SETUP_RESULTS+=("$name:disabled")
49
58
  fi
50
59
  }
51
60
 
61
+ run_script "$SCRIPT_DIR/setup-symlink-claude.sh" "true"
62
+ run_script "$SCRIPT_DIR/setup-auth.sh" "$SETUP_AUTH"
52
63
  run_script "$SCRIPT_DIR/setup-config.sh" "$SETUP_CONFIG"
53
64
  run_script "$SCRIPT_DIR/setup-aliases.sh" "$SETUP_ALIASES"
54
65
  run_script "$SCRIPT_DIR/setup-plugins.sh" "$SETUP_PLUGINS"
55
- run_script "$SCRIPT_DIR/setup-irie-claude.sh" "true"
66
+ run_script "$SCRIPT_DIR/setup-projects.sh" "$SETUP_PROJECTS"
56
67
 
57
68
  # Non-blocking: check for Claude Code updates in background
58
69
  if [ "$SETUP_UPDATE_CLAUDE" = "true" ]; then
59
70
  if [ -f "$SCRIPT_DIR/setup-update-claude.sh" ]; then
60
- echo "Running setup-update-claude (background)..."
71
+ echo " Claude Code update checking in background..."
72
+ echo " (If 'claude' fails, wait a moment and retry)"
61
73
  bash "$SCRIPT_DIR/setup-update-claude.sh" &
62
74
  disown
75
+ SETUP_RESULTS+=("setup-update-claude:ok")
76
+ else
77
+ SETUP_RESULTS+=("setup-update-claude:missing")
63
78
  fi
64
79
  else
65
- echo "Skipping setup-update-claude (disabled)"
80
+ echo " setup-update-claude... skipped (disabled)"
81
+ SETUP_RESULTS+=("setup-update-claude:disabled")
66
82
  fi
67
83
 
84
+ echo ""
68
85
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
69
- echo " Setup Complete"
86
+ echo " Setup Summary"
87
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
88
+ FAILURES=0
89
+ for result in "${SETUP_RESULTS[@]}"; do
90
+ name="${result%%:*}"
91
+ status="${result##*:}"
92
+ case "$status" in
93
+ ok) printf " ✓ %s\n" "$name" ;;
94
+ failed) printf " ✗ %s (FAILED)\n" "$name"; FAILURES=$((FAILURES + 1)) ;;
95
+ disabled) printf " - %s (disabled)\n" "$name" ;;
96
+ missing) printf " ? %s (not found)\n" "$name" ;;
97
+ esac
98
+ done
99
+ ELAPSED=$(( $(date +%s) - SETUP_START ))
100
+ echo ""
101
+ if [ $FAILURES -gt 0 ]; then
102
+ echo " $FAILURES step(s) failed. Check output above for details."
103
+ fi
104
+ echo " Completed in ${ELAPSED}s"
70
105
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"