@ornexus/neocortex 4.0.1
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.
Potentially problematic release.
This version of @ornexus/neocortex might be problematic. Click here for more details.
- package/LICENSE +56 -0
- package/README.md +32 -0
- package/install.js +486 -0
- package/install.ps1 +1790 -0
- package/install.sh +1587 -0
- package/package.json +104 -0
- package/packages/client/dist/adapters/adapter-registry.d.ts +61 -0
- package/packages/client/dist/adapters/adapter-registry.js +106 -0
- package/packages/client/dist/adapters/antigravity-adapter.d.ts +18 -0
- package/packages/client/dist/adapters/antigravity-adapter.js +77 -0
- package/packages/client/dist/adapters/claude-code-adapter.d.ts +19 -0
- package/packages/client/dist/adapters/claude-code-adapter.js +79 -0
- package/packages/client/dist/adapters/codex-adapter.d.ts +19 -0
- package/packages/client/dist/adapters/codex-adapter.js +80 -0
- package/packages/client/dist/adapters/cursor-adapter.d.ts +19 -0
- package/packages/client/dist/adapters/cursor-adapter.js +115 -0
- package/packages/client/dist/adapters/gemini-adapter.d.ts +18 -0
- package/packages/client/dist/adapters/gemini-adapter.js +71 -0
- package/packages/client/dist/adapters/index.d.ts +19 -0
- package/packages/client/dist/adapters/index.js +21 -0
- package/packages/client/dist/adapters/platform-detector.d.ts +46 -0
- package/packages/client/dist/adapters/platform-detector.js +106 -0
- package/packages/client/dist/adapters/target-adapter.d.ts +70 -0
- package/packages/client/dist/adapters/target-adapter.js +12 -0
- package/packages/client/dist/adapters/vscode-adapter.d.ts +19 -0
- package/packages/client/dist/adapters/vscode-adapter.js +72 -0
- package/packages/client/dist/agent/refresh-stubs.d.ts +65 -0
- package/packages/client/dist/agent/refresh-stubs.js +234 -0
- package/packages/client/dist/agent/update-agent-yaml.d.ts +26 -0
- package/packages/client/dist/agent/update-agent-yaml.js +102 -0
- package/packages/client/dist/agent/update-description.d.ts +45 -0
- package/packages/client/dist/agent/update-description.js +251 -0
- package/packages/client/dist/cache/crypto-utils.d.ts +30 -0
- package/packages/client/dist/cache/crypto-utils.js +76 -0
- package/packages/client/dist/cache/encrypted-cache.d.ts +30 -0
- package/packages/client/dist/cache/encrypted-cache.js +94 -0
- package/packages/client/dist/cache/in-memory-asset-cache.d.ts +59 -0
- package/packages/client/dist/cache/in-memory-asset-cache.js +70 -0
- package/packages/client/dist/cache/index.d.ts +13 -0
- package/packages/client/dist/cache/index.js +13 -0
- package/packages/client/dist/cli.d.ts +14 -0
- package/packages/client/dist/cli.js +194 -0
- package/packages/client/dist/commands/activate.d.ts +55 -0
- package/packages/client/dist/commands/activate.js +390 -0
- package/packages/client/dist/commands/cache-status.d.ts +39 -0
- package/packages/client/dist/commands/cache-status.js +112 -0
- package/packages/client/dist/commands/invoke.d.ts +70 -0
- package/packages/client/dist/commands/invoke.js +490 -0
- package/packages/client/dist/config/resolver-selection.d.ts +40 -0
- package/packages/client/dist/config/resolver-selection.js +278 -0
- package/packages/client/dist/config/secure-config.d.ts +78 -0
- package/packages/client/dist/config/secure-config.js +269 -0
- package/packages/client/dist/constants.d.ts +25 -0
- package/packages/client/dist/constants.js +25 -0
- package/packages/client/dist/context/context-collector.d.ts +28 -0
- package/packages/client/dist/context/context-collector.js +222 -0
- package/packages/client/dist/context/context-sanitizer.d.ts +28 -0
- package/packages/client/dist/context/context-sanitizer.js +145 -0
- package/packages/client/dist/index.d.ts +55 -0
- package/packages/client/dist/index.js +38 -0
- package/packages/client/dist/license/index.d.ts +5 -0
- package/packages/client/dist/license/index.js +5 -0
- package/packages/client/dist/license/license-client.d.ts +79 -0
- package/packages/client/dist/license/license-client.js +257 -0
- package/packages/client/dist/machine/fingerprint.d.ts +34 -0
- package/packages/client/dist/machine/fingerprint.js +160 -0
- package/packages/client/dist/machine/index.d.ts +5 -0
- package/packages/client/dist/machine/index.js +5 -0
- package/packages/client/dist/resilience/circuit-breaker.d.ts +70 -0
- package/packages/client/dist/resilience/circuit-breaker.js +170 -0
- package/packages/client/dist/resilience/degradation-manager.d.ts +67 -0
- package/packages/client/dist/resilience/degradation-manager.js +164 -0
- package/packages/client/dist/resilience/freshness-indicator.d.ts +59 -0
- package/packages/client/dist/resilience/freshness-indicator.js +100 -0
- package/packages/client/dist/resilience/index.d.ts +8 -0
- package/packages/client/dist/resilience/index.js +8 -0
- package/packages/client/dist/resilience/recovery-detector.d.ts +59 -0
- package/packages/client/dist/resilience/recovery-detector.js +74 -0
- package/packages/client/dist/resolvers/asset-resolver.d.ts +79 -0
- package/packages/client/dist/resolvers/asset-resolver.js +13 -0
- package/packages/client/dist/resolvers/local-resolver.d.ts +26 -0
- package/packages/client/dist/resolvers/local-resolver.js +218 -0
- package/packages/client/dist/resolvers/remote-resolver.d.ts +91 -0
- package/packages/client/dist/resolvers/remote-resolver.js +282 -0
- package/packages/client/dist/telemetry/index.d.ts +5 -0
- package/packages/client/dist/telemetry/index.js +5 -0
- package/packages/client/dist/telemetry/offline-queue.d.ts +57 -0
- package/packages/client/dist/telemetry/offline-queue.js +131 -0
- package/packages/client/dist/tier/index.d.ts +5 -0
- package/packages/client/dist/tier/index.js +5 -0
- package/packages/client/dist/tier/tier-aware-client.d.ts +97 -0
- package/packages/client/dist/tier/tier-aware-client.js +260 -0
- package/packages/client/dist/types/index.d.ts +140 -0
- package/packages/client/dist/types/index.js +38 -0
- package/postinstall.js +272 -0
- package/targets-stubs/antigravity/README.md +36 -0
- package/targets-stubs/antigravity/gemini.md +22 -0
- package/targets-stubs/antigravity/install-antigravity.sh +44 -0
- package/targets-stubs/antigravity/mcp-config.json +9 -0
- package/targets-stubs/antigravity/skill/SKILL.md +67 -0
- package/targets-stubs/claude-code/README.md +20 -0
- package/targets-stubs/claude-code/neocortex.agent.yaml +24 -0
- package/targets-stubs/claude-code/neocortex.md +125 -0
- package/targets-stubs/codex/README.md +32 -0
- package/targets-stubs/codex/agents.md +61 -0
- package/targets-stubs/codex/config-mcp.toml +6 -0
- package/targets-stubs/codex/install-codex.sh +61 -0
- package/targets-stubs/cursor/README.md +33 -0
- package/targets-stubs/cursor/agent.md +94 -0
- package/targets-stubs/cursor/install-cursor.sh +35 -0
- package/targets-stubs/cursor/mcp.json +11 -0
- package/targets-stubs/gemini-cli/README.md +34 -0
- package/targets-stubs/gemini-cli/agent.md +101 -0
- package/targets-stubs/gemini-cli/gemini.md +16 -0
- package/targets-stubs/gemini-cli/install-gemini.sh +56 -0
- package/targets-stubs/gemini-cli/settings-mcp.json +11 -0
- package/targets-stubs/vscode/README.md +34 -0
- package/targets-stubs/vscode/agent.md +102 -0
- package/targets-stubs/vscode/copilot-instructions.md +16 -0
- package/targets-stubs/vscode/install-vscode.sh +42 -0
- package/targets-stubs/vscode/mcp.json +13 -0
package/install.sh
ADDED
|
@@ -0,0 +1,1587 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Neocortex - Instalador Unix
|
|
4
|
+
# Development Orchestrator
|
|
5
|
+
|
|
6
|
+
# Versao do instalador
|
|
7
|
+
VERSION="4.0.1"
|
|
8
|
+
|
|
9
|
+
# Flags
|
|
10
|
+
MIGRATION_DETECTED=false
|
|
11
|
+
MIGRATION_SOURCES=""
|
|
12
|
+
CODERABBIT_INSTALLED=false
|
|
13
|
+
LEGACY_ITEMS=""
|
|
14
|
+
LEGACY_WARNINGS=0
|
|
15
|
+
SELECTED_TARGETS=""
|
|
16
|
+
VALID_TARGETS="claude-code cursor vscode gemini-cli codex antigravity"
|
|
17
|
+
# LOCAL_MODE removed (Epic 50): thin-client ALWAYS, zero IP on client
|
|
18
|
+
# SSoT: packages/client/src/constants.ts DEFAULT_SERVER_URL
|
|
19
|
+
NEOCORTEX_SERVER_URL="https://api.neocortex.sh"
|
|
20
|
+
|
|
21
|
+
# =============================================================================
|
|
22
|
+
# DETECCAO DE AMBIENTE
|
|
23
|
+
# =============================================================================
|
|
24
|
+
|
|
25
|
+
is_interactive() { [ -t 0 ] && [ -t 1 ]; }
|
|
26
|
+
|
|
27
|
+
supports_colors() { [ -t 1 ] && [ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]; }
|
|
28
|
+
|
|
29
|
+
supports_unicode() {
|
|
30
|
+
case "${LANG:-}${LC_ALL:-}${LC_CTYPE:-}" in
|
|
31
|
+
*UTF-8*|*utf-8*|*utf8*) return 0 ;;
|
|
32
|
+
*) return 1 ;;
|
|
33
|
+
esac
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if [ -z "$HOME" ]; then
|
|
37
|
+
HOME=$(getent passwd "$(whoami)" | cut -d: -f6 2>/dev/null || echo "/root")
|
|
38
|
+
export HOME
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# =============================================================================
|
|
42
|
+
# PARSING DE ARGUMENTOS
|
|
43
|
+
# =============================================================================
|
|
44
|
+
|
|
45
|
+
AUTO_YES=false
|
|
46
|
+
DEBUG_MODE=false
|
|
47
|
+
SKIP_PROJECT_DIRS=false
|
|
48
|
+
CREATE_PROJECT=false
|
|
49
|
+
QUIET_MODE=false
|
|
50
|
+
NO_BANNER=false
|
|
51
|
+
CLEANUP_LEGACY=false
|
|
52
|
+
|
|
53
|
+
while [[ $# -gt 0 ]]; do
|
|
54
|
+
case $1 in
|
|
55
|
+
-y|--yes) AUTO_YES=true; shift ;;
|
|
56
|
+
-d|--debug) DEBUG_MODE=true; shift ;;
|
|
57
|
+
-s|--skip-project) SKIP_PROJECT_DIRS=true; shift ;;
|
|
58
|
+
-q|--quiet) QUIET_MODE=true; shift ;;
|
|
59
|
+
--no-banner) NO_BANNER=true; shift ;;
|
|
60
|
+
--cleanup-legacy) CLEANUP_LEGACY=true; shift ;;
|
|
61
|
+
--targets=*) SELECTED_TARGETS="${1#--targets=}"; shift ;;
|
|
62
|
+
--create-project) CREATE_PROJECT=true; shift ;;
|
|
63
|
+
--local) warn "--local flag removed: thin-client mode is now mandatory (zero IP on client)"; shift ;;
|
|
64
|
+
--server-url=*) NEOCORTEX_SERVER_URL="${1#--server-url=}"; shift ;;
|
|
65
|
+
-h|--help)
|
|
66
|
+
echo ""
|
|
67
|
+
echo " Neocortex Installer v${VERSION}"
|
|
68
|
+
echo " Development Orchestrator"
|
|
69
|
+
echo ""
|
|
70
|
+
echo " Uso: npx @ornexus/neocortex [opcoes]"
|
|
71
|
+
echo ""
|
|
72
|
+
echo " Opcoes:"
|
|
73
|
+
echo " -y, --yes Modo automatico (Claude Code only)"
|
|
74
|
+
echo " --targets=<lista> Plataformas separadas por virgula"
|
|
75
|
+
echo " --create-project Instalar estrutura no projeto"
|
|
76
|
+
echo " -s, --skip-project Nao perguntar sobre projeto"
|
|
77
|
+
echo " -q, --quiet Modo silencioso"
|
|
78
|
+
echo " --cleanup-legacy (Redundante) Limpeza agora e automatica"
|
|
79
|
+
echo " --local (Removido) Thin-client obrigatorio, zero IP no client"
|
|
80
|
+
echo " --server-url=<url> URL do server Neocortex (padrao: api.neocortex.sh)"
|
|
81
|
+
echo " -d, --debug Modo debug"
|
|
82
|
+
echo " -h, --help Mostra esta ajuda"
|
|
83
|
+
echo ""
|
|
84
|
+
echo " Plataformas: claude-code, cursor, vscode, gemini-cli, codex, antigravity"
|
|
85
|
+
exit 0
|
|
86
|
+
;;
|
|
87
|
+
*) shift ;;
|
|
88
|
+
esac
|
|
89
|
+
done
|
|
90
|
+
|
|
91
|
+
if ! is_interactive; then
|
|
92
|
+
AUTO_YES=true
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# =============================================================================
|
|
96
|
+
# CORES E SIMBOLOS
|
|
97
|
+
# =============================================================================
|
|
98
|
+
|
|
99
|
+
if supports_colors && [ "$QUIET_MODE" = false ]; then
|
|
100
|
+
RED='\033[0;31m'
|
|
101
|
+
GREEN='\033[0;32m'
|
|
102
|
+
YELLOW='\033[1;33m'
|
|
103
|
+
BLUE='\033[0;34m'
|
|
104
|
+
MAGENTA='\033[0;35m'
|
|
105
|
+
CYAN='\033[0;36m'
|
|
106
|
+
WHITE='\033[1;37m'
|
|
107
|
+
GRAY='\033[0;90m'
|
|
108
|
+
BOLD='\033[1m'
|
|
109
|
+
DIM='\033[2m'
|
|
110
|
+
NC='\033[0m'
|
|
111
|
+
else
|
|
112
|
+
RED='' GREEN='' YELLOW='' BLUE='' MAGENTA='' CYAN=''
|
|
113
|
+
WHITE='' GRAY='' BOLD='' DIM='' NC=''
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
if supports_unicode; then
|
|
117
|
+
SYM_OK="✓"
|
|
118
|
+
SYM_FAIL="✗"
|
|
119
|
+
SYM_WARN="!"
|
|
120
|
+
SYM_ARROW="→"
|
|
121
|
+
SYM_DOT="·"
|
|
122
|
+
SYM_SPINNER_FRAMES=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
|
123
|
+
else
|
|
124
|
+
SYM_OK="ok"
|
|
125
|
+
SYM_FAIL="x"
|
|
126
|
+
SYM_WARN="!"
|
|
127
|
+
SYM_ARROW="->"
|
|
128
|
+
SYM_DOT="."
|
|
129
|
+
SYM_SPINNER_FRAMES=('|' '/' '-' '\')
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
# =============================================================================
|
|
133
|
+
# FUNCOES DE LOG
|
|
134
|
+
# =============================================================================
|
|
135
|
+
|
|
136
|
+
# Step header: [1/5] Installing core...
|
|
137
|
+
step() {
|
|
138
|
+
local num=$1 total=$2 msg=$3
|
|
139
|
+
echo ""
|
|
140
|
+
echo -e " ${BOLD}${CYAN}[$num/$total]${NC} ${BOLD}$msg${NC}"
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# Success line
|
|
144
|
+
ok() { echo -e " ${GREEN}${SYM_OK}${NC} $1"; }
|
|
145
|
+
|
|
146
|
+
# Warning line
|
|
147
|
+
warn() { echo -e " ${YELLOW}${SYM_WARN}${NC} $1"; }
|
|
148
|
+
|
|
149
|
+
# Error line
|
|
150
|
+
fail() { echo -e " ${RED}${SYM_FAIL}${NC} $1"; }
|
|
151
|
+
|
|
152
|
+
# Info line
|
|
153
|
+
info() { echo -e " ${DIM}${SYM_ARROW} $1${NC}"; }
|
|
154
|
+
|
|
155
|
+
# Debug line
|
|
156
|
+
debug() { [ "$DEBUG_MODE" = true ] && echo -e " ${GRAY}[debug] $1${NC}"; }
|
|
157
|
+
|
|
158
|
+
# Spinner for background tasks
|
|
159
|
+
run_with_spinner() {
|
|
160
|
+
local msg="$1"
|
|
161
|
+
shift
|
|
162
|
+
"$@" &
|
|
163
|
+
local pid=$!
|
|
164
|
+
local i=0
|
|
165
|
+
local len=${#SYM_SPINNER_FRAMES[@]}
|
|
166
|
+
|
|
167
|
+
while kill -0 "$pid" 2>/dev/null; do
|
|
168
|
+
printf "\r ${CYAN}%s${NC} %s" "${SYM_SPINNER_FRAMES[$((i % len))]}" "$msg"
|
|
169
|
+
i=$((i + 1))
|
|
170
|
+
sleep 0.08
|
|
171
|
+
done
|
|
172
|
+
|
|
173
|
+
wait "$pid"
|
|
174
|
+
local exit_code=$?
|
|
175
|
+
|
|
176
|
+
if [ $exit_code -eq 0 ]; then
|
|
177
|
+
printf "\r ${GREEN}${SYM_OK}${NC} %s\n" "$msg"
|
|
178
|
+
else
|
|
179
|
+
printf "\r ${RED}${SYM_FAIL}${NC} %s\n" "$msg"
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
return $exit_code
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
# =============================================================================
|
|
186
|
+
# FUNCOES DE COPIA (silenciosas - sem log individual)
|
|
187
|
+
# =============================================================================
|
|
188
|
+
|
|
189
|
+
copy_file() {
|
|
190
|
+
local src="$1" dest="$2"
|
|
191
|
+
debug "Copiando: $src -> $dest"
|
|
192
|
+
if [ -f "$src" ]; then
|
|
193
|
+
cp "$src" "$dest" 2>/dev/null
|
|
194
|
+
else
|
|
195
|
+
debug "Arquivo nao encontrado: $src"
|
|
196
|
+
return 1
|
|
197
|
+
fi
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
copy_dir() {
|
|
201
|
+
local src="$1" dest="$2"
|
|
202
|
+
debug "Copiando dir: $src -> $dest"
|
|
203
|
+
[ -d "$src" ] && cp -r "$src" "$dest" 2>/dev/null
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
# Patch description tier in any agent file (YAML frontmatter or Markdown H1)
|
|
207
|
+
# Usage: patch_description_tier <file>
|
|
208
|
+
# Reads tier from ~/.neocortex/config.json and replaces ANY tier label with actual tier.
|
|
209
|
+
# Supports: (Free) <-> (Pro) <-> (Enterprise) in any direction.
|
|
210
|
+
# Works on both frontmatter description and banner body lines.
|
|
211
|
+
patch_description_tier() {
|
|
212
|
+
local target_file="$1"
|
|
213
|
+
local config_file="${HOME}/.neocortex/config.json"
|
|
214
|
+
|
|
215
|
+
[ -f "$target_file" ] || return 0
|
|
216
|
+
[ -f "$config_file" ] || return 0
|
|
217
|
+
|
|
218
|
+
local tier
|
|
219
|
+
tier=$(cat "$config_file" 2>/dev/null | grep -o '"tier"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
|
|
220
|
+
[ -n "$tier" ] || return 0
|
|
221
|
+
|
|
222
|
+
local tier_label
|
|
223
|
+
case "$tier" in
|
|
224
|
+
free) tier_label="Free" ;;
|
|
225
|
+
pro) tier_label="Pro" ;;
|
|
226
|
+
enterprise) tier_label="Enterprise" ;;
|
|
227
|
+
*) return 0 ;;
|
|
228
|
+
esac
|
|
229
|
+
|
|
230
|
+
# Replace ANY tier label with the correct one in non-banner lines
|
|
231
|
+
# (frontmatter description, body text, H1 headers, etc.)
|
|
232
|
+
# Each sed expression replaces one possible source tier with the target tier.
|
|
233
|
+
# If source == target, sed is a no-op (replaces X with X). This is correct.
|
|
234
|
+
sed -i.bak \
|
|
235
|
+
-e "s/(Free)/(${tier_label})/g" \
|
|
236
|
+
-e "s/(Pro)/(${tier_label})/g" \
|
|
237
|
+
-e "s/(Enterprise)/(${tier_label})/g" \
|
|
238
|
+
"$target_file" 2>/dev/null
|
|
239
|
+
rm -f "${target_file}.bak" 2>/dev/null
|
|
240
|
+
|
|
241
|
+
# Fix banner alignment: the OrNexus Team line has a fixed 62-char frame.
|
|
242
|
+
# After replacing tier labels of different lengths, re-pad to maintain alignment.
|
|
243
|
+
# Pattern: "│ ## ### ###### ## OrNexus Team (Tier)<spaces>│"
|
|
244
|
+
# Inner width must be exactly 60 chars (│ + 60 + │ = 62).
|
|
245
|
+
if grep -q "OrNexus Team (${tier_label})" "$target_file" 2>/dev/null; then
|
|
246
|
+
local prefix="│ ## ### ###### ## " # 25 chars inner prefix
|
|
247
|
+
local content="OrNexus Team (${tier_label})"
|
|
248
|
+
local content_len=${#content}
|
|
249
|
+
local pad_len=$((60 - 25 - content_len))
|
|
250
|
+
local padding=""
|
|
251
|
+
local i
|
|
252
|
+
for ((i=0; i<pad_len; i++)); do padding="${padding} "; done
|
|
253
|
+
local new_line="${prefix}${content}${padding}│"
|
|
254
|
+
# Replace the (possibly misaligned) banner line with the correctly padded one
|
|
255
|
+
sed -i.bak -e "s|│ ## ### ###### ## OrNexus Team ([^)]*).*│|${new_line}|" "$target_file" 2>/dev/null
|
|
256
|
+
rm -f "${target_file}.bak" 2>/dev/null
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
debug "Tier patched to (${tier_label}) in $(basename "$target_file")"
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
# =============================================================================
|
|
263
|
+
# DETECCAO DE VERSAO ANTIGA
|
|
264
|
+
# =============================================================================
|
|
265
|
+
|
|
266
|
+
detect_old_installation() {
|
|
267
|
+
debug "Verificando instalacoes antigas..."
|
|
268
|
+
|
|
269
|
+
local dest_dir="${HOME}/.claude/agents/neocortex"
|
|
270
|
+
|
|
271
|
+
if [ -f "$dest_dir/.version" ]; then
|
|
272
|
+
INSTALLED_VERSION=$(cat "$dest_dir/.version" 2>/dev/null)
|
|
273
|
+
local new_version=""
|
|
274
|
+
if [ -f "$SOURCE_DIR/package.json" ]; then
|
|
275
|
+
new_version=$(grep '"version"' "$SOURCE_DIR/package.json" 2>/dev/null | head -1 | sed 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
|
|
276
|
+
fi
|
|
277
|
+
if [ -n "$INSTALLED_VERSION" ] && [ -n "$new_version" ]; then
|
|
278
|
+
if [ "$INSTALLED_VERSION" = "$new_version" ]; then
|
|
279
|
+
info "Reinstalando v$new_version"
|
|
280
|
+
else
|
|
281
|
+
info "Atualizando v$INSTALLED_VERSION ${SYM_ARROW} v$new_version"
|
|
282
|
+
fi
|
|
283
|
+
fi
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
# Check for old files
|
|
287
|
+
local old_files=()
|
|
288
|
+
if [ -f "$dest_dir/step-registry.json" ]; then
|
|
289
|
+
if ! grep -q "state.json" "$dest_dir/workflow.md" 2>/dev/null; then
|
|
290
|
+
old_files+=("workflow antigo")
|
|
291
|
+
fi
|
|
292
|
+
fi
|
|
293
|
+
|
|
294
|
+
if grep -r "orchestrator.db" "$dest_dir" 2>/dev/null | grep -v "backup" | grep -v "migrated" > /dev/null; then
|
|
295
|
+
old_files+=("referencias SQLite")
|
|
296
|
+
fi
|
|
297
|
+
|
|
298
|
+
if [ ${#old_files[@]} -gt 0 ]; then
|
|
299
|
+
MIGRATION_DETECTED=true
|
|
300
|
+
MIGRATION_SOURCES="${old_files[*]}"
|
|
301
|
+
debug "Detectado: $MIGRATION_SOURCES"
|
|
302
|
+
fi
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
# =============================================================================
|
|
306
|
+
# LIMPEZA AUTOMATICA DE ARTEFATOS LEGADOS (auto_cleanup_legacy)
|
|
307
|
+
# Unifica: detect_legacy_artifacts + cleanup_legacy_artifacts + cleanup_legacy_ip
|
|
308
|
+
# + cleanup_legacy_ip_project + npm globals + cross-platform cleanup
|
|
309
|
+
# Roda AUTOMATICAMENTE a cada instalacao - sem necessidade de --cleanup-legacy
|
|
310
|
+
# Idempotente: rodar multiplas vezes sem efeitos colaterais
|
|
311
|
+
# Seguro: NAO remove dados do usuario (stories, epics, state.json, config.json)
|
|
312
|
+
# =============================================================================
|
|
313
|
+
|
|
314
|
+
auto_cleanup_legacy() {
|
|
315
|
+
local removed=0
|
|
316
|
+
local claude_dir="$HOME/.claude"
|
|
317
|
+
local neocortex_dir="$claude_dir/agents/neocortex"
|
|
318
|
+
local skills_dir="$claude_dir/skills/neocortex"
|
|
319
|
+
|
|
320
|
+
debug "Executando limpeza automatica de artefatos legados..."
|
|
321
|
+
|
|
322
|
+
# ─── Helper: remove e loga ───────────────────────────────────────────
|
|
323
|
+
_remove_legacy() {
|
|
324
|
+
local target="$1"
|
|
325
|
+
local label="$2"
|
|
326
|
+
if [ -e "$target" ]; then
|
|
327
|
+
local display="${target/#$HOME/~}"
|
|
328
|
+
if rm -rf "$target" 2>/dev/null; then
|
|
329
|
+
info "Removido: $display ($label)"
|
|
330
|
+
removed=$((removed + 1))
|
|
331
|
+
else
|
|
332
|
+
debug "Falha ao remover: $display"
|
|
333
|
+
fi
|
|
334
|
+
fi
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
# ─── Categoria 1: Pacotes NPM globais legados ────────────────────────
|
|
338
|
+
if command -v npm >/dev/null 2>&1; then
|
|
339
|
+
# Verificar e remover pacotes npm globais legados
|
|
340
|
+
for pkg in "@ornexus/neocortex-cli" "@ornexus-ai/neocortex"; do
|
|
341
|
+
if npm list -g "$pkg" --depth=0 2>/dev/null | grep -q "$pkg"; then
|
|
342
|
+
if npm uninstall -g "$pkg" 2>/dev/null; then
|
|
343
|
+
info "Removido: $pkg (pacote npm global legado)"
|
|
344
|
+
removed=$((removed + 1))
|
|
345
|
+
else
|
|
346
|
+
debug "Falha ao remover pacote npm: $pkg"
|
|
347
|
+
fi
|
|
348
|
+
fi
|
|
349
|
+
done
|
|
350
|
+
|
|
351
|
+
# Verificar binario neocortex-cli no PATH
|
|
352
|
+
local ncli_path
|
|
353
|
+
ncli_path=$(command -v neocortex-cli 2>/dev/null || true)
|
|
354
|
+
if [ -n "$ncli_path" ]; then
|
|
355
|
+
# Verificar se e um link/binario npm (nao remover se for outro tool)
|
|
356
|
+
if [[ "$ncli_path" == *"node_modules"* ]] || [[ "$ncli_path" == *"npm"* ]]; then
|
|
357
|
+
rm -f "$ncli_path" 2>/dev/null && {
|
|
358
|
+
info "Removido: $ncli_path (binario neocortex-cli legado)"
|
|
359
|
+
removed=$((removed + 1))
|
|
360
|
+
}
|
|
361
|
+
fi
|
|
362
|
+
fi
|
|
363
|
+
fi
|
|
364
|
+
|
|
365
|
+
# ─── Categoria 2: Claude Code (~/.claude/) ────────────────────────────
|
|
366
|
+
# IP proprietaria de versoes anteriores
|
|
367
|
+
_remove_legacy "$neocortex_dir/core" "IP legada"
|
|
368
|
+
_remove_legacy "$skills_dir" "skills legadas"
|
|
369
|
+
_remove_legacy "$neocortex_dir/workflow.md" "workflow removido no Tier 3"
|
|
370
|
+
_remove_legacy "$neocortex_dir/package.json" "arquivo desnecessario"
|
|
371
|
+
_remove_legacy "$neocortex_dir/README.md" "arquivo desnecessario"
|
|
372
|
+
|
|
373
|
+
# Step folders legados
|
|
374
|
+
for folder in steps-c steps-e steps-p steps-r steps-u; do
|
|
375
|
+
_remove_legacy "$neocortex_dir/$folder" "steps legados"
|
|
376
|
+
done
|
|
377
|
+
|
|
378
|
+
# Artefatos de instalacoes anteriores
|
|
379
|
+
_remove_legacy "$claude_dir/agents/.git" "repo git antigo"
|
|
380
|
+
_remove_legacy "$claude_dir/.claude" "diretorio aninhado (erro BMAD)"
|
|
381
|
+
_remove_legacy "$claude_dir/agents-ldtn" "diretorio legado"
|
|
382
|
+
_remove_legacy "$claude_dir/.superclaude-metadata.json" "metadata SuperClaude"
|
|
383
|
+
|
|
384
|
+
# Backups SuperClaude antigos
|
|
385
|
+
if [ -d "$claude_dir/backups" ]; then
|
|
386
|
+
for f in "$claude_dir/backups"/superclaude_backup_*.tar.gz; do
|
|
387
|
+
[ -f "$f" ] || continue
|
|
388
|
+
_remove_legacy "$f" "backup SuperClaude antigo"
|
|
389
|
+
done
|
|
390
|
+
fi
|
|
391
|
+
|
|
392
|
+
# ─── Categoria 3: Cursor ──────────────────────────────────────────────
|
|
393
|
+
_remove_legacy "$HOME/.cursor/neocortex" "configs Cursor legadas"
|
|
394
|
+
# .cursorrules com referencias neocortex (verificar antes de remover)
|
|
395
|
+
if [ -f "$HOME/.cursorrules" ]; then
|
|
396
|
+
if grep -qi "neocortex\|ornexus\|synapse" "$HOME/.cursorrules" 2>/dev/null; then
|
|
397
|
+
_remove_legacy "$HOME/.cursorrules" "cursorrules com refs legadas"
|
|
398
|
+
fi
|
|
399
|
+
fi
|
|
400
|
+
|
|
401
|
+
# ─── Categoria 4: VS Code ─────────────────────────────────────────────
|
|
402
|
+
_remove_legacy "$HOME/.vscode/neocortex" "configs VS Code legadas"
|
|
403
|
+
# .instructions.md com referencias neocortex antigo
|
|
404
|
+
if [ -f "$HOME/.instructions.md" ]; then
|
|
405
|
+
if grep -qi "neocortex\|ornexus\|synapse" "$HOME/.instructions.md" 2>/dev/null; then
|
|
406
|
+
_remove_legacy "$HOME/.instructions.md" "instructions.md com refs legadas"
|
|
407
|
+
fi
|
|
408
|
+
fi
|
|
409
|
+
|
|
410
|
+
# ─── Categoria 5: Gemini CLI ──────────────────────────────────────────
|
|
411
|
+
_remove_legacy "$HOME/.gemini/neocortex" "configs Gemini legadas"
|
|
412
|
+
|
|
413
|
+
# ─── Categoria 6: Codex ───────────────────────────────────────────────
|
|
414
|
+
_remove_legacy "$HOME/.codex/neocortex" "configs Codex legadas"
|
|
415
|
+
|
|
416
|
+
# ─── Categoria 7: Antigravity ─────────────────────────────────────────
|
|
417
|
+
# Configs legadas de Antigravity sao gerenciadas pelo adapter, sem path fixo global
|
|
418
|
+
|
|
419
|
+
# ─── Categoria 8: Plaintext cache cleanup (Epic 62 - GAP 1) ─────────
|
|
420
|
+
local cache_dir="$HOME/.neocortex/cache"
|
|
421
|
+
if [ -d "$cache_dir" ]; then
|
|
422
|
+
# Remove plaintext menu-cache.json
|
|
423
|
+
_remove_legacy "$cache_dir/menu-cache.json" "cache plaintext (menu)"
|
|
424
|
+
|
|
425
|
+
# Remove any non-.enc files in cache dir (excluding directories)
|
|
426
|
+
for cache_file in "$cache_dir"/*; do
|
|
427
|
+
[ -f "$cache_file" ] || continue
|
|
428
|
+
case "$cache_file" in
|
|
429
|
+
*.enc) continue ;; # Keep encrypted cache files
|
|
430
|
+
*) _remove_legacy "$cache_file" "cache plaintext" ;;
|
|
431
|
+
esac
|
|
432
|
+
done
|
|
433
|
+
fi
|
|
434
|
+
|
|
435
|
+
# ─── Resultado ────────────────────────────────────────────────────────
|
|
436
|
+
if [ $removed -gt 0 ]; then
|
|
437
|
+
ok "$removed artefato(s) legado(s) removido(s) automaticamente"
|
|
438
|
+
else
|
|
439
|
+
debug "Nenhum artefato legado encontrado"
|
|
440
|
+
fi
|
|
441
|
+
|
|
442
|
+
# Resetar contadores legados (compatibilidade)
|
|
443
|
+
LEGACY_ITEMS=""
|
|
444
|
+
LEGACY_WARNINGS=0
|
|
445
|
+
|
|
446
|
+
return 0
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
# Cleanup de IP legada em projetos individuais (project-level)
|
|
450
|
+
auto_cleanup_legacy_project() {
|
|
451
|
+
local project_dir="$1"
|
|
452
|
+
local neocortex_dir="$project_dir/.claude/agents/neocortex"
|
|
453
|
+
local skills_dir="$project_dir/.claude/skills/neocortex"
|
|
454
|
+
local cleaned=false
|
|
455
|
+
|
|
456
|
+
# Remover core/ e seus subdiretorios
|
|
457
|
+
if [ -d "$neocortex_dir/core" ]; then
|
|
458
|
+
rm -rf "$neocortex_dir/core"
|
|
459
|
+
cleaned=true
|
|
460
|
+
fi
|
|
461
|
+
|
|
462
|
+
# Remover step folders
|
|
463
|
+
for folder in steps-c steps-e steps-p steps-r steps-u; do
|
|
464
|
+
if [ -d "$neocortex_dir/$folder" ]; then
|
|
465
|
+
rm -rf "$neocortex_dir/$folder"
|
|
466
|
+
cleaned=true
|
|
467
|
+
fi
|
|
468
|
+
done
|
|
469
|
+
|
|
470
|
+
# Remover skills
|
|
471
|
+
if [ -d "$skills_dir" ]; then
|
|
472
|
+
rm -rf "$skills_dir"
|
|
473
|
+
cleaned=true
|
|
474
|
+
fi
|
|
475
|
+
|
|
476
|
+
# Remover arquivos desnecessarios
|
|
477
|
+
for file in package.json README.md workflow.md; do
|
|
478
|
+
if [ -f "$neocortex_dir/$file" ]; then
|
|
479
|
+
rm -f "$neocortex_dir/$file"
|
|
480
|
+
cleaned=true
|
|
481
|
+
fi
|
|
482
|
+
done
|
|
483
|
+
|
|
484
|
+
if [ "$cleaned" = true ]; then
|
|
485
|
+
info "IP proprietaria removida do projeto (agora servida via server remoto)"
|
|
486
|
+
fi
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
detect_project_migration_needs() {
|
|
490
|
+
local migration_needed=false
|
|
491
|
+
local sources=()
|
|
492
|
+
|
|
493
|
+
[ -f ".neocortex/orchestrator.db" ] && { migration_needed=true; sources+=("orchestrator.db"); }
|
|
494
|
+
for yaml_path in "bmad-output/sprint-status.yaml" ".neocortex/sprint-status.yaml" "docs/sprint-status.yaml"; do
|
|
495
|
+
[ -f "$yaml_path" ] && { migration_needed=true; sources+=("$yaml_path"); }
|
|
496
|
+
done
|
|
497
|
+
|
|
498
|
+
if [ "$migration_needed" = true ]; then
|
|
499
|
+
echo "${sources[*]}"
|
|
500
|
+
return 0
|
|
501
|
+
fi
|
|
502
|
+
return 1
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
# =============================================================================
|
|
506
|
+
# BANNER
|
|
507
|
+
# =============================================================================
|
|
508
|
+
|
|
509
|
+
show_banner() {
|
|
510
|
+
[ "$QUIET_MODE" = true ] && return
|
|
511
|
+
[ "$NO_BANNER" = true ] && return
|
|
512
|
+
|
|
513
|
+
echo ""
|
|
514
|
+
echo -e "${CYAN} #######${NC}"
|
|
515
|
+
echo -e "${CYAN} ### ########${NC}"
|
|
516
|
+
echo -e "${CYAN} ######### #####${NC}"
|
|
517
|
+
echo -e "${CYAN} ## ############## ${BOLD}N E O C O R T E X${NC}"
|
|
518
|
+
echo -e "${CYAN} ## ### ###### ## ${BOLD}v${VERSION}${NC}"
|
|
519
|
+
echo -e "${CYAN} ## ### ### ##${NC}"
|
|
520
|
+
echo -e "${CYAN} ## ###### ### ## ${DIM}OrNexus Team${NC}"
|
|
521
|
+
echo -e "${CYAN} ############### ##${NC}"
|
|
522
|
+
echo -e "${CYAN} ##### ########${NC}"
|
|
523
|
+
echo -e "${CYAN} ######## ##${NC}"
|
|
524
|
+
echo -e "${CYAN} #######${NC}"
|
|
525
|
+
echo ""
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
# =============================================================================
|
|
529
|
+
# DETECCAO DO DIRETORIO FONTE
|
|
530
|
+
# =============================================================================
|
|
531
|
+
|
|
532
|
+
detect_source_dir() {
|
|
533
|
+
if [ -n "${BASH_SOURCE[0]}" ]; then
|
|
534
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)"
|
|
535
|
+
elif [ -n "$0" ]; then
|
|
536
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
|
|
537
|
+
else
|
|
538
|
+
SCRIPT_DIR="$PWD"
|
|
539
|
+
fi
|
|
540
|
+
|
|
541
|
+
local source_found=false
|
|
542
|
+
if [ -f "$SCRIPT_DIR/targets-stubs/claude-code/neocortex.md" ] || \
|
|
543
|
+
[ -f "$SCRIPT_DIR/targets/claude-code/neocortex.md" ] || \
|
|
544
|
+
[ -f "$SCRIPT_DIR/neocortex.md" ]; then
|
|
545
|
+
source_found=true
|
|
546
|
+
else
|
|
547
|
+
for possible_dir in \
|
|
548
|
+
"$SCRIPT_DIR" \
|
|
549
|
+
"$(npm root -g 2>/dev/null)/@ornexus/neocortex" \
|
|
550
|
+
"$(npm root -g 2>/dev/null)/neocortex" \
|
|
551
|
+
"$(dirname "$0")" \
|
|
552
|
+
"$PWD/node_modules/@ornexus/neocortex" \
|
|
553
|
+
"$PWD/node_modules/neocortex" \
|
|
554
|
+
"$HOME/.npm/_npx/"*"/node_modules/@ornexus/neocortex" \
|
|
555
|
+
"$HOME/.npm/_npx/"*"/node_modules/neocortex"; do
|
|
556
|
+
if [ -f "$possible_dir/targets-stubs/claude-code/neocortex.md" ] 2>/dev/null || \
|
|
557
|
+
[ -f "$possible_dir/targets/claude-code/neocortex.md" ] 2>/dev/null || \
|
|
558
|
+
[ -f "$possible_dir/neocortex.md" ] 2>/dev/null; then
|
|
559
|
+
SCRIPT_DIR="$possible_dir"
|
|
560
|
+
source_found=true
|
|
561
|
+
break
|
|
562
|
+
fi
|
|
563
|
+
done
|
|
564
|
+
fi
|
|
565
|
+
|
|
566
|
+
SOURCE_DIR="$SCRIPT_DIR"
|
|
567
|
+
debug "Source: $SOURCE_DIR"
|
|
568
|
+
|
|
569
|
+
# Epic 65: Emit visible warning when stubs cannot be found
|
|
570
|
+
if [ "$source_found" = false ]; then
|
|
571
|
+
warn "Arquivos de instalacao (stubs) nao encontrados em nenhum path conhecido"
|
|
572
|
+
warn "SCRIPT_DIR: $SCRIPT_DIR"
|
|
573
|
+
warn "Tente reinstalar: npm install -g @ornexus/neocortex"
|
|
574
|
+
fi
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
# =============================================================================
|
|
578
|
+
# TIER 3: CONFIG DO THIN CLIENT
|
|
579
|
+
# =============================================================================
|
|
580
|
+
|
|
581
|
+
setup_thin_client_config() {
|
|
582
|
+
local config_dir="${HOME}/.neocortex"
|
|
583
|
+
local config_file="${config_dir}/config.json"
|
|
584
|
+
|
|
585
|
+
mkdir -p "$config_dir" 2>/dev/null
|
|
586
|
+
mkdir -p "$config_dir/cache" 2>/dev/null
|
|
587
|
+
|
|
588
|
+
# Story 61.4 - F4 remediation: restrictive permissions
|
|
589
|
+
chmod 700 "$config_dir" 2>/dev/null
|
|
590
|
+
chmod 700 "$config_dir/cache" 2>/dev/null
|
|
591
|
+
|
|
592
|
+
if [ -f "$config_file" ]; then
|
|
593
|
+
# Preservar config existente, atualizar apenas serverUrl se necessario
|
|
594
|
+
local existing_mode
|
|
595
|
+
existing_mode=$(grep '"mode"' "$config_file" 2>/dev/null | head -1 | sed 's/.*"mode"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
|
|
596
|
+
|
|
597
|
+
if [ "$existing_mode" = "active" ] || [ "$existing_mode" = "local" ] || [ "$existing_mode" = "remote" ]; then
|
|
598
|
+
# ─── Config schema migration (Epic 62 - GAP 4) ────────────────
|
|
599
|
+
# Check if config needs schema migration
|
|
600
|
+
local has_config_version
|
|
601
|
+
has_config_version=$(grep '"configVersion"' "$config_file" 2>/dev/null)
|
|
602
|
+
if [ -z "$has_config_version" ]; then
|
|
603
|
+
debug "Migrando schema do config.json (adicionando configVersion)"
|
|
604
|
+
if command -v node >/dev/null 2>&1; then
|
|
605
|
+
node -e "
|
|
606
|
+
const fs = require('fs');
|
|
607
|
+
const path = '$config_file';
|
|
608
|
+
try {
|
|
609
|
+
const cfg = JSON.parse(fs.readFileSync(path, 'utf-8'));
|
|
610
|
+
// Add configVersion
|
|
611
|
+
cfg.configVersion = 1;
|
|
612
|
+
// Remove known obsolete fields from old base template
|
|
613
|
+
delete cfg.version;
|
|
614
|
+
delete cfg.cache;
|
|
615
|
+
// Clean obsolete tier:3 from old base template (preserve real tier values)
|
|
616
|
+
if (cfg.tier === 3 && cfg.mode === 'pending-activation') {
|
|
617
|
+
delete cfg.tier;
|
|
618
|
+
}
|
|
619
|
+
fs.writeFileSync(path, JSON.stringify(cfg, null, 2) + '\n');
|
|
620
|
+
}" 2>/dev/null
|
|
621
|
+
chmod 600 "$config_file" 2>/dev/null
|
|
622
|
+
debug "Config migrada para configVersion 1"
|
|
623
|
+
fi
|
|
624
|
+
fi
|
|
625
|
+
# ─── Fix stale localhost serverUrl (Epic 70 - Story 70.02) ────
|
|
626
|
+
# If config has localhost serverUrl and installer has production URL,
|
|
627
|
+
# update serverUrl while preserving all other config fields.
|
|
628
|
+
local existing_server_url
|
|
629
|
+
existing_server_url=$(grep '"serverUrl"' "$config_file" 2>/dev/null | head -1 | sed 's/.*"serverUrl"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
|
|
630
|
+
|
|
631
|
+
if echo "$existing_server_url" | grep -qE '^https?://(localhost|127\.0\.0\.1|0\.0\.0\.0)(:[0-9]+)?'; then
|
|
632
|
+
if [ "$NEOCORTEX_SERVER_URL" != "$existing_server_url" ]; then
|
|
633
|
+
debug "Fixing stale localhost serverUrl: $existing_server_url -> $NEOCORTEX_SERVER_URL"
|
|
634
|
+
if command -v node >/dev/null 2>&1; then
|
|
635
|
+
node -e "
|
|
636
|
+
const fs = require('fs');
|
|
637
|
+
const p = '$config_file';
|
|
638
|
+
try {
|
|
639
|
+
const raw = fs.readFileSync(p, 'utf-8').replace(/^\uFEFF/, '');
|
|
640
|
+
const cfg = JSON.parse(raw);
|
|
641
|
+
cfg.serverUrl = '${NEOCORTEX_SERVER_URL}';
|
|
642
|
+
fs.writeFileSync(p, JSON.stringify(cfg, null, 2) + '\n');
|
|
643
|
+
}" 2>/dev/null
|
|
644
|
+
chmod 600 "$config_file" 2>/dev/null
|
|
645
|
+
debug "serverUrl updated to $NEOCORTEX_SERVER_URL"
|
|
646
|
+
fi
|
|
647
|
+
fi
|
|
648
|
+
fi
|
|
649
|
+
|
|
650
|
+
# ─── Fix stale ornexus.com serverUrl (Epic P46 - Story P46.03) ────
|
|
651
|
+
# If config has old api.neocortex.ornexus.com URL, update to api.neocortex.sh
|
|
652
|
+
if echo "$existing_server_url" | grep -qE 'api\.neocortex\.ornexus\.com'; then
|
|
653
|
+
debug "Fixing old ornexus.com serverUrl: $existing_server_url -> $NEOCORTEX_SERVER_URL"
|
|
654
|
+
if command -v node >/dev/null 2>&1; then
|
|
655
|
+
node -e "
|
|
656
|
+
const fs = require('fs');
|
|
657
|
+
const p = '$config_file';
|
|
658
|
+
try {
|
|
659
|
+
const raw = fs.readFileSync(p, 'utf-8').replace(/^\uFEFF/, '');
|
|
660
|
+
const cfg = JSON.parse(raw);
|
|
661
|
+
cfg.serverUrl = '${NEOCORTEX_SERVER_URL}';
|
|
662
|
+
fs.writeFileSync(p, JSON.stringify(cfg, null, 2) + '\n');
|
|
663
|
+
}" 2>/dev/null
|
|
664
|
+
chmod 600 "$config_file" 2>/dev/null
|
|
665
|
+
debug "serverUrl updated from ornexus.com to $NEOCORTEX_SERVER_URL"
|
|
666
|
+
fi
|
|
667
|
+
fi
|
|
668
|
+
|
|
669
|
+
# Story 61.4 - ensure permissions are always enforced even on existing configs
|
|
670
|
+
chmod 600 "$config_file" 2>/dev/null
|
|
671
|
+
debug "Config existente preservada (mode=$existing_mode)"
|
|
672
|
+
return 0
|
|
673
|
+
fi
|
|
674
|
+
fi
|
|
675
|
+
|
|
676
|
+
# Criar config base para thin client
|
|
677
|
+
cat > "$config_file" << EOFCONFIG
|
|
678
|
+
{
|
|
679
|
+
"configVersion": 1,
|
|
680
|
+
"mode": "pending-activation",
|
|
681
|
+
"serverUrl": "${NEOCORTEX_SERVER_URL}",
|
|
682
|
+
"resilience": {
|
|
683
|
+
"circuitBreaker": true,
|
|
684
|
+
"maxRetries": 3,
|
|
685
|
+
"timeoutMs": 5000
|
|
686
|
+
},
|
|
687
|
+
"installedAt": "$(date -Iseconds 2>/dev/null || date '+%Y-%m-%dT%H:%M:%S')",
|
|
688
|
+
"installerVersion": "${VERSION}"
|
|
689
|
+
}
|
|
690
|
+
EOFCONFIG
|
|
691
|
+
|
|
692
|
+
# Story 61.4 - F4 remediation: config file readable only by owner
|
|
693
|
+
chmod 600 "$config_file" 2>/dev/null
|
|
694
|
+
|
|
695
|
+
debug "Thin client config criada: $config_file"
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
# cleanup_legacy_ip_project() substituida por auto_cleanup_legacy_project()
|
|
699
|
+
# (definida na secao de limpeza automatica acima)
|
|
700
|
+
|
|
701
|
+
# =============================================================================
|
|
702
|
+
# INSTALACAO CORE
|
|
703
|
+
# =============================================================================
|
|
704
|
+
|
|
705
|
+
install_core() {
|
|
706
|
+
local errors=0
|
|
707
|
+
|
|
708
|
+
CLAUDE_DIR="${HOME}/.claude"
|
|
709
|
+
AGENTS_DIR="${CLAUDE_DIR}/agents"
|
|
710
|
+
DEST_DIR="${AGENTS_DIR}/neocortex"
|
|
711
|
+
|
|
712
|
+
debug "HOME=$HOME | DEST=$DEST_DIR | MODE=thin-client"
|
|
713
|
+
|
|
714
|
+
# Create directories
|
|
715
|
+
if [ ! -d "$CLAUDE_DIR" ]; then
|
|
716
|
+
mkdir -p "$CLAUDE_DIR" 2>/dev/null || { fail "Falha ao criar $CLAUDE_DIR"; return 1; }
|
|
717
|
+
fi
|
|
718
|
+
mkdir -p "$AGENTS_DIR" 2>/dev/null || { fail "Falha ao criar $AGENTS_DIR"; return 1; }
|
|
719
|
+
|
|
720
|
+
# Clean previous installation
|
|
721
|
+
if [ -d "$DEST_DIR" ]; then
|
|
722
|
+
rm -rf "$DEST_DIR" 2>/dev/null
|
|
723
|
+
debug "Instalacao anterior removida"
|
|
724
|
+
fi
|
|
725
|
+
mkdir -p "$DEST_DIR" 2>/dev/null || { fail "Falha ao criar $DEST_DIR"; return 1; }
|
|
726
|
+
|
|
727
|
+
# Thin-client ONLY: zero IP on client, all content from server
|
|
728
|
+
setup_thin_client_config
|
|
729
|
+
|
|
730
|
+
# ─── Version-aware cache purge on upgrade (Epic 62 - GAP 2+3) ───────
|
|
731
|
+
local pkg_version=""
|
|
732
|
+
if [ -f "$SOURCE_DIR/package.json" ]; then
|
|
733
|
+
pkg_version=$(grep '"version"' "$SOURCE_DIR/package.json" 2>/dev/null | head -1 | sed 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
|
|
734
|
+
fi
|
|
735
|
+
|
|
736
|
+
if [ -n "$pkg_version" ]; then
|
|
737
|
+
local old_version=""
|
|
738
|
+
# Read existing .version from either location
|
|
739
|
+
if [ -f "$DEST_DIR/.version" ]; then
|
|
740
|
+
old_version=$(cat "$DEST_DIR/.version" 2>/dev/null | tr -d '[:space:]')
|
|
741
|
+
elif [ -f "$HOME/.neocortex/.version" ]; then
|
|
742
|
+
old_version=$(cat "$HOME/.neocortex/.version" 2>/dev/null | tr -d '[:space:]')
|
|
743
|
+
fi
|
|
744
|
+
|
|
745
|
+
# If version changed, purge all cache files
|
|
746
|
+
if [ -n "$old_version" ] && [ "$old_version" != "$pkg_version" ]; then
|
|
747
|
+
local cache_dir="$HOME/.neocortex/cache"
|
|
748
|
+
if [ -d "$cache_dir" ]; then
|
|
749
|
+
local purged=0
|
|
750
|
+
# Remove all .enc files
|
|
751
|
+
for enc_file in "$cache_dir"/*.enc; do
|
|
752
|
+
[ -f "$enc_file" ] || continue
|
|
753
|
+
rm -f "$enc_file" 2>/dev/null && purged=$((purged + 1))
|
|
754
|
+
done
|
|
755
|
+
# Remove menu-cache.json (redundancy with 62.1)
|
|
756
|
+
[ -f "$cache_dir/menu-cache.json" ] && rm -f "$cache_dir/menu-cache.json" 2>/dev/null && purged=$((purged + 1))
|
|
757
|
+
# Remove any other non-directory files
|
|
758
|
+
for cache_file in "$cache_dir"/*; do
|
|
759
|
+
[ -f "$cache_file" ] || continue
|
|
760
|
+
rm -f "$cache_file" 2>/dev/null && purged=$((purged + 1))
|
|
761
|
+
done
|
|
762
|
+
[ $purged -gt 0 ] && info "Cache purgado: versao alterada de $old_version para $pkg_version ($purged arquivo(s))"
|
|
763
|
+
fi
|
|
764
|
+
fi
|
|
765
|
+
|
|
766
|
+
# Write version file
|
|
767
|
+
echo "$pkg_version" > "$DEST_DIR/.version"
|
|
768
|
+
fi
|
|
769
|
+
|
|
770
|
+
if [ $errors -eq 0 ]; then
|
|
771
|
+
ok "Remote mode configured ${DIM}(thin client ready)${NC}"
|
|
772
|
+
else
|
|
773
|
+
fail "Core instalado com erros"
|
|
774
|
+
fi
|
|
775
|
+
|
|
776
|
+
return $errors
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
# =============================================================================
|
|
780
|
+
# INSTALACAO DE SKILLS
|
|
781
|
+
# =============================================================================
|
|
782
|
+
|
|
783
|
+
install_skills() {
|
|
784
|
+
local errors=0
|
|
785
|
+
|
|
786
|
+
SKILLS_DIR="${CLAUDE_DIR}/skills"
|
|
787
|
+
SKILLS_DEST="${SKILLS_DIR}/neocortex"
|
|
788
|
+
SKILLS_SOURCE="${SOURCE_DIR}/core/skills"
|
|
789
|
+
|
|
790
|
+
# Thin-client: skills delivered by remote server, never copied
|
|
791
|
+
ok "Skills: delivered by remote server"
|
|
792
|
+
return 0
|
|
793
|
+
|
|
794
|
+
mkdir -p "$SKILLS_DIR" 2>/dev/null || { fail "Falha ao criar $SKILLS_DIR"; return 1; }
|
|
795
|
+
|
|
796
|
+
# Clean previous
|
|
797
|
+
[ -d "$SKILLS_DEST" ] && rm -rf "$SKILLS_DEST" 2>/dev/null
|
|
798
|
+
|
|
799
|
+
if cp -r "$SKILLS_SOURCE" "$SKILLS_DEST" 2>/dev/null; then
|
|
800
|
+
# Count skills
|
|
801
|
+
local skill_count=0
|
|
802
|
+
for skill_file in "$SKILLS_DEST"/step-skills/*/*.md "$SKILLS_DEST"/domain-skills/*/*.md; do
|
|
803
|
+
if [ -f "$skill_file" ] && [[ "$(basename "$skill_file")" != "_template.md" ]]; then
|
|
804
|
+
((skill_count++))
|
|
805
|
+
fi
|
|
806
|
+
done
|
|
807
|
+
ok "Skills instaladas ${DIM}($skill_count skills) [modo local]${NC}"
|
|
808
|
+
else
|
|
809
|
+
fail "Falha ao copiar skills"
|
|
810
|
+
((errors++))
|
|
811
|
+
fi
|
|
812
|
+
|
|
813
|
+
return $errors
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
# =============================================================================
|
|
817
|
+
# INSTALACAO DE AGENT (Claude Code)
|
|
818
|
+
# =============================================================================
|
|
819
|
+
|
|
820
|
+
install_agent() {
|
|
821
|
+
local errors=0
|
|
822
|
+
|
|
823
|
+
CLAUDE_TARGET_DIR="$SOURCE_DIR/targets/claude-code"
|
|
824
|
+
if [ ! -d "$CLAUDE_TARGET_DIR" ]; then
|
|
825
|
+
CLAUDE_TARGET_DIR="$SOURCE_DIR/targets-stubs/claude-code"
|
|
826
|
+
fi
|
|
827
|
+
if [ ! -d "$CLAUDE_TARGET_DIR" ]; then
|
|
828
|
+
CLAUDE_TARGET_DIR="$SOURCE_DIR"
|
|
829
|
+
fi
|
|
830
|
+
|
|
831
|
+
# Epic 65: Early validation — if neither source file exists, emit clear error
|
|
832
|
+
if [ ! -f "$CLAUDE_TARGET_DIR/neocortex.md" ] && [ ! -f "$CLAUDE_TARGET_DIR/neocortex.agent.yaml" ]; then
|
|
833
|
+
fail "Arquivos fonte nao encontrados em: $CLAUDE_TARGET_DIR"
|
|
834
|
+
fail "Diretorio de origem ($SOURCE_DIR) pode estar incompleto"
|
|
835
|
+
fail "Tente reinstalar: npm install -g @ornexus/neocortex"
|
|
836
|
+
return 2
|
|
837
|
+
fi
|
|
838
|
+
|
|
839
|
+
# Tier 3 Stub-Only: copiar apenas 2 arquivos de interface (stubs minimos)
|
|
840
|
+
copy_file "$CLAUDE_TARGET_DIR/neocortex.md" "$DEST_DIR/" || ((errors++))
|
|
841
|
+
copy_file "$CLAUDE_TARGET_DIR/neocortex.agent.yaml" "$DEST_DIR/" || ((errors++))
|
|
842
|
+
|
|
843
|
+
# Epic 65: Post-copy verification
|
|
844
|
+
if [ ! -f "$DEST_DIR/neocortex.md" ]; then
|
|
845
|
+
fail "neocortex.md nao encontrado no destino apos copia: $DEST_DIR/"
|
|
846
|
+
((errors++))
|
|
847
|
+
fi
|
|
848
|
+
if [ ! -f "$DEST_DIR/neocortex.agent.yaml" ]; then
|
|
849
|
+
fail "neocortex.agent.yaml nao encontrado no destino apos copia: $DEST_DIR/"
|
|
850
|
+
((errors++))
|
|
851
|
+
fi
|
|
852
|
+
|
|
853
|
+
# Dynamic description: patch tier from existing config (if activated)
|
|
854
|
+
patch_description_tier "$DEST_DIR/neocortex.md"
|
|
855
|
+
patch_description_tier "$DEST_DIR/neocortex.agent.yaml"
|
|
856
|
+
|
|
857
|
+
# Cleanup: remover workflow.md de instalacoes anteriores (v3.8 -> v3.9)
|
|
858
|
+
if [ -f "$DEST_DIR/workflow.md" ]; then
|
|
859
|
+
rm -f "$DEST_DIR/workflow.md"
|
|
860
|
+
[ "$QUIET_MODE" = false ] && echo " Removed workflow.md (content now server-side)"
|
|
861
|
+
fi
|
|
862
|
+
|
|
863
|
+
# Thin-client ONLY: 2 stub files + server-side orchestration
|
|
864
|
+
|
|
865
|
+
return $errors
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
# =============================================================================
|
|
869
|
+
# INSTALACAO DE TARGETS
|
|
870
|
+
# =============================================================================
|
|
871
|
+
|
|
872
|
+
install_targets() {
|
|
873
|
+
local targets="$1"
|
|
874
|
+
local target_errors=0
|
|
875
|
+
local target_count=0
|
|
876
|
+
local target_results=()
|
|
877
|
+
|
|
878
|
+
targets=$(echo "$targets" | tr ',' ' ')
|
|
879
|
+
|
|
880
|
+
for target in $targets; do
|
|
881
|
+
local adapter_script="$SOURCE_DIR/targets/$target/install-${target}.sh"
|
|
882
|
+
local func_name="install_$(echo "$target" | tr '-' '_')"
|
|
883
|
+
|
|
884
|
+
if [ ! -f "$adapter_script" ]; then
|
|
885
|
+
if [ "$target" = "claude-code" ]; then
|
|
886
|
+
install_agent
|
|
887
|
+
local result=$?
|
|
888
|
+
if [ $result -eq 0 ]; then
|
|
889
|
+
target_results+=("$target:OK")
|
|
890
|
+
((target_count++))
|
|
891
|
+
ok "${BOLD}claude-code${NC} ${DIM}(thin client)${NC}"
|
|
892
|
+
else
|
|
893
|
+
target_results+=("$target:FAIL")
|
|
894
|
+
((target_errors++))
|
|
895
|
+
fail "claude-code"
|
|
896
|
+
fi
|
|
897
|
+
continue
|
|
898
|
+
fi
|
|
899
|
+
|
|
900
|
+
warn "$target ${DIM}(adapter nao encontrado)${NC}"
|
|
901
|
+
target_results+=("$target:SKIP")
|
|
902
|
+
((target_errors++))
|
|
903
|
+
continue
|
|
904
|
+
fi
|
|
905
|
+
|
|
906
|
+
. "$adapter_script"
|
|
907
|
+
|
|
908
|
+
if type "$func_name" >/dev/null 2>&1; then
|
|
909
|
+
"$func_name" "$SOURCE_DIR" "$HOME"
|
|
910
|
+
local result=$?
|
|
911
|
+
if [ $result -eq 0 ]; then
|
|
912
|
+
target_results+=("$target:OK")
|
|
913
|
+
((target_count++))
|
|
914
|
+
ok "${BOLD}$target${NC}"
|
|
915
|
+
else
|
|
916
|
+
target_results+=("$target:WARN")
|
|
917
|
+
warn "$target ${DIM}(instalado com avisos)${NC}"
|
|
918
|
+
((target_count++))
|
|
919
|
+
fi
|
|
920
|
+
else
|
|
921
|
+
fail "$target ${DIM}(funcao $func_name nao encontrada)${NC}"
|
|
922
|
+
target_results+=("$target:FAIL")
|
|
923
|
+
((target_errors++))
|
|
924
|
+
fi
|
|
925
|
+
done
|
|
926
|
+
|
|
927
|
+
TARGET_RESULTS=("${target_results[@]}")
|
|
928
|
+
TARGET_COUNT=$target_count
|
|
929
|
+
|
|
930
|
+
return $target_errors
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
# =============================================================================
|
|
934
|
+
# CARREGAMENTO DE .ENV
|
|
935
|
+
# =============================================================================
|
|
936
|
+
|
|
937
|
+
load_env_file() {
|
|
938
|
+
local env_file=""
|
|
939
|
+
for possible_env in "./.env" "$SOURCE_DIR/.env"; do
|
|
940
|
+
[ -f "$possible_env" ] && { env_file="$possible_env"; break; }
|
|
941
|
+
done
|
|
942
|
+
|
|
943
|
+
if [ -n "$env_file" ] && [ -f "$env_file" ]; then
|
|
944
|
+
local loaded=0
|
|
945
|
+
while IFS='=' read -r key value || [ -n "$key" ]; do
|
|
946
|
+
[[ -z "$key" || "$key" =~ ^[[:space:]]*# ]] && continue
|
|
947
|
+
key=$(echo "$key" | xargs)
|
|
948
|
+
value=$(echo "$value" | xargs)
|
|
949
|
+
value="${value%\"}"; value="${value#\"}"
|
|
950
|
+
value="${value%\'}"; value="${value#\'}"
|
|
951
|
+
if [ -n "$value" ] && [ -z "${!key}" ]; then
|
|
952
|
+
export "$key=$value"
|
|
953
|
+
((loaded++))
|
|
954
|
+
fi
|
|
955
|
+
done < "$env_file"
|
|
956
|
+
[ $loaded -gt 0 ] && info "$loaded variavel(eis) carregada(s) do .env"
|
|
957
|
+
return 0
|
|
958
|
+
fi
|
|
959
|
+
return 1
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
# =============================================================================
|
|
963
|
+
# CONFIGURACAO DE TOKENS
|
|
964
|
+
# =============================================================================
|
|
965
|
+
|
|
966
|
+
prompt_tokens() {
|
|
967
|
+
[ "$QUIET_MODE" = true ] && return
|
|
968
|
+
[ -n "$CONTEXT7_API_KEY" ] && return
|
|
969
|
+
|
|
970
|
+
if [ "$AUTO_YES" = true ]; then
|
|
971
|
+
return
|
|
972
|
+
fi
|
|
973
|
+
|
|
974
|
+
echo ""
|
|
975
|
+
info "MCP Context7 nao configurado ${DIM}(opcional)${NC}"
|
|
976
|
+
echo -ne " Configurar agora? ${BOLD}[s/N]:${NC} "
|
|
977
|
+
|
|
978
|
+
local response=""
|
|
979
|
+
if read -r -t 15 response 2>/dev/null; then
|
|
980
|
+
: # resposta recebida
|
|
981
|
+
else
|
|
982
|
+
echo ""
|
|
983
|
+
return
|
|
984
|
+
fi
|
|
985
|
+
|
|
986
|
+
if [[ "$response" =~ ^([sS][iI]?[mM]?|[yY][eE]?[sS]?)$ ]]; then
|
|
987
|
+
echo ""
|
|
988
|
+
info "Obtenha sua API Key em: ${WHITE}https://context7.com${NC}"
|
|
989
|
+
echo -ne " Cole sua ${BOLD}CONTEXT7_API_KEY${NC}: "
|
|
990
|
+
|
|
991
|
+
local api_key=""
|
|
992
|
+
if read -r -t 60 api_key 2>/dev/null; then
|
|
993
|
+
if [[ "$api_key" =~ ^ctx7sk- ]]; then
|
|
994
|
+
export CONTEXT7_API_KEY="$api_key"
|
|
995
|
+
ok "CONTEXT7_API_KEY configurada"
|
|
996
|
+
echo ""
|
|
997
|
+
info "Para persistir: ${DIM}echo 'export CONTEXT7_API_KEY=\"$api_key\"' >> ~/.bashrc${NC}"
|
|
998
|
+
elif [ -n "$api_key" ]; then
|
|
999
|
+
warn "Formato invalido (esperado: ctx7sk-...)"
|
|
1000
|
+
fi
|
|
1001
|
+
fi
|
|
1002
|
+
fi
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
# =============================================================================
|
|
1006
|
+
# INSTALACAO DE MCP SERVERS
|
|
1007
|
+
# =============================================================================
|
|
1008
|
+
|
|
1009
|
+
install_mcps() {
|
|
1010
|
+
if ! command -v claude >/dev/null 2>&1; then
|
|
1011
|
+
info "Claude CLI nao encontrado ${DIM}(MCPs serao instalados depois)${NC}"
|
|
1012
|
+
return 0
|
|
1013
|
+
fi
|
|
1014
|
+
|
|
1015
|
+
local mcp_results=""
|
|
1016
|
+
|
|
1017
|
+
if claude mcp list 2>/dev/null | grep -q "^playwright"; then
|
|
1018
|
+
mcp_results="${mcp_results}playwright:OK "
|
|
1019
|
+
else
|
|
1020
|
+
if claude mcp add playwright npx @playwright/mcp@latest 2>/dev/null; then
|
|
1021
|
+
mcp_results="${mcp_results}playwright:OK "
|
|
1022
|
+
else
|
|
1023
|
+
mcp_results="${mcp_results}playwright:FAIL "
|
|
1024
|
+
fi
|
|
1025
|
+
fi
|
|
1026
|
+
|
|
1027
|
+
if claude mcp list 2>/dev/null | grep -q "^context7"; then
|
|
1028
|
+
mcp_results="${mcp_results}context7:OK "
|
|
1029
|
+
elif [ -n "$CONTEXT7_API_KEY" ]; then
|
|
1030
|
+
if claude mcp add --transport http context7 https://mcp.context7.com/mcp --header "CONTEXT7_API_KEY: $CONTEXT7_API_KEY" 2>/dev/null; then
|
|
1031
|
+
mcp_results="${mcp_results}context7:OK "
|
|
1032
|
+
else
|
|
1033
|
+
mcp_results="${mcp_results}context7:FAIL "
|
|
1034
|
+
fi
|
|
1035
|
+
else
|
|
1036
|
+
mcp_results="${mcp_results}context7:SKIP "
|
|
1037
|
+
fi
|
|
1038
|
+
|
|
1039
|
+
# Show compact MCP results
|
|
1040
|
+
local mcp_ok="" mcp_skip=""
|
|
1041
|
+
for entry in $mcp_results; do
|
|
1042
|
+
local name="${entry%%:*}"
|
|
1043
|
+
local status="${entry##*:}"
|
|
1044
|
+
case "$status" in
|
|
1045
|
+
OK) mcp_ok="${mcp_ok}${name}, " ;;
|
|
1046
|
+
SKIP) mcp_skip="${mcp_skip}${name}, " ;;
|
|
1047
|
+
esac
|
|
1048
|
+
done
|
|
1049
|
+
|
|
1050
|
+
[ -n "$mcp_ok" ] && ok "MCPs: ${DIM}${mcp_ok%, }${NC}"
|
|
1051
|
+
[ -n "$mcp_skip" ] && info "MCPs pendentes: ${DIM}${mcp_skip%, }${NC}"
|
|
1052
|
+
|
|
1053
|
+
return 0
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
# =============================================================================
|
|
1057
|
+
# INSTALACAO DO CODERABBIT CLI
|
|
1058
|
+
# =============================================================================
|
|
1059
|
+
|
|
1060
|
+
install_coderabbit() {
|
|
1061
|
+
if command -v coderabbit >/dev/null 2>&1; then
|
|
1062
|
+
ok "CodeRabbit CLI"
|
|
1063
|
+
CODERABBIT_INSTALLED=true
|
|
1064
|
+
return 0
|
|
1065
|
+
fi
|
|
1066
|
+
|
|
1067
|
+
if command -v curl >/dev/null 2>&1 && curl -fsSL https://cli.coderabbit.ai/install.sh | sh 2>/dev/null; then
|
|
1068
|
+
ok "CodeRabbit CLI ${DIM}(instalado)${NC}"
|
|
1069
|
+
CODERABBIT_INSTALLED=true
|
|
1070
|
+
elif command -v npm >/dev/null 2>&1 && npm install -g coderabbit 2>/dev/null; then
|
|
1071
|
+
ok "CodeRabbit CLI ${DIM}(via npm)${NC}"
|
|
1072
|
+
CODERABBIT_INSTALLED=true
|
|
1073
|
+
else
|
|
1074
|
+
info "CodeRabbit CLI ${DIM}(instale depois: curl -fsSL https://cli.coderabbit.ai/install.sh | sh)${NC}"
|
|
1075
|
+
CODERABBIT_INSTALLED=false
|
|
1076
|
+
fi
|
|
1077
|
+
|
|
1078
|
+
return 0
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
# =============================================================================
|
|
1082
|
+
# VERIFICACAO POS-INSTALACAO
|
|
1083
|
+
# =============================================================================
|
|
1084
|
+
|
|
1085
|
+
verify_installation() {
|
|
1086
|
+
local fails=0
|
|
1087
|
+
local warns=0
|
|
1088
|
+
local report=""
|
|
1089
|
+
|
|
1090
|
+
if ! echo "$SELECTED_TARGETS" | grep -q "claude-code"; then
|
|
1091
|
+
return 0
|
|
1092
|
+
fi
|
|
1093
|
+
|
|
1094
|
+
# ─── Layer 1: File existence + minimum size ─────────────────────────
|
|
1095
|
+
local check_fname check_min check_fpath check_size check_ext check_first_line check_display
|
|
1096
|
+
for check_fname in neocortex.md neocortex.agent.yaml; do
|
|
1097
|
+
case "$check_fname" in
|
|
1098
|
+
neocortex.md) check_min=512 ;;
|
|
1099
|
+
neocortex.agent.yaml) check_min=128 ;;
|
|
1100
|
+
esac
|
|
1101
|
+
check_fpath="${DEST_DIR}/${check_fname}"
|
|
1102
|
+
|
|
1103
|
+
if [ ! -f "$check_fpath" ]; then
|
|
1104
|
+
report="${report}FAIL ${check_fname} (nao encontrado)\n"
|
|
1105
|
+
fails=$((fails + 1))
|
|
1106
|
+
else
|
|
1107
|
+
check_size=$(wc -c < "$check_fpath" 2>/dev/null | tr -d ' ')
|
|
1108
|
+
if [ "$check_size" -lt "$check_min" ] 2>/dev/null; then
|
|
1109
|
+
report="${report}FAIL ${check_fname} (${check_size} bytes - possivelmente corrompido, minimo ${check_min})\n"
|
|
1110
|
+
fails=$((fails + 1))
|
|
1111
|
+
else
|
|
1112
|
+
# ─── Layer 2: Content marker (frontmatter) ──────────────
|
|
1113
|
+
check_ext="${check_fname##*.}"
|
|
1114
|
+
if [ "$check_ext" = "md" ]; then
|
|
1115
|
+
check_first_line=$(head -1 "$check_fpath" 2>/dev/null)
|
|
1116
|
+
if [ "$check_first_line" != "---" ]; then
|
|
1117
|
+
report="${report}WARN ${check_fname} (formato invalido - sem frontmatter)\n"
|
|
1118
|
+
warns=$((warns + 1))
|
|
1119
|
+
else
|
|
1120
|
+
if [ "$check_size" -ge 1024 ]; then check_display="$((check_size / 1024))KB"; else check_display="${check_size}B"; fi
|
|
1121
|
+
report="${report}OK ${check_fname} (${check_display})\n"
|
|
1122
|
+
fi
|
|
1123
|
+
else
|
|
1124
|
+
if [ "$check_size" -ge 1024 ]; then check_display="$((check_size / 1024))KB"; else check_display="${check_size}B"; fi
|
|
1125
|
+
report="${report}OK ${check_fname} (${check_display})\n"
|
|
1126
|
+
fi
|
|
1127
|
+
fi
|
|
1128
|
+
fi
|
|
1129
|
+
done
|
|
1130
|
+
|
|
1131
|
+
if false; then
|
|
1132
|
+
# ─── Layer 3: Step directories (removed - thin-client only) ──────
|
|
1133
|
+
for dir in steps-c steps-e steps-p steps-r steps-u; do
|
|
1134
|
+
local dir_path="$DEST_DIR/$dir"
|
|
1135
|
+
if [ ! -d "$dir_path" ]; then
|
|
1136
|
+
report="${report}FAIL ${dir}/ (diretorio nao encontrado)\n"
|
|
1137
|
+
fails=$((fails + 1))
|
|
1138
|
+
else
|
|
1139
|
+
local md_count=0
|
|
1140
|
+
for f in "$dir_path"/*.md; do
|
|
1141
|
+
[ -f "$f" ] && md_count=$((md_count + 1))
|
|
1142
|
+
done
|
|
1143
|
+
if [ $md_count -eq 0 ]; then
|
|
1144
|
+
report="${report}WARN ${dir}/ (vazio - nenhum arquivo .md)\n"
|
|
1145
|
+
warns=$((warns + 1))
|
|
1146
|
+
else
|
|
1147
|
+
report="${report}OK ${dir}/ (${md_count} arquivos)\n"
|
|
1148
|
+
fi
|
|
1149
|
+
fi
|
|
1150
|
+
done
|
|
1151
|
+
|
|
1152
|
+
# ─── Layer 3b: Core directory (local mode only) ──────────────────
|
|
1153
|
+
if [ ! -d "$DEST_DIR/core" ]; then
|
|
1154
|
+
report="${report}FAIL core/ (diretorio nao encontrado)\n"
|
|
1155
|
+
fails=$((fails + 1))
|
|
1156
|
+
else
|
|
1157
|
+
report="${report}OK core/\n"
|
|
1158
|
+
fi
|
|
1159
|
+
else
|
|
1160
|
+
# ─── Layer 3: Thin client config (remote mode) ───────────────────
|
|
1161
|
+
local config_file="${HOME}/.neocortex/config.json"
|
|
1162
|
+
if [ -f "$config_file" ]; then
|
|
1163
|
+
report="${report}OK ~/.neocortex/config.json (thin client configured)\n"
|
|
1164
|
+
else
|
|
1165
|
+
report="${report}WARN ~/.neocortex/config.json (nao encontrado)\n"
|
|
1166
|
+
warns=$((warns + 1))
|
|
1167
|
+
fi
|
|
1168
|
+
|
|
1169
|
+
# Verify NO IP directories exist
|
|
1170
|
+
local ip_found=false
|
|
1171
|
+
for dir in core steps-c steps-e steps-p steps-r steps-u; do
|
|
1172
|
+
if [ -d "$DEST_DIR/$dir" ]; then
|
|
1173
|
+
report="${report}WARN ${dir}/ ainda existe (deveria ter sido removido)\n"
|
|
1174
|
+
warns=$((warns + 1))
|
|
1175
|
+
ip_found=true
|
|
1176
|
+
fi
|
|
1177
|
+
done
|
|
1178
|
+
if [ "$ip_found" = false ]; then
|
|
1179
|
+
report="${report}OK Zero IP no filesystem (modo remoto)\n"
|
|
1180
|
+
fi
|
|
1181
|
+
fi
|
|
1182
|
+
|
|
1183
|
+
# ─── Display report ─────────────────────────────────────────────────
|
|
1184
|
+
if [ $fails -eq 0 ] && [ $warns -eq 0 ]; then
|
|
1185
|
+
# All good - compact output
|
|
1186
|
+
if [ "$QUIET_MODE" != true ]; then
|
|
1187
|
+
ok "Instalacao verificada"
|
|
1188
|
+
fi
|
|
1189
|
+
return 0
|
|
1190
|
+
fi
|
|
1191
|
+
|
|
1192
|
+
# Show detailed report when issues found
|
|
1193
|
+
if [ "$QUIET_MODE" = true ] && [ $fails -eq 0 ]; then
|
|
1194
|
+
return 0 # In quiet mode, skip warnings-only report
|
|
1195
|
+
fi
|
|
1196
|
+
|
|
1197
|
+
echo ""
|
|
1198
|
+
info "Verificacao pos-instalacao:"
|
|
1199
|
+
echo -e "$report" | while IFS= read -r line; do
|
|
1200
|
+
[ -z "$line" ] && continue
|
|
1201
|
+
local status="${line%% *}"
|
|
1202
|
+
local detail="${line#* }"
|
|
1203
|
+
case "$status" in
|
|
1204
|
+
OK) echo -e " ${GREEN}${SYM_OK}${NC} $detail" ;;
|
|
1205
|
+
WARN) echo -e " ${YELLOW}${SYM_WARN}${NC} $detail" ;;
|
|
1206
|
+
FAIL) echo -e " ${RED}${SYM_FAIL}${NC} $detail" ;;
|
|
1207
|
+
esac
|
|
1208
|
+
done
|
|
1209
|
+
|
|
1210
|
+
[ $fails -gt 0 ] && return 1
|
|
1211
|
+
return 0
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
# =============================================================================
|
|
1215
|
+
# RESULTADO
|
|
1216
|
+
# =============================================================================
|
|
1217
|
+
|
|
1218
|
+
show_result() {
|
|
1219
|
+
local install_status=$1
|
|
1220
|
+
[ "$QUIET_MODE" = true ] && return
|
|
1221
|
+
|
|
1222
|
+
echo ""
|
|
1223
|
+
echo -e " ${DIM}────────────────────────────────────────${NC}"
|
|
1224
|
+
|
|
1225
|
+
if [ $install_status -eq 0 ] && verify_installation; then
|
|
1226
|
+
echo ""
|
|
1227
|
+
|
|
1228
|
+
# Success logo (brain/cortex shape, text centered vertically)
|
|
1229
|
+
echo -e "${CYAN} #######${NC}"
|
|
1230
|
+
echo -e "${CYAN} ### ########${NC}"
|
|
1231
|
+
echo -e "${CYAN} ######### #####${NC}"
|
|
1232
|
+
echo -e "${CYAN} ## ############## ${BOLD}N E O C O R T E X${NC}"
|
|
1233
|
+
echo -e "${CYAN} ## ### ###### ## ${BOLD}v${VERSION}${NC}"
|
|
1234
|
+
echo -e "${CYAN} ## ### ### ##${NC}"
|
|
1235
|
+
echo -e "${CYAN} ## ###### ### ## ${GREEN}${BOLD}Installation complete!${NC}"
|
|
1236
|
+
echo -e "${CYAN} ############### ## ${DIM}OrNexus Team${NC}"
|
|
1237
|
+
echo -e "${CYAN} ##### ########${NC}"
|
|
1238
|
+
echo -e "${CYAN} ######## ##${NC}"
|
|
1239
|
+
echo -e "${CYAN} #######${NC}"
|
|
1240
|
+
echo ""
|
|
1241
|
+
echo -e " ${DIM}Mode:${NC} Remote (thin client)"
|
|
1242
|
+
echo -e " ${DIM}Status:${NC} Ready to activate"
|
|
1243
|
+
echo ""
|
|
1244
|
+
echo -e " ${DIM}Activate your license:${NC}"
|
|
1245
|
+
echo -e " ${CYAN}neocortex activate YOUR-LICENSE-KEY${NC}"
|
|
1246
|
+
echo ""
|
|
1247
|
+
echo -e " ${DIM}Get your key at:${NC} ${CYAN}https://neocortex.ornexus.com/login${NC}"
|
|
1248
|
+
echo ""
|
|
1249
|
+
echo -e " ${DIM}After activation:${NC}"
|
|
1250
|
+
echo -e " ${CYAN}@neocortex *menu${NC}"
|
|
1251
|
+
echo ""
|
|
1252
|
+
else
|
|
1253
|
+
echo ""
|
|
1254
|
+
echo -e " ${RED}${BOLD}Instalacao com problemas${NC}"
|
|
1255
|
+
echo ""
|
|
1256
|
+
echo -e " Execute novamente com ${BOLD}--debug${NC} para detalhes:"
|
|
1257
|
+
echo -e " ${YELLOW}npx @ornexus/neocortex --debug${NC}"
|
|
1258
|
+
echo ""
|
|
1259
|
+
return 1
|
|
1260
|
+
fi
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
# =============================================================================
|
|
1264
|
+
# CRIACAO DE DIRETORIOS DO PROJETO
|
|
1265
|
+
# =============================================================================
|
|
1266
|
+
|
|
1267
|
+
create_project_dirs() {
|
|
1268
|
+
[ "$QUIET_MODE" = true ] && return
|
|
1269
|
+
[ "$SKIP_PROJECT_DIRS" = true ] && return
|
|
1270
|
+
|
|
1271
|
+
local should_create=false
|
|
1272
|
+
|
|
1273
|
+
if [ "$CREATE_PROJECT" = true ] || [ "$AUTO_YES" = true ]; then
|
|
1274
|
+
should_create=true
|
|
1275
|
+
else
|
|
1276
|
+
echo -ne " Instalar estrutura no projeto atual? ${BOLD}[s/N]:${NC} "
|
|
1277
|
+
|
|
1278
|
+
local response="n"
|
|
1279
|
+
if read -r -t 30 response </dev/tty 2>/dev/null; then
|
|
1280
|
+
:
|
|
1281
|
+
else
|
|
1282
|
+
echo ""
|
|
1283
|
+
response="n"
|
|
1284
|
+
fi
|
|
1285
|
+
|
|
1286
|
+
[[ "$response" =~ ^([sS][iI]?[mM]?|[yY][eE]?[sS]?)$ ]] && should_create=true
|
|
1287
|
+
fi
|
|
1288
|
+
|
|
1289
|
+
if [ "$should_create" = true ]; then
|
|
1290
|
+
local project_dir="$PWD"
|
|
1291
|
+
|
|
1292
|
+
# Clean previous project files
|
|
1293
|
+
rm -rf "$project_dir/targets/claude-code" 2>/dev/null
|
|
1294
|
+
rm -f "$project_dir/.cursor/agents/neocortex.md" 2>/dev/null
|
|
1295
|
+
rm -rf "$project_dir/.cursor/skills" 2>/dev/null
|
|
1296
|
+
rm -f "$project_dir/.github/agents/neocortex.md" 2>/dev/null
|
|
1297
|
+
rm -rf "$project_dir/.github/skills" 2>/dev/null
|
|
1298
|
+
rm -f "$project_dir/.github/copilot-instructions.md" 2>/dev/null
|
|
1299
|
+
rm -rf "$project_dir/.agent/skills/neocortex" 2>/dev/null
|
|
1300
|
+
rm -f "$project_dir/AGENTS.md" 2>/dev/null
|
|
1301
|
+
rm -rf "$project_dir/.agents/skills" 2>/dev/null
|
|
1302
|
+
rm -f "$project_dir/GEMINI.md" 2>/dev/null
|
|
1303
|
+
rm -rf "$project_dir/.claude/agents/neocortex" 2>/dev/null
|
|
1304
|
+
rm -rf "$project_dir/.claude/skills/neocortex" 2>/dev/null
|
|
1305
|
+
|
|
1306
|
+
# Thin-client: cleanup any legacy core/ from previous local installs
|
|
1307
|
+
rm -rf "$project_dir/core" 2>/dev/null
|
|
1308
|
+
|
|
1309
|
+
# Create base directories
|
|
1310
|
+
mkdir -p "$project_dir/.neocortex/specs" \
|
|
1311
|
+
"$project_dir/.neocortex/planning" \
|
|
1312
|
+
"$project_dir/docs/stories" \
|
|
1313
|
+
"$project_dir/docs/epics" \
|
|
1314
|
+
"$project_dir/docs/proposals" 2>/dev/null
|
|
1315
|
+
|
|
1316
|
+
# Copy state template if needed
|
|
1317
|
+
if [ ! -f "$project_dir/.neocortex/state.json" ]; then
|
|
1318
|
+
[ -f "$SOURCE_DIR/core/data/state-template.json" ] && \
|
|
1319
|
+
cp "$SOURCE_DIR/core/data/state-template.json" "$project_dir/.neocortex/state.json"
|
|
1320
|
+
fi
|
|
1321
|
+
|
|
1322
|
+
# Thin-client: never copy core/ to project
|
|
1323
|
+
|
|
1324
|
+
# Install target-specific files
|
|
1325
|
+
local targets_list
|
|
1326
|
+
targets_list=$(echo "$SELECTED_TARGETS" | tr ',' ' ')
|
|
1327
|
+
local target_summary=""
|
|
1328
|
+
|
|
1329
|
+
for target in $targets_list; do
|
|
1330
|
+
case "$target" in
|
|
1331
|
+
claude-code)
|
|
1332
|
+
local claude_target_dir="$SOURCE_DIR/targets/claude-code"
|
|
1333
|
+
if [ -d "$claude_target_dir" ]; then
|
|
1334
|
+
mkdir -p "$project_dir/.claude/agents/neocortex"
|
|
1335
|
+
|
|
1336
|
+
# Tier 3: copiar apenas 2 stub files
|
|
1337
|
+
cp "$claude_target_dir/neocortex.md" "$project_dir/.claude/agents/neocortex/" 2>/dev/null
|
|
1338
|
+
cp "$claude_target_dir/neocortex.agent.yaml" "$project_dir/.claude/agents/neocortex/" 2>/dev/null
|
|
1339
|
+
# Cleanup workflow.md from previous versions
|
|
1340
|
+
rm -f "$project_dir/.claude/agents/neocortex/workflow.md" 2>/dev/null
|
|
1341
|
+
|
|
1342
|
+
# Dynamic description: patch tier
|
|
1343
|
+
patch_description_tier "$project_dir/.claude/agents/neocortex/neocortex.md"
|
|
1344
|
+
patch_description_tier "$project_dir/.claude/agents/neocortex/neocortex.agent.yaml"
|
|
1345
|
+
|
|
1346
|
+
# Thin-client: cleanup legacy IP from previous installs
|
|
1347
|
+
auto_cleanup_legacy_project "$project_dir"
|
|
1348
|
+
target_summary="${target_summary}claude-code, "
|
|
1349
|
+
fi
|
|
1350
|
+
;;
|
|
1351
|
+
cursor)
|
|
1352
|
+
# IP Protection: ALWAYS thin-client, zero IP on client
|
|
1353
|
+
local stub_adapter="$SOURCE_DIR/targets-stubs/cursor/install-cursor.sh"
|
|
1354
|
+
if [ -f "$stub_adapter" ]; then
|
|
1355
|
+
. "$stub_adapter"
|
|
1356
|
+
install_cursor "$SOURCE_DIR" "$project_dir"
|
|
1357
|
+
patch_description_tier "$project_dir/.cursor/agents/neocortex.md"
|
|
1358
|
+
target_summary="${target_summary}cursor, "
|
|
1359
|
+
fi
|
|
1360
|
+
;;
|
|
1361
|
+
vscode)
|
|
1362
|
+
local stub_adapter="$SOURCE_DIR/targets-stubs/vscode/install-vscode.sh"
|
|
1363
|
+
if [ -f "$stub_adapter" ]; then
|
|
1364
|
+
. "$stub_adapter"
|
|
1365
|
+
install_vscode "$SOURCE_DIR" "$project_dir"
|
|
1366
|
+
patch_description_tier "$project_dir/.github/agents/neocortex.md"
|
|
1367
|
+
target_summary="${target_summary}vscode, "
|
|
1368
|
+
fi
|
|
1369
|
+
;;
|
|
1370
|
+
gemini-cli)
|
|
1371
|
+
local stub_adapter="$SOURCE_DIR/targets-stubs/gemini-cli/install-gemini.sh"
|
|
1372
|
+
if [ -f "$stub_adapter" ]; then
|
|
1373
|
+
. "$stub_adapter"
|
|
1374
|
+
install_gemini "$SOURCE_DIR" "$project_dir"
|
|
1375
|
+
local gemini_home="${GEMINI_HOME:-$HOME/.gemini}"
|
|
1376
|
+
patch_description_tier "$gemini_home/agents/neocortex.md"
|
|
1377
|
+
target_summary="${target_summary}gemini-cli, "
|
|
1378
|
+
fi
|
|
1379
|
+
;;
|
|
1380
|
+
codex)
|
|
1381
|
+
local stub_adapter="$SOURCE_DIR/targets-stubs/codex/install-codex.sh"
|
|
1382
|
+
if [ -f "$stub_adapter" ]; then
|
|
1383
|
+
. "$stub_adapter"
|
|
1384
|
+
install_codex "$SOURCE_DIR" "$project_dir"
|
|
1385
|
+
patch_description_tier "$project_dir/AGENTS.md"
|
|
1386
|
+
local codex_home="${CODEX_HOME:-$HOME/.codex}"
|
|
1387
|
+
patch_description_tier "$codex_home/AGENTS.md"
|
|
1388
|
+
target_summary="${target_summary}codex, "
|
|
1389
|
+
fi
|
|
1390
|
+
;;
|
|
1391
|
+
antigravity)
|
|
1392
|
+
local stub_adapter="$SOURCE_DIR/targets-stubs/antigravity/install-antigravity.sh"
|
|
1393
|
+
if [ -f "$stub_adapter" ]; then
|
|
1394
|
+
. "$stub_adapter"
|
|
1395
|
+
install_antigravity "$SOURCE_DIR" "$project_dir"
|
|
1396
|
+
patch_description_tier "$project_dir/.agent/skills/neocortex/SKILL.md"
|
|
1397
|
+
patch_description_tier "$project_dir/GEMINI.md"
|
|
1398
|
+
target_summary="${target_summary}antigravity, "
|
|
1399
|
+
fi
|
|
1400
|
+
;;
|
|
1401
|
+
esac
|
|
1402
|
+
done
|
|
1403
|
+
|
|
1404
|
+
echo ""
|
|
1405
|
+
ok "Estrutura do projeto instalada ${DIM}(${target_summary%, })${NC}"
|
|
1406
|
+
echo ""
|
|
1407
|
+
info "Proximo passo: ${CYAN}@neocortex *init @docs/epics.md${NC}"
|
|
1408
|
+
fi
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
# =============================================================================
|
|
1412
|
+
# MIGRACAO
|
|
1413
|
+
# =============================================================================
|
|
1414
|
+
|
|
1415
|
+
show_migration_info() {
|
|
1416
|
+
[ "$QUIET_MODE" = true ] && return
|
|
1417
|
+
|
|
1418
|
+
local project_sources
|
|
1419
|
+
if project_sources=$(detect_project_migration_needs); then
|
|
1420
|
+
echo ""
|
|
1421
|
+
warn "${BOLD}Migracao detectada${NC}"
|
|
1422
|
+
for src in $project_sources; do
|
|
1423
|
+
info "Arquivo antigo: $src"
|
|
1424
|
+
done
|
|
1425
|
+
info "Execute: ${CYAN}@neocortex *init @docs/epics.md${NC} para migrar"
|
|
1426
|
+
fi
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
# =============================================================================
|
|
1430
|
+
# TIER-BASED PLATFORM GATING (Story 57.4)
|
|
1431
|
+
# =============================================================================
|
|
1432
|
+
|
|
1433
|
+
# Read tier from config.json (defaults to "free" if not found)
|
|
1434
|
+
get_tier_from_config() {
|
|
1435
|
+
local config_file="$HOME/.neocortex/config.json"
|
|
1436
|
+
if [ -f "$config_file" ]; then
|
|
1437
|
+
local tier
|
|
1438
|
+
tier=$(cat "$config_file" 2>/dev/null | grep -o '"tier"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
|
|
1439
|
+
[ -n "$tier" ] && echo "$tier" || echo "free"
|
|
1440
|
+
else
|
|
1441
|
+
echo "free"
|
|
1442
|
+
fi
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
# Check if a platform is allowed for the current tier
|
|
1446
|
+
# Returns 0 if allowed, 1 if blocked
|
|
1447
|
+
check_platform_tier() {
|
|
1448
|
+
local platform=$1
|
|
1449
|
+
local tier=$2
|
|
1450
|
+
case "$platform" in
|
|
1451
|
+
claude-code) return 0 ;; # Always allowed
|
|
1452
|
+
cursor|gemini-cli|codex)
|
|
1453
|
+
if [ "$tier" = "free" ]; then
|
|
1454
|
+
warn "$platform requer plano Pro. Plataforma disponivel: Claude Code."
|
|
1455
|
+
info "Upgrade: ${CYAN}https://neocortex.dev/pricing${NC}"
|
|
1456
|
+
return 1
|
|
1457
|
+
fi
|
|
1458
|
+
return 0
|
|
1459
|
+
;;
|
|
1460
|
+
vscode|antigravity)
|
|
1461
|
+
if [ "$tier" != "enterprise" ]; then
|
|
1462
|
+
warn "$platform requer plano Enterprise."
|
|
1463
|
+
info "Upgrade: ${CYAN}https://neocortex.dev/pricing${NC}"
|
|
1464
|
+
return 1
|
|
1465
|
+
fi
|
|
1466
|
+
return 0
|
|
1467
|
+
;;
|
|
1468
|
+
esac
|
|
1469
|
+
return 0 # Unknown platforms pass through
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
# =============================================================================
|
|
1473
|
+
# MAIN
|
|
1474
|
+
# =============================================================================
|
|
1475
|
+
|
|
1476
|
+
TOTAL_STEPS=4
|
|
1477
|
+
|
|
1478
|
+
main() {
|
|
1479
|
+
show_banner
|
|
1480
|
+
detect_source_dir
|
|
1481
|
+
detect_old_installation
|
|
1482
|
+
|
|
1483
|
+
# Determine targets
|
|
1484
|
+
if [ -z "$SELECTED_TARGETS" ]; then
|
|
1485
|
+
SELECTED_TARGETS="claude-code"
|
|
1486
|
+
fi
|
|
1487
|
+
|
|
1488
|
+
debug "Targets: $SELECTED_TARGETS"
|
|
1489
|
+
|
|
1490
|
+
# Read user tier for platform gating
|
|
1491
|
+
local user_tier
|
|
1492
|
+
user_tier=$(get_tier_from_config)
|
|
1493
|
+
debug "User tier: $user_tier"
|
|
1494
|
+
|
|
1495
|
+
# Validate targets (name check + tier check)
|
|
1496
|
+
local invalid_targets=""
|
|
1497
|
+
local blocked_targets=""
|
|
1498
|
+
for target in $(echo "$SELECTED_TARGETS" | tr ',' ' '); do
|
|
1499
|
+
echo "$VALID_TARGETS" | grep -wq "$target" || invalid_targets="$invalid_targets $target"
|
|
1500
|
+
done
|
|
1501
|
+
|
|
1502
|
+
if [ -n "$invalid_targets" ]; then
|
|
1503
|
+
fail "Plataformas invalidas:$invalid_targets"
|
|
1504
|
+
fail "Validas: $VALID_TARGETS"
|
|
1505
|
+
exit 1
|
|
1506
|
+
fi
|
|
1507
|
+
|
|
1508
|
+
# Filter out tier-blocked platforms
|
|
1509
|
+
local allowed_targets=""
|
|
1510
|
+
for target in $(echo "$SELECTED_TARGETS" | tr ',' ' '); do
|
|
1511
|
+
if check_platform_tier "$target" "$user_tier"; then
|
|
1512
|
+
if [ -z "$allowed_targets" ]; then
|
|
1513
|
+
allowed_targets="$target"
|
|
1514
|
+
else
|
|
1515
|
+
allowed_targets="$allowed_targets,$target"
|
|
1516
|
+
fi
|
|
1517
|
+
else
|
|
1518
|
+
blocked_targets="$blocked_targets $target"
|
|
1519
|
+
fi
|
|
1520
|
+
done
|
|
1521
|
+
|
|
1522
|
+
if [ -n "$blocked_targets" ]; then
|
|
1523
|
+
info "Plataforma(s) bloqueada(s) por tier:$blocked_targets"
|
|
1524
|
+
fi
|
|
1525
|
+
|
|
1526
|
+
# Use only allowed targets
|
|
1527
|
+
if [ -n "$allowed_targets" ]; then
|
|
1528
|
+
SELECTED_TARGETS="$allowed_targets"
|
|
1529
|
+
else
|
|
1530
|
+
SELECTED_TARGETS="claude-code"
|
|
1531
|
+
info "Usando plataforma padrao: claude-code"
|
|
1532
|
+
fi
|
|
1533
|
+
|
|
1534
|
+
# Count targets for step total
|
|
1535
|
+
local target_count=$(echo "$SELECTED_TARGETS" | tr ',' ' ' | wc -w | tr -d ' ')
|
|
1536
|
+
if echo "$SELECTED_TARGETS" | grep -q "claude-code"; then
|
|
1537
|
+
TOTAL_STEPS=6 # cleanup + core + skills + targets + mcps + tools
|
|
1538
|
+
fi
|
|
1539
|
+
|
|
1540
|
+
# Step 1: Limpeza automatica de versoes anteriores
|
|
1541
|
+
step 1 $TOTAL_STEPS "Limpeza de versoes anteriores"
|
|
1542
|
+
auto_cleanup_legacy
|
|
1543
|
+
|
|
1544
|
+
# Step 2: Core
|
|
1545
|
+
step 2 $TOTAL_STEPS "Instalando core"
|
|
1546
|
+
install_core
|
|
1547
|
+
local core_result=$?
|
|
1548
|
+
if [ $core_result -ne 0 ]; then
|
|
1549
|
+
fail "Falha na instalacao do core"
|
|
1550
|
+
exit 1
|
|
1551
|
+
fi
|
|
1552
|
+
|
|
1553
|
+
# Step 3: Skills
|
|
1554
|
+
step 3 $TOTAL_STEPS "Instalando skills"
|
|
1555
|
+
install_skills
|
|
1556
|
+
|
|
1557
|
+
# Step 4: Targets
|
|
1558
|
+
step 4 $TOTAL_STEPS "Instalando ${BOLD}$target_count${NC} plataforma(s)"
|
|
1559
|
+
install_targets "$SELECTED_TARGETS"
|
|
1560
|
+
|
|
1561
|
+
# Step 5-6: Claude Code extras
|
|
1562
|
+
if echo "$SELECTED_TARGETS" | grep -q "claude-code"; then
|
|
1563
|
+
load_env_file
|
|
1564
|
+
prompt_tokens
|
|
1565
|
+
|
|
1566
|
+
step 5 $TOTAL_STEPS "Configurando MCPs"
|
|
1567
|
+
install_mcps
|
|
1568
|
+
|
|
1569
|
+
step 6 $TOTAL_STEPS "Verificando ferramentas"
|
|
1570
|
+
install_coderabbit
|
|
1571
|
+
fi
|
|
1572
|
+
|
|
1573
|
+
# Result
|
|
1574
|
+
show_result $core_result
|
|
1575
|
+
local result_code=$?
|
|
1576
|
+
|
|
1577
|
+
if [ $result_code -eq 0 ]; then
|
|
1578
|
+
show_migration_info
|
|
1579
|
+
create_project_dirs
|
|
1580
|
+
echo -e " ${DIM}Desenvolvido por OrNexus Team${NC}"
|
|
1581
|
+
echo ""
|
|
1582
|
+
fi
|
|
1583
|
+
|
|
1584
|
+
exit $result_code
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
main
|