@ornexus/neocortex 4.59.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.
- package/LICENSE +56 -0
- package/LICENSE-COMMERCIAL.md +70 -0
- package/README.md +58 -0
- package/dist/sbom.cdx.json +7067 -0
- package/docs/install/coderabbit-manual-setup.md +86 -0
- package/docs/install/installer-diagnostics.md +107 -0
- package/docs/install/linux-global-install.md +97 -0
- package/install.js +572 -0
- package/install.ps1 +2214 -0
- package/install.sh +2013 -0
- package/package.json +118 -0
- package/packages/client/dist/adapters/adapter-registry.d.ts +61 -0
- package/packages/client/dist/adapters/adapter-registry.js +1 -0
- package/packages/client/dist/adapters/antigravity-adapter.d.ts +18 -0
- package/packages/client/dist/adapters/antigravity-adapter.js +2 -0
- package/packages/client/dist/adapters/claude-code-adapter.d.ts +19 -0
- package/packages/client/dist/adapters/claude-code-adapter.js +3 -0
- package/packages/client/dist/adapters/codex-adapter.d.ts +19 -0
- package/packages/client/dist/adapters/codex-adapter.js +2 -0
- package/packages/client/dist/adapters/cursor-adapter.d.ts +19 -0
- package/packages/client/dist/adapters/cursor-adapter.js +4 -0
- package/packages/client/dist/adapters/gemini-adapter.d.ts +18 -0
- package/packages/client/dist/adapters/gemini-adapter.js +2 -0
- package/packages/client/dist/adapters/index.d.ts +19 -0
- package/packages/client/dist/adapters/index.js +1 -0
- package/packages/client/dist/adapters/platform-detector.d.ts +48 -0
- package/packages/client/dist/adapters/platform-detector.js +1 -0
- package/packages/client/dist/adapters/target-adapter.d.ts +70 -0
- package/packages/client/dist/adapters/target-adapter.js +0 -0
- package/packages/client/dist/adapters/vscode-adapter.d.ts +19 -0
- package/packages/client/dist/adapters/vscode-adapter.js +2 -0
- package/packages/client/dist/agent/refresh-stubs.d.ts +80 -0
- package/packages/client/dist/agent/refresh-stubs.js +2 -0
- package/packages/client/dist/agent/update-agent-yaml.d.ts +26 -0
- package/packages/client/dist/agent/update-agent-yaml.js +1 -0
- package/packages/client/dist/agent/update-description.d.ts +45 -0
- package/packages/client/dist/agent/update-description.js +1 -0
- package/packages/client/dist/cache/crypto-utils.d.ts +30 -0
- package/packages/client/dist/cache/crypto-utils.js +1 -0
- package/packages/client/dist/cache/encrypted-cache.d.ts +30 -0
- package/packages/client/dist/cache/encrypted-cache.js +1 -0
- package/packages/client/dist/cache/in-memory-asset-cache.d.ts +62 -0
- package/packages/client/dist/cache/in-memory-asset-cache.js +1 -0
- package/packages/client/dist/cache/index.d.ts +13 -0
- package/packages/client/dist/cache/index.js +1 -0
- package/packages/client/dist/cache/protected-pi-boundary.d.ts +19 -0
- package/packages/client/dist/cache/protected-pi-boundary.js +1 -0
- package/packages/client/dist/checkpoint/checkpoint-client-reader.d.ts +45 -0
- package/packages/client/dist/checkpoint/checkpoint-client-reader.js +2 -0
- package/packages/client/dist/checkpoint/index.d.ts +12 -0
- package/packages/client/dist/checkpoint/index.js +1 -0
- package/packages/client/dist/checkpoint/shared-checkpoint-types.d.ts +85 -0
- package/packages/client/dist/checkpoint/shared-checkpoint-types.js +1 -0
- package/packages/client/dist/cli.d.ts +14 -0
- package/packages/client/dist/cli.js +48 -0
- package/packages/client/dist/commands/activate.d.ts +55 -0
- package/packages/client/dist/commands/activate.js +8 -0
- package/packages/client/dist/commands/cache-status.d.ts +39 -0
- package/packages/client/dist/commands/cache-status.js +2 -0
- package/packages/client/dist/commands/invoke.d.ts +229 -0
- package/packages/client/dist/commands/invoke.js +63 -0
- package/packages/client/dist/commands/refresh-memory.d.ts +11 -0
- package/packages/client/dist/commands/refresh-memory.js +1 -0
- package/packages/client/dist/config/resolver-selection.d.ts +40 -0
- package/packages/client/dist/config/resolver-selection.js +1 -0
- package/packages/client/dist/config/secure-config.d.ts +78 -0
- package/packages/client/dist/config/secure-config.js +12 -0
- package/packages/client/dist/constants.d.ts +25 -0
- package/packages/client/dist/constants.js +1 -0
- package/packages/client/dist/context/context-collector.d.ts +28 -0
- package/packages/client/dist/context/context-collector.js +2 -0
- package/packages/client/dist/context/context-sanitizer.d.ts +28 -0
- package/packages/client/dist/context/context-sanitizer.js +1 -0
- package/packages/client/dist/continuity/continuity-client-state-store.d.ts +183 -0
- package/packages/client/dist/continuity/continuity-client-state-store.js +1 -0
- package/packages/client/dist/continuity/invoke-hooks.d.ts +18 -0
- package/packages/client/dist/continuity/invoke-hooks.js +1 -0
- package/packages/client/dist/continuity/migrations/001-initial-schema.d.ts +11 -0
- package/packages/client/dist/continuity/migrations/001-initial-schema.js +263 -0
- package/packages/client/dist/continuity/sqlite-store.d.ts +409 -0
- package/packages/client/dist/continuity/sqlite-store.js +226 -0
- package/packages/client/dist/errors/error-messages.d.ts +40 -0
- package/packages/client/dist/errors/error-messages.js +2 -0
- package/packages/client/dist/graph-retrieval/pre-command-hook.d.ts +31 -0
- package/packages/client/dist/graph-retrieval/pre-command-hook.js +1 -0
- package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.d.ts +77 -0
- package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -0
- package/packages/client/dist/i18n/first-run.d.ts +23 -0
- package/packages/client/dist/i18n/first-run.js +2 -0
- package/packages/client/dist/index.d.ts +56 -0
- package/packages/client/dist/index.js +1 -0
- package/packages/client/dist/license/index.d.ts +5 -0
- package/packages/client/dist/license/index.js +1 -0
- package/packages/client/dist/license/license-client.d.ts +79 -0
- package/packages/client/dist/license/license-client.js +1 -0
- package/packages/client/dist/machine/fingerprint.d.ts +34 -0
- package/packages/client/dist/machine/fingerprint.js +2 -0
- package/packages/client/dist/machine/index.d.ts +5 -0
- package/packages/client/dist/machine/index.js +1 -0
- package/packages/client/dist/memory/project-memory-writer.d.ts +74 -0
- package/packages/client/dist/memory/project-memory-writer.js +36 -0
- package/packages/client/dist/memory/shared-project-memory-types.d.ts +370 -0
- package/packages/client/dist/memory/shared-project-memory-types.js +2 -0
- package/packages/client/dist/policy/architecture-policy.d.ts +40 -0
- package/packages/client/dist/policy/architecture-policy.js +2 -0
- package/packages/client/dist/policy/index.d.ts +8 -0
- package/packages/client/dist/policy/index.js +1 -0
- package/packages/client/dist/policy/shared-policy-types.d.ts +89 -0
- package/packages/client/dist/policy/shared-policy-types.js +0 -0
- package/packages/client/dist/resilience/circuit-breaker.d.ts +70 -0
- package/packages/client/dist/resilience/circuit-breaker.js +1 -0
- package/packages/client/dist/resilience/degradation-manager.d.ts +67 -0
- package/packages/client/dist/resilience/degradation-manager.js +1 -0
- package/packages/client/dist/resilience/freshness-indicator.d.ts +59 -0
- package/packages/client/dist/resilience/freshness-indicator.js +1 -0
- package/packages/client/dist/resilience/index.d.ts +8 -0
- package/packages/client/dist/resilience/index.js +1 -0
- package/packages/client/dist/resilience/recovery-detector.d.ts +59 -0
- package/packages/client/dist/resilience/recovery-detector.js +1 -0
- package/packages/client/dist/resolvers/asset-resolver.d.ts +79 -0
- package/packages/client/dist/resolvers/asset-resolver.js +0 -0
- package/packages/client/dist/resolvers/local-resolver.d.ts +26 -0
- package/packages/client/dist/resolvers/local-resolver.js +8 -0
- package/packages/client/dist/resolvers/remote-resolver.d.ts +91 -0
- package/packages/client/dist/resolvers/remote-resolver.js +1 -0
- package/packages/client/dist/runner/cli.d.ts +121 -0
- package/packages/client/dist/runner/cli.js +20 -0
- package/packages/client/dist/runner/scheduler.d.ts +116 -0
- package/packages/client/dist/runner/scheduler.js +6 -0
- package/packages/client/dist/runner-cli.d.ts +9 -0
- package/packages/client/dist/runner-cli.js +3 -0
- package/packages/client/dist/state/project-state-snapshot.d.ts +15 -0
- package/packages/client/dist/state/project-state-snapshot.js +1 -0
- package/packages/client/dist/state/state-json-repair.d.ts +17 -0
- package/packages/client/dist/state/state-json-repair.js +3 -0
- package/packages/client/dist/telemetry/index.d.ts +5 -0
- package/packages/client/dist/telemetry/index.js +1 -0
- package/packages/client/dist/telemetry/offline-queue.d.ts +57 -0
- package/packages/client/dist/telemetry/offline-queue.js +1 -0
- package/packages/client/dist/tier/index.d.ts +5 -0
- package/packages/client/dist/tier/index.js +1 -0
- package/packages/client/dist/tier/tier-aware-client.d.ts +105 -0
- package/packages/client/dist/tier/tier-aware-client.js +1 -0
- package/packages/client/dist/types/index.d.ts +140 -0
- package/packages/client/dist/types/index.js +1 -0
- package/packages/client/dist/yoloop/discovery-hook.d.ts +85 -0
- package/packages/client/dist/yoloop/discovery-hook.js +2 -0
- package/packages/client/dist/yoloop/index.d.ts +10 -0
- package/packages/client/dist/yoloop/index.js +1 -0
- package/packages/client/dist/yoloop/invoke-hooks.d.ts +125 -0
- package/packages/client/dist/yoloop/invoke-hooks.js +5 -0
- package/packages/client/dist/yoloop/shared-discover-epics.d.ts +289 -0
- package/packages/client/dist/yoloop/shared-discover-epics.js +1 -0
- package/packages/client/dist/yoloop/shared-yoloop-types.d.ts +172 -0
- package/packages/client/dist/yoloop/shared-yoloop-types.js +1 -0
- package/packages/client/dist/yoloop/yoloop-client-state-store.d.ts +124 -0
- package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -0
- package/postinstall.js +754 -0
- package/targets-stubs/antigravity/README.md +36 -0
- package/targets-stubs/antigravity/gemini.md +29 -0
- package/targets-stubs/antigravity/install-antigravity.sh +153 -0
- package/targets-stubs/antigravity/mcp-config.json +30 -0
- package/targets-stubs/antigravity/skill/SKILL.md +159 -0
- package/targets-stubs/claude-code/.mcp.json +32 -0
- package/targets-stubs/claude-code/README.md +20 -0
- package/targets-stubs/claude-code/neocortex-root.agent.yaml +42 -0
- package/targets-stubs/claude-code/neocortex-root.md +310 -0
- package/targets-stubs/claude-code/neocortex.agent.yaml +42 -0
- package/targets-stubs/claude-code/neocortex.md +378 -0
- package/targets-stubs/codex/AGENTS.md +244 -0
- package/targets-stubs/codex/README.md +47 -0
- package/targets-stubs/codex/config-mcp.toml +22 -0
- package/targets-stubs/codex/install-codex.sh +63 -0
- package/targets-stubs/codex/neocortex.toml +29 -0
- package/targets-stubs/cursor/README.md +33 -0
- package/targets-stubs/cursor/agent.md +204 -0
- package/targets-stubs/cursor/install-cursor.sh +50 -0
- package/targets-stubs/cursor/mcp.json +30 -0
- package/targets-stubs/gemini-cli/README.md +34 -0
- package/targets-stubs/gemini-cli/agent.md +234 -0
- package/targets-stubs/gemini-cli/agents/neocortex.md +54 -0
- package/targets-stubs/gemini-cli/gemini.md +46 -0
- package/targets-stubs/gemini-cli/install-gemini.sh +70 -0
- package/targets-stubs/gemini-cli/settings-mcp.json +30 -0
- package/targets-stubs/kimi/mcp.json +33 -0
- package/targets-stubs/kimi/neocortex.md +54 -0
- package/targets-stubs/lib/mcp-merge.js +189 -0
- package/targets-stubs/openclaw/README.md +12 -0
- package/targets-stubs/openclaw/SKILL.md +88 -0
- package/targets-stubs/opencode/neocortex-root.md +261 -0
- package/targets-stubs/opencode/neocortex.md +59 -0
- package/targets-stubs/opencode/opencode-mcp.json +35 -0
- package/targets-stubs/vscode/README.md +34 -0
- package/targets-stubs/vscode/copilot-instructions.md +47 -0
- package/targets-stubs/vscode/install-vscode.sh +72 -0
- package/targets-stubs/vscode/mcp.json +36 -0
- package/targets-stubs/vscode/neocortex.agent.md +245 -0
package/install.sh
ADDED
|
@@ -0,0 +1,2013 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Neocortex - Instalador Unix
|
|
4
|
+
# Development Orchestrator
|
|
5
|
+
|
|
6
|
+
# Versao do instalador
|
|
7
|
+
VERSION="4.59.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 gemini-cli codex antigravity opencode kimi openclaw"
|
|
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
|
+
DRY_RUN=false
|
|
53
|
+
|
|
54
|
+
while [[ $# -gt 0 ]]; do
|
|
55
|
+
case $1 in
|
|
56
|
+
-y|--yes) AUTO_YES=true; shift ;;
|
|
57
|
+
-d|--debug) DEBUG_MODE=true; shift ;;
|
|
58
|
+
-s|--skip-project) SKIP_PROJECT_DIRS=true; shift ;;
|
|
59
|
+
-q|--quiet) QUIET_MODE=true; shift ;;
|
|
60
|
+
--dry-run) DRY_RUN=true; shift ;;
|
|
61
|
+
--no-banner) NO_BANNER=true; shift ;;
|
|
62
|
+
--cleanup-legacy) CLEANUP_LEGACY=true; shift ;;
|
|
63
|
+
--targets=*) SELECTED_TARGETS="${1#--targets=}"; shift ;;
|
|
64
|
+
--create-project) CREATE_PROJECT=true; shift ;;
|
|
65
|
+
--local) warn "--local flag removed: thin-client mode is now mandatory (zero IP on client)"; shift ;;
|
|
66
|
+
--server-url=*) NEOCORTEX_SERVER_URL="${1#--server-url=}"; shift ;;
|
|
67
|
+
-h|--help)
|
|
68
|
+
echo ""
|
|
69
|
+
echo " Neocortex Installer v${VERSION}"
|
|
70
|
+
echo " Development Orchestrator"
|
|
71
|
+
echo ""
|
|
72
|
+
echo " Uso: npx @ornexus/neocortex [opcoes]"
|
|
73
|
+
echo ""
|
|
74
|
+
echo " Opcoes:"
|
|
75
|
+
echo " -y, --yes Modo automatico (Claude Code only)"
|
|
76
|
+
echo " --targets=<lista> Plataformas separadas por virgula"
|
|
77
|
+
echo " --create-project Instalar estrutura no projeto"
|
|
78
|
+
echo " -s, --skip-project Nao perguntar sobre projeto"
|
|
79
|
+
echo " -q, --quiet Modo silencioso"
|
|
80
|
+
echo " --dry-run Mostra adapters/stubs resolvidos sem modificar arquivos"
|
|
81
|
+
echo " --cleanup-legacy (Redundante) Limpeza agora e automatica"
|
|
82
|
+
echo " --local (Removido) Thin-client obrigatorio, zero IP no client"
|
|
83
|
+
echo " --server-url=<url> URL do server Neocortex (padrao: api.neocortex.sh)"
|
|
84
|
+
echo " -d, --debug Modo debug"
|
|
85
|
+
echo " -h, --help Mostra esta ajuda"
|
|
86
|
+
echo ""
|
|
87
|
+
echo " Plataformas: claude-code, cursor, vscode, gemini, codex, antigravity, opencode, kimi, openclaw"
|
|
88
|
+
exit 0
|
|
89
|
+
;;
|
|
90
|
+
*) shift ;;
|
|
91
|
+
esac
|
|
92
|
+
done
|
|
93
|
+
|
|
94
|
+
if ! is_interactive; then
|
|
95
|
+
AUTO_YES=true
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# =============================================================================
|
|
99
|
+
# CORES E SIMBOLOS
|
|
100
|
+
# =============================================================================
|
|
101
|
+
|
|
102
|
+
if supports_colors && [ "$QUIET_MODE" = false ]; then
|
|
103
|
+
RED='\033[0;31m'
|
|
104
|
+
GREEN='\033[0;32m'
|
|
105
|
+
YELLOW='\033[1;33m'
|
|
106
|
+
BLUE='\033[0;34m'
|
|
107
|
+
MAGENTA='\033[0;35m'
|
|
108
|
+
CYAN='\033[0;36m'
|
|
109
|
+
WHITE='\033[1;37m'
|
|
110
|
+
GRAY='\033[0;90m'
|
|
111
|
+
BOLD='\033[1m'
|
|
112
|
+
DIM='\033[2m'
|
|
113
|
+
NC='\033[0m'
|
|
114
|
+
else
|
|
115
|
+
RED='' GREEN='' YELLOW='' BLUE='' MAGENTA='' CYAN=''
|
|
116
|
+
WHITE='' GRAY='' BOLD='' DIM='' NC=''
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
if supports_unicode; then
|
|
120
|
+
SYM_OK="✓"
|
|
121
|
+
SYM_FAIL="✗"
|
|
122
|
+
SYM_WARN="!"
|
|
123
|
+
SYM_ARROW="→"
|
|
124
|
+
SYM_DOT="·"
|
|
125
|
+
SYM_SPINNER_FRAMES=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
|
126
|
+
else
|
|
127
|
+
SYM_OK="ok"
|
|
128
|
+
SYM_FAIL="x"
|
|
129
|
+
SYM_WARN="!"
|
|
130
|
+
SYM_ARROW="->"
|
|
131
|
+
SYM_DOT="."
|
|
132
|
+
SYM_SPINNER_FRAMES=('|' '/' '-' '\')
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# =============================================================================
|
|
136
|
+
# FUNCOES DE LOG
|
|
137
|
+
# =============================================================================
|
|
138
|
+
|
|
139
|
+
# Step header: [1/5] Installing core...
|
|
140
|
+
step() {
|
|
141
|
+
local num=$1 total=$2 msg=$3
|
|
142
|
+
echo ""
|
|
143
|
+
echo -e " ${BOLD}${CYAN}[$num/$total]${NC} ${BOLD}$msg${NC}"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# Success line
|
|
147
|
+
ok() { echo -e " ${GREEN}${SYM_OK}${NC} $1"; }
|
|
148
|
+
|
|
149
|
+
# Warning line
|
|
150
|
+
warn() { echo -e " ${YELLOW}${SYM_WARN}${NC} $1"; }
|
|
151
|
+
|
|
152
|
+
# Error line
|
|
153
|
+
fail() { echo -e " ${RED}${SYM_FAIL}${NC} $1"; }
|
|
154
|
+
|
|
155
|
+
# Info line
|
|
156
|
+
info() { echo -e " ${DIM}${SYM_ARROW} $1${NC}"; }
|
|
157
|
+
|
|
158
|
+
# Public-safe diagnostic helpers. Installer diagnostics must stay bounded and must
|
|
159
|
+
# not print raw child output, private paths, tokens or vendor onboarding text.
|
|
160
|
+
redact_public_text() {
|
|
161
|
+
local msg="${1:-}"
|
|
162
|
+
if [ -n "${HOME:-}" ]; then msg="${msg//$HOME/~}"; fi
|
|
163
|
+
if [ -n "${SOURCE_DIR:-}" ]; then msg="${msg//$SOURCE_DIR/<install-dir>}"; fi
|
|
164
|
+
if [ -n "${SCRIPT_DIR:-}" ]; then msg="${msg//$SCRIPT_DIR/<install-dir>}"; fi
|
|
165
|
+
if [ -n "${PWD:-}" ]; then msg="${msg//$PWD/<project-dir>}"; fi
|
|
166
|
+
printf '%s' "$msg" | sed -E \
|
|
167
|
+
-e 's#/home/[^[:space:]]+#<redacted-path>#g' \
|
|
168
|
+
-e 's#/Users/[^[:space:]]+#<redacted-path>#g' \
|
|
169
|
+
-e 's#/(tmp|media|mnt|workspace|workspaces)/[^[:space:]]+#<redacted-path>#g' \
|
|
170
|
+
-e 's#C:\\Users\\[^[:space:]]+#<redacted-path>#g' \
|
|
171
|
+
-e 's#[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}#<redacted-email>#g' \
|
|
172
|
+
-e 's#(^|[^A-Za-z0-9])(NX|ghp|github_pat|ctx7sk|sk|pk)[_-][A-Za-z0-9_.-]{8,}#\1<redacted-token>#g'
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
sanitize_diag_token() {
|
|
176
|
+
local value
|
|
177
|
+
value="$(redact_public_text "${1:-unknown}")"
|
|
178
|
+
value="$(printf '%s' "$value" | tr -c 'A-Za-z0-9_.:/-' '_')"
|
|
179
|
+
value="${value%%_}"
|
|
180
|
+
[ -n "$value" ] && printf '%s' "$value" || printf '%s' "unknown"
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
emit_installer_diagnostic() {
|
|
184
|
+
local tool phase status reason_code code signal timed_out retry_attempt timeout_ms summary doc_path
|
|
185
|
+
tool="$(sanitize_diag_token "${1:-neocortex}")"
|
|
186
|
+
phase="$(sanitize_diag_token "${2:-installer}")"
|
|
187
|
+
status="$(sanitize_diag_token "${3:-info}")"
|
|
188
|
+
reason_code="$(sanitize_diag_token "${4:-P177_PUBLIC_SAFE_LOG_REDACTED}")"
|
|
189
|
+
code="$(sanitize_diag_token "${5:-null}")"
|
|
190
|
+
signal="$(sanitize_diag_token "${6:-null}")"
|
|
191
|
+
timed_out="$(sanitize_diag_token "${7:-false}")"
|
|
192
|
+
retry_attempt="$(sanitize_diag_token "${8:-0}")"
|
|
193
|
+
timeout_ms="$(sanitize_diag_token "${9:-0}")"
|
|
194
|
+
summary="$(sanitize_diag_token "${10:-public_safe_bounded_diagnostic}")"
|
|
195
|
+
doc_path="$(sanitize_diag_token "${11:-docs/install/coderabbit-manual-setup.md}")"
|
|
196
|
+
echo " [neocortex-installer] tool=${tool} phase=${phase} status=${status} reasonCode=${reason_code} code=${code} signal=${signal} timedOut=${timed_out} retryAttempt=${retry_attempt} timeoutMs=${timeout_ms} docPath=${doc_path} redactionReasonCode=P177_PUBLIC_SAFE_LOG_REDACTED summary=${summary}"
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# Debug line
|
|
200
|
+
debug() { [ "$DEBUG_MODE" = true ] && echo -e " ${GRAY}[debug] $(redact_public_text "$1")${NC}"; }
|
|
201
|
+
|
|
202
|
+
get_effective_uid() {
|
|
203
|
+
if [ -n "${NEOCORTEX_TEST_EFFECTIVE_UID:-}" ]; then
|
|
204
|
+
printf '%s\n' "$NEOCORTEX_TEST_EFFECTIVE_UID"
|
|
205
|
+
return 0
|
|
206
|
+
fi
|
|
207
|
+
if command -v id >/dev/null 2>&1; then
|
|
208
|
+
id -u 2>/dev/null || true
|
|
209
|
+
return 0
|
|
210
|
+
fi
|
|
211
|
+
printf '\n'
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
get_platform_name() {
|
|
215
|
+
if [ -n "${NEOCORTEX_TEST_UNAME:-}" ]; then
|
|
216
|
+
printf '%s\n' "$NEOCORTEX_TEST_UNAME"
|
|
217
|
+
return 0
|
|
218
|
+
fi
|
|
219
|
+
uname -s 2>/dev/null || printf '\n'
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
is_linux_platform() {
|
|
223
|
+
[ "$(get_platform_name)" = "Linux" ]
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
is_root_like_home() {
|
|
227
|
+
case "${1:-}" in
|
|
228
|
+
""|"/"|"/root"|"/root/"*) return 0 ;;
|
|
229
|
+
*) return 1 ;;
|
|
230
|
+
esac
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
is_system_npm_prefix() {
|
|
234
|
+
case "${1:-}" in
|
|
235
|
+
"/usr"|"/usr/"*|"/usr/local"|"/usr/local/"*|"/opt"|"/opt/"*|"/root"|"/root/"*) return 0 ;;
|
|
236
|
+
*) return 1 ;;
|
|
237
|
+
esac
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
is_safe_nonroot_home() {
|
|
241
|
+
local candidate="${1:-}"
|
|
242
|
+
[ -n "$candidate" ] || return 1
|
|
243
|
+
is_root_like_home "$candidate" && return 1
|
|
244
|
+
case "$candidate" in
|
|
245
|
+
*$'\n'*|*":"*) return 1 ;;
|
|
246
|
+
esac
|
|
247
|
+
return 0
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
resolve_sudo_user_home() {
|
|
251
|
+
if [ -n "${NEOCORTEX_TEST_SUDO_HOME:-}" ]; then
|
|
252
|
+
printf '%s\n' "$NEOCORTEX_TEST_SUDO_HOME"
|
|
253
|
+
return 0
|
|
254
|
+
fi
|
|
255
|
+
[ -n "${SUDO_USER:-}" ] || return 1
|
|
256
|
+
[ "${SUDO_USER:-}" != "root" ] || return 1
|
|
257
|
+
if command -v getent >/dev/null 2>&1; then
|
|
258
|
+
getent passwd "$SUDO_USER" 2>/dev/null | cut -d: -f6
|
|
259
|
+
return 0
|
|
260
|
+
fi
|
|
261
|
+
return 1
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
warn_linux_root_home_context() {
|
|
265
|
+
is_linux_platform || return 0
|
|
266
|
+
|
|
267
|
+
local effective_uid sudo_context root_home system_prefix intended_home can_resolve_intended
|
|
268
|
+
effective_uid="$(get_effective_uid)"
|
|
269
|
+
sudo_context=false
|
|
270
|
+
root_home=false
|
|
271
|
+
system_prefix=false
|
|
272
|
+
can_resolve_intended=false
|
|
273
|
+
|
|
274
|
+
[ -n "${SUDO_USER:-}" ] && [ "${SUDO_USER:-}" != "root" ] && sudo_context=true
|
|
275
|
+
is_root_like_home "${HOME:-}" && root_home=true
|
|
276
|
+
is_system_npm_prefix "${npm_config_prefix:-}" && system_prefix=true
|
|
277
|
+
|
|
278
|
+
intended_home="$(resolve_sudo_user_home 2>/dev/null || true)"
|
|
279
|
+
if is_safe_nonroot_home "$intended_home"; then
|
|
280
|
+
can_resolve_intended=true
|
|
281
|
+
fi
|
|
282
|
+
|
|
283
|
+
if [ "$effective_uid" = "0" ] || [ "$sudo_context" = true ] || [ "$root_home" = true ] || [ "$system_prefix" = true ]; then
|
|
284
|
+
emit_installer_diagnostic "neocortex" "root-home-check" "warning" "P177_ROOT_HOME_DETECTED" "null" "null" "false" "0" "0" "root_home_context_detected" "docs/install/linux-global-install.md"
|
|
285
|
+
warn "P177_ROOT_HOME_DETECTED: Linux root/sudo install may write Neocortex user config under an elevated HOME."
|
|
286
|
+
info "Prefer a no-sudo npm user prefix or a Node version manager before retrying."
|
|
287
|
+
if [ "$sudo_context" = true ] && [ "$can_resolve_intended" = true ]; then
|
|
288
|
+
info "Intended-user HOME was detected but not printed; rerun without sudo or configure that user's npm prefix."
|
|
289
|
+
if [ "${NEOCORTEX_USE_SUDO_USER_HOME:-}" = "1" ] && [ "$effective_uid" != "0" ]; then
|
|
290
|
+
HOME="$intended_home"
|
|
291
|
+
export HOME
|
|
292
|
+
info "Using intended-user HOME because NEOCORTEX_USE_SUDO_USER_HOME=1 and the effective user is not root."
|
|
293
|
+
fi
|
|
294
|
+
else
|
|
295
|
+
info "Intended-user HOME unresolved [TBD]; review docs/install/linux-global-install.md before writing user config."
|
|
296
|
+
fi
|
|
297
|
+
fi
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
is_neocortex_source_project() {
|
|
301
|
+
local candidate_dir="$1"
|
|
302
|
+
local package_file="$candidate_dir/package.json"
|
|
303
|
+
|
|
304
|
+
[ -f "$package_file" ] || return 1
|
|
305
|
+
grep -q '"name"[[:space:]]*:[[:space:]]*"@ornexus/neocortex"' "$package_file" 2>/dev/null || return 1
|
|
306
|
+
[ -d "$candidate_dir/core" ] || return 1
|
|
307
|
+
[ -d "$candidate_dir/packages/server" ] || return 1
|
|
308
|
+
[ -d "$candidate_dir/targets-stubs" ] || return 1
|
|
309
|
+
[ -f "$candidate_dir/install.sh" ] || return 1
|
|
310
|
+
|
|
311
|
+
return 0
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
resolve_target_dir() {
|
|
315
|
+
local target="$1"
|
|
316
|
+
local stub_dir="$SOURCE_DIR/targets-stubs/$target"
|
|
317
|
+
local dev_dir="$SOURCE_DIR/targets/$target"
|
|
318
|
+
|
|
319
|
+
[ "$target" = "gemini" ] && stub_dir="$SOURCE_DIR/targets-stubs/gemini-cli" && dev_dir="$SOURCE_DIR/targets/gemini-cli"
|
|
320
|
+
|
|
321
|
+
if [ -d "$stub_dir" ]; then
|
|
322
|
+
printf '%s\n' "$stub_dir"
|
|
323
|
+
return 0
|
|
324
|
+
fi
|
|
325
|
+
|
|
326
|
+
if is_neocortex_source_project "$SOURCE_DIR" && [ -d "$dev_dir" ]; then
|
|
327
|
+
debug "Fallback dev targets/ para $target: $dev_dir"
|
|
328
|
+
printf '%s\n' "$dev_dir"
|
|
329
|
+
return 0
|
|
330
|
+
fi
|
|
331
|
+
|
|
332
|
+
warn "$target ${DIM}(adapter nao encontrado; paths tentados: $stub_dir, $dev_dir)${NC}"
|
|
333
|
+
return 1
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
resolve_target_adapter_script() {
|
|
337
|
+
local target="$1"
|
|
338
|
+
local adapter_name="$target"
|
|
339
|
+
[ "$target" = "gemini-cli" ] && adapter_name="gemini"
|
|
340
|
+
|
|
341
|
+
local target_dir
|
|
342
|
+
target_dir=$(resolve_target_dir "$target") || return 1
|
|
343
|
+
local adapter_script="$target_dir/install-${adapter_name}.sh"
|
|
344
|
+
if [ -f "$adapter_script" ]; then
|
|
345
|
+
printf '%s\n' "$adapter_script"
|
|
346
|
+
return 0
|
|
347
|
+
fi
|
|
348
|
+
warn "$target ${DIM}(adapter nao encontrado; path tentado: $adapter_script)${NC}"
|
|
349
|
+
return 1
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
# Spinner for background tasks
|
|
353
|
+
run_with_spinner() {
|
|
354
|
+
local msg="$1"
|
|
355
|
+
shift
|
|
356
|
+
"$@" &
|
|
357
|
+
local pid=$!
|
|
358
|
+
local i=0
|
|
359
|
+
local len=${#SYM_SPINNER_FRAMES[@]}
|
|
360
|
+
|
|
361
|
+
while kill -0 "$pid" 2>/dev/null; do
|
|
362
|
+
printf "\r ${CYAN}%s${NC} %s" "${SYM_SPINNER_FRAMES[$((i % len))]}" "$msg"
|
|
363
|
+
i=$((i + 1))
|
|
364
|
+
sleep 0.08
|
|
365
|
+
done
|
|
366
|
+
|
|
367
|
+
wait "$pid"
|
|
368
|
+
local exit_code=$?
|
|
369
|
+
|
|
370
|
+
if [ $exit_code -eq 0 ]; then
|
|
371
|
+
printf "\r ${GREEN}${SYM_OK}${NC} %s\n" "$msg"
|
|
372
|
+
else
|
|
373
|
+
printf "\r ${RED}${SYM_FAIL}${NC} %s\n" "$msg"
|
|
374
|
+
fi
|
|
375
|
+
|
|
376
|
+
return $exit_code
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
# =============================================================================
|
|
380
|
+
# FUNCOES DE COPIA (silenciosas - sem log individual)
|
|
381
|
+
# =============================================================================
|
|
382
|
+
|
|
383
|
+
copy_file() {
|
|
384
|
+
local src="$1" dest="$2"
|
|
385
|
+
debug "Copiando: $src -> $dest"
|
|
386
|
+
if [ -f "$src" ]; then
|
|
387
|
+
cp "$src" "$dest" 2>/dev/null
|
|
388
|
+
else
|
|
389
|
+
debug "Arquivo nao encontrado: $src"
|
|
390
|
+
return 1
|
|
391
|
+
fi
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
copy_dir() {
|
|
395
|
+
local src="$1" dest="$2"
|
|
396
|
+
debug "Copiando dir: $src -> $dest"
|
|
397
|
+
[ -d "$src" ] && cp -r "$src" "$dest" 2>/dev/null
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
# Patch description tier in any agent file (YAML frontmatter or Markdown H1)
|
|
401
|
+
# Usage: patch_description_tier <file>
|
|
402
|
+
# Reads tier from ~/.neocortex/config.json and replaces ANY tier label with actual tier.
|
|
403
|
+
# Supports: (Free) <-> (Pro) <-> (Enterprise) in any direction.
|
|
404
|
+
# Works on both frontmatter description and banner body lines.
|
|
405
|
+
patch_description_tier() {
|
|
406
|
+
local target_file="$1"
|
|
407
|
+
local config_file="${HOME}/.neocortex/config.json"
|
|
408
|
+
|
|
409
|
+
[ -f "$target_file" ] || return 0
|
|
410
|
+
[ -f "$config_file" ] || return 0
|
|
411
|
+
|
|
412
|
+
local tier
|
|
413
|
+
tier=$(cat "$config_file" 2>/dev/null | grep -o '"tier"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
|
|
414
|
+
[ -n "$tier" ] || return 0
|
|
415
|
+
|
|
416
|
+
local tier_label
|
|
417
|
+
case "$tier" in
|
|
418
|
+
free) tier_label="Free" ;;
|
|
419
|
+
pro) tier_label="Pro" ;;
|
|
420
|
+
enterprise) tier_label="Enterprise" ;;
|
|
421
|
+
*) return 0 ;;
|
|
422
|
+
esac
|
|
423
|
+
|
|
424
|
+
# Replace ANY tier label with the correct one in non-banner lines
|
|
425
|
+
# (frontmatter description, body text, H1 headers, etc.)
|
|
426
|
+
# Each sed expression replaces one possible source tier with the target tier.
|
|
427
|
+
# If source == target, sed is a no-op (replaces X with X). This is correct.
|
|
428
|
+
sed -i.bak \
|
|
429
|
+
-e "s/(Free)/(${tier_label})/g" \
|
|
430
|
+
-e "s/(Pro)/(${tier_label})/g" \
|
|
431
|
+
-e "s/(Enterprise)/(${tier_label})/g" \
|
|
432
|
+
"$target_file" 2>/dev/null
|
|
433
|
+
rm -f "${target_file}.bak" 2>/dev/null
|
|
434
|
+
|
|
435
|
+
# Fix banner alignment: the OrNexus Team line has a fixed 62-char frame.
|
|
436
|
+
# After replacing tier labels of different lengths, re-pad to maintain alignment.
|
|
437
|
+
# Pattern: "│ ## ### ###### ## OrNexus Team (Tier)<spaces>│"
|
|
438
|
+
# Inner width must be exactly 60 chars (│ + 60 + │ = 62).
|
|
439
|
+
if grep -q "OrNexus Team (${tier_label})" "$target_file" 2>/dev/null; then
|
|
440
|
+
local prefix="│ ## ### ###### ## " # 25 chars inner prefix
|
|
441
|
+
local content="OrNexus Team (${tier_label})"
|
|
442
|
+
local content_len=${#content}
|
|
443
|
+
local pad_len=$((60 - 25 - content_len))
|
|
444
|
+
local padding=""
|
|
445
|
+
local i
|
|
446
|
+
for ((i=0; i<pad_len; i++)); do padding="${padding} "; done
|
|
447
|
+
local new_line="${prefix}${content}${padding}│"
|
|
448
|
+
# Replace the (possibly misaligned) banner line with the correctly padded one
|
|
449
|
+
sed -i.bak -e "s|│ ## ### ###### ## OrNexus Team ([^)]*).*│|${new_line}|" "$target_file" 2>/dev/null
|
|
450
|
+
rm -f "${target_file}.bak" 2>/dev/null
|
|
451
|
+
fi
|
|
452
|
+
|
|
453
|
+
debug "Tier patched to (${tier_label}) in $(basename "$target_file")"
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
# =============================================================================
|
|
457
|
+
# DETECCAO DE VERSAO ANTIGA
|
|
458
|
+
# =============================================================================
|
|
459
|
+
|
|
460
|
+
detect_old_installation() {
|
|
461
|
+
debug "Verificando instalacoes antigas..."
|
|
462
|
+
|
|
463
|
+
local dest_dir="${HOME}/.claude/agents/neocortex"
|
|
464
|
+
|
|
465
|
+
if [ -f "$dest_dir/.version" ]; then
|
|
466
|
+
INSTALLED_VERSION=$(cat "$dest_dir/.version" 2>/dev/null)
|
|
467
|
+
local new_version=""
|
|
468
|
+
if [ -f "$SOURCE_DIR/package.json" ]; then
|
|
469
|
+
new_version=$(grep '"version"' "$SOURCE_DIR/package.json" 2>/dev/null | head -1 | sed 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
|
|
470
|
+
fi
|
|
471
|
+
if [ -n "$INSTALLED_VERSION" ] && [ -n "$new_version" ]; then
|
|
472
|
+
if [ "$INSTALLED_VERSION" = "$new_version" ]; then
|
|
473
|
+
info "Reinstalando v$new_version"
|
|
474
|
+
else
|
|
475
|
+
info "Atualizando v$INSTALLED_VERSION ${SYM_ARROW} v$new_version"
|
|
476
|
+
fi
|
|
477
|
+
fi
|
|
478
|
+
fi
|
|
479
|
+
|
|
480
|
+
# Check for old files
|
|
481
|
+
local old_files=()
|
|
482
|
+
if [ -f "$dest_dir/step-registry.json" ]; then
|
|
483
|
+
if ! grep -q "state.json" "$dest_dir/workflow.md" 2>/dev/null; then
|
|
484
|
+
old_files+=("workflow antigo")
|
|
485
|
+
fi
|
|
486
|
+
fi
|
|
487
|
+
|
|
488
|
+
if grep -r "orchestrator.db" "$dest_dir" 2>/dev/null | grep -v "backup" | grep -v "migrated" > /dev/null; then
|
|
489
|
+
old_files+=("referencias SQLite")
|
|
490
|
+
fi
|
|
491
|
+
|
|
492
|
+
if [ ${#old_files[@]} -gt 0 ]; then
|
|
493
|
+
MIGRATION_DETECTED=true
|
|
494
|
+
MIGRATION_SOURCES="${old_files[*]}"
|
|
495
|
+
debug "Detectado: $MIGRATION_SOURCES"
|
|
496
|
+
fi
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
# =============================================================================
|
|
500
|
+
# LIMPEZA AUTOMATICA DE ARTEFATOS LEGADOS (auto_cleanup_legacy)
|
|
501
|
+
# Unifica: detect_legacy_artifacts + cleanup_legacy_artifacts + cleanup_legacy_ip
|
|
502
|
+
# + cleanup_legacy_ip_project + npm globals + cross-platform cleanup
|
|
503
|
+
# Roda AUTOMATICAMENTE a cada instalacao - sem necessidade de --cleanup-legacy
|
|
504
|
+
# Idempotente: rodar multiplas vezes sem efeitos colaterais
|
|
505
|
+
# Seguro: NAO remove dados do usuario (stories, epics, state.json, config.json)
|
|
506
|
+
# =============================================================================
|
|
507
|
+
|
|
508
|
+
auto_cleanup_legacy() {
|
|
509
|
+
local removed=0
|
|
510
|
+
local claude_dir="$HOME/.claude"
|
|
511
|
+
local neocortex_dir="$claude_dir/agents/neocortex"
|
|
512
|
+
local skills_dir="$claude_dir/skills/neocortex"
|
|
513
|
+
|
|
514
|
+
debug "Executando limpeza automatica de artefatos legados..."
|
|
515
|
+
|
|
516
|
+
# ─── Helper: remove e loga ───────────────────────────────────────────
|
|
517
|
+
_remove_legacy() {
|
|
518
|
+
local target="$1"
|
|
519
|
+
local label="$2"
|
|
520
|
+
if [ -e "$target" ]; then
|
|
521
|
+
local display="${target/#$HOME/~}"
|
|
522
|
+
if rm -rf "$target" 2>/dev/null; then
|
|
523
|
+
info "Removido: $display ($label)"
|
|
524
|
+
removed=$((removed + 1))
|
|
525
|
+
else
|
|
526
|
+
debug "Falha ao remover: $display"
|
|
527
|
+
fi
|
|
528
|
+
fi
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
# ─── Categoria 1: Pacotes NPM globais legados ────────────────────────
|
|
532
|
+
if command -v npm >/dev/null 2>&1; then
|
|
533
|
+
# Verificar e remover pacotes npm globais legados
|
|
534
|
+
for pkg in "@ornexus/neocortex-cli" "@ornexus-ai/neocortex"; do
|
|
535
|
+
if npm list -g "$pkg" --depth=0 2>/dev/null | grep -q "$pkg"; then
|
|
536
|
+
if npm uninstall -g "$pkg" 2>/dev/null; then
|
|
537
|
+
info "Removido: $pkg (pacote npm global legado)"
|
|
538
|
+
removed=$((removed + 1))
|
|
539
|
+
else
|
|
540
|
+
debug "Falha ao remover pacote npm: $pkg"
|
|
541
|
+
fi
|
|
542
|
+
fi
|
|
543
|
+
done
|
|
544
|
+
|
|
545
|
+
# Verificar binario neocortex-cli no PATH
|
|
546
|
+
local ncli_path
|
|
547
|
+
ncli_path=$(command -v neocortex-cli 2>/dev/null || true)
|
|
548
|
+
if [ -n "$ncli_path" ]; then
|
|
549
|
+
# Verificar se e um link/binario npm (nao remover se for outro tool)
|
|
550
|
+
if [[ "$ncli_path" == *"node_modules"* ]] || [[ "$ncli_path" == *"npm"* ]]; then
|
|
551
|
+
rm -f "$ncli_path" 2>/dev/null && {
|
|
552
|
+
info "Removido: $ncli_path (binario neocortex-cli legado)"
|
|
553
|
+
removed=$((removed + 1))
|
|
554
|
+
}
|
|
555
|
+
fi
|
|
556
|
+
fi
|
|
557
|
+
fi
|
|
558
|
+
|
|
559
|
+
# ─── Categoria 2: Claude Code (~/.claude/) ────────────────────────────
|
|
560
|
+
# IP proprietaria de versoes anteriores
|
|
561
|
+
_remove_legacy "$neocortex_dir/core" "IP legada"
|
|
562
|
+
_remove_legacy "$skills_dir" "skills legadas"
|
|
563
|
+
_remove_legacy "$neocortex_dir/workflow.md" "workflow removido no Tier 3"
|
|
564
|
+
_remove_legacy "$neocortex_dir/package.json" "arquivo desnecessario"
|
|
565
|
+
_remove_legacy "$neocortex_dir/README.md" "arquivo desnecessario"
|
|
566
|
+
|
|
567
|
+
# Step folders legados
|
|
568
|
+
for folder in steps-c steps-e steps-p steps-r steps-u; do
|
|
569
|
+
_remove_legacy "$neocortex_dir/$folder" "steps legados"
|
|
570
|
+
done
|
|
571
|
+
|
|
572
|
+
# Artefatos de instalacoes anteriores
|
|
573
|
+
_remove_legacy "$claude_dir/agents/.git" "repo git antigo"
|
|
574
|
+
_remove_legacy "$claude_dir/.claude" "diretorio aninhado (erro BMAD)"
|
|
575
|
+
_remove_legacy "$claude_dir/agents-ldtn" "diretorio legado"
|
|
576
|
+
_remove_legacy "$claude_dir/.superclaude-metadata.json" "metadata SuperClaude"
|
|
577
|
+
|
|
578
|
+
# Backups SuperClaude antigos
|
|
579
|
+
if [ -d "$claude_dir/backups" ]; then
|
|
580
|
+
for f in "$claude_dir/backups"/superclaude_backup_*.tar.gz; do
|
|
581
|
+
[ -f "$f" ] || continue
|
|
582
|
+
_remove_legacy "$f" "backup SuperClaude antigo"
|
|
583
|
+
done
|
|
584
|
+
fi
|
|
585
|
+
|
|
586
|
+
# ─── Categoria 3: Cursor ──────────────────────────────────────────────
|
|
587
|
+
_remove_legacy "$HOME/.cursor/neocortex" "configs Cursor legadas"
|
|
588
|
+
# .cursorrules com referencias neocortex (verificar antes de remover)
|
|
589
|
+
if [ -f "$HOME/.cursorrules" ]; then
|
|
590
|
+
if grep -qi "neocortex\|ornexus\|synapse" "$HOME/.cursorrules" 2>/dev/null; then
|
|
591
|
+
_remove_legacy "$HOME/.cursorrules" "cursorrules com refs legadas"
|
|
592
|
+
fi
|
|
593
|
+
fi
|
|
594
|
+
|
|
595
|
+
# ─── Categoria 4: VS Code ─────────────────────────────────────────────
|
|
596
|
+
_remove_legacy "$HOME/.vscode/neocortex" "configs VS Code legadas"
|
|
597
|
+
# .instructions.md com referencias neocortex antigo
|
|
598
|
+
if [ -f "$HOME/.instructions.md" ]; then
|
|
599
|
+
if grep -qi "neocortex\|ornexus\|synapse" "$HOME/.instructions.md" 2>/dev/null; then
|
|
600
|
+
_remove_legacy "$HOME/.instructions.md" "instructions.md com refs legadas"
|
|
601
|
+
fi
|
|
602
|
+
fi
|
|
603
|
+
|
|
604
|
+
# ─── Categoria 5: Gemini CLI ──────────────────────────────────────────
|
|
605
|
+
_remove_legacy "$HOME/.gemini/neocortex" "configs Gemini legadas"
|
|
606
|
+
|
|
607
|
+
# ─── Categoria 6: Codex ───────────────────────────────────────────────
|
|
608
|
+
_remove_legacy "$HOME/.codex/neocortex" "configs Codex legadas"
|
|
609
|
+
|
|
610
|
+
# ─── Categoria 7: Antigravity ─────────────────────────────────────────
|
|
611
|
+
# Configs legadas de Antigravity sao gerenciadas pelo adapter, sem path fixo global
|
|
612
|
+
|
|
613
|
+
# ─── Categoria 8: Plaintext cache cleanup (Epic 62 - GAP 1) ─────────
|
|
614
|
+
local cache_dir="$HOME/.neocortex/cache"
|
|
615
|
+
if [ -d "$cache_dir" ]; then
|
|
616
|
+
# Remove plaintext menu-cache.json
|
|
617
|
+
_remove_legacy "$cache_dir/menu-cache.json" "cache plaintext (menu)"
|
|
618
|
+
|
|
619
|
+
# Remove any non-.enc files in cache dir (excluding directories)
|
|
620
|
+
for cache_file in "$cache_dir"/*; do
|
|
621
|
+
[ -f "$cache_file" ] || continue
|
|
622
|
+
case "$cache_file" in
|
|
623
|
+
*.enc) continue ;; # Keep encrypted cache files
|
|
624
|
+
*) _remove_legacy "$cache_file" "cache plaintext" ;;
|
|
625
|
+
esac
|
|
626
|
+
done
|
|
627
|
+
fi
|
|
628
|
+
|
|
629
|
+
# ─── Resultado ────────────────────────────────────────────────────────
|
|
630
|
+
if [ $removed -gt 0 ]; then
|
|
631
|
+
ok "$removed artefato(s) legado(s) removido(s) automaticamente"
|
|
632
|
+
else
|
|
633
|
+
debug "Nenhum artefato legado encontrado"
|
|
634
|
+
fi
|
|
635
|
+
|
|
636
|
+
# Resetar contadores legados (compatibilidade)
|
|
637
|
+
LEGACY_ITEMS=""
|
|
638
|
+
LEGACY_WARNINGS=0
|
|
639
|
+
|
|
640
|
+
return 0
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
# Cleanup de IP legada em projetos individuais (project-level)
|
|
644
|
+
auto_cleanup_legacy_project() {
|
|
645
|
+
local project_dir="$1"
|
|
646
|
+
local neocortex_dir="$project_dir/.claude/agents/neocortex"
|
|
647
|
+
local skills_dir="$project_dir/.claude/skills/neocortex"
|
|
648
|
+
local cleaned=false
|
|
649
|
+
|
|
650
|
+
# Remover core/ e seus subdiretorios
|
|
651
|
+
if [ -d "$neocortex_dir/core" ]; then
|
|
652
|
+
rm -rf "$neocortex_dir/core"
|
|
653
|
+
cleaned=true
|
|
654
|
+
fi
|
|
655
|
+
|
|
656
|
+
# Remover step folders
|
|
657
|
+
for folder in steps-c steps-e steps-p steps-r steps-u; do
|
|
658
|
+
if [ -d "$neocortex_dir/$folder" ]; then
|
|
659
|
+
rm -rf "$neocortex_dir/$folder"
|
|
660
|
+
cleaned=true
|
|
661
|
+
fi
|
|
662
|
+
done
|
|
663
|
+
|
|
664
|
+
# Remover skills
|
|
665
|
+
if [ -d "$skills_dir" ]; then
|
|
666
|
+
rm -rf "$skills_dir"
|
|
667
|
+
cleaned=true
|
|
668
|
+
fi
|
|
669
|
+
|
|
670
|
+
# Remover arquivos desnecessarios
|
|
671
|
+
for file in package.json README.md workflow.md; do
|
|
672
|
+
if [ -f "$neocortex_dir/$file" ]; then
|
|
673
|
+
rm -f "$neocortex_dir/$file"
|
|
674
|
+
cleaned=true
|
|
675
|
+
fi
|
|
676
|
+
done
|
|
677
|
+
|
|
678
|
+
if [ "$cleaned" = true ]; then
|
|
679
|
+
info "IP proprietaria removida do projeto (agora servida via server remoto)"
|
|
680
|
+
fi
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
detect_project_migration_needs() {
|
|
684
|
+
local migration_needed=false
|
|
685
|
+
local sources=()
|
|
686
|
+
|
|
687
|
+
[ -f ".neocortex/orchestrator.db" ] && { migration_needed=true; sources+=("orchestrator.db"); }
|
|
688
|
+
for yaml_path in "bmad-output/sprint-status.yaml" ".neocortex/sprint-status.yaml" "docs/sprint-status.yaml"; do
|
|
689
|
+
[ -f "$yaml_path" ] && { migration_needed=true; sources+=("$yaml_path"); }
|
|
690
|
+
done
|
|
691
|
+
|
|
692
|
+
if [ "$migration_needed" = true ]; then
|
|
693
|
+
echo "${sources[*]}"
|
|
694
|
+
return 0
|
|
695
|
+
fi
|
|
696
|
+
return 1
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
# =============================================================================
|
|
700
|
+
# BANNER
|
|
701
|
+
# =============================================================================
|
|
702
|
+
|
|
703
|
+
show_banner() {
|
|
704
|
+
[ "$QUIET_MODE" = true ] && return
|
|
705
|
+
[ "$NO_BANNER" = true ] && return
|
|
706
|
+
|
|
707
|
+
echo ""
|
|
708
|
+
echo -e "${CYAN} #######${NC}"
|
|
709
|
+
echo -e "${CYAN} ### ########${NC}"
|
|
710
|
+
echo -e "${CYAN} ######### #####${NC}"
|
|
711
|
+
echo -e "${CYAN} ## ############## ${BOLD}N E O C O R T E X${NC}"
|
|
712
|
+
echo -e "${CYAN} ## ### ###### ## ${BOLD}v${VERSION}${NC}"
|
|
713
|
+
echo -e "${CYAN} ## ### ### ##${NC}"
|
|
714
|
+
echo -e "${CYAN} ## ###### ### ## ${DIM}OrNexus Team${NC}"
|
|
715
|
+
echo -e "${CYAN} ############### ##${NC}"
|
|
716
|
+
echo -e "${CYAN} ##### ########${NC}"
|
|
717
|
+
echo -e "${CYAN} ######## ##${NC}"
|
|
718
|
+
echo -e "${CYAN} #######${NC}"
|
|
719
|
+
echo ""
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
# =============================================================================
|
|
723
|
+
# DETECCAO DO DIRETORIO FONTE
|
|
724
|
+
# =============================================================================
|
|
725
|
+
|
|
726
|
+
detect_source_dir() {
|
|
727
|
+
if [ -n "${BASH_SOURCE[0]}" ]; then
|
|
728
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)"
|
|
729
|
+
elif [ -n "$0" ]; then
|
|
730
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" 2>/dev/null && pwd)"
|
|
731
|
+
else
|
|
732
|
+
SCRIPT_DIR="$PWD"
|
|
733
|
+
fi
|
|
734
|
+
|
|
735
|
+
local source_found=false
|
|
736
|
+
if [ -f "$SCRIPT_DIR/targets-stubs/claude-code/neocortex.md" ] || \
|
|
737
|
+
[ -f "$SCRIPT_DIR/neocortex.md" ]; then
|
|
738
|
+
source_found=true
|
|
739
|
+
else
|
|
740
|
+
for possible_dir in \
|
|
741
|
+
"$SCRIPT_DIR" \
|
|
742
|
+
"$(npm root -g 2>/dev/null)/@ornexus/neocortex" \
|
|
743
|
+
"$(npm root -g 2>/dev/null)/neocortex" \
|
|
744
|
+
"$(dirname "$0")" \
|
|
745
|
+
"$PWD/node_modules/@ornexus/neocortex" \
|
|
746
|
+
"$PWD/node_modules/neocortex" \
|
|
747
|
+
"$HOME/.npm/_npx/"*"/node_modules/@ornexus/neocortex" \
|
|
748
|
+
"$HOME/.npm/_npx/"*"/node_modules/neocortex"; do
|
|
749
|
+
if [ -f "$possible_dir/targets-stubs/claude-code/neocortex.md" ] 2>/dev/null || \
|
|
750
|
+
[ -f "$possible_dir/neocortex.md" ] 2>/dev/null; then
|
|
751
|
+
SCRIPT_DIR="$possible_dir"
|
|
752
|
+
source_found=true
|
|
753
|
+
break
|
|
754
|
+
fi
|
|
755
|
+
done
|
|
756
|
+
fi
|
|
757
|
+
|
|
758
|
+
SOURCE_DIR="$SCRIPT_DIR"
|
|
759
|
+
debug "Source: $SOURCE_DIR"
|
|
760
|
+
|
|
761
|
+
# Epic 65: Emit visible warning when stubs cannot be found
|
|
762
|
+
if [ "$source_found" = false ]; then
|
|
763
|
+
warn "Arquivos de instalacao (stubs) nao encontrados em nenhum path conhecido"
|
|
764
|
+
warn "SCRIPT_DIR: $SCRIPT_DIR"
|
|
765
|
+
warn "Tente reinstalar: npm install -g @ornexus/neocortex"
|
|
766
|
+
fi
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
show_target_resolution_dry_run() {
|
|
770
|
+
local targets="$1"
|
|
771
|
+
targets=$(echo "$targets" | tr ',' ' ')
|
|
772
|
+
info "Dry-run: nenhum arquivo do usuario sera modificado"
|
|
773
|
+
for target in $targets; do
|
|
774
|
+
local resolved
|
|
775
|
+
if resolved=$(resolve_target_dir "$target"); then
|
|
776
|
+
info "Dry-run target=$target adapter=$resolved scope=published-stub"
|
|
777
|
+
case "$target" in
|
|
778
|
+
cursor)
|
|
779
|
+
info "Dry-run MCP target=cursor file=${CURSOR_HOME:-$HOME/.cursor}/mcp.json root=mcpServers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom"
|
|
780
|
+
;;
|
|
781
|
+
vscode)
|
|
782
|
+
local vscode_mcp
|
|
783
|
+
if [ -n "$VSCODE_MCP_FILE" ]; then vscode_mcp="$VSCODE_MCP_FILE"; elif [ "$(uname -s 2>/dev/null)" = "Darwin" ]; then vscode_mcp="$HOME/Library/Application Support/Code/User/mcp.json"; else vscode_mcp="${XDG_CONFIG_HOME:-$HOME/.config}/Code/User/mcp.json"; fi
|
|
784
|
+
info "Dry-run MCP target=vscode file=$vscode_mcp root=servers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom"
|
|
785
|
+
;;
|
|
786
|
+
gemini|gemini-cli)
|
|
787
|
+
info "Dry-run MCP target=gemini file=${GEMINI_HOME:-$HOME/.gemini}/settings.json root=mcpServers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom"
|
|
788
|
+
;;
|
|
789
|
+
codex)
|
|
790
|
+
info "Dry-run MCP target=codex file=${CODEX_HOME:-$HOME/.codex}/config.toml root=mcp_servers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=replace-managed-sections-preserve-unknown"
|
|
791
|
+
;;
|
|
792
|
+
opencode)
|
|
793
|
+
info "Dry-run MCP target=opencode file=${OPENCODE_HOME:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/opencode.json root=mcp managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom"
|
|
794
|
+
;;
|
|
795
|
+
kimi)
|
|
796
|
+
info "Dry-run MCP target=kimi file=${KIMI_HOME:-$HOME/.kimi}/mcp.json root=mcpServers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom"
|
|
797
|
+
;;
|
|
798
|
+
openclaw)
|
|
799
|
+
info "Dry-run Skill target=openclaw file=${OPENCLAW_HOME:-$HOME/.openclaw}/skills/neocortex/SKILL.md action=copy-skill-preserve-openclaw-json config=${OPENCLAW_HOME:-$HOME/.openclaw}/openclaw.json non-overwrite"
|
|
800
|
+
info "Dry-run project target=openclaw file=.openclaw/skills/neocortex/SKILL.md action=copy-skill-when-create-project"
|
|
801
|
+
;;
|
|
802
|
+
esac
|
|
803
|
+
else
|
|
804
|
+
info "Dry-run target=$target adapter=NOT_FOUND"
|
|
805
|
+
fi
|
|
806
|
+
done
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
# =============================================================================
|
|
810
|
+
# TIER 3: CONFIG DO THIN CLIENT
|
|
811
|
+
# =============================================================================
|
|
812
|
+
|
|
813
|
+
setup_thin_client_config() {
|
|
814
|
+
local config_dir="${HOME}/.neocortex"
|
|
815
|
+
local config_file="${config_dir}/config.json"
|
|
816
|
+
|
|
817
|
+
mkdir -p "$config_dir" 2>/dev/null
|
|
818
|
+
mkdir -p "$config_dir/cache" 2>/dev/null
|
|
819
|
+
|
|
820
|
+
# Story 61.4 - F4 remediation: restrictive permissions
|
|
821
|
+
chmod 700 "$config_dir" 2>/dev/null
|
|
822
|
+
chmod 700 "$config_dir/cache" 2>/dev/null
|
|
823
|
+
|
|
824
|
+
if [ -f "$config_file" ]; then
|
|
825
|
+
# Preservar config existente, atualizar apenas serverUrl se necessario
|
|
826
|
+
local existing_mode
|
|
827
|
+
existing_mode=$(grep '"mode"' "$config_file" 2>/dev/null | head -1 | sed 's/.*"mode"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
|
|
828
|
+
|
|
829
|
+
if [ "$existing_mode" = "active" ] || [ "$existing_mode" = "local" ] || [ "$existing_mode" = "remote" ]; then
|
|
830
|
+
# ─── Config schema migration (Epic 62 - GAP 4) ────────────────
|
|
831
|
+
# Check if config needs schema migration
|
|
832
|
+
local has_config_version
|
|
833
|
+
has_config_version=$(grep '"configVersion"' "$config_file" 2>/dev/null)
|
|
834
|
+
if [ -z "$has_config_version" ]; then
|
|
835
|
+
debug "Migrando schema do config.json (adicionando configVersion)"
|
|
836
|
+
if command -v node >/dev/null 2>&1; then
|
|
837
|
+
node -e "
|
|
838
|
+
const fs = require('fs');
|
|
839
|
+
const path = '$config_file';
|
|
840
|
+
try {
|
|
841
|
+
const cfg = JSON.parse(fs.readFileSync(path, 'utf-8'));
|
|
842
|
+
// Add configVersion
|
|
843
|
+
cfg.configVersion = 1;
|
|
844
|
+
// Remove known obsolete fields from old base template
|
|
845
|
+
delete cfg.version;
|
|
846
|
+
delete cfg.cache;
|
|
847
|
+
// Clean obsolete tier:3 from old base template (preserve real tier values)
|
|
848
|
+
if (cfg.tier === 3 && cfg.mode === 'pending-activation') {
|
|
849
|
+
delete cfg.tier;
|
|
850
|
+
}
|
|
851
|
+
fs.writeFileSync(path, JSON.stringify(cfg, null, 2) + '\n');
|
|
852
|
+
}" 2>/dev/null
|
|
853
|
+
chmod 600 "$config_file" 2>/dev/null
|
|
854
|
+
debug "Config migrada para configVersion 1"
|
|
855
|
+
fi
|
|
856
|
+
fi
|
|
857
|
+
# ─── Fix stale localhost serverUrl (Epic 70 - Story 70.02) ────
|
|
858
|
+
# If config has localhost serverUrl and installer has production URL,
|
|
859
|
+
# update serverUrl while preserving all other config fields.
|
|
860
|
+
local existing_server_url
|
|
861
|
+
existing_server_url=$(grep '"serverUrl"' "$config_file" 2>/dev/null | head -1 | sed 's/.*"serverUrl"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
|
|
862
|
+
|
|
863
|
+
if echo "$existing_server_url" | grep -qE '^https?://(localhost|127\.0\.0\.1|0\.0\.0\.0)(:[0-9]+)?'; then
|
|
864
|
+
if [ "$NEOCORTEX_SERVER_URL" != "$existing_server_url" ]; then
|
|
865
|
+
debug "Fixing stale localhost serverUrl: $existing_server_url -> $NEOCORTEX_SERVER_URL"
|
|
866
|
+
if command -v node >/dev/null 2>&1; then
|
|
867
|
+
node -e "
|
|
868
|
+
const fs = require('fs');
|
|
869
|
+
const p = '$config_file';
|
|
870
|
+
try {
|
|
871
|
+
const raw = fs.readFileSync(p, 'utf-8').replace(/^\uFEFF/, '');
|
|
872
|
+
const cfg = JSON.parse(raw);
|
|
873
|
+
cfg.serverUrl = '${NEOCORTEX_SERVER_URL}';
|
|
874
|
+
fs.writeFileSync(p, JSON.stringify(cfg, null, 2) + '\n');
|
|
875
|
+
}" 2>/dev/null
|
|
876
|
+
chmod 600 "$config_file" 2>/dev/null
|
|
877
|
+
debug "serverUrl updated to $NEOCORTEX_SERVER_URL"
|
|
878
|
+
fi
|
|
879
|
+
fi
|
|
880
|
+
fi
|
|
881
|
+
|
|
882
|
+
# ─── Fix stale legacy serverUrl (Epic P46 - Story P46.03) ────
|
|
883
|
+
# If config has a deprecated API host, update to api.neocortex.sh.
|
|
884
|
+
local legacy_server_host
|
|
885
|
+
legacy_server_host="$(printf '%s.%s.%s.%s' api neocortex ornexus com)"
|
|
886
|
+
if echo "$existing_server_url" | grep -qF "$legacy_server_host"; then
|
|
887
|
+
debug "Fixing legacy serverUrl: $existing_server_url -> $NEOCORTEX_SERVER_URL"
|
|
888
|
+
if command -v node >/dev/null 2>&1; then
|
|
889
|
+
node -e "
|
|
890
|
+
const fs = require('fs');
|
|
891
|
+
const p = '$config_file';
|
|
892
|
+
try {
|
|
893
|
+
const raw = fs.readFileSync(p, 'utf-8').replace(/^\uFEFF/, '');
|
|
894
|
+
const cfg = JSON.parse(raw);
|
|
895
|
+
cfg.serverUrl = '${NEOCORTEX_SERVER_URL}';
|
|
896
|
+
fs.writeFileSync(p, JSON.stringify(cfg, null, 2) + '\n');
|
|
897
|
+
}" 2>/dev/null
|
|
898
|
+
chmod 600 "$config_file" 2>/dev/null
|
|
899
|
+
debug "serverUrl updated from legacy host to $NEOCORTEX_SERVER_URL"
|
|
900
|
+
fi
|
|
901
|
+
fi
|
|
902
|
+
|
|
903
|
+
# Story 61.4 - ensure permissions are always enforced even on existing configs
|
|
904
|
+
chmod 600 "$config_file" 2>/dev/null
|
|
905
|
+
debug "Config existente preservada (mode=$existing_mode)"
|
|
906
|
+
return 0
|
|
907
|
+
fi
|
|
908
|
+
fi
|
|
909
|
+
|
|
910
|
+
# Criar config base para thin client
|
|
911
|
+
cat > "$config_file" << EOFCONFIG
|
|
912
|
+
{
|
|
913
|
+
"configVersion": 1,
|
|
914
|
+
"mode": "pending-activation",
|
|
915
|
+
"serverUrl": "${NEOCORTEX_SERVER_URL}",
|
|
916
|
+
"resilience": {
|
|
917
|
+
"circuitBreaker": true,
|
|
918
|
+
"maxRetries": 3,
|
|
919
|
+
"timeoutMs": 5000
|
|
920
|
+
},
|
|
921
|
+
"installedAt": "$(date -Iseconds 2>/dev/null || date '+%Y-%m-%dT%H:%M:%S')",
|
|
922
|
+
"installerVersion": "${VERSION}"
|
|
923
|
+
}
|
|
924
|
+
EOFCONFIG
|
|
925
|
+
|
|
926
|
+
# Story 61.4 - F4 remediation: config file readable only by owner
|
|
927
|
+
chmod 600 "$config_file" 2>/dev/null
|
|
928
|
+
|
|
929
|
+
debug "Thin client config criada: $config_file"
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
# cleanup_legacy_ip_project() substituida por auto_cleanup_legacy_project()
|
|
933
|
+
# (definida na secao de limpeza automatica acima)
|
|
934
|
+
|
|
935
|
+
# =============================================================================
|
|
936
|
+
# INSTALACAO CORE
|
|
937
|
+
# =============================================================================
|
|
938
|
+
|
|
939
|
+
install_core() {
|
|
940
|
+
local errors=0
|
|
941
|
+
|
|
942
|
+
CLAUDE_DIR="${HOME}/.claude"
|
|
943
|
+
AGENTS_DIR="${CLAUDE_DIR}/agents"
|
|
944
|
+
DEST_DIR="${AGENTS_DIR}/neocortex"
|
|
945
|
+
|
|
946
|
+
debug "HOME=$HOME | DEST=$DEST_DIR | MODE=thin-client"
|
|
947
|
+
|
|
948
|
+
# Create directories
|
|
949
|
+
if [ ! -d "$CLAUDE_DIR" ]; then
|
|
950
|
+
mkdir -p "$CLAUDE_DIR" 2>/dev/null || { fail "Falha ao criar $CLAUDE_DIR"; return 1; }
|
|
951
|
+
fi
|
|
952
|
+
mkdir -p "$AGENTS_DIR" 2>/dev/null || { fail "Falha ao criar $AGENTS_DIR"; return 1; }
|
|
953
|
+
|
|
954
|
+
# Clean previous installation
|
|
955
|
+
if [ -d "$DEST_DIR" ]; then
|
|
956
|
+
rm -rf "$DEST_DIR" 2>/dev/null
|
|
957
|
+
debug "Instalacao anterior removida"
|
|
958
|
+
fi
|
|
959
|
+
mkdir -p "$DEST_DIR" 2>/dev/null || { fail "Falha ao criar $DEST_DIR"; return 1; }
|
|
960
|
+
|
|
961
|
+
# Thin-client ONLY: zero IP on client, all content from server
|
|
962
|
+
setup_thin_client_config
|
|
963
|
+
|
|
964
|
+
# ─── Version-aware cache purge on upgrade (Epic 62 - GAP 2+3) ───────
|
|
965
|
+
local pkg_version=""
|
|
966
|
+
if [ -f "$SOURCE_DIR/package.json" ]; then
|
|
967
|
+
pkg_version=$(grep '"version"' "$SOURCE_DIR/package.json" 2>/dev/null | head -1 | sed 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
|
|
968
|
+
fi
|
|
969
|
+
|
|
970
|
+
if [ -n "$pkg_version" ]; then
|
|
971
|
+
local old_version=""
|
|
972
|
+
# Read existing .version from either location
|
|
973
|
+
if [ -f "$DEST_DIR/.version" ]; then
|
|
974
|
+
old_version=$(cat "$DEST_DIR/.version" 2>/dev/null | tr -d '[:space:]')
|
|
975
|
+
elif [ -f "$HOME/.neocortex/.version" ]; then
|
|
976
|
+
old_version=$(cat "$HOME/.neocortex/.version" 2>/dev/null | tr -d '[:space:]')
|
|
977
|
+
fi
|
|
978
|
+
|
|
979
|
+
# If version changed, purge all cache files
|
|
980
|
+
if [ -n "$old_version" ] && [ "$old_version" != "$pkg_version" ]; then
|
|
981
|
+
local cache_dir="$HOME/.neocortex/cache"
|
|
982
|
+
if [ -d "$cache_dir" ]; then
|
|
983
|
+
local purged=0
|
|
984
|
+
# Remove all .enc files
|
|
985
|
+
for enc_file in "$cache_dir"/*.enc; do
|
|
986
|
+
[ -f "$enc_file" ] || continue
|
|
987
|
+
rm -f "$enc_file" 2>/dev/null && purged=$((purged + 1))
|
|
988
|
+
done
|
|
989
|
+
# Remove menu-cache.json (redundancy with 62.1)
|
|
990
|
+
[ -f "$cache_dir/menu-cache.json" ] && rm -f "$cache_dir/menu-cache.json" 2>/dev/null && purged=$((purged + 1))
|
|
991
|
+
# Remove any other non-directory files
|
|
992
|
+
for cache_file in "$cache_dir"/*; do
|
|
993
|
+
[ -f "$cache_file" ] || continue
|
|
994
|
+
rm -f "$cache_file" 2>/dev/null && purged=$((purged + 1))
|
|
995
|
+
done
|
|
996
|
+
[ $purged -gt 0 ] && info "Cache purgado: versao alterada de $old_version para $pkg_version ($purged arquivo(s))"
|
|
997
|
+
fi
|
|
998
|
+
fi
|
|
999
|
+
|
|
1000
|
+
# Write version file
|
|
1001
|
+
echo "$pkg_version" > "$DEST_DIR/.version"
|
|
1002
|
+
fi
|
|
1003
|
+
|
|
1004
|
+
if [ $errors -eq 0 ]; then
|
|
1005
|
+
ok "Remote mode configured ${DIM}(thin client ready)${NC}"
|
|
1006
|
+
else
|
|
1007
|
+
fail "Core instalado com erros"
|
|
1008
|
+
fi
|
|
1009
|
+
|
|
1010
|
+
return $errors
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
# =============================================================================
|
|
1014
|
+
# INSTALACAO DE SKILLS
|
|
1015
|
+
# =============================================================================
|
|
1016
|
+
|
|
1017
|
+
install_skills() {
|
|
1018
|
+
local errors=0
|
|
1019
|
+
|
|
1020
|
+
SKILLS_DIR="${CLAUDE_DIR}/skills"
|
|
1021
|
+
SKILLS_DEST="${SKILLS_DIR}/neocortex"
|
|
1022
|
+
SKILLS_SOURCE="${SOURCE_DIR}/core/skills"
|
|
1023
|
+
|
|
1024
|
+
# Thin-client: skills delivered by remote server, never copied
|
|
1025
|
+
ok "Skills: delivered by remote server"
|
|
1026
|
+
return 0
|
|
1027
|
+
|
|
1028
|
+
mkdir -p "$SKILLS_DIR" 2>/dev/null || { fail "Falha ao criar $SKILLS_DIR"; return 1; }
|
|
1029
|
+
|
|
1030
|
+
# Clean previous
|
|
1031
|
+
[ -d "$SKILLS_DEST" ] && rm -rf "$SKILLS_DEST" 2>/dev/null
|
|
1032
|
+
|
|
1033
|
+
if cp -r "$SKILLS_SOURCE" "$SKILLS_DEST" 2>/dev/null; then
|
|
1034
|
+
# Count skills
|
|
1035
|
+
local skill_count=0
|
|
1036
|
+
for skill_file in "$SKILLS_DEST"/step-skills/*/*.md "$SKILLS_DEST"/domain-skills/*/*.md; do
|
|
1037
|
+
if [ -f "$skill_file" ] && [[ "$(basename "$skill_file")" != "_template.md" ]]; then
|
|
1038
|
+
((skill_count++))
|
|
1039
|
+
fi
|
|
1040
|
+
done
|
|
1041
|
+
ok "Skills instaladas ${DIM}($skill_count skills) [modo local]${NC}"
|
|
1042
|
+
else
|
|
1043
|
+
fail "Falha ao copiar skills"
|
|
1044
|
+
((errors++))
|
|
1045
|
+
fi
|
|
1046
|
+
|
|
1047
|
+
return $errors
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
# =============================================================================
|
|
1051
|
+
# INSTALACAO DE AGENT (Claude Code)
|
|
1052
|
+
# =============================================================================
|
|
1053
|
+
|
|
1054
|
+
install_agent() {
|
|
1055
|
+
local errors=0
|
|
1056
|
+
|
|
1057
|
+
CLAUDE_TARGET_DIR="$SOURCE_DIR/targets-stubs/claude-code"
|
|
1058
|
+
if [ ! -d "$CLAUDE_TARGET_DIR" ] && is_neocortex_source_project "$SOURCE_DIR" && [ -d "$SOURCE_DIR/targets/claude-code" ]; then
|
|
1059
|
+
CLAUDE_TARGET_DIR="$SOURCE_DIR/targets/claude-code"
|
|
1060
|
+
fi
|
|
1061
|
+
if [ ! -d "$CLAUDE_TARGET_DIR" ]; then
|
|
1062
|
+
CLAUDE_TARGET_DIR="$SOURCE_DIR"
|
|
1063
|
+
fi
|
|
1064
|
+
|
|
1065
|
+
# Epic 65: Early validation — if neither source file exists, emit clear error
|
|
1066
|
+
if [ ! -f "$CLAUDE_TARGET_DIR/neocortex.md" ] && [ ! -f "$CLAUDE_TARGET_DIR/neocortex.agent.yaml" ]; then
|
|
1067
|
+
fail "Arquivos fonte nao encontrados para target claude-code em: $CLAUDE_TARGET_DIR"
|
|
1068
|
+
fail "Diretorio de origem ($SOURCE_DIR) pode estar incompleto"
|
|
1069
|
+
fail "Tente reinstalar: npm install -g @ornexus/neocortex"
|
|
1070
|
+
return 2
|
|
1071
|
+
fi
|
|
1072
|
+
|
|
1073
|
+
# Tier 3 Stub-Only: copiar apenas 2 arquivos de interface (stubs minimos)
|
|
1074
|
+
copy_file "$CLAUDE_TARGET_DIR/neocortex.md" "$DEST_DIR/" || ((errors++))
|
|
1075
|
+
copy_file "$CLAUDE_TARGET_DIR/neocortex.agent.yaml" "$DEST_DIR/" || ((errors++))
|
|
1076
|
+
|
|
1077
|
+
# Epic 65: Post-copy verification
|
|
1078
|
+
if [ ! -f "$DEST_DIR/neocortex.md" ]; then
|
|
1079
|
+
fail "neocortex.md nao encontrado no destino apos copia: $DEST_DIR/"
|
|
1080
|
+
((errors++))
|
|
1081
|
+
fi
|
|
1082
|
+
if [ ! -f "$DEST_DIR/neocortex.agent.yaml" ]; then
|
|
1083
|
+
fail "neocortex.agent.yaml nao encontrado no destino apos copia: $DEST_DIR/"
|
|
1084
|
+
((errors++))
|
|
1085
|
+
fi
|
|
1086
|
+
|
|
1087
|
+
# Dynamic description: patch tier from existing config (if activated)
|
|
1088
|
+
patch_description_tier "$DEST_DIR/neocortex.md"
|
|
1089
|
+
patch_description_tier "$DEST_DIR/neocortex.agent.yaml"
|
|
1090
|
+
|
|
1091
|
+
# Cleanup: remover workflow.md de instalacoes anteriores (v3.8 -> v3.9)
|
|
1092
|
+
if [ -f "$DEST_DIR/workflow.md" ]; then
|
|
1093
|
+
rm -f "$DEST_DIR/workflow.md"
|
|
1094
|
+
[ "$QUIET_MODE" = false ] && echo " Removed workflow.md (content now server-side)"
|
|
1095
|
+
fi
|
|
1096
|
+
|
|
1097
|
+
# Thin-client ONLY: 2 stub files + server-side orchestration
|
|
1098
|
+
|
|
1099
|
+
return $errors
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
# =============================================================================
|
|
1103
|
+
# INSTALACAO DE TARGETS
|
|
1104
|
+
# =============================================================================
|
|
1105
|
+
|
|
1106
|
+
install_targets() {
|
|
1107
|
+
local targets="$1"
|
|
1108
|
+
local target_errors=0
|
|
1109
|
+
local target_count=0
|
|
1110
|
+
local target_results=()
|
|
1111
|
+
|
|
1112
|
+
targets=$(echo "$targets" | tr ',' ' ')
|
|
1113
|
+
|
|
1114
|
+
for target in $targets; do
|
|
1115
|
+
local func_target="$target"
|
|
1116
|
+
[ "$target" = "gemini-cli" ] && func_target="gemini"
|
|
1117
|
+
local func_name="install_$(echo "$func_target" | tr '-' '_')"
|
|
1118
|
+
local adapter_script=""
|
|
1119
|
+
|
|
1120
|
+
case "$target" in
|
|
1121
|
+
opencode)
|
|
1122
|
+
local opencode_target="$SOURCE_DIR/targets-stubs/opencode"
|
|
1123
|
+
local opencode_home="${OPENCODE_HOME:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}"
|
|
1124
|
+
if [ -f "$opencode_target/neocortex.md" ] && [ -f "$opencode_target/neocortex-root.md" ]; then
|
|
1125
|
+
mkdir -p "$opencode_home/agents" 2>/dev/null
|
|
1126
|
+
if cp "$opencode_target/neocortex.md" "$opencode_home/agents/neocortex.md" 2>/dev/null && \
|
|
1127
|
+
cp "$opencode_target/neocortex-root.md" "$opencode_home/agents/neocortex-root.md" 2>/dev/null; then
|
|
1128
|
+
if [ -f "$opencode_target/opencode-mcp.json" ] && command -v node >/dev/null 2>&1; then
|
|
1129
|
+
node "$SOURCE_DIR/targets-stubs/lib/mcp-merge.js" json "$opencode_target/opencode-mcp.json" "$opencode_home/opencode.json" mcp OPENCODE || true
|
|
1130
|
+
fi
|
|
1131
|
+
patch_description_tier "$opencode_home/agents/neocortex.md"
|
|
1132
|
+
patch_description_tier "$opencode_home/agents/neocortex-root.md"
|
|
1133
|
+
target_results+=("$target:OK")
|
|
1134
|
+
((target_count++))
|
|
1135
|
+
ok "${BOLD}opencode${NC} ${DIM}(root + subagent)${NC}"
|
|
1136
|
+
else
|
|
1137
|
+
target_results+=("$target:SKIP")
|
|
1138
|
+
warn "opencode ${DIM}(perfil indisponivel)${NC}"
|
|
1139
|
+
fi
|
|
1140
|
+
else
|
|
1141
|
+
target_results+=("$target:SKIP")
|
|
1142
|
+
warn "opencode ${DIM}(root/subagent stubs nao encontrados)${NC}"
|
|
1143
|
+
fi
|
|
1144
|
+
continue
|
|
1145
|
+
;;
|
|
1146
|
+
kimi)
|
|
1147
|
+
local kimi_target="$SOURCE_DIR/targets-stubs/kimi"
|
|
1148
|
+
local kimi_home="${KIMI_HOME:-$HOME/.kimi}"
|
|
1149
|
+
if [ -f "$kimi_target/neocortex.md" ]; then
|
|
1150
|
+
mkdir -p "$kimi_home/skills/neocortex" "$HOME/.config/agents/skills/neocortex" 2>/dev/null
|
|
1151
|
+
local kimi_ok=0
|
|
1152
|
+
cp "$kimi_target/neocortex.md" "$kimi_home/skills/neocortex/SKILL.md" 2>/dev/null || kimi_ok=1
|
|
1153
|
+
cp "$kimi_target/neocortex.md" "$HOME/.config/agents/skills/neocortex/SKILL.md" 2>/dev/null || kimi_ok=1
|
|
1154
|
+
if [ -f "$kimi_target/mcp.json" ] && command -v node >/dev/null 2>&1; then
|
|
1155
|
+
node "$SOURCE_DIR/targets-stubs/lib/mcp-merge.js" json "$kimi_target/mcp.json" "$kimi_home/mcp.json" mcpServers KIMI || true
|
|
1156
|
+
fi
|
|
1157
|
+
if [ $kimi_ok -eq 0 ]; then
|
|
1158
|
+
patch_description_tier "$kimi_home/skills/neocortex/SKILL.md"
|
|
1159
|
+
patch_description_tier "$HOME/.config/agents/skills/neocortex/SKILL.md"
|
|
1160
|
+
target_results+=("$target:OK")
|
|
1161
|
+
((target_count++))
|
|
1162
|
+
ok "${BOLD}kimi${NC} ${DIM}(native-needs-validation skill)${NC}"
|
|
1163
|
+
else
|
|
1164
|
+
target_results+=("$target:SKIP")
|
|
1165
|
+
warn "kimi ${DIM}(perfil indisponivel)${NC}"
|
|
1166
|
+
fi
|
|
1167
|
+
else
|
|
1168
|
+
target_results+=("$target:SKIP")
|
|
1169
|
+
warn "kimi ${DIM}(adapter nao encontrado)${NC}"
|
|
1170
|
+
fi
|
|
1171
|
+
continue
|
|
1172
|
+
;;
|
|
1173
|
+
openclaw)
|
|
1174
|
+
local openclaw_target="$SOURCE_DIR/targets-stubs/openclaw"
|
|
1175
|
+
local openclaw_home="${OPENCLAW_HOME:-$HOME/.openclaw}"
|
|
1176
|
+
local openclaw_dest="$openclaw_home/skills/neocortex"
|
|
1177
|
+
if [ -f "$openclaw_target/SKILL.md" ]; then
|
|
1178
|
+
mkdir -p "$openclaw_dest" 2>/dev/null
|
|
1179
|
+
if cp "$openclaw_target/SKILL.md" "$openclaw_dest/SKILL.md" 2>/dev/null; then
|
|
1180
|
+
patch_description_tier "$openclaw_dest/SKILL.md"
|
|
1181
|
+
target_results+=("$target:OK")
|
|
1182
|
+
((target_count++))
|
|
1183
|
+
ok "${BOLD}openclaw${NC} ${DIM}(Skill thin-client; openclaw.json preservado)${NC}"
|
|
1184
|
+
else
|
|
1185
|
+
target_results+=("$target:SKIP")
|
|
1186
|
+
warn "openclaw ${DIM}(falha ao copiar Skill para $openclaw_dest/SKILL.md)${NC}"
|
|
1187
|
+
fi
|
|
1188
|
+
else
|
|
1189
|
+
target_results+=("$target:SKIP")
|
|
1190
|
+
warn "openclaw ${DIM}(Skill stub nao encontrado; path tentado: $openclaw_target/SKILL.md)${NC}"
|
|
1191
|
+
fi
|
|
1192
|
+
continue
|
|
1193
|
+
;;
|
|
1194
|
+
esac
|
|
1195
|
+
|
|
1196
|
+
if ! adapter_script=$(resolve_target_adapter_script "$target"); then
|
|
1197
|
+
if [ "$target" = "claude-code" ]; then
|
|
1198
|
+
install_agent
|
|
1199
|
+
local result=$?
|
|
1200
|
+
if [ $result -eq 0 ]; then
|
|
1201
|
+
target_results+=("$target:OK")
|
|
1202
|
+
((target_count++))
|
|
1203
|
+
ok "${BOLD}claude-code${NC} ${DIM}(thin client)${NC}"
|
|
1204
|
+
else
|
|
1205
|
+
target_results+=("$target:FAIL")
|
|
1206
|
+
((target_errors++))
|
|
1207
|
+
fail "claude-code"
|
|
1208
|
+
fi
|
|
1209
|
+
continue
|
|
1210
|
+
fi
|
|
1211
|
+
|
|
1212
|
+
target_results+=("$target:SKIP")
|
|
1213
|
+
((target_errors++))
|
|
1214
|
+
continue
|
|
1215
|
+
fi
|
|
1216
|
+
|
|
1217
|
+
. "$adapter_script"
|
|
1218
|
+
|
|
1219
|
+
if type "$func_name" >/dev/null 2>&1; then
|
|
1220
|
+
"$func_name" "$SOURCE_DIR" "$HOME"
|
|
1221
|
+
local result=$?
|
|
1222
|
+
if [ $result -eq 0 ]; then
|
|
1223
|
+
target_results+=("$target:OK")
|
|
1224
|
+
((target_count++))
|
|
1225
|
+
ok "${BOLD}$target${NC}"
|
|
1226
|
+
else
|
|
1227
|
+
target_results+=("$target:WARN")
|
|
1228
|
+
warn "$target ${DIM}(instalado com avisos)${NC}"
|
|
1229
|
+
((target_count++))
|
|
1230
|
+
fi
|
|
1231
|
+
else
|
|
1232
|
+
fail "$target ${DIM}(funcao $func_name nao encontrada)${NC}"
|
|
1233
|
+
target_results+=("$target:FAIL")
|
|
1234
|
+
((target_errors++))
|
|
1235
|
+
fi
|
|
1236
|
+
done
|
|
1237
|
+
|
|
1238
|
+
TARGET_RESULTS=("${target_results[@]}")
|
|
1239
|
+
TARGET_COUNT=$target_count
|
|
1240
|
+
|
|
1241
|
+
return $target_errors
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
# =============================================================================
|
|
1245
|
+
# CARREGAMENTO DE .ENV
|
|
1246
|
+
# =============================================================================
|
|
1247
|
+
|
|
1248
|
+
load_env_file() {
|
|
1249
|
+
local env_file=""
|
|
1250
|
+
for possible_env in "./.env" "$SOURCE_DIR/.env"; do
|
|
1251
|
+
[ -f "$possible_env" ] && { env_file="$possible_env"; break; }
|
|
1252
|
+
done
|
|
1253
|
+
|
|
1254
|
+
if [ -n "$env_file" ] && [ -f "$env_file" ]; then
|
|
1255
|
+
local loaded=0
|
|
1256
|
+
while IFS='=' read -r key value || [ -n "$key" ]; do
|
|
1257
|
+
[[ -z "$key" || "$key" =~ ^[[:space:]]*# ]] && continue
|
|
1258
|
+
key=$(echo "$key" | xargs)
|
|
1259
|
+
value=$(echo "$value" | xargs)
|
|
1260
|
+
value="${value%\"}"; value="${value#\"}"
|
|
1261
|
+
value="${value%\'}"; value="${value#\'}"
|
|
1262
|
+
if [ -n "$value" ] && [ -z "${!key}" ]; then
|
|
1263
|
+
export "$key=$value"
|
|
1264
|
+
((loaded++))
|
|
1265
|
+
fi
|
|
1266
|
+
done < "$env_file"
|
|
1267
|
+
[ $loaded -gt 0 ] && info "$loaded variavel(eis) carregada(s) do .env"
|
|
1268
|
+
return 0
|
|
1269
|
+
fi
|
|
1270
|
+
return 1
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
# =============================================================================
|
|
1274
|
+
# CONFIGURACAO DE TOKENS
|
|
1275
|
+
# =============================================================================
|
|
1276
|
+
|
|
1277
|
+
prompt_tokens() {
|
|
1278
|
+
[ "$QUIET_MODE" = true ] && return
|
|
1279
|
+
[ -n "$CONTEXT7_API_KEY" ] && return
|
|
1280
|
+
|
|
1281
|
+
if [ "$AUTO_YES" = true ]; then
|
|
1282
|
+
return
|
|
1283
|
+
fi
|
|
1284
|
+
|
|
1285
|
+
echo ""
|
|
1286
|
+
info "MCP Context7 nao configurado ${DIM}(opcional)${NC}"
|
|
1287
|
+
echo -ne " Configurar agora? ${BOLD}[s/N]:${NC} "
|
|
1288
|
+
|
|
1289
|
+
local response=""
|
|
1290
|
+
if read -r -t 15 response 2>/dev/null; then
|
|
1291
|
+
: # resposta recebida
|
|
1292
|
+
else
|
|
1293
|
+
echo ""
|
|
1294
|
+
return
|
|
1295
|
+
fi
|
|
1296
|
+
|
|
1297
|
+
if [[ "$response" =~ ^([sS][iI]?[mM]?|[yY][eE]?[sS]?)$ ]]; then
|
|
1298
|
+
echo ""
|
|
1299
|
+
info "Obtenha sua API Key em: ${WHITE}https://context7.com${NC}"
|
|
1300
|
+
echo -ne " Cole sua ${BOLD}CONTEXT7_API_KEY${NC}: "
|
|
1301
|
+
|
|
1302
|
+
local api_key=""
|
|
1303
|
+
if read -r -t 60 api_key 2>/dev/null; then
|
|
1304
|
+
if [[ "$api_key" =~ ^ctx7sk- ]]; then
|
|
1305
|
+
export CONTEXT7_API_KEY="$api_key"
|
|
1306
|
+
ok "CONTEXT7_API_KEY configurada"
|
|
1307
|
+
echo ""
|
|
1308
|
+
info "Para persistir: ${DIM}echo 'export CONTEXT7_API_KEY=\"$api_key\"' >> ~/.bashrc${NC}"
|
|
1309
|
+
elif [ -n "$api_key" ]; then
|
|
1310
|
+
warn "Formato invalido (esperado: ctx7sk-...)"
|
|
1311
|
+
fi
|
|
1312
|
+
fi
|
|
1313
|
+
fi
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
# =============================================================================
|
|
1317
|
+
# INSTALACAO DE MCP SERVERS
|
|
1318
|
+
# =============================================================================
|
|
1319
|
+
|
|
1320
|
+
install_mcps() {
|
|
1321
|
+
if ! command -v claude >/dev/null 2>&1; then
|
|
1322
|
+
warn "Claude CLI nao encontrado ${DIM}(MCPs Claude user-scope serao instalados depois; outros targets continuam)${NC}"
|
|
1323
|
+
return 0
|
|
1324
|
+
fi
|
|
1325
|
+
|
|
1326
|
+
local mcp_results=""
|
|
1327
|
+
local browser_use_env_args=()
|
|
1328
|
+
|
|
1329
|
+
[ -n "${OPENAI_API_KEY:-}" ] && browser_use_env_args+=(-e "OPENAI_API_KEY=$OPENAI_API_KEY")
|
|
1330
|
+
[ -n "${ANTHROPIC_API_KEY:-}" ] && browser_use_env_args+=(-e "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY")
|
|
1331
|
+
|
|
1332
|
+
if claude mcp add --scope user playwright -- npx -y @playwright/mcp@latest 2>/dev/null; then
|
|
1333
|
+
mcp_results="${mcp_results}playwright:OK "
|
|
1334
|
+
else
|
|
1335
|
+
mcp_results="${mcp_results}playwright:FAIL "
|
|
1336
|
+
fi
|
|
1337
|
+
|
|
1338
|
+
if [ -n "${CONTEXT7_API_KEY:-}" ]; then
|
|
1339
|
+
if claude mcp add --scope user --transport http context7 https://mcp.context7.com/mcp --header "CONTEXT7_API_KEY: $CONTEXT7_API_KEY" 2>/dev/null; then
|
|
1340
|
+
mcp_results="${mcp_results}context7:OK "
|
|
1341
|
+
else
|
|
1342
|
+
mcp_results="${mcp_results}context7:FAIL "
|
|
1343
|
+
fi
|
|
1344
|
+
else
|
|
1345
|
+
mcp_results="${mcp_results}context7:SKIP "
|
|
1346
|
+
fi
|
|
1347
|
+
|
|
1348
|
+
# browser_use: optional (requires uvx and at least one supported LLM API key).
|
|
1349
|
+
# Supported env vars are referenced by name only in logs: OPENAI_API_KEY,
|
|
1350
|
+
# ANTHROPIC_API_KEY.
|
|
1351
|
+
if [ ${#browser_use_env_args[@]} -gt 0 ]; then
|
|
1352
|
+
if ! command -v uvx >/dev/null 2>&1; then
|
|
1353
|
+
warn "uvx nao encontrado ${DIM}(Browser Use MCP requer uvx; instale uv/uvx e tente novamente)${NC}"
|
|
1354
|
+
mcp_results="${mcp_results}browser_use:FAIL "
|
|
1355
|
+
elif claude mcp add --scope user browser_use "${browser_use_env_args[@]}" -- uvx --from 'browser-use[cli]' browser-use --mcp 2>/dev/null; then
|
|
1356
|
+
mcp_results="${mcp_results}browser_use:OK "
|
|
1357
|
+
else
|
|
1358
|
+
mcp_results="${mcp_results}browser_use:FAIL "
|
|
1359
|
+
fi
|
|
1360
|
+
else
|
|
1361
|
+
mcp_results="${mcp_results}browser_use:SKIP "
|
|
1362
|
+
fi
|
|
1363
|
+
|
|
1364
|
+
# Design/component MCPs follow the same baseline install pattern as
|
|
1365
|
+
# Playwright because public target fragments include them by default.
|
|
1366
|
+
if claude mcp add --scope user --transport http figma https://mcp.figma.com/mcp 2>/dev/null; then
|
|
1367
|
+
mcp_results="${mcp_results}figma:OK "
|
|
1368
|
+
else
|
|
1369
|
+
mcp_results="${mcp_results}figma:FAIL "
|
|
1370
|
+
fi
|
|
1371
|
+
|
|
1372
|
+
if claude mcp add --scope user shadcn -- npx shadcn@latest mcp 2>/dev/null; then
|
|
1373
|
+
mcp_results="${mcp_results}shadcn:OK "
|
|
1374
|
+
else
|
|
1375
|
+
mcp_results="${mcp_results}shadcn:FAIL "
|
|
1376
|
+
fi
|
|
1377
|
+
|
|
1378
|
+
if claude mcp add --scope user chrome-devtools -- npx -y chrome-devtools-mcp@latest 2>/dev/null; then
|
|
1379
|
+
mcp_results="${mcp_results}chrome-devtools:OK "
|
|
1380
|
+
else
|
|
1381
|
+
mcp_results="${mcp_results}chrome-devtools:FAIL "
|
|
1382
|
+
fi
|
|
1383
|
+
|
|
1384
|
+
# Show compact MCP results
|
|
1385
|
+
local mcp_ok="" mcp_skip=""
|
|
1386
|
+
for entry in $mcp_results; do
|
|
1387
|
+
local name="${entry%%:*}"
|
|
1388
|
+
local status="${entry##*:}"
|
|
1389
|
+
case "$status" in
|
|
1390
|
+
OK) mcp_ok="${mcp_ok}${name}, " ;;
|
|
1391
|
+
SKIP) mcp_skip="${mcp_skip}${name}, " ;;
|
|
1392
|
+
esac
|
|
1393
|
+
done
|
|
1394
|
+
|
|
1395
|
+
[ -n "$mcp_ok" ] && ok "MCPs: ${DIM}${mcp_ok%, }${NC}"
|
|
1396
|
+
[ -n "$mcp_skip" ] && info "MCPs pendentes: ${DIM}${mcp_skip%, }${NC}"
|
|
1397
|
+
|
|
1398
|
+
return 0
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
# =============================================================================
|
|
1402
|
+
# STATUS DO CODERABBIT CLI (OPCIONAL / MANUAL)
|
|
1403
|
+
# =============================================================================
|
|
1404
|
+
|
|
1405
|
+
is_ci_environment() {
|
|
1406
|
+
[ "${CI:-}" = "true" ] || [ "${GITHUB_ACTIONS:-}" = "true" ] || [ "${TF_BUILD:-}" = "true" ]
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
coderabbit_skip_reason() {
|
|
1410
|
+
if is_ci_environment; then
|
|
1411
|
+
printf '%s\n' "P177_OPTIONAL_TOOL_SKIPPED_CI"
|
|
1412
|
+
elif ! is_interactive || [ "$AUTO_YES" = true ] || [ "$QUIET_MODE" = true ]; then
|
|
1413
|
+
printf '%s\n' "P177_OPTIONAL_TOOL_SKIPPED_NONINTERACTIVE"
|
|
1414
|
+
else
|
|
1415
|
+
printf '%s\n' "P177_OPTIONAL_TOOL_OPT_IN_REQUIRED"
|
|
1416
|
+
fi
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
optional_tool_info() {
|
|
1420
|
+
echo -e " ${DIM}${SYM_ARROW} $1${NC}"
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
report_coderabbit_status() {
|
|
1424
|
+
if command -v coderabbit >/dev/null 2>&1; then
|
|
1425
|
+
emit_installer_diagnostic "coderabbit" "optional-tool-setup" "detected" "P177_OPTIONAL_TOOL_DETECTED_MANUAL" "0" "null" "false" "0" "0" "manual_optional_tool_detected" "docs/install/coderabbit-manual-setup.md"
|
|
1426
|
+
ok "CodeRabbit CLI ${DIM}(opcional/manual detectado)${NC}"
|
|
1427
|
+
CODERABBIT_INSTALLED=true
|
|
1428
|
+
return 0
|
|
1429
|
+
fi
|
|
1430
|
+
|
|
1431
|
+
local reason_code
|
|
1432
|
+
reason_code="$(coderabbit_skip_reason)"
|
|
1433
|
+
emit_installer_diagnostic "coderabbit" "optional-tool-setup" "skipped" "$reason_code" "null" "null" "false" "0" "0" "optional_tool_setup_skipped" "docs/install/coderabbit-manual-setup.md"
|
|
1434
|
+
optional_tool_info "CodeRabbit CLI ${DIM}(opcional; setup manual em docs/install/coderabbit-manual-setup.md; ${reason_code})${NC}"
|
|
1435
|
+
CODERABBIT_INSTALLED=false
|
|
1436
|
+
|
|
1437
|
+
return 0
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
# =============================================================================
|
|
1441
|
+
# VERIFICACAO POS-INSTALACAO
|
|
1442
|
+
# =============================================================================
|
|
1443
|
+
|
|
1444
|
+
verify_installation() {
|
|
1445
|
+
local fails=0
|
|
1446
|
+
local warns=0
|
|
1447
|
+
local report=""
|
|
1448
|
+
|
|
1449
|
+
if ! echo "$SELECTED_TARGETS" | grep -q "claude-code"; then
|
|
1450
|
+
return 0
|
|
1451
|
+
fi
|
|
1452
|
+
|
|
1453
|
+
# ─── Layer 1: File existence + minimum size ─────────────────────────
|
|
1454
|
+
local check_fname check_min check_fpath check_size check_ext check_first_line check_display
|
|
1455
|
+
for check_fname in neocortex.md neocortex.agent.yaml; do
|
|
1456
|
+
case "$check_fname" in
|
|
1457
|
+
neocortex.md) check_min=512 ;;
|
|
1458
|
+
neocortex.agent.yaml) check_min=128 ;;
|
|
1459
|
+
esac
|
|
1460
|
+
check_fpath="${DEST_DIR}/${check_fname}"
|
|
1461
|
+
|
|
1462
|
+
if [ ! -f "$check_fpath" ]; then
|
|
1463
|
+
report="${report}FAIL ${check_fname} (nao encontrado)\n"
|
|
1464
|
+
fails=$((fails + 1))
|
|
1465
|
+
else
|
|
1466
|
+
check_size=$(wc -c < "$check_fpath" 2>/dev/null | tr -d ' ')
|
|
1467
|
+
if [ "$check_size" -lt "$check_min" ] 2>/dev/null; then
|
|
1468
|
+
report="${report}FAIL ${check_fname} (${check_size} bytes - possivelmente corrompido, minimo ${check_min})\n"
|
|
1469
|
+
fails=$((fails + 1))
|
|
1470
|
+
else
|
|
1471
|
+
# ─── Layer 2: Content marker (frontmatter) ──────────────
|
|
1472
|
+
check_ext="${check_fname##*.}"
|
|
1473
|
+
if [ "$check_ext" = "md" ]; then
|
|
1474
|
+
check_first_line=$(head -1 "$check_fpath" 2>/dev/null)
|
|
1475
|
+
if [ "$check_first_line" != "---" ]; then
|
|
1476
|
+
report="${report}WARN ${check_fname} (formato invalido - sem frontmatter)\n"
|
|
1477
|
+
warns=$((warns + 1))
|
|
1478
|
+
else
|
|
1479
|
+
if [ "$check_size" -ge 1024 ]; then check_display="$((check_size / 1024))KB"; else check_display="${check_size}B"; fi
|
|
1480
|
+
report="${report}OK ${check_fname} (${check_display})\n"
|
|
1481
|
+
fi
|
|
1482
|
+
else
|
|
1483
|
+
if [ "$check_size" -ge 1024 ]; then check_display="$((check_size / 1024))KB"; else check_display="${check_size}B"; fi
|
|
1484
|
+
report="${report}OK ${check_fname} (${check_display})\n"
|
|
1485
|
+
fi
|
|
1486
|
+
fi
|
|
1487
|
+
fi
|
|
1488
|
+
done
|
|
1489
|
+
|
|
1490
|
+
if false; then
|
|
1491
|
+
# ─── Layer 3: Step directories (removed - thin-client only) ──────
|
|
1492
|
+
for dir in steps-c steps-e steps-p steps-r steps-u; do
|
|
1493
|
+
local dir_path="$DEST_DIR/$dir"
|
|
1494
|
+
if [ ! -d "$dir_path" ]; then
|
|
1495
|
+
report="${report}FAIL ${dir}/ (diretorio nao encontrado)\n"
|
|
1496
|
+
fails=$((fails + 1))
|
|
1497
|
+
else
|
|
1498
|
+
local md_count=0
|
|
1499
|
+
for f in "$dir_path"/*.md; do
|
|
1500
|
+
[ -f "$f" ] && md_count=$((md_count + 1))
|
|
1501
|
+
done
|
|
1502
|
+
if [ $md_count -eq 0 ]; then
|
|
1503
|
+
report="${report}WARN ${dir}/ (vazio - nenhum arquivo .md)\n"
|
|
1504
|
+
warns=$((warns + 1))
|
|
1505
|
+
else
|
|
1506
|
+
report="${report}OK ${dir}/ (${md_count} arquivos)\n"
|
|
1507
|
+
fi
|
|
1508
|
+
fi
|
|
1509
|
+
done
|
|
1510
|
+
|
|
1511
|
+
# ─── Layer 3b: Core directory (local mode only) ──────────────────
|
|
1512
|
+
if [ ! -d "$DEST_DIR/core" ]; then
|
|
1513
|
+
report="${report}FAIL core/ (diretorio nao encontrado)\n"
|
|
1514
|
+
fails=$((fails + 1))
|
|
1515
|
+
else
|
|
1516
|
+
report="${report}OK core/\n"
|
|
1517
|
+
fi
|
|
1518
|
+
else
|
|
1519
|
+
# ─── Layer 3: Thin client config (remote mode) ───────────────────
|
|
1520
|
+
local config_file="${HOME}/.neocortex/config.json"
|
|
1521
|
+
if [ -f "$config_file" ]; then
|
|
1522
|
+
report="${report}OK ~/.neocortex/config.json (thin client configured)\n"
|
|
1523
|
+
else
|
|
1524
|
+
report="${report}WARN ~/.neocortex/config.json (nao encontrado)\n"
|
|
1525
|
+
warns=$((warns + 1))
|
|
1526
|
+
fi
|
|
1527
|
+
|
|
1528
|
+
# Verify NO IP directories exist
|
|
1529
|
+
local ip_found=false
|
|
1530
|
+
for dir in core steps-c steps-e steps-p steps-r steps-u; do
|
|
1531
|
+
if [ -d "$DEST_DIR/$dir" ]; then
|
|
1532
|
+
report="${report}WARN ${dir}/ ainda existe (deveria ter sido removido)\n"
|
|
1533
|
+
warns=$((warns + 1))
|
|
1534
|
+
ip_found=true
|
|
1535
|
+
fi
|
|
1536
|
+
done
|
|
1537
|
+
if [ "$ip_found" = false ]; then
|
|
1538
|
+
report="${report}OK Zero IP no filesystem (modo remoto)\n"
|
|
1539
|
+
fi
|
|
1540
|
+
fi
|
|
1541
|
+
|
|
1542
|
+
# ─── Display report ─────────────────────────────────────────────────
|
|
1543
|
+
if [ $fails -eq 0 ] && [ $warns -eq 0 ]; then
|
|
1544
|
+
# All good - compact output
|
|
1545
|
+
if [ "$QUIET_MODE" != true ]; then
|
|
1546
|
+
ok "Instalacao verificada"
|
|
1547
|
+
fi
|
|
1548
|
+
return 0
|
|
1549
|
+
fi
|
|
1550
|
+
|
|
1551
|
+
# Show detailed report when issues found
|
|
1552
|
+
if [ "$QUIET_MODE" = true ] && [ $fails -eq 0 ]; then
|
|
1553
|
+
return 0 # In quiet mode, skip warnings-only report
|
|
1554
|
+
fi
|
|
1555
|
+
|
|
1556
|
+
echo ""
|
|
1557
|
+
info "Verificacao pos-instalacao:"
|
|
1558
|
+
echo -e "$report" | while IFS= read -r line; do
|
|
1559
|
+
[ -z "$line" ] && continue
|
|
1560
|
+
local status="${line%% *}"
|
|
1561
|
+
local detail="${line#* }"
|
|
1562
|
+
case "$status" in
|
|
1563
|
+
OK) echo -e " ${GREEN}${SYM_OK}${NC} $detail" ;;
|
|
1564
|
+
WARN) echo -e " ${YELLOW}${SYM_WARN}${NC} $detail" ;;
|
|
1565
|
+
FAIL) echo -e " ${RED}${SYM_FAIL}${NC} $detail" ;;
|
|
1566
|
+
esac
|
|
1567
|
+
done
|
|
1568
|
+
|
|
1569
|
+
[ $fails -gt 0 ] && return 1
|
|
1570
|
+
return 0
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
# =============================================================================
|
|
1574
|
+
# RESULTADO
|
|
1575
|
+
# =============================================================================
|
|
1576
|
+
|
|
1577
|
+
show_result() {
|
|
1578
|
+
local install_status=$1
|
|
1579
|
+
[ "$QUIET_MODE" = true ] && return
|
|
1580
|
+
|
|
1581
|
+
echo ""
|
|
1582
|
+
echo -e " ${DIM}────────────────────────────────────────${NC}"
|
|
1583
|
+
|
|
1584
|
+
if [ $install_status -eq 0 ] && verify_installation; then
|
|
1585
|
+
echo ""
|
|
1586
|
+
|
|
1587
|
+
# Success logo (brain/cortex shape, text centered vertically)
|
|
1588
|
+
echo -e "${CYAN} #######${NC}"
|
|
1589
|
+
echo -e "${CYAN} ### ########${NC}"
|
|
1590
|
+
echo -e "${CYAN} ######### #####${NC}"
|
|
1591
|
+
echo -e "${CYAN} ## ############## ${BOLD}N E O C O R T E X${NC}"
|
|
1592
|
+
echo -e "${CYAN} ## ### ###### ## ${BOLD}v${VERSION}${NC}"
|
|
1593
|
+
echo -e "${CYAN} ## ### ### ##${NC}"
|
|
1594
|
+
echo -e "${CYAN} ## ###### ### ## ${GREEN}${BOLD}Installation complete!${NC}"
|
|
1595
|
+
echo -e "${CYAN} ############### ## ${DIM}OrNexus Team${NC}"
|
|
1596
|
+
echo -e "${CYAN} ##### ########${NC}"
|
|
1597
|
+
echo -e "${CYAN} ######## ##${NC}"
|
|
1598
|
+
echo -e "${CYAN} #######${NC}"
|
|
1599
|
+
echo ""
|
|
1600
|
+
echo -e " ${DIM}Mode:${NC} Remote (thin client)"
|
|
1601
|
+
echo -e " ${DIM}Status:${NC} Ready to activate"
|
|
1602
|
+
echo ""
|
|
1603
|
+
echo -e " ${DIM}Activate your license:${NC}"
|
|
1604
|
+
echo -e " ${CYAN}neocortex activate YOUR-LICENSE-KEY${NC}"
|
|
1605
|
+
echo ""
|
|
1606
|
+
echo -e " ${DIM}Get your key at:${NC} ${CYAN}https://neocortex.sh/portal/login${NC}"
|
|
1607
|
+
echo ""
|
|
1608
|
+
echo -e " ${DIM}After activation:${NC}"
|
|
1609
|
+
echo -e " ${CYAN}@neocortex *menu${NC}"
|
|
1610
|
+
echo ""
|
|
1611
|
+
else
|
|
1612
|
+
echo ""
|
|
1613
|
+
echo -e " ${RED}${BOLD}Instalacao com problemas${NC}"
|
|
1614
|
+
echo ""
|
|
1615
|
+
echo -e " Execute novamente com ${BOLD}--debug${NC} para detalhes:"
|
|
1616
|
+
echo -e " ${YELLOW}npx @ornexus/neocortex --debug${NC}"
|
|
1617
|
+
echo ""
|
|
1618
|
+
return 1
|
|
1619
|
+
fi
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
# =============================================================================
|
|
1623
|
+
# CRIACAO DE DIRETORIOS DO PROJETO
|
|
1624
|
+
# =============================================================================
|
|
1625
|
+
|
|
1626
|
+
create_project_dirs() {
|
|
1627
|
+
[ "$QUIET_MODE" = true ] && return
|
|
1628
|
+
[ "$SKIP_PROJECT_DIRS" = true ] && return
|
|
1629
|
+
|
|
1630
|
+
local should_create=false
|
|
1631
|
+
|
|
1632
|
+
if [ "$CREATE_PROJECT" = true ] || [ "$AUTO_YES" = true ]; then
|
|
1633
|
+
should_create=true
|
|
1634
|
+
else
|
|
1635
|
+
echo -ne " Instalar estrutura no projeto atual? ${BOLD}[s/N]:${NC} "
|
|
1636
|
+
|
|
1637
|
+
local response="n"
|
|
1638
|
+
if read -r -t 30 response </dev/tty 2>/dev/null; then
|
|
1639
|
+
:
|
|
1640
|
+
else
|
|
1641
|
+
echo ""
|
|
1642
|
+
response="n"
|
|
1643
|
+
fi
|
|
1644
|
+
|
|
1645
|
+
[[ "$response" =~ ^([sS][iI]?[mM]?|[yY][eE]?[sS]?)$ ]] && should_create=true
|
|
1646
|
+
fi
|
|
1647
|
+
|
|
1648
|
+
if [ "$should_create" = true ]; then
|
|
1649
|
+
local project_dir="$PWD"
|
|
1650
|
+
|
|
1651
|
+
if is_neocortex_source_project "$project_dir"; then
|
|
1652
|
+
warn "Recusando --create-project dentro do repo fonte do Neocortex"
|
|
1653
|
+
warn "Isso preserva core/ e arquivos de desenvolvimento locais"
|
|
1654
|
+
return
|
|
1655
|
+
fi
|
|
1656
|
+
|
|
1657
|
+
# Clean previous project files
|
|
1658
|
+
rm -rf "$project_dir/targets/claude-code" 2>/dev/null
|
|
1659
|
+
rm -f "$project_dir/.cursor/agents/neocortex.md" 2>/dev/null
|
|
1660
|
+
rm -rf "$project_dir/.cursor/skills" 2>/dev/null
|
|
1661
|
+
rm -f "$project_dir/.github/agents/neocortex.md" 2>/dev/null
|
|
1662
|
+
rm -rf "$project_dir/.github/skills" 2>/dev/null
|
|
1663
|
+
# P151: project memory entrypoints are refreshed through the canonical
|
|
1664
|
+
# writer so human-authored content outside owned blocks is preserved.
|
|
1665
|
+
rm -rf "$project_dir/.agent/skills/neocortex" 2>/dev/null
|
|
1666
|
+
rm -f "$project_dir/.opencode/agents/neocortex.md" 2>/dev/null
|
|
1667
|
+
rm -rf "$project_dir/.kimi/skills/neocortex" 2>/dev/null
|
|
1668
|
+
rm -rf "$project_dir/.openclaw/skills/neocortex" 2>/dev/null
|
|
1669
|
+
rm -rf "$project_dir/.agents/skills" 2>/dev/null
|
|
1670
|
+
rm -rf "$project_dir/.claude/agents/neocortex" 2>/dev/null
|
|
1671
|
+
rm -rf "$project_dir/.claude/skills/neocortex" 2>/dev/null
|
|
1672
|
+
|
|
1673
|
+
# Thin-client: cleanup any legacy core/ from previous local installs
|
|
1674
|
+
rm -rf "$project_dir/core" 2>/dev/null
|
|
1675
|
+
|
|
1676
|
+
# Create base directories
|
|
1677
|
+
mkdir -p "$project_dir/.neocortex/specs" \
|
|
1678
|
+
"$project_dir/.neocortex/planning" \
|
|
1679
|
+
"$project_dir/docs/stories" \
|
|
1680
|
+
"$project_dir/docs/epics" \
|
|
1681
|
+
"$project_dir/docs/proposals" 2>/dev/null
|
|
1682
|
+
|
|
1683
|
+
# Copy state template if needed
|
|
1684
|
+
if [ ! -f "$project_dir/.neocortex/state.json" ]; then
|
|
1685
|
+
[ -f "$SOURCE_DIR/core/data/state-template.json" ] && \
|
|
1686
|
+
cp "$SOURCE_DIR/core/data/state-template.json" "$project_dir/.neocortex/state.json"
|
|
1687
|
+
fi
|
|
1688
|
+
|
|
1689
|
+
# Thin-client: never copy core/ to project
|
|
1690
|
+
|
|
1691
|
+
# Install target-specific files
|
|
1692
|
+
local targets_list
|
|
1693
|
+
targets_list=$(echo "$SELECTED_TARGETS" | tr ',' ' ')
|
|
1694
|
+
local target_summary=""
|
|
1695
|
+
|
|
1696
|
+
for target in $targets_list; do
|
|
1697
|
+
case "$target" in
|
|
1698
|
+
claude-code)
|
|
1699
|
+
local claude_target_dir
|
|
1700
|
+
claude_target_dir=$(resolve_target_dir "claude-code") || true
|
|
1701
|
+
if [ -d "$claude_target_dir" ]; then
|
|
1702
|
+
mkdir -p "$project_dir/.claude/agents/neocortex"
|
|
1703
|
+
|
|
1704
|
+
# Tier 3: copiar apenas 2 stub files
|
|
1705
|
+
cp "$claude_target_dir/neocortex.md" "$project_dir/.claude/agents/neocortex/" 2>/dev/null
|
|
1706
|
+
cp "$claude_target_dir/neocortex.agent.yaml" "$project_dir/.claude/agents/neocortex/" 2>/dev/null
|
|
1707
|
+
# Cleanup workflow.md from previous versions
|
|
1708
|
+
rm -f "$project_dir/.claude/agents/neocortex/workflow.md" 2>/dev/null
|
|
1709
|
+
|
|
1710
|
+
# Dynamic description: patch tier
|
|
1711
|
+
patch_description_tier "$project_dir/.claude/agents/neocortex/neocortex.md"
|
|
1712
|
+
patch_description_tier "$project_dir/.claude/agents/neocortex/neocortex.agent.yaml"
|
|
1713
|
+
|
|
1714
|
+
# Thin-client: cleanup legacy IP from previous installs
|
|
1715
|
+
auto_cleanup_legacy_project "$project_dir"
|
|
1716
|
+
target_summary="${target_summary}claude-code, "
|
|
1717
|
+
fi
|
|
1718
|
+
;;
|
|
1719
|
+
cursor)
|
|
1720
|
+
# IP Protection: ALWAYS thin-client, zero IP on client
|
|
1721
|
+
local stub_adapter="$SOURCE_DIR/targets-stubs/cursor/install-cursor.sh"
|
|
1722
|
+
if [ -f "$stub_adapter" ]; then
|
|
1723
|
+
. "$stub_adapter"
|
|
1724
|
+
install_cursor "$SOURCE_DIR" "$project_dir"
|
|
1725
|
+
patch_description_tier "$project_dir/.cursor/agents/neocortex.md"
|
|
1726
|
+
target_summary="${target_summary}cursor, "
|
|
1727
|
+
fi
|
|
1728
|
+
;;
|
|
1729
|
+
vscode)
|
|
1730
|
+
local stub_adapter="$SOURCE_DIR/targets-stubs/vscode/install-vscode.sh"
|
|
1731
|
+
if [ -f "$stub_adapter" ]; then
|
|
1732
|
+
. "$stub_adapter"
|
|
1733
|
+
install_vscode "$SOURCE_DIR" "$project_dir"
|
|
1734
|
+
patch_description_tier "$project_dir/.github/agents/neocortex.md"
|
|
1735
|
+
patch_description_tier "$project_dir/.github/agents/neocortex.agent.md"
|
|
1736
|
+
target_summary="${target_summary}vscode, "
|
|
1737
|
+
fi
|
|
1738
|
+
;;
|
|
1739
|
+
gemini|gemini-cli)
|
|
1740
|
+
local stub_adapter="$SOURCE_DIR/targets-stubs/gemini-cli/install-gemini.sh"
|
|
1741
|
+
if [ -f "$stub_adapter" ]; then
|
|
1742
|
+
. "$stub_adapter"
|
|
1743
|
+
install_gemini "$SOURCE_DIR" "$project_dir"
|
|
1744
|
+
local gemini_home="${GEMINI_HOME:-$HOME/.gemini}"
|
|
1745
|
+
patch_description_tier "$gemini_home/agents/neocortex.md"
|
|
1746
|
+
patch_description_tier "$project_dir/.gemini/agents/neocortex.md"
|
|
1747
|
+
target_summary="${target_summary}gemini, "
|
|
1748
|
+
fi
|
|
1749
|
+
;;
|
|
1750
|
+
codex)
|
|
1751
|
+
local stub_adapter="$SOURCE_DIR/targets-stubs/codex/install-codex.sh"
|
|
1752
|
+
if [ -f "$stub_adapter" ]; then
|
|
1753
|
+
. "$stub_adapter"
|
|
1754
|
+
install_codex "$SOURCE_DIR" "$project_dir"
|
|
1755
|
+
patch_description_tier "$project_dir/AGENTS.md"
|
|
1756
|
+
local codex_home="${CODEX_HOME:-$HOME/.codex}"
|
|
1757
|
+
patch_description_tier "$codex_home/AGENTS.md"
|
|
1758
|
+
patch_description_tier "$codex_home/agents/neocortex.toml"
|
|
1759
|
+
patch_description_tier "$project_dir/.codex/agents/neocortex.toml"
|
|
1760
|
+
target_summary="${target_summary}codex, "
|
|
1761
|
+
fi
|
|
1762
|
+
;;
|
|
1763
|
+
antigravity)
|
|
1764
|
+
local stub_adapter="$SOURCE_DIR/targets-stubs/antigravity/install-antigravity.sh"
|
|
1765
|
+
if [ -f "$stub_adapter" ]; then
|
|
1766
|
+
. "$stub_adapter"
|
|
1767
|
+
install_antigravity "$SOURCE_DIR" "$project_dir"
|
|
1768
|
+
patch_description_tier "$project_dir/.agent/skills/neocortex/SKILL.md"
|
|
1769
|
+
patch_description_tier "$project_dir/GEMINI.md"
|
|
1770
|
+
target_summary="${target_summary}antigravity, "
|
|
1771
|
+
fi
|
|
1772
|
+
;;
|
|
1773
|
+
opencode)
|
|
1774
|
+
local opencode_target="$SOURCE_DIR/targets-stubs/opencode"
|
|
1775
|
+
if [ -f "$opencode_target/neocortex.md" ] && [ -f "$opencode_target/neocortex-root.md" ]; then
|
|
1776
|
+
mkdir -p "$project_dir/.opencode/agents" 2>/dev/null
|
|
1777
|
+
cp "$opencode_target/neocortex.md" "$project_dir/.opencode/agents/neocortex.md" 2>/dev/null
|
|
1778
|
+
cp "$opencode_target/neocortex-root.md" "$project_dir/.opencode/agents/neocortex-root.md" 2>/dev/null
|
|
1779
|
+
patch_description_tier "$project_dir/.opencode/agents/neocortex.md"
|
|
1780
|
+
patch_description_tier "$project_dir/.opencode/agents/neocortex-root.md"
|
|
1781
|
+
target_summary="${target_summary}opencode, "
|
|
1782
|
+
fi
|
|
1783
|
+
;;
|
|
1784
|
+
kimi)
|
|
1785
|
+
local kimi_target="$SOURCE_DIR/targets-stubs/kimi"
|
|
1786
|
+
if [ -f "$kimi_target/neocortex.md" ]; then
|
|
1787
|
+
mkdir -p "$project_dir/.kimi/skills/neocortex" 2>/dev/null
|
|
1788
|
+
cp "$kimi_target/neocortex.md" "$project_dir/.kimi/skills/neocortex/SKILL.md" 2>/dev/null
|
|
1789
|
+
patch_description_tier "$project_dir/.kimi/skills/neocortex/SKILL.md"
|
|
1790
|
+
target_summary="${target_summary}kimi, "
|
|
1791
|
+
fi
|
|
1792
|
+
;;
|
|
1793
|
+
openclaw)
|
|
1794
|
+
local openclaw_target="$SOURCE_DIR/targets-stubs/openclaw"
|
|
1795
|
+
if [ -f "$openclaw_target/SKILL.md" ]; then
|
|
1796
|
+
mkdir -p "$project_dir/.openclaw/skills/neocortex" 2>/dev/null
|
|
1797
|
+
cp "$openclaw_target/SKILL.md" "$project_dir/.openclaw/skills/neocortex/SKILL.md" 2>/dev/null
|
|
1798
|
+
patch_description_tier "$project_dir/.openclaw/skills/neocortex/SKILL.md"
|
|
1799
|
+
target_summary="${target_summary}openclaw, "
|
|
1800
|
+
fi
|
|
1801
|
+
;;
|
|
1802
|
+
esac
|
|
1803
|
+
done
|
|
1804
|
+
|
|
1805
|
+
# P151: bootstrap NEOCORTEX.md first, then repair platform wrappers or
|
|
1806
|
+
# semantic symlinks for the selected project targets. This is best-effort
|
|
1807
|
+
# during install because older packages may not ship the JS handler yet.
|
|
1808
|
+
if [ -f "$SOURCE_DIR/packages/client/dist/cli.js" ]; then
|
|
1809
|
+
local memory_targets=""
|
|
1810
|
+
for target in $targets_list; do
|
|
1811
|
+
case "$target" in
|
|
1812
|
+
gemini) target="gemini-cli" ;;
|
|
1813
|
+
esac
|
|
1814
|
+
if [ -z "$memory_targets" ]; then
|
|
1815
|
+
memory_targets="$target"
|
|
1816
|
+
else
|
|
1817
|
+
memory_targets="$memory_targets,$target"
|
|
1818
|
+
fi
|
|
1819
|
+
done
|
|
1820
|
+
node "$SOURCE_DIR/packages/client/dist/cli.js" refresh-memory --project-root "$project_dir" --targets "$memory_targets" >/dev/null 2>&1 || \
|
|
1821
|
+
warn "Nao foi possivel reparar memoria canonica automaticamente; execute: neocortex-client refresh-memory --project-root '$project_dir' --targets '$memory_targets'"
|
|
1822
|
+
fi
|
|
1823
|
+
|
|
1824
|
+
echo ""
|
|
1825
|
+
ok "Estrutura do projeto instalada ${DIM}(${target_summary%, })${NC}"
|
|
1826
|
+
echo ""
|
|
1827
|
+
info "Proximo passo: ${CYAN}@neocortex *init @docs/epics.md${NC}"
|
|
1828
|
+
fi
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
# =============================================================================
|
|
1832
|
+
# MIGRACAO
|
|
1833
|
+
# =============================================================================
|
|
1834
|
+
|
|
1835
|
+
show_migration_info() {
|
|
1836
|
+
[ "$QUIET_MODE" = true ] && return
|
|
1837
|
+
|
|
1838
|
+
local project_sources
|
|
1839
|
+
if project_sources=$(detect_project_migration_needs); then
|
|
1840
|
+
echo ""
|
|
1841
|
+
warn "${BOLD}Migracao detectada${NC}"
|
|
1842
|
+
for src in $project_sources; do
|
|
1843
|
+
info "Arquivo antigo: $src"
|
|
1844
|
+
done
|
|
1845
|
+
info "Execute: ${CYAN}@neocortex *init @docs/epics.md${NC} para migrar"
|
|
1846
|
+
fi
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
# =============================================================================
|
|
1850
|
+
# TIER-BASED PLATFORM GATING (Story 57.4)
|
|
1851
|
+
# =============================================================================
|
|
1852
|
+
|
|
1853
|
+
# Read tier from config.json (defaults to "free" if not found)
|
|
1854
|
+
get_tier_from_config() {
|
|
1855
|
+
local config_file="$HOME/.neocortex/config.json"
|
|
1856
|
+
if [ -f "$config_file" ]; then
|
|
1857
|
+
local tier
|
|
1858
|
+
tier=$(cat "$config_file" 2>/dev/null | grep -o '"tier"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')
|
|
1859
|
+
[ -n "$tier" ] && echo "$tier" || echo "free"
|
|
1860
|
+
else
|
|
1861
|
+
echo "free"
|
|
1862
|
+
fi
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
# Check if a platform is allowed for the current tier
|
|
1866
|
+
# Returns 0 if allowed, 1 if blocked
|
|
1867
|
+
check_platform_tier() {
|
|
1868
|
+
local platform=$1
|
|
1869
|
+
local tier=$2
|
|
1870
|
+
case "$platform" in
|
|
1871
|
+
claude-code) return 0 ;; # Always allowed
|
|
1872
|
+
cursor|gemini|gemini-cli|codex|opencode)
|
|
1873
|
+
if [ "$tier" = "free" ]; then
|
|
1874
|
+
warn "$platform requer plano Pro. Plataforma disponivel: Claude Code."
|
|
1875
|
+
info "Upgrade: ${CYAN}https://neocortex.sh/portal/dashboard/plans${NC}"
|
|
1876
|
+
return 1
|
|
1877
|
+
fi
|
|
1878
|
+
return 0
|
|
1879
|
+
;;
|
|
1880
|
+
vscode|antigravity|kimi)
|
|
1881
|
+
if [ "$tier" != "enterprise" ]; then
|
|
1882
|
+
warn "$platform requer plano Enterprise."
|
|
1883
|
+
info "Upgrade: ${CYAN}https://neocortex.sh/portal/dashboard/plans${NC}"
|
|
1884
|
+
return 1
|
|
1885
|
+
fi
|
|
1886
|
+
return 0
|
|
1887
|
+
;;
|
|
1888
|
+
esac
|
|
1889
|
+
return 0 # Unknown platforms pass through
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
# =============================================================================
|
|
1893
|
+
# MAIN
|
|
1894
|
+
# =============================================================================
|
|
1895
|
+
|
|
1896
|
+
TOTAL_STEPS=4
|
|
1897
|
+
|
|
1898
|
+
main() {
|
|
1899
|
+
show_banner
|
|
1900
|
+
warn_linux_root_home_context
|
|
1901
|
+
detect_source_dir
|
|
1902
|
+
detect_old_installation
|
|
1903
|
+
|
|
1904
|
+
# Determine targets
|
|
1905
|
+
if [ -z "$SELECTED_TARGETS" ]; then
|
|
1906
|
+
SELECTED_TARGETS="claude-code"
|
|
1907
|
+
fi
|
|
1908
|
+
|
|
1909
|
+
debug "Targets: $SELECTED_TARGETS"
|
|
1910
|
+
|
|
1911
|
+
# Read user tier for platform gating
|
|
1912
|
+
local user_tier
|
|
1913
|
+
user_tier=$(get_tier_from_config)
|
|
1914
|
+
debug "User tier: $user_tier"
|
|
1915
|
+
|
|
1916
|
+
# Validate targets (name check + tier check)
|
|
1917
|
+
local invalid_targets=""
|
|
1918
|
+
local blocked_targets=""
|
|
1919
|
+
for target in $(echo "$SELECTED_TARGETS" | tr ',' ' '); do
|
|
1920
|
+
echo "$VALID_TARGETS" | grep -wq "$target" || invalid_targets="$invalid_targets $target"
|
|
1921
|
+
done
|
|
1922
|
+
|
|
1923
|
+
if [ -n "$invalid_targets" ]; then
|
|
1924
|
+
fail "Plataformas invalidas:$invalid_targets"
|
|
1925
|
+
fail "Validas: $VALID_TARGETS"
|
|
1926
|
+
exit 1
|
|
1927
|
+
fi
|
|
1928
|
+
|
|
1929
|
+
# Filter out tier-blocked platforms
|
|
1930
|
+
local allowed_targets=""
|
|
1931
|
+
for target in $(echo "$SELECTED_TARGETS" | tr ',' ' '); do
|
|
1932
|
+
if check_platform_tier "$target" "$user_tier"; then
|
|
1933
|
+
if [ -z "$allowed_targets" ]; then
|
|
1934
|
+
allowed_targets="$target"
|
|
1935
|
+
else
|
|
1936
|
+
allowed_targets="$allowed_targets,$target"
|
|
1937
|
+
fi
|
|
1938
|
+
else
|
|
1939
|
+
blocked_targets="$blocked_targets $target"
|
|
1940
|
+
fi
|
|
1941
|
+
done
|
|
1942
|
+
|
|
1943
|
+
if [ -n "$blocked_targets" ]; then
|
|
1944
|
+
info "Plataforma(s) bloqueada(s) por tier:$blocked_targets"
|
|
1945
|
+
fi
|
|
1946
|
+
|
|
1947
|
+
# Use only allowed targets
|
|
1948
|
+
if [ -n "$allowed_targets" ]; then
|
|
1949
|
+
SELECTED_TARGETS="$allowed_targets"
|
|
1950
|
+
else
|
|
1951
|
+
SELECTED_TARGETS="claude-code"
|
|
1952
|
+
info "Usando plataforma padrao: claude-code"
|
|
1953
|
+
fi
|
|
1954
|
+
|
|
1955
|
+
if [ "$DRY_RUN" = true ]; then
|
|
1956
|
+
show_target_resolution_dry_run "$SELECTED_TARGETS"
|
|
1957
|
+
exit 0
|
|
1958
|
+
fi
|
|
1959
|
+
|
|
1960
|
+
# Count targets for step total
|
|
1961
|
+
local target_count=$(echo "$SELECTED_TARGETS" | tr ',' ' ' | wc -w | tr -d ' ')
|
|
1962
|
+
if echo "$SELECTED_TARGETS" | grep -q "claude-code"; then
|
|
1963
|
+
TOTAL_STEPS=6 # cleanup + core + skills + targets + mcps + tools
|
|
1964
|
+
fi
|
|
1965
|
+
|
|
1966
|
+
# Step 1: Limpeza automatica de versoes anteriores
|
|
1967
|
+
step 1 $TOTAL_STEPS "Limpeza de versoes anteriores"
|
|
1968
|
+
auto_cleanup_legacy
|
|
1969
|
+
|
|
1970
|
+
# Step 2: Core
|
|
1971
|
+
step 2 $TOTAL_STEPS "Instalando core"
|
|
1972
|
+
install_core
|
|
1973
|
+
local core_result=$?
|
|
1974
|
+
if [ $core_result -ne 0 ]; then
|
|
1975
|
+
fail "Falha na instalacao do core"
|
|
1976
|
+
exit 1
|
|
1977
|
+
fi
|
|
1978
|
+
|
|
1979
|
+
# Step 3: Skills
|
|
1980
|
+
step 3 $TOTAL_STEPS "Instalando skills"
|
|
1981
|
+
install_skills
|
|
1982
|
+
|
|
1983
|
+
# Step 4: Targets
|
|
1984
|
+
step 4 $TOTAL_STEPS "Instalando ${BOLD}$target_count${NC} plataforma(s)"
|
|
1985
|
+
install_targets "$SELECTED_TARGETS"
|
|
1986
|
+
|
|
1987
|
+
# Step 5-6: Claude Code extras
|
|
1988
|
+
if echo "$SELECTED_TARGETS" | grep -q "claude-code"; then
|
|
1989
|
+
load_env_file
|
|
1990
|
+
prompt_tokens
|
|
1991
|
+
|
|
1992
|
+
step 5 $TOTAL_STEPS "Configurando MCPs"
|
|
1993
|
+
install_mcps
|
|
1994
|
+
|
|
1995
|
+
step 6 $TOTAL_STEPS "Verificando ferramentas"
|
|
1996
|
+
report_coderabbit_status
|
|
1997
|
+
fi
|
|
1998
|
+
|
|
1999
|
+
# Result
|
|
2000
|
+
show_result $core_result
|
|
2001
|
+
local result_code=$?
|
|
2002
|
+
|
|
2003
|
+
if [ $result_code -eq 0 ]; then
|
|
2004
|
+
show_migration_info
|
|
2005
|
+
create_project_dirs
|
|
2006
|
+
echo -e " ${DIM}Desenvolvido por OrNexus Team${NC}"
|
|
2007
|
+
echo ""
|
|
2008
|
+
fi
|
|
2009
|
+
|
|
2010
|
+
exit $result_code
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
main
|