@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.
Files changed (197) hide show
  1. package/LICENSE +56 -0
  2. package/LICENSE-COMMERCIAL.md +70 -0
  3. package/README.md +58 -0
  4. package/dist/sbom.cdx.json +7067 -0
  5. package/docs/install/coderabbit-manual-setup.md +86 -0
  6. package/docs/install/installer-diagnostics.md +107 -0
  7. package/docs/install/linux-global-install.md +97 -0
  8. package/install.js +572 -0
  9. package/install.ps1 +2214 -0
  10. package/install.sh +2013 -0
  11. package/package.json +118 -0
  12. package/packages/client/dist/adapters/adapter-registry.d.ts +61 -0
  13. package/packages/client/dist/adapters/adapter-registry.js +1 -0
  14. package/packages/client/dist/adapters/antigravity-adapter.d.ts +18 -0
  15. package/packages/client/dist/adapters/antigravity-adapter.js +2 -0
  16. package/packages/client/dist/adapters/claude-code-adapter.d.ts +19 -0
  17. package/packages/client/dist/adapters/claude-code-adapter.js +3 -0
  18. package/packages/client/dist/adapters/codex-adapter.d.ts +19 -0
  19. package/packages/client/dist/adapters/codex-adapter.js +2 -0
  20. package/packages/client/dist/adapters/cursor-adapter.d.ts +19 -0
  21. package/packages/client/dist/adapters/cursor-adapter.js +4 -0
  22. package/packages/client/dist/adapters/gemini-adapter.d.ts +18 -0
  23. package/packages/client/dist/adapters/gemini-adapter.js +2 -0
  24. package/packages/client/dist/adapters/index.d.ts +19 -0
  25. package/packages/client/dist/adapters/index.js +1 -0
  26. package/packages/client/dist/adapters/platform-detector.d.ts +48 -0
  27. package/packages/client/dist/adapters/platform-detector.js +1 -0
  28. package/packages/client/dist/adapters/target-adapter.d.ts +70 -0
  29. package/packages/client/dist/adapters/target-adapter.js +0 -0
  30. package/packages/client/dist/adapters/vscode-adapter.d.ts +19 -0
  31. package/packages/client/dist/adapters/vscode-adapter.js +2 -0
  32. package/packages/client/dist/agent/refresh-stubs.d.ts +80 -0
  33. package/packages/client/dist/agent/refresh-stubs.js +2 -0
  34. package/packages/client/dist/agent/update-agent-yaml.d.ts +26 -0
  35. package/packages/client/dist/agent/update-agent-yaml.js +1 -0
  36. package/packages/client/dist/agent/update-description.d.ts +45 -0
  37. package/packages/client/dist/agent/update-description.js +1 -0
  38. package/packages/client/dist/cache/crypto-utils.d.ts +30 -0
  39. package/packages/client/dist/cache/crypto-utils.js +1 -0
  40. package/packages/client/dist/cache/encrypted-cache.d.ts +30 -0
  41. package/packages/client/dist/cache/encrypted-cache.js +1 -0
  42. package/packages/client/dist/cache/in-memory-asset-cache.d.ts +62 -0
  43. package/packages/client/dist/cache/in-memory-asset-cache.js +1 -0
  44. package/packages/client/dist/cache/index.d.ts +13 -0
  45. package/packages/client/dist/cache/index.js +1 -0
  46. package/packages/client/dist/cache/protected-pi-boundary.d.ts +19 -0
  47. package/packages/client/dist/cache/protected-pi-boundary.js +1 -0
  48. package/packages/client/dist/checkpoint/checkpoint-client-reader.d.ts +45 -0
  49. package/packages/client/dist/checkpoint/checkpoint-client-reader.js +2 -0
  50. package/packages/client/dist/checkpoint/index.d.ts +12 -0
  51. package/packages/client/dist/checkpoint/index.js +1 -0
  52. package/packages/client/dist/checkpoint/shared-checkpoint-types.d.ts +85 -0
  53. package/packages/client/dist/checkpoint/shared-checkpoint-types.js +1 -0
  54. package/packages/client/dist/cli.d.ts +14 -0
  55. package/packages/client/dist/cli.js +48 -0
  56. package/packages/client/dist/commands/activate.d.ts +55 -0
  57. package/packages/client/dist/commands/activate.js +8 -0
  58. package/packages/client/dist/commands/cache-status.d.ts +39 -0
  59. package/packages/client/dist/commands/cache-status.js +2 -0
  60. package/packages/client/dist/commands/invoke.d.ts +229 -0
  61. package/packages/client/dist/commands/invoke.js +63 -0
  62. package/packages/client/dist/commands/refresh-memory.d.ts +11 -0
  63. package/packages/client/dist/commands/refresh-memory.js +1 -0
  64. package/packages/client/dist/config/resolver-selection.d.ts +40 -0
  65. package/packages/client/dist/config/resolver-selection.js +1 -0
  66. package/packages/client/dist/config/secure-config.d.ts +78 -0
  67. package/packages/client/dist/config/secure-config.js +12 -0
  68. package/packages/client/dist/constants.d.ts +25 -0
  69. package/packages/client/dist/constants.js +1 -0
  70. package/packages/client/dist/context/context-collector.d.ts +28 -0
  71. package/packages/client/dist/context/context-collector.js +2 -0
  72. package/packages/client/dist/context/context-sanitizer.d.ts +28 -0
  73. package/packages/client/dist/context/context-sanitizer.js +1 -0
  74. package/packages/client/dist/continuity/continuity-client-state-store.d.ts +183 -0
  75. package/packages/client/dist/continuity/continuity-client-state-store.js +1 -0
  76. package/packages/client/dist/continuity/invoke-hooks.d.ts +18 -0
  77. package/packages/client/dist/continuity/invoke-hooks.js +1 -0
  78. package/packages/client/dist/continuity/migrations/001-initial-schema.d.ts +11 -0
  79. package/packages/client/dist/continuity/migrations/001-initial-schema.js +263 -0
  80. package/packages/client/dist/continuity/sqlite-store.d.ts +409 -0
  81. package/packages/client/dist/continuity/sqlite-store.js +226 -0
  82. package/packages/client/dist/errors/error-messages.d.ts +40 -0
  83. package/packages/client/dist/errors/error-messages.js +2 -0
  84. package/packages/client/dist/graph-retrieval/pre-command-hook.d.ts +31 -0
  85. package/packages/client/dist/graph-retrieval/pre-command-hook.js +1 -0
  86. package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.d.ts +77 -0
  87. package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -0
  88. package/packages/client/dist/i18n/first-run.d.ts +23 -0
  89. package/packages/client/dist/i18n/first-run.js +2 -0
  90. package/packages/client/dist/index.d.ts +56 -0
  91. package/packages/client/dist/index.js +1 -0
  92. package/packages/client/dist/license/index.d.ts +5 -0
  93. package/packages/client/dist/license/index.js +1 -0
  94. package/packages/client/dist/license/license-client.d.ts +79 -0
  95. package/packages/client/dist/license/license-client.js +1 -0
  96. package/packages/client/dist/machine/fingerprint.d.ts +34 -0
  97. package/packages/client/dist/machine/fingerprint.js +2 -0
  98. package/packages/client/dist/machine/index.d.ts +5 -0
  99. package/packages/client/dist/machine/index.js +1 -0
  100. package/packages/client/dist/memory/project-memory-writer.d.ts +74 -0
  101. package/packages/client/dist/memory/project-memory-writer.js +36 -0
  102. package/packages/client/dist/memory/shared-project-memory-types.d.ts +370 -0
  103. package/packages/client/dist/memory/shared-project-memory-types.js +2 -0
  104. package/packages/client/dist/policy/architecture-policy.d.ts +40 -0
  105. package/packages/client/dist/policy/architecture-policy.js +2 -0
  106. package/packages/client/dist/policy/index.d.ts +8 -0
  107. package/packages/client/dist/policy/index.js +1 -0
  108. package/packages/client/dist/policy/shared-policy-types.d.ts +89 -0
  109. package/packages/client/dist/policy/shared-policy-types.js +0 -0
  110. package/packages/client/dist/resilience/circuit-breaker.d.ts +70 -0
  111. package/packages/client/dist/resilience/circuit-breaker.js +1 -0
  112. package/packages/client/dist/resilience/degradation-manager.d.ts +67 -0
  113. package/packages/client/dist/resilience/degradation-manager.js +1 -0
  114. package/packages/client/dist/resilience/freshness-indicator.d.ts +59 -0
  115. package/packages/client/dist/resilience/freshness-indicator.js +1 -0
  116. package/packages/client/dist/resilience/index.d.ts +8 -0
  117. package/packages/client/dist/resilience/index.js +1 -0
  118. package/packages/client/dist/resilience/recovery-detector.d.ts +59 -0
  119. package/packages/client/dist/resilience/recovery-detector.js +1 -0
  120. package/packages/client/dist/resolvers/asset-resolver.d.ts +79 -0
  121. package/packages/client/dist/resolvers/asset-resolver.js +0 -0
  122. package/packages/client/dist/resolvers/local-resolver.d.ts +26 -0
  123. package/packages/client/dist/resolvers/local-resolver.js +8 -0
  124. package/packages/client/dist/resolvers/remote-resolver.d.ts +91 -0
  125. package/packages/client/dist/resolvers/remote-resolver.js +1 -0
  126. package/packages/client/dist/runner/cli.d.ts +121 -0
  127. package/packages/client/dist/runner/cli.js +20 -0
  128. package/packages/client/dist/runner/scheduler.d.ts +116 -0
  129. package/packages/client/dist/runner/scheduler.js +6 -0
  130. package/packages/client/dist/runner-cli.d.ts +9 -0
  131. package/packages/client/dist/runner-cli.js +3 -0
  132. package/packages/client/dist/state/project-state-snapshot.d.ts +15 -0
  133. package/packages/client/dist/state/project-state-snapshot.js +1 -0
  134. package/packages/client/dist/state/state-json-repair.d.ts +17 -0
  135. package/packages/client/dist/state/state-json-repair.js +3 -0
  136. package/packages/client/dist/telemetry/index.d.ts +5 -0
  137. package/packages/client/dist/telemetry/index.js +1 -0
  138. package/packages/client/dist/telemetry/offline-queue.d.ts +57 -0
  139. package/packages/client/dist/telemetry/offline-queue.js +1 -0
  140. package/packages/client/dist/tier/index.d.ts +5 -0
  141. package/packages/client/dist/tier/index.js +1 -0
  142. package/packages/client/dist/tier/tier-aware-client.d.ts +105 -0
  143. package/packages/client/dist/tier/tier-aware-client.js +1 -0
  144. package/packages/client/dist/types/index.d.ts +140 -0
  145. package/packages/client/dist/types/index.js +1 -0
  146. package/packages/client/dist/yoloop/discovery-hook.d.ts +85 -0
  147. package/packages/client/dist/yoloop/discovery-hook.js +2 -0
  148. package/packages/client/dist/yoloop/index.d.ts +10 -0
  149. package/packages/client/dist/yoloop/index.js +1 -0
  150. package/packages/client/dist/yoloop/invoke-hooks.d.ts +125 -0
  151. package/packages/client/dist/yoloop/invoke-hooks.js +5 -0
  152. package/packages/client/dist/yoloop/shared-discover-epics.d.ts +289 -0
  153. package/packages/client/dist/yoloop/shared-discover-epics.js +1 -0
  154. package/packages/client/dist/yoloop/shared-yoloop-types.d.ts +172 -0
  155. package/packages/client/dist/yoloop/shared-yoloop-types.js +1 -0
  156. package/packages/client/dist/yoloop/yoloop-client-state-store.d.ts +124 -0
  157. package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -0
  158. package/postinstall.js +754 -0
  159. package/targets-stubs/antigravity/README.md +36 -0
  160. package/targets-stubs/antigravity/gemini.md +29 -0
  161. package/targets-stubs/antigravity/install-antigravity.sh +153 -0
  162. package/targets-stubs/antigravity/mcp-config.json +30 -0
  163. package/targets-stubs/antigravity/skill/SKILL.md +159 -0
  164. package/targets-stubs/claude-code/.mcp.json +32 -0
  165. package/targets-stubs/claude-code/README.md +20 -0
  166. package/targets-stubs/claude-code/neocortex-root.agent.yaml +42 -0
  167. package/targets-stubs/claude-code/neocortex-root.md +310 -0
  168. package/targets-stubs/claude-code/neocortex.agent.yaml +42 -0
  169. package/targets-stubs/claude-code/neocortex.md +378 -0
  170. package/targets-stubs/codex/AGENTS.md +244 -0
  171. package/targets-stubs/codex/README.md +47 -0
  172. package/targets-stubs/codex/config-mcp.toml +22 -0
  173. package/targets-stubs/codex/install-codex.sh +63 -0
  174. package/targets-stubs/codex/neocortex.toml +29 -0
  175. package/targets-stubs/cursor/README.md +33 -0
  176. package/targets-stubs/cursor/agent.md +204 -0
  177. package/targets-stubs/cursor/install-cursor.sh +50 -0
  178. package/targets-stubs/cursor/mcp.json +30 -0
  179. package/targets-stubs/gemini-cli/README.md +34 -0
  180. package/targets-stubs/gemini-cli/agent.md +234 -0
  181. package/targets-stubs/gemini-cli/agents/neocortex.md +54 -0
  182. package/targets-stubs/gemini-cli/gemini.md +46 -0
  183. package/targets-stubs/gemini-cli/install-gemini.sh +70 -0
  184. package/targets-stubs/gemini-cli/settings-mcp.json +30 -0
  185. package/targets-stubs/kimi/mcp.json +33 -0
  186. package/targets-stubs/kimi/neocortex.md +54 -0
  187. package/targets-stubs/lib/mcp-merge.js +189 -0
  188. package/targets-stubs/openclaw/README.md +12 -0
  189. package/targets-stubs/openclaw/SKILL.md +88 -0
  190. package/targets-stubs/opencode/neocortex-root.md +261 -0
  191. package/targets-stubs/opencode/neocortex.md +59 -0
  192. package/targets-stubs/opencode/opencode-mcp.json +35 -0
  193. package/targets-stubs/vscode/README.md +34 -0
  194. package/targets-stubs/vscode/copilot-instructions.md +47 -0
  195. package/targets-stubs/vscode/install-vscode.sh +72 -0
  196. package/targets-stubs/vscode/mcp.json +36 -0
  197. 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