@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.ps1 ADDED
@@ -0,0 +1,2214 @@
1
+ # Neocortex - Instalador Windows
2
+ # Development Orchestrator
3
+
4
+ param(
5
+ [switch]$Yes,
6
+ [switch]$Debug,
7
+ [switch]$SkipProject,
8
+ [switch]$CreateProject,
9
+ [switch]$Quiet,
10
+ [switch]$DryRun,
11
+ [switch]$NoBanner,
12
+ [switch]$CleanupLegacy,
13
+ [switch]$Local,
14
+ [switch]$Help,
15
+ [string]$Targets = "",
16
+ # SSoT: packages/client/src/constants.ts DEFAULT_SERVER_URL
17
+ [string]$ServerUrl = "https://api.neocortex.sh"
18
+ )
19
+
20
+ $VERSION = "4.59.1"
21
+
22
+ # =============================================================================
23
+ # CONFIGURACOES
24
+ # =============================================================================
25
+
26
+ $ErrorActionPreference = "Continue"
27
+ $script:Errors = 0
28
+ $script:MigrationDetected = $false
29
+ $script:MigrationSources = @()
30
+ $script:LegacyItems = @()
31
+ $script:LegacyWarnings = 0
32
+
33
+ $AUTO_YES = $Yes
34
+ $DEBUG_MODE = $Debug
35
+ $SKIP_PROJECT_DIRS = $SkipProject
36
+ $QUIET_MODE = $Quiet
37
+ $NO_BANNER = $NoBanner
38
+ # LOCAL_MODE removed (Epic 50): thin-client ALWAYS, zero IP on client
39
+ $LOCAL_MODE = $false
40
+ $NEOCORTEX_SERVER_URL = $ServerUrl
41
+
42
+ function Test-Interactive {
43
+ try {
44
+ if (-not [Environment]::UserInteractive) { return $false }
45
+ if ($null -eq $Host.UI -or $null -eq $Host.UI.RawUI) { return $false }
46
+ return $true
47
+ } catch { return $false }
48
+ }
49
+
50
+ if (-not (Test-Interactive)) {
51
+ $AUTO_YES = $true
52
+ }
53
+
54
+ # =============================================================================
55
+ # AJUDA
56
+ # =============================================================================
57
+
58
+ if ($Help) {
59
+ Write-Host ""
60
+ Write-Host " Neocortex Installer v$VERSION" -ForegroundColor White
61
+ Write-Host " Development Orchestrator" -ForegroundColor DarkGray
62
+ Write-Host ""
63
+ Write-Host " Uso: npx @ornexus/neocortex [opcoes]"
64
+ Write-Host ""
65
+ Write-Host " Opcoes:" -ForegroundColor DarkGray
66
+ Write-Host " -Yes Modo automatico (Claude Code only)" -ForegroundColor Cyan
67
+ Write-Host " -Targets <lista> Plataformas separadas por virgula" -ForegroundColor Cyan
68
+ Write-Host " -CreateProject Instalar estrutura no projeto" -ForegroundColor Cyan
69
+ Write-Host " -SkipProject Nao perguntar sobre projeto" -ForegroundColor Cyan
70
+ Write-Host " -Quiet Modo silencioso" -ForegroundColor Cyan
71
+ Write-Host " -DryRun Mostra adapters/stubs resolvidos sem modificar arquivos" -ForegroundColor Cyan
72
+ Write-Host " -CleanupLegacy (Redundante) Limpeza agora e automatica" -ForegroundColor Cyan
73
+ Write-Host " -Local (Removido) Thin-client obrigatorio, zero IP no client" -ForegroundColor DarkGray
74
+ Write-Host " -ServerUrl <url> URL do server Neocortex" -ForegroundColor Cyan
75
+ Write-Host " -Debug Modo debug" -ForegroundColor Cyan
76
+ Write-Host " -Help Mostra esta ajuda" -ForegroundColor Cyan
77
+ Write-Host ""
78
+ Write-Host " Plataformas: claude-code, cursor, vscode, gemini, codex, antigravity, opencode, kimi, openclaw" -ForegroundColor DarkGray
79
+ exit 0
80
+ }
81
+
82
+ # =============================================================================
83
+ # FUNCOES DE LOG
84
+ # =============================================================================
85
+
86
+ function Write-Step {
87
+ param([int]$Num, [int]$Total, [string]$Message)
88
+ Write-Host ""
89
+ Write-Host " [$Num/$Total] " -ForegroundColor Cyan -NoNewline
90
+ Write-Host $Message -ForegroundColor White
91
+ }
92
+
93
+ function Write-Ok {
94
+ param([string]$Message)
95
+ if (-not $QUIET_MODE) {
96
+ Write-Host " " -NoNewline
97
+ Write-Host "[ok]" -ForegroundColor Green -NoNewline
98
+ Write-Host " $Message"
99
+ }
100
+ }
101
+
102
+ function Write-Warn {
103
+ param([string]$Message)
104
+ Write-Host " " -NoNewline
105
+ Write-Host "[!]" -ForegroundColor Yellow -NoNewline
106
+ Write-Host " $Message"
107
+ }
108
+
109
+ function Write-Fail {
110
+ param([string]$Message)
111
+ Write-Host " " -NoNewline
112
+ Write-Host "[x]" -ForegroundColor Red -NoNewline
113
+ Write-Host " $Message"
114
+ }
115
+
116
+ function Test-NeocortexSourceProject {
117
+ param([string]$Path)
118
+
119
+ $packageFile = Join-Path $Path "package.json"
120
+ if (-not (Test-Path $packageFile -PathType Leaf)) { return $false }
121
+
122
+ try {
123
+ $packageJson = Get-Content -Path $packageFile -Raw | ConvertFrom-Json
124
+ } catch {
125
+ return $false
126
+ }
127
+
128
+ if ($packageJson.name -ne "@ornexus/neocortex") { return $false }
129
+ if (-not (Test-Path (Join-Path $Path "core") -PathType Container)) { return $false }
130
+ if (-not (Test-Path (Join-Path $Path "packages\server") -PathType Container)) { return $false }
131
+ if (-not (Test-Path (Join-Path $Path "targets-stubs") -PathType Container)) { return $false }
132
+ if (-not (Test-Path (Join-Path $Path "install.ps1") -PathType Leaf)) { return $false }
133
+
134
+ return $true
135
+ }
136
+
137
+ function Resolve-TargetDir {
138
+ param([string]$Target)
139
+
140
+ $stubName = if ($Target -eq "gemini") { "gemini-cli" } else { $Target }
141
+ $stubDir = Join-Path $script:SourceDir "targets-stubs\$stubName"
142
+ $devDir = Join-Path $script:SourceDir "targets\$stubName"
143
+
144
+ if (Test-Path $stubDir -PathType Container) { return $stubDir }
145
+ if ((Test-NeocortexSourceProject $script:SourceDir) -and (Test-Path $devDir -PathType Container)) {
146
+ Write-Dbg "Fallback dev targets/ para $Target`: $devDir"
147
+ return $devDir
148
+ }
149
+
150
+ Write-Warn "$Target (adapter nao encontrado; paths tentados: $stubDir, $devDir)"
151
+ return $null
152
+ }
153
+
154
+ function Show-TargetResolutionDryRun {
155
+ param([string[]]$TargetList)
156
+ Write-Info "Dry-run: nenhum arquivo do usuario sera modificado"
157
+ foreach ($target in $TargetList) {
158
+ $resolved = Resolve-TargetDir -Target $target
159
+ if ($resolved) {
160
+ Write-Info "Dry-run target=$target adapter=$resolved scope=published-stub"
161
+ switch ($target) {
162
+ "cursor" { Write-Info "Dry-run MCP target=cursor file=$HOME\.cursor\mcp.json root=mcpServers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom" }
163
+ "vscode" { Write-Info "Dry-run MCP target=vscode file=$(Get-VsCodeUserMcpPath) root=servers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom" }
164
+ "gemini" { Write-Info "Dry-run MCP target=gemini file=$HOME\.gemini\settings.json root=mcpServers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom" }
165
+ "gemini-cli" { Write-Info "Dry-run MCP target=gemini file=$HOME\.gemini\settings.json root=mcpServers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom" }
166
+ "codex" { Write-Info "Dry-run MCP target=codex file=$HOME\.codex\config.toml root=mcp_servers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=replace-managed-sections-preserve-unknown" }
167
+ "opencode" { Write-Info "Dry-run MCP target=opencode file=$(Join-Path (Get-OpenCodeHome) 'opencode.json') root=mcp managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom" }
168
+ "kimi" { Write-Info "Dry-run MCP target=kimi file=$(Join-Path (Get-KimiHome) 'mcp.json') root=mcpServers managed=playwright,context7,browser_use,figma,shadcn,chrome-devtools action=add-or-update-preserve-custom" }
169
+ "openclaw" {
170
+ $openClawHome = if ($env:OPENCLAW_HOME) { $env:OPENCLAW_HOME } else { Join-Path $HOME ".openclaw" }
171
+ Write-Info "Dry-run Skill target=openclaw file=$(Join-Path $openClawHome 'skills\neocortex\SKILL.md') action=copy-skill-preserve-openclaw-json config=$(Join-Path $openClawHome 'openclaw.json') non-overwrite"
172
+ Write-Info "Dry-run project target=openclaw file=.openclaw\skills\neocortex\SKILL.md action=copy-skill-when-create-project"
173
+ }
174
+ }
175
+ } else {
176
+ Write-Info "Dry-run target=$target adapter=NOT_FOUND"
177
+ }
178
+ }
179
+ }
180
+
181
+ function Write-Info {
182
+ param([string]$Message)
183
+ if (-not $QUIET_MODE) {
184
+ Write-Host " -> " -ForegroundColor DarkGray -NoNewline
185
+ Write-Host $Message -ForegroundColor DarkGray
186
+ }
187
+ }
188
+
189
+ function ConvertTo-PublicDiagnosticText {
190
+ param([string]$Value)
191
+ $text = if ($null -eq $Value) { "" } else { [string]$Value }
192
+ if ($HOME) { $text = $text.Replace($HOME, "~") }
193
+ if ($env:USERPROFILE) { $text = $text.Replace($env:USERPROFILE, "~") }
194
+ if ($script:SourceDir) { $text = $text.Replace($script:SourceDir, "<install-dir>") }
195
+ try { $text = $text.Replace((Get-Location).Path, "<project-dir>") } catch { }
196
+ $text = [regex]::Replace($text, '/home/[^\s"'']+', '<redacted-path>')
197
+ $text = [regex]::Replace($text, '/Users/[^\s"'']+', '<redacted-path>')
198
+ $text = [regex]::Replace($text, '/(tmp|media|mnt|workspace|workspaces)/[^\s"'']+', '<redacted-path>')
199
+ $text = [regex]::Replace($text, 'C:\\Users\\[^\s"'']+', '<redacted-path>', 'IgnoreCase')
200
+ $text = [regex]::Replace($text, '[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}', '<redacted-email>', 'IgnoreCase')
201
+ $text = [regex]::Replace($text, '\b(NX|ghp|github_pat|ctx7sk|sk|pk)[_-][A-Za-z0-9_.-]{8,}\b', '<redacted-token>')
202
+ return $text.Trim()
203
+ }
204
+
205
+ function ConvertTo-PublicDiagnosticToken {
206
+ param([string]$Value, [string]$Fallback = "unknown")
207
+ $text = ConvertTo-PublicDiagnosticText -Value $Value
208
+ $token = [regex]::Replace($text, '[^A-Za-z0-9_.:/-]', '_')
209
+ $token = [regex]::Replace($token, '_+', '_').Trim('_')
210
+ if ($token) { return $token }
211
+ return $Fallback
212
+ }
213
+
214
+ function Write-InstallerDiagnostic {
215
+ param(
216
+ [string]$Tool = "neocortex",
217
+ [string]$Phase = "installer",
218
+ [string]$Status = "info",
219
+ [string]$ReasonCode = "P177_PUBLIC_SAFE_LOG_REDACTED",
220
+ [string]$Code = "null",
221
+ [string]$Signal = "null",
222
+ [bool]$TimedOut = $false,
223
+ [int]$RetryAttempt = 0,
224
+ [int]$TimeoutMs = 0,
225
+ [string]$Summary = "public_safe_bounded_diagnostic",
226
+ [string]$DocPath = "docs/install/coderabbit-manual-setup.md"
227
+ )
228
+ $toolToken = ConvertTo-PublicDiagnosticToken -Value $Tool -Fallback "neocortex"
229
+ $phaseToken = ConvertTo-PublicDiagnosticToken -Value $Phase -Fallback "installer"
230
+ $statusToken = ConvertTo-PublicDiagnosticToken -Value $Status -Fallback "info"
231
+ $reasonToken = ConvertTo-PublicDiagnosticToken -Value $ReasonCode -Fallback "P177_PUBLIC_SAFE_LOG_REDACTED"
232
+ $codeToken = ConvertTo-PublicDiagnosticToken -Value $Code -Fallback "null"
233
+ $signalToken = ConvertTo-PublicDiagnosticToken -Value $Signal -Fallback "null"
234
+ $summaryToken = ConvertTo-PublicDiagnosticToken -Value $Summary -Fallback "public_safe_bounded_diagnostic"
235
+ $docToken = ConvertTo-PublicDiagnosticToken -Value $DocPath -Fallback "docs/install/coderabbit-manual-setup.md"
236
+ $timedOutText = if ($TimedOut) { "true" } else { "false" }
237
+ Write-Host " [neocortex-installer] tool=$toolToken phase=$phaseToken status=$statusToken reasonCode=$reasonToken code=$codeToken signal=$signalToken timedOut=$timedOutText retryAttempt=$RetryAttempt timeoutMs=$TimeoutMs docPath=$docToken redactionReasonCode=P177_PUBLIC_SAFE_LOG_REDACTED summary=$summaryToken"
238
+ }
239
+
240
+ function Write-Dbg {
241
+ param([string]$Message)
242
+ if ($DEBUG_MODE) {
243
+ Write-Host " [debug] $(ConvertTo-PublicDiagnosticText -Value $Message)" -ForegroundColor DarkGray
244
+ }
245
+ }
246
+
247
+ # =============================================================================
248
+ # UTF-8 BOM-FREE WRITE HELPER (Epic 63 - Story 63.1)
249
+ # PowerShell 5.1 writes UTF-8 WITH BOM via Out-File -Encoding utf8 and
250
+ # Set-Content -Encoding utf8. Node.js JSON.parse fails on BOM prefix.
251
+ # This helper uses .NET API to write UTF-8 WITHOUT BOM, compatible with
252
+ # both PowerShell 5.1 and 7+.
253
+ # =============================================================================
254
+
255
+ function Write-Utf8NoBom {
256
+ param(
257
+ [string]$Path,
258
+ [string]$Content
259
+ )
260
+ $utf8NoBom = [System.Text.UTF8Encoding]::new($false)
261
+ [System.IO.File]::WriteAllText($Path, $Content, $utf8NoBom)
262
+ }
263
+
264
+ # =============================================================================
265
+ # ACL HELPERS (Story 66.1 - Windows file permission hardening)
266
+ # Fail-open: never throws, never blocks installation
267
+ # =============================================================================
268
+
269
+ function Set-FileAcl {
270
+ param([string]$Path)
271
+ try {
272
+ $username = $env:USERNAME
273
+ if (-not $username) { return }
274
+ & icacls $Path /inheritance:r /grant:r "${username}:(F)" 2>$null | Out-Null
275
+ } catch { }
276
+ }
277
+
278
+ function Set-DirAcl {
279
+ param([string]$Path)
280
+ try {
281
+ $username = $env:USERNAME
282
+ if (-not $username) { return }
283
+ & icacls $Path /inheritance:r /grant:r "${username}:(OI)(CI)(F)" 2>$null | Out-Null
284
+ } catch { }
285
+ }
286
+
287
+ # =============================================================================
288
+ # MCP JSON CONFIG MERGE HELPER (additive, preserves user-defined servers)
289
+ # Fail-open: on error, leaves existing file untouched.
290
+ # $RootKey: top-level key containing the servers map.
291
+ # - "mcpServers" for Cursor and Gemini CLI settings
292
+ # - "servers" for VS Code
293
+ # - "" for Antigravity (flat JSON: keys are server names)
294
+ # =============================================================================
295
+
296
+ function Remove-DisabledOptionalMcpServers {
297
+ param(
298
+ [object]$Config,
299
+ [string]$RootKey = "mcpServers"
300
+ )
301
+
302
+ $optionalServers = @{
303
+ "storybook" = $env:NEOCORTEX_ENABLE_STORYBOOK_MCP
304
+ }
305
+
306
+ $container = $Config
307
+ if ($RootKey -ne "") {
308
+ $container = $Config.$RootKey
309
+ }
310
+ if (-not $container) { return $Config }
311
+
312
+ foreach ($name in $optionalServers.Keys) {
313
+ if ($optionalServers[$name] -ne "1" -and ($container.PSObject.Properties.Name -contains $name)) {
314
+ $container.PSObject.Properties.Remove($name)
315
+ }
316
+ }
317
+
318
+ return $Config
319
+ }
320
+
321
+ function Select-EnabledMcpToml {
322
+ param([string]$Content)
323
+
324
+ $enabled = @{
325
+ "storybook" = $env:NEOCORTEX_ENABLE_STORYBOOK_MCP
326
+ }
327
+ $out = New-Object System.Collections.Generic.List[string]
328
+ $include = $true
329
+
330
+ foreach ($line in ($Content -split "`r?`n")) {
331
+ if ($line -match '^\[mcp_servers\.([^\]]+)\]$') {
332
+ $name = $Matches[1]
333
+ $include = -not $enabled.ContainsKey($name) -or $enabled[$name] -eq "1"
334
+ }
335
+ if ($include) { $out.Add($line) }
336
+ }
337
+
338
+ return (($out -join "`n").TrimEnd() + "`n")
339
+ }
340
+
341
+ function Merge-JsonMcpConfig {
342
+ param(
343
+ [string]$StubFile,
344
+ [string]$DestFile,
345
+ [string]$RootKey = "mcpServers",
346
+ [string[]]$ManagedTopLevelNames = @()
347
+ )
348
+
349
+ if (-not (Test-Path $StubFile)) { return }
350
+
351
+ if (-not (Test-Path $DestFile)) {
352
+ $destDir = Split-Path -Parent $DestFile
353
+ if ($destDir -and -not (Test-Path $destDir)) {
354
+ New-Item -ItemType Directory -Path $destDir -Force -ErrorAction SilentlyContinue | Out-Null
355
+ Set-DirAcl -Path $destDir
356
+ }
357
+ try {
358
+ $stub = Get-Content $StubFile -Raw -ErrorAction Stop | ConvertFrom-Json
359
+ $stub = Remove-DisabledOptionalMcpServers -Config $stub -RootKey $RootKey
360
+ Write-Utf8NoBom -Path $DestFile -Content ($stub | ConvertTo-Json -Depth 20)
361
+ } catch { }
362
+ return
363
+ }
364
+
365
+ try {
366
+ $existing = Get-Content $DestFile -Raw -ErrorAction Stop | ConvertFrom-Json
367
+ $stub = Get-Content $StubFile -Raw -ErrorAction Stop | ConvertFrom-Json
368
+ $stub = Remove-DisabledOptionalMcpServers -Config $stub -RootKey $RootKey
369
+
370
+ if ($RootKey -eq "") {
371
+ # Flat JSON: iterate over stub top-level properties
372
+ foreach ($prop in $stub.PSObject.Properties) {
373
+ if ($existing.PSObject.Properties.Name -contains $prop.Name) {
374
+ $existing.$($prop.Name) = $prop.Value
375
+ } else {
376
+ $existing | Add-Member -NotePropertyName $prop.Name -NotePropertyValue $prop.Value -Force
377
+ }
378
+ }
379
+ } else {
380
+ if (-not $stub.$RootKey) { return }
381
+ if (-not $existing.$RootKey) {
382
+ $existing | Add-Member -NotePropertyName $RootKey -NotePropertyValue ([pscustomobject]@{}) -Force
383
+ }
384
+
385
+ $antigravityMigrated = $false
386
+ foreach ($managedName in $ManagedTopLevelNames) {
387
+ if ($existing.PSObject.Properties.Name -contains $managedName) {
388
+ $value = $existing.$managedName
389
+ if ($value -and ($value.PSObject.Properties.Name -contains "httpUrl") -and -not ($value.PSObject.Properties.Name -contains "serverUrl")) {
390
+ $value | Add-Member -NotePropertyName "serverUrl" -NotePropertyValue $value.httpUrl -Force
391
+ $value.PSObject.Properties.Remove("httpUrl")
392
+ }
393
+ $existing.$RootKey | Add-Member -NotePropertyName $managedName -NotePropertyValue $value -Force
394
+ $existing.PSObject.Properties.Remove($managedName)
395
+ $antigravityMigrated = $true
396
+ }
397
+ }
398
+
399
+ foreach ($prop in $stub.$RootKey.PSObject.Properties) {
400
+ if ($existing.$RootKey.PSObject.Properties.Name -contains $prop.Name) {
401
+ $existing.$RootKey.$($prop.Name) = $prop.Value
402
+ } else {
403
+ $existing.$RootKey | Add-Member -NotePropertyName $prop.Name -NotePropertyValue $prop.Value -Force
404
+ }
405
+ }
406
+
407
+ if ($ManagedTopLevelNames.Count -gt 0) {
408
+ if ($antigravityMigrated) {
409
+ $stamp = (Get-Date).ToString("yyyyMMddTHHmmss")
410
+ $backup = "$DestFile.neocortex-backup-structural-$stamp"
411
+ Copy-Item -Path $DestFile -Destination $backup -Force -ErrorAction SilentlyContinue
412
+ Write-Info "Antigravity MCP backupCreated=$backup"
413
+ }
414
+ Write-Info "Antigravity MCP merge: managed entries migrated/updated under mcpServers; custom top-level entries preserved."
415
+ }
416
+ }
417
+
418
+ Write-Utf8NoBom -Path $DestFile -Content ($existing | ConvertTo-Json -Depth 20)
419
+ } catch {
420
+ try {
421
+ $stamp = (Get-Date).ToString("yyyyMMddTHHmmss")
422
+ $backup = "$DestFile.neocortex-backup-$stamp"
423
+ Copy-Item -Path $DestFile -Destination $backup -Force -ErrorAction Stop
424
+ Write-Warn "MCP config invalido: $DestFile; backup criado em $backup; corrija a sintaxe e execute novamente. Original preservado."
425
+ } catch { }
426
+ }
427
+ }
428
+
429
+ function Get-VsCodeUserMcpPath {
430
+ if ($env:VSCODE_MCP_FILE) { return $env:VSCODE_MCP_FILE }
431
+ if ($env:APPDATA) { return (Join-Path $env:APPDATA "Code\User\mcp.json") }
432
+ return (Join-Path $HOME ".vscode\mcp.json")
433
+ }
434
+
435
+ function Get-OpenCodeHome {
436
+ if ($env:OPENCODE_HOME) { return $env:OPENCODE_HOME }
437
+ if ($env:XDG_CONFIG_HOME) { return (Join-Path $env:XDG_CONFIG_HOME "opencode") }
438
+ return (Join-Path $HOME ".config\opencode")
439
+ }
440
+
441
+ function Get-KimiHome {
442
+ if ($env:KIMI_HOME) { return $env:KIMI_HOME }
443
+ return (Join-Path $HOME ".kimi")
444
+ }
445
+
446
+ function Merge-CodexMcpToml {
447
+ param([string]$StubFile, [string]$DestFile)
448
+
449
+ if (-not (Test-Path $StubFile)) { return }
450
+ $managed = @("playwright", "context7", "browser_use", "figma", "shadcn", "chrome-devtools")
451
+ $destDir = Split-Path -Parent $DestFile
452
+ if ($destDir -and -not (Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir -Force -ErrorAction SilentlyContinue | Out-Null }
453
+ $stub = Get-Content $StubFile -Raw
454
+
455
+ $sections = New-Object System.Collections.Generic.List[string]
456
+ foreach ($name in $managed) {
457
+ $pattern = "(?ms)^\[mcp_servers\." + [regex]::Escape($name) + "\].*?(?=^\[|\z)"
458
+ $match = [regex]::Match($stub, $pattern)
459
+ if ($match.Success) { $sections.Add($match.Value.Trim()) }
460
+ }
461
+ $block = ($sections -join "`n`n")
462
+
463
+ if (-not (Test-Path $DestFile)) {
464
+ Write-Utf8NoBom -Path $DestFile -Content ("# Neocortex MCP Servers`n$block`n")
465
+ return
466
+ }
467
+
468
+ try {
469
+ $existing = Get-Content $DestFile -Raw -ErrorAction Stop
470
+ foreach ($line in ($existing -split "`r?`n")) {
471
+ $trimmed = $line.Trim()
472
+ if ($trimmed.StartsWith("[") -and ($trimmed -notmatch '^\[[^\]]+\]$')) { throw "invalid table header: $trimmed" }
473
+ $quoteCount = ([regex]::Matches($trimmed, '(?<!\\)"')).Count
474
+ if (($quoteCount % 2) -ne 0) { throw "unbalanced quote: $trimmed" }
475
+ }
476
+
477
+ foreach ($name in $managed) {
478
+ $pattern = "(?ms)^\[mcp_servers\." + [regex]::Escape($name) + "\].*?(?=^\[|\z)"
479
+ $existing = [regex]::Replace($existing, $pattern, "")
480
+ }
481
+ $existing = ($existing -replace '(?ms)\n*# Neocortex MCP Servers\s*\z', '').TrimEnd()
482
+ $next = "$existing`n`n# Neocortex MCP Servers`n$block`n"
483
+ if ($next -ne (Get-Content $DestFile -Raw)) { Write-Utf8NoBom -Path $DestFile -Content $next }
484
+ } catch {
485
+ try {
486
+ $stamp = (Get-Date).ToString("yyyyMMddTHHmmss")
487
+ $backup = "$DestFile.neocortex-backup-$stamp"
488
+ Copy-Item -Path $DestFile -Destination $backup -Force -ErrorAction Stop
489
+ Write-Warn "Codex config.toml invalido: $DestFile; backup criado em $backup; corrija a sintaxe e execute novamente. Original preservado."
490
+ } catch { }
491
+ }
492
+ }
493
+
494
+ function Read-HostWithTimeout {
495
+ param([int]$TimeoutSeconds = 10, [string]$Default = "n")
496
+ try {
497
+ if (-not (Test-Interactive)) { return $Default }
498
+ $endTime = [DateTime]::Now.AddSeconds($TimeoutSeconds)
499
+ while (-not $Host.UI.RawUI.KeyAvailable) {
500
+ if ([DateTime]::Now -ge $endTime) {
501
+ Write-Host ""
502
+ return $Default
503
+ }
504
+ Start-Sleep -Milliseconds 100
505
+ }
506
+ return Read-Host
507
+ } catch { return $Default }
508
+ }
509
+
510
+ # =============================================================================
511
+ # BANNER
512
+ # =============================================================================
513
+
514
+ function Show-Banner {
515
+ if ($QUIET_MODE) { return }
516
+ if ($NO_BANNER) { return }
517
+
518
+ Write-Host ""
519
+ Write-Host " #######" -ForegroundColor Cyan
520
+ Write-Host " ### ########" -ForegroundColor Cyan
521
+ Write-Host " ######### #####" -ForegroundColor Cyan
522
+ Write-Host -NoNewline " ## ############## " -ForegroundColor Cyan
523
+ Write-Host "N E O C O R T E X" -ForegroundColor White
524
+ Write-Host -NoNewline " ## ### ###### ## " -ForegroundColor Cyan
525
+ Write-Host "v$VERSION" -ForegroundColor White
526
+ Write-Host " ## ### ### ##" -ForegroundColor Cyan
527
+ Write-Host -NoNewline " ## ###### ### ## " -ForegroundColor Cyan
528
+ Write-Host "OrNexus Team" -ForegroundColor DarkGray
529
+ Write-Host " ############### ##" -ForegroundColor Cyan
530
+ Write-Host " ##### ########" -ForegroundColor Cyan
531
+ Write-Host " ######## ##" -ForegroundColor Cyan
532
+ Write-Host " #######" -ForegroundColor Cyan
533
+ Write-Host ""
534
+ }
535
+
536
+ # =============================================================================
537
+ # FUNCOES DE COPIA (silenciosas)
538
+ # =============================================================================
539
+
540
+ function Copy-Silent {
541
+ param([string]$Source, [string]$Destination)
542
+ Write-Dbg "Copiando: $Source -> $Destination"
543
+ if (Test-Path $Source -PathType Leaf) {
544
+ try {
545
+ # Epic 65: -ErrorAction Stop converts non-terminating errors to terminating
546
+ # Without this, Copy-Item on Windows silently fails and try/catch never triggers
547
+ Copy-Item -Path $Source -Destination $Destination -Force -ErrorAction Stop
548
+ # Post-copy verification: confirm file arrived at destination
549
+ $destFile = if ($Destination.EndsWith('\') -or $Destination.EndsWith('/') -or (Test-Path $Destination -PathType Container)) {
550
+ Join-Path $Destination (Split-Path $Source -Leaf)
551
+ } else { $Destination }
552
+ if (-not (Test-Path $destFile -PathType Leaf)) {
553
+ Write-Dbg "Copy-Item retornou OK mas arquivo nao existe no destino: $destFile"
554
+ $script:Errors++
555
+ return $false
556
+ }
557
+ return $true
558
+ }
559
+ catch {
560
+ Write-Dbg "Copy-Item falhou: $_"
561
+ $script:Errors++
562
+ return $false
563
+ }
564
+ }
565
+ Write-Dbg "Arquivo nao encontrado: $Source"
566
+ $script:Errors++
567
+ return $false
568
+ }
569
+
570
+ function Copy-DirSilent {
571
+ param([string]$Source, [string]$Destination)
572
+ Write-Dbg "Copiando dir: $Source -> $Destination"
573
+ if (Test-Path $Source -PathType Container) {
574
+ try {
575
+ Copy-Item -Path $Source -Destination $Destination -Recurse -Force -ErrorAction Stop
576
+ return $true
577
+ }
578
+ catch {
579
+ Write-Dbg "Copy-Item dir falhou: $_"
580
+ return $false
581
+ }
582
+ }
583
+ return $false
584
+ }
585
+
586
+ # =============================================================================
587
+ # DETECCAO DO DIRETORIO FONTE
588
+ # =============================================================================
589
+
590
+ function Get-SourceDirectory {
591
+ if ($PSScriptRoot) {
592
+ $script:SourceDir = $PSScriptRoot
593
+ } else {
594
+ $script:SourceDir = Get-Location
595
+ }
596
+ Write-Dbg "Source: $script:SourceDir"
597
+
598
+ if (-not (Test-Path "$script:SourceDir\targets-stubs\claude-code\neocortex.md") -and
599
+ -not (Test-Path "$script:SourceDir\neocortex.md")) {
600
+ Write-Dbg "Stubs nao encontrados em PSScriptRoot, buscando em paths alternativos..."
601
+ $possibleDirs = @(
602
+ $script:SourceDir,
603
+ # Scoped paths (primary) -- npm publishes as @ornexus/neocortex
604
+ "$env:APPDATA\npm\node_modules\@ornexus\neocortex",
605
+ "$env:LOCALAPPDATA\npm-cache\_npx\*\node_modules\@ornexus\neocortex",
606
+ ".\node_modules\@ornexus\neocortex",
607
+ # Unscoped paths (fallback for backward compat)
608
+ "$env:APPDATA\npm\node_modules\neocortex",
609
+ "$env:LOCALAPPDATA\npm-cache\_npx\*\node_modules\neocortex",
610
+ ".\node_modules\neocortex"
611
+ )
612
+ $script:SourceFound = $false
613
+ foreach ($dir in $possibleDirs) {
614
+ # Epic 64 (Story 64.6): Debug each possibleDir tested
615
+ Write-Dbg "Get-SourceDirectory: testing $dir"
616
+ $expandedDirs = Get-Item $dir -ErrorAction SilentlyContinue
617
+ foreach ($expanded in $expandedDirs) {
618
+ $found = (Test-Path "$($expanded.FullName)\targets-stubs\claude-code\neocortex.md") -or
619
+ (Test-Path "$($expanded.FullName)\neocortex.md")
620
+ Write-Dbg "Get-SourceDirectory: $($expanded.FullName) found=$found"
621
+ if ($found) {
622
+ $script:SourceDir = $expanded.FullName
623
+ $script:SourceFound = $true
624
+ break
625
+ }
626
+ }
627
+ if ($script:SourceFound) { break }
628
+ }
629
+ # Epic 65: Emit visible warning when stubs cannot be found anywhere
630
+ if (-not $script:SourceFound) {
631
+ Write-Warn "Arquivos de instalacao (stubs) nao encontrados em nenhum path conhecido"
632
+ Write-Warn "PSScriptRoot: $PSScriptRoot"
633
+ Write-Warn "Tente reinstalar: npm install -g @ornexus/neocortex"
634
+ }
635
+ }
636
+ }
637
+
638
+ # =============================================================================
639
+ # DETECCAO DE VERSAO ANTIGA
640
+ # =============================================================================
641
+
642
+ function Test-OldInstallation {
643
+ Write-Dbg "Verificando instalacoes antigas..."
644
+ $destDir = "$env:USERPROFILE\.claude\agents\neocortex"
645
+
646
+ $versionFile = "$destDir\.version"
647
+ if (Test-Path $versionFile) {
648
+ $installedVersion = (Get-Content $versionFile -Raw).Trim()
649
+ $pkgJsonPath = Join-Path $script:SourceDir "package.json"
650
+ if (Test-Path $pkgJsonPath) {
651
+ $pkgJson = Get-Content $pkgJsonPath -Raw | ConvertFrom-Json -ErrorAction SilentlyContinue
652
+ if ($pkgJson.version) {
653
+ if ($installedVersion -eq $pkgJson.version) {
654
+ Write-Info "Reinstalando v$($pkgJson.version)"
655
+ } else {
656
+ Write-Info "Atualizando v$installedVersion -> v$($pkgJson.version)"
657
+ }
658
+ }
659
+ }
660
+ }
661
+ }
662
+
663
+ function Test-ProjectMigrationNeeds {
664
+ $sources = @()
665
+ if (Test-Path ".neocortex\orchestrator.db") { $sources += "orchestrator.db" }
666
+ @("bmad-output\sprint-status.yaml", ".neocortex\sprint-status.yaml", "docs\sprint-status.yaml") | ForEach-Object {
667
+ if (Test-Path $_) { $sources += $_ }
668
+ }
669
+ if ($sources.Count -gt 0) { return $sources }
670
+ return $null
671
+ }
672
+
673
+ # =============================================================================
674
+ # LIMPEZA AUTOMATICA DE ARTEFATOS LEGADOS (Invoke-AutoCleanupLegacy)
675
+ # Unifica: Test-LegacyArtifacts + Invoke-CleanupLegacy + Remove-LegacyIP
676
+ # + Remove-LegacyIPProject + npm globals + cross-platform cleanup
677
+ # Roda AUTOMATICAMENTE a cada instalacao - sem necessidade de -CleanupLegacy
678
+ # Idempotente: rodar multiplas vezes sem efeitos colaterais
679
+ # Seguro: NAO remove dados do usuario (stories, epics, state.json, config.json)
680
+ # =============================================================================
681
+
682
+ function Invoke-AutoCleanupLegacy {
683
+ $removed = 0
684
+ $claudeDir = "$env:USERPROFILE\.claude"
685
+ $neocortexDir = "$claudeDir\agents\neocortex"
686
+ $skillsDir = "$claudeDir\skills\neocortex"
687
+
688
+ Write-Dbg "Executando limpeza automatica de artefatos legados..."
689
+
690
+ # --- Helper: remove e loga ---
691
+ function Remove-LegacyItem {
692
+ param([string]$Target, [string]$Label)
693
+ if (Test-Path $Target) {
694
+ $displayItem = $Target -replace [regex]::Escape($env:USERPROFILE), "~"
695
+ try {
696
+ Remove-Item -Path $Target -Recurse -Force -ErrorAction Stop
697
+ Write-Info "Removido: $displayItem ($Label)"
698
+ $script:autoRemoved++
699
+ } catch {
700
+ Write-Dbg "Falha ao remover: $displayItem"
701
+ }
702
+ }
703
+ }
704
+
705
+ $script:autoRemoved = 0
706
+
707
+ # --- Categoria 1: Pacotes NPM globais legados ---
708
+ $npmCmd = Get-Command npm -ErrorAction SilentlyContinue
709
+ if ($npmCmd) {
710
+ foreach ($pkg in @("@ornexus/neocortex-cli", "@ornexus-ai/neocortex")) {
711
+ $npmList = & npm list -g $pkg --depth=0 2>$null
712
+ if ($npmList -match [regex]::Escape($pkg)) {
713
+ try {
714
+ & npm uninstall -g $pkg 2>$null
715
+ if ($LASTEXITCODE -eq 0) {
716
+ Write-Info "Removido: $pkg (pacote npm global legado)"
717
+ $script:autoRemoved++
718
+ }
719
+ } catch {
720
+ Write-Dbg "Falha ao remover pacote npm: $pkg"
721
+ }
722
+ }
723
+ }
724
+
725
+ # Verificar binario neocortex-cli no PATH
726
+ $ncliCmd = Get-Command neocortex-cli -ErrorAction SilentlyContinue
727
+ if ($ncliCmd -and $ncliCmd.Source -match "node_modules|npm") {
728
+ try {
729
+ Remove-Item -Path $ncliCmd.Source -Force -ErrorAction Stop
730
+ Write-Info "Removido: $($ncliCmd.Source) (binario neocortex-cli legado)"
731
+ $script:autoRemoved++
732
+ } catch {
733
+ Write-Dbg "Falha ao remover binario neocortex-cli"
734
+ }
735
+ }
736
+ }
737
+
738
+ # --- Categoria 2: Claude Code (~\.claude\) ---
739
+ # IP proprietaria de versoes anteriores
740
+ Remove-LegacyItem "$neocortexDir\core" "IP legada"
741
+ Remove-LegacyItem $skillsDir "skills legadas"
742
+ Remove-LegacyItem "$neocortexDir\workflow.md" "workflow removido no Tier 3"
743
+ Remove-LegacyItem "$neocortexDir\package.json" "arquivo desnecessario"
744
+ Remove-LegacyItem "$neocortexDir\README.md" "arquivo desnecessario"
745
+
746
+ # Step folders legados
747
+ foreach ($folder in @("steps-c", "steps-e", "steps-p", "steps-r", "steps-u")) {
748
+ Remove-LegacyItem "$neocortexDir\$folder" "steps legados"
749
+ }
750
+
751
+ # Artefatos de instalacoes anteriores
752
+ Remove-LegacyItem "$claudeDir\agents\.git" "repo git antigo"
753
+ Remove-LegacyItem "$claudeDir\.claude" "diretorio aninhado (erro BMAD)"
754
+ Remove-LegacyItem "$claudeDir\agents-ldtn" "diretorio legado"
755
+ Remove-LegacyItem "$claudeDir\.superclaude-metadata.json" "metadata SuperClaude"
756
+
757
+ # Backups SuperClaude antigos
758
+ if (Test-Path "$claudeDir\backups" -PathType Container) {
759
+ Get-ChildItem -Path "$claudeDir\backups" -Filter "superclaude_backup_*.tar.gz" -File -ErrorAction SilentlyContinue | ForEach-Object {
760
+ Remove-LegacyItem $_.FullName "backup SuperClaude antigo"
761
+ }
762
+ }
763
+
764
+ # --- Categoria 3: Cursor ---
765
+ Remove-LegacyItem "$env:USERPROFILE\.cursor\neocortex" "configs Cursor legadas"
766
+ # .cursorrules com referencias neocortex (verificar antes de remover)
767
+ $cursorrules = "$env:USERPROFILE\.cursorrules"
768
+ if (Test-Path $cursorrules -PathType Leaf) {
769
+ $content = Get-Content $cursorrules -Raw -ErrorAction SilentlyContinue
770
+ if ($content -match "(?i)neocortex|ornexus|synapse") {
771
+ Remove-LegacyItem $cursorrules "cursorrules com refs legadas"
772
+ }
773
+ }
774
+
775
+ # --- Categoria 4: VS Code ---
776
+ Remove-LegacyItem "$env:USERPROFILE\.vscode\neocortex" "configs VS Code legadas"
777
+ # .instructions.md com referencias neocortex antigo
778
+ $instructionsMd = "$env:USERPROFILE\.instructions.md"
779
+ if (Test-Path $instructionsMd -PathType Leaf) {
780
+ $content = Get-Content $instructionsMd -Raw -ErrorAction SilentlyContinue
781
+ if ($content -match "(?i)neocortex|ornexus|synapse") {
782
+ Remove-LegacyItem $instructionsMd "instructions.md com refs legadas"
783
+ }
784
+ }
785
+
786
+ # --- Categoria 5: Gemini CLI ---
787
+ Remove-LegacyItem "$env:USERPROFILE\.gemini\neocortex" "configs Gemini legadas"
788
+
789
+ # --- Categoria 6: Codex ---
790
+ Remove-LegacyItem "$env:USERPROFILE\.codex\neocortex" "configs Codex legadas"
791
+
792
+ # --- Categoria 7: Antigravity ---
793
+ # Configs legadas de Antigravity sao gerenciadas pelo adapter, sem path fixo global
794
+
795
+ # --- Categoria 8: Plaintext cache cleanup (Epic 62 - GAP 1) ---
796
+ $cacheDir = "$env:USERPROFILE\.neocortex\cache"
797
+ if (Test-Path $cacheDir -PathType Container) {
798
+ # Remove plaintext menu-cache.json
799
+ Remove-LegacyItem "$cacheDir\menu-cache.json" "cache plaintext (menu)"
800
+
801
+ # Remove any non-.enc files in cache dir (excluding directories)
802
+ Get-ChildItem -Path $cacheDir -File -ErrorAction SilentlyContinue | Where-Object {
803
+ $_.Extension -ne ".enc"
804
+ } | ForEach-Object {
805
+ Remove-LegacyItem $_.FullName "cache plaintext"
806
+ }
807
+ }
808
+
809
+ # --- Resultado ---
810
+ $removed = $script:autoRemoved
811
+ if ($removed -gt 0) {
812
+ Write-Ok "$removed artefato(s) legado(s) removido(s) automaticamente"
813
+ } else {
814
+ Write-Dbg "Nenhum artefato legado encontrado"
815
+ }
816
+
817
+ # Resetar contadores legados (compatibilidade)
818
+ $script:LegacyItems = @()
819
+ $script:LegacyWarnings = 0
820
+ }
821
+
822
+ # Cleanup de IP legada em projetos individuais (project-level)
823
+ function Invoke-AutoCleanupLegacyProject {
824
+ param([string]$ProjectDir)
825
+
826
+ $neocortexDir = "$ProjectDir\.claude\agents\neocortex"
827
+ $skillsDir = "$ProjectDir\.claude\skills\neocortex"
828
+ $cleaned = $false
829
+
830
+ # Remover core/ e seus subdiretorios
831
+ if (Test-Path "$neocortexDir\core") {
832
+ Remove-Item -Path "$neocortexDir\core" -Recurse -Force -ErrorAction SilentlyContinue
833
+ $cleaned = $true
834
+ }
835
+
836
+ # Remover step folders
837
+ foreach ($folder in @("steps-c", "steps-e", "steps-p", "steps-r", "steps-u")) {
838
+ if (Test-Path "$neocortexDir\$folder") {
839
+ Remove-Item -Path "$neocortexDir\$folder" -Recurse -Force -ErrorAction SilentlyContinue
840
+ $cleaned = $true
841
+ }
842
+ }
843
+
844
+ # Remover skills
845
+ if (Test-Path $skillsDir) {
846
+ Remove-Item -Path $skillsDir -Recurse -Force -ErrorAction SilentlyContinue
847
+ $cleaned = $true
848
+ }
849
+
850
+ # Remover arquivos desnecessarios
851
+ foreach ($file in @("package.json", "README.md", "workflow.md")) {
852
+ if (Test-Path "$neocortexDir\$file") {
853
+ Remove-Item -Path "$neocortexDir\$file" -Force -ErrorAction SilentlyContinue
854
+ $cleaned = $true
855
+ }
856
+ }
857
+
858
+ if ($cleaned) {
859
+ Write-Info "IP proprietaria removida do projeto (agora servida via server remoto)"
860
+ }
861
+ }
862
+
863
+ # =============================================================================
864
+ # VERIFICACAO POS-INSTALACAO
865
+ # =============================================================================
866
+
867
+ function Verify-Installation {
868
+ $fails = 0
869
+ $warns = 0
870
+ $report = @()
871
+
872
+ if ($script:SelectedTargets -notcontains "claude-code") {
873
+ return $true
874
+ }
875
+
876
+ # --- Layer 1: File existence + minimum size ---
877
+ $criticalFiles = @(
878
+ @{ Name = "neocortex.md"; MinBytes = 512; Label = "Agent principal (stub)" },
879
+ @{ Name = "neocortex.agent.yaml"; MinBytes = 128; Label = "Agent config YAML (stub)" }
880
+ )
881
+
882
+ foreach ($entry in $criticalFiles) {
883
+ $fpath = Join-Path $script:DestDir $entry.Name
884
+ $fname = $entry.Name
885
+ # Epic 64 (Story 64.6): Debug file verification details
886
+ $fileExists = Test-Path $fpath -PathType Leaf
887
+ $fileSize = if ($fileExists) { (Get-Item $fpath).Length } else { 0 }
888
+ Write-Dbg "Verify-Installation: $fname exists=$fileExists size=$fileSize path=$fpath"
889
+
890
+ if (-not (Test-Path $fpath -PathType Leaf)) {
891
+ $report += @{ Status = "FAIL"; Detail = "$fname (nao encontrado)" }
892
+ $fails++
893
+ } else {
894
+ $fileInfo = Get-Item $fpath
895
+ $size = $fileInfo.Length
896
+
897
+ if ($size -lt $entry.MinBytes) {
898
+ $report += @{ Status = "FAIL"; Detail = "$fname ($size bytes - possivelmente corrompido, minimo $($entry.MinBytes))" }
899
+ $fails++
900
+ } else {
901
+ # --- Layer 2: Content marker (frontmatter) ---
902
+ $ext = [System.IO.Path]::GetExtension($fname)
903
+ if ($ext -eq ".md") {
904
+ $firstLine = (Get-Content $fpath -TotalCount 1 -ErrorAction SilentlyContinue)
905
+ if ($firstLine -ne "---") {
906
+ $report += @{ Status = "WARN"; Detail = "$fname (formato invalido - sem frontmatter)" }
907
+ $warns++
908
+ } else {
909
+ $displaySize = if ($size -ge 1024) { "$([math]::Floor($size / 1024))KB" } else { "${size}B" }
910
+ $report += @{ Status = "OK"; Detail = "$fname ($displaySize)" }
911
+ }
912
+ } else {
913
+ $displaySize = if ($size -ge 1024) { "$([math]::Floor($size / 1024))KB" } else { "${size}B" }
914
+ $report += @{ Status = "OK"; Detail = "$fname ($displaySize)" }
915
+ }
916
+ }
917
+ }
918
+ }
919
+
920
+ if ($LOCAL_MODE) {
921
+ # --- Layer 3: Step directories (local mode only) ---
922
+ foreach ($dir in @("steps-c", "steps-e", "steps-p", "steps-r", "steps-u")) {
923
+ $dirPath = Join-Path $script:DestDir $dir
924
+ if (-not (Test-Path $dirPath -PathType Container)) {
925
+ $report += @{ Status = "FAIL"; Detail = "$dir/ (diretorio nao encontrado)" }
926
+ $fails++
927
+ } else {
928
+ $mdCount = (Get-ChildItem -Path $dirPath -Filter "*.md" -File -ErrorAction SilentlyContinue).Count
929
+ if ($mdCount -eq 0) {
930
+ $report += @{ Status = "WARN"; Detail = "$dir/ (vazio - nenhum arquivo .md)" }
931
+ $warns++
932
+ } else {
933
+ $report += @{ Status = "OK"; Detail = "$dir/ ($mdCount arquivos)" }
934
+ }
935
+ }
936
+ }
937
+
938
+ # --- Layer 3b: Core directory (local mode only) ---
939
+ $corePath = Join-Path $script:DestDir "core"
940
+ if (-not (Test-Path $corePath -PathType Container)) {
941
+ $report += @{ Status = "FAIL"; Detail = "core/ (diretorio nao encontrado)" }
942
+ $fails++
943
+ } else {
944
+ $report += @{ Status = "OK"; Detail = "core/" }
945
+ }
946
+ } else {
947
+ # --- Layer 3: Thin client config (remote mode) ---
948
+ $configFile = "$env:USERPROFILE\.neocortex\config.json"
949
+ if (Test-Path $configFile -PathType Leaf) {
950
+ $report += @{ Status = "OK"; Detail = "~\.neocortex\config.json (thin client configured)" }
951
+ } else {
952
+ $report += @{ Status = "WARN"; Detail = "~\.neocortex\config.json (nao encontrado)" }
953
+ $warns++
954
+ }
955
+
956
+ # Verify NO IP directories exist
957
+ $ipFound = $false
958
+ foreach ($dir in @("core", "steps-c", "steps-e", "steps-p", "steps-r", "steps-u")) {
959
+ if (Test-Path (Join-Path $script:DestDir $dir) -PathType Container) {
960
+ $report += @{ Status = "WARN"; Detail = "$dir/ ainda existe (deveria ter sido removido)" }
961
+ $warns++
962
+ $ipFound = $true
963
+ }
964
+ }
965
+ if (-not $ipFound) {
966
+ $report += @{ Status = "OK"; Detail = "Zero IP no filesystem (modo remoto)" }
967
+ }
968
+ }
969
+
970
+ # --- Layer 4: Tool availability (Story 66.4 AC1/AC2) ---
971
+ $claudeCmd = Get-Command claude -ErrorAction SilentlyContinue
972
+ if (-not $claudeCmd) {
973
+ $claudeCmd = Get-Command claude.exe -ErrorAction SilentlyContinue
974
+ }
975
+ if (-not $claudeCmd) {
976
+ $report += @{ Status = "WARN"; Detail = "Claude Code CLI nao encontrado no PATH" }
977
+ $warns++
978
+ Write-Info "Instale Claude Code: https://claude.ai/download"
979
+ } else {
980
+ $report += @{ Status = "OK"; Detail = "Claude Code CLI encontrado" }
981
+ }
982
+
983
+ $ncCmd = Get-Command neocortex-client -ErrorAction SilentlyContinue
984
+ if (-not $ncCmd) {
985
+ $ncCmd = Get-Command "neocortex-client.cmd" -ErrorAction SilentlyContinue
986
+ }
987
+ if (-not $ncCmd) {
988
+ $report += @{ Status = "WARN"; Detail = "neocortex-client nao encontrado no PATH" }
989
+ $warns++
990
+ Write-Info "Pode ser necessario reabrir o terminal apos npm install -g"
991
+ } else {
992
+ $report += @{ Status = "OK"; Detail = "neocortex-client encontrado" }
993
+ }
994
+
995
+ # --- Display report ---
996
+ if ($fails -eq 0 -and $warns -eq 0) {
997
+ if (-not $QUIET_MODE) {
998
+ Write-Ok "Instalacao verificada"
999
+ }
1000
+ return $true
1001
+ }
1002
+
1003
+ # Epic 64 (Story 64.3): In quiet mode, report FAILs before returning
1004
+ if ($QUIET_MODE) {
1005
+ if ($fails -gt 0) {
1006
+ # Critical failures MUST be visible even in quiet mode
1007
+ Write-Fail "Verificacao falhou: $fails arquivo(s) critico(s) faltando"
1008
+ foreach ($entry in ($report | Where-Object { $_.Status -eq "FAIL" })) {
1009
+ Write-Fail " $($entry.Detail)"
1010
+ }
1011
+ return $false
1012
+ }
1013
+ return $true # In quiet mode, skip warnings-only report
1014
+ }
1015
+
1016
+ Write-Host ""
1017
+ Write-Info "Verificacao pos-instalacao:"
1018
+ foreach ($entry in $report) {
1019
+ switch ($entry.Status) {
1020
+ "OK" { Write-Host " " -NoNewline; Write-Host "[ok]" -ForegroundColor Green -NoNewline; Write-Host " $($entry.Detail)" }
1021
+ "WARN" { Write-Host " " -NoNewline; Write-Host "[!]" -ForegroundColor Yellow -NoNewline; Write-Host " $($entry.Detail)" }
1022
+ "FAIL" { Write-Host " " -NoNewline; Write-Host "[x]" -ForegroundColor Red -NoNewline; Write-Host " $($entry.Detail)" }
1023
+ }
1024
+ }
1025
+
1026
+ if ($fails -gt 0) { return $false }
1027
+ return $true
1028
+ }
1029
+
1030
+ # =============================================================================
1031
+ # TIER 3: CONFIG DO THIN CLIENT
1032
+ # =============================================================================
1033
+
1034
+ function Set-ThinClientConfig {
1035
+ $configDir = "$env:USERPROFILE\.neocortex"
1036
+ $configFile = "$configDir\config.json"
1037
+
1038
+ New-Item -ItemType Directory -Path $configDir -Force -ErrorAction SilentlyContinue | Out-Null
1039
+ New-Item -ItemType Directory -Path "$configDir\cache" -Force -ErrorAction SilentlyContinue | Out-Null
1040
+
1041
+ # Story 66.1 AC5: Apply ACLs on ~/.neocortex/ and cache/
1042
+ Set-DirAcl $configDir
1043
+ Set-DirAcl "$configDir\cache"
1044
+
1045
+ if (Test-Path $configFile) {
1046
+ try {
1047
+ $existingConfig = Get-Content $configFile -Raw | ConvertFrom-Json -ErrorAction SilentlyContinue
1048
+ if ($existingConfig.mode -eq "active" -or $existingConfig.mode -eq "local" -or $existingConfig.mode -eq "remote") {
1049
+ # --- Config schema migration (Epic 62 - GAP 4) ---
1050
+ if (-not $existingConfig.configVersion) {
1051
+ Write-Dbg "Migrando schema do config.json (adicionando configVersion)"
1052
+ try {
1053
+ # Add configVersion
1054
+ $existingConfig | Add-Member -NotePropertyName "configVersion" -NotePropertyValue 1 -Force
1055
+ # Remove known obsolete fields
1056
+ $existingConfig.PSObject.Properties.Remove("version")
1057
+ $existingConfig.PSObject.Properties.Remove("cache")
1058
+ # Clean obsolete tier:3 from old base template
1059
+ if ($existingConfig.tier -eq 3 -and $existingConfig.mode -eq "pending-activation") {
1060
+ $existingConfig.PSObject.Properties.Remove("tier")
1061
+ }
1062
+ Write-Utf8NoBom -Path $configFile -Content ($existingConfig | ConvertTo-Json -Depth 5)
1063
+ Write-Dbg "Config migrada para configVersion 1"
1064
+ } catch {
1065
+ Write-Dbg "Falha na migracao do config: $_"
1066
+ }
1067
+ }
1068
+ # --- Fix stale localhost serverUrl (Epic 70 - Story 70.02) ----
1069
+ # If config has localhost serverUrl and installer has production URL,
1070
+ # update serverUrl while preserving all other config fields.
1071
+ $existingServerUrl = $existingConfig.serverUrl
1072
+ if ($existingServerUrl -match '^https?://(localhost|127\.0\.0\.1|0\.0\.0\.0)(:\d+)?') {
1073
+ if ($NEOCORTEX_SERVER_URL -ne $existingServerUrl) {
1074
+ Write-Dbg "Fixing stale localhost serverUrl: $existingServerUrl -> $NEOCORTEX_SERVER_URL"
1075
+ try {
1076
+ $existingConfig.serverUrl = $NEOCORTEX_SERVER_URL
1077
+ Write-Utf8NoBom -Path $configFile -Content ($existingConfig | ConvertTo-Json -Depth 5)
1078
+ Set-FileAcl $configFile
1079
+ Write-Dbg "serverUrl updated to $NEOCORTEX_SERVER_URL"
1080
+ } catch {
1081
+ Write-Dbg "Failed to update serverUrl: $_"
1082
+ }
1083
+ }
1084
+ }
1085
+
1086
+ # --- Fix stale legacy serverUrl (Epic P46 - Story P46.04) ----
1087
+ $legacyServerHost = @('api', 'neocortex', 'ornexus', 'com') -join '.'
1088
+ if ($existingServerUrl -like "*$legacyServerHost*") {
1089
+ Write-Dbg "Fixing legacy serverUrl: $existingServerUrl -> $NEOCORTEX_SERVER_URL"
1090
+ try {
1091
+ $existingConfig.serverUrl = $NEOCORTEX_SERVER_URL
1092
+ Write-Utf8NoBom -Path $configFile -Content ($existingConfig | ConvertTo-Json -Depth 5)
1093
+ Set-FileAcl $configFile
1094
+ Write-Dbg "serverUrl updated from legacy host to $NEOCORTEX_SERVER_URL"
1095
+ } catch {
1096
+ Write-Dbg "Failed to update serverUrl: $_"
1097
+ }
1098
+ }
1099
+
1100
+ Write-Dbg "Config existente preservada (mode=$($existingConfig.mode))"
1101
+ return
1102
+ }
1103
+ } catch { }
1104
+ }
1105
+
1106
+ $config = @{
1107
+ configVersion = 1
1108
+ mode = "pending-activation"
1109
+ serverUrl = $NEOCORTEX_SERVER_URL
1110
+ resilience = @{
1111
+ circuitBreaker = $true
1112
+ maxRetries = 3
1113
+ timeoutMs = 5000
1114
+ }
1115
+ installedAt = (Get-Date -Format "o")
1116
+ installerVersion = $VERSION
1117
+ }
1118
+
1119
+ Write-Utf8NoBom -Path $configFile -Content ($config | ConvertTo-Json -Depth 5)
1120
+ # Story 66.1 AC4: Apply ACL on config.json
1121
+ Set-FileAcl $configFile
1122
+ Write-Dbg "Thin client config criada: $configFile"
1123
+ }
1124
+
1125
+ # Remove-LegacyIPProject substituida por Invoke-AutoCleanupLegacyProject
1126
+ # (definida na secao de limpeza automatica acima)
1127
+
1128
+ # =============================================================================
1129
+ # INSTALACAO CORE
1130
+ # =============================================================================
1131
+
1132
+ function Install-Core {
1133
+ $script:ClaudeDir = "$env:USERPROFILE\.claude"
1134
+ $script:AgentsDir = "$script:ClaudeDir\agents"
1135
+ $script:DestDir = "$script:AgentsDir\neocortex"
1136
+
1137
+ Write-Dbg "DEST=$script:DestDir"
1138
+
1139
+ # Create directories with verification (Epic 65)
1140
+ if (-not (Test-Path $script:ClaudeDir)) {
1141
+ try { New-Item -ItemType Directory -Path $script:ClaudeDir -Force -ErrorAction Stop | Out-Null }
1142
+ catch { Write-Fail "Falha ao criar $script:ClaudeDir`: $_"; return $false }
1143
+ }
1144
+ try { New-Item -ItemType Directory -Path $script:AgentsDir -Force -ErrorAction Stop | Out-Null }
1145
+ catch { Write-Fail "Falha ao criar $script:AgentsDir`: $_"; return $false }
1146
+
1147
+ # Clean previous
1148
+ if (Test-Path $script:DestDir) {
1149
+ try {
1150
+ Remove-Item -Path $script:DestDir -Recurse -Force -ErrorAction Stop
1151
+ } catch {
1152
+ Write-Warn "Falha ao remover instalacao anterior: $_ (tentando continuar)"
1153
+ }
1154
+ }
1155
+ try { New-Item -ItemType Directory -Path $script:DestDir -Force -ErrorAction Stop | Out-Null }
1156
+ catch { Write-Fail "Falha ao criar $script:DestDir`: $_"; return $false }
1157
+
1158
+ # Verify directory was actually created (Epic 65: anti-silent-failure)
1159
+ if (-not (Test-Path $script:DestDir -PathType Container)) {
1160
+ Write-Fail "Diretorio $script:DestDir nao existe apos criacao"
1161
+ return $false
1162
+ }
1163
+
1164
+ if ($LOCAL_MODE) {
1165
+ # Modo local: copiar IP completa (comportamento legado para devs)
1166
+ $coreSource = Join-Path $script:SourceDir "core"
1167
+ if (Test-Path $coreSource -PathType Container) {
1168
+ Copy-Item -Path $coreSource -Destination "$script:DestDir\" -Recurse -Force -ErrorAction SilentlyContinue
1169
+ }
1170
+ $r = Copy-Silent "$($script:SourceDir)\package.json" "$script:DestDir\"
1171
+ if (-not $r) { Write-Fail "Falha ao copiar package.json (Install-Core)" }
1172
+ $r = Copy-Silent "$($script:SourceDir)\README.md" "$script:DestDir\"
1173
+ if (-not $r) { Write-Fail "Falha ao copiar README.md (Install-Core)" }
1174
+ } else {
1175
+ # Tier 3 Remote Mode: NAO copiar IP
1176
+ # Nota: Invoke-AutoCleanupLegacy ja rodou no inicio - aqui apenas config
1177
+ Set-ThinClientConfig
1178
+ }
1179
+
1180
+ # --- Version-aware cache purge on upgrade (Epic 62 - GAP 2+3) ---
1181
+ $pkgJsonPath = Join-Path $script:SourceDir "package.json"
1182
+ $pkgVersion = $null
1183
+ if (Test-Path $pkgJsonPath) {
1184
+ $pkgJson = Get-Content $pkgJsonPath -Raw | ConvertFrom-Json -ErrorAction SilentlyContinue
1185
+ $pkgVersion = $pkgJson.version
1186
+ }
1187
+
1188
+ if ($pkgVersion) {
1189
+ $oldVersion = $null
1190
+ # Read existing .version from either location
1191
+ $versionPaths = @("$script:DestDir\.version", "$env:USERPROFILE\.neocortex\.version")
1192
+ foreach ($vp in $versionPaths) {
1193
+ if (Test-Path $vp -PathType Leaf) {
1194
+ $oldVersion = (Get-Content $vp -Raw -ErrorAction SilentlyContinue).Trim()
1195
+ if ($oldVersion) { break }
1196
+ }
1197
+ }
1198
+
1199
+ # If version changed, purge all cache files
1200
+ if ($oldVersion -and $oldVersion -ne $pkgVersion) {
1201
+ $cachePurgeDir = "$env:USERPROFILE\.neocortex\cache"
1202
+ if (Test-Path $cachePurgeDir -PathType Container) {
1203
+ $purged = 0
1204
+ Get-ChildItem -Path $cachePurgeDir -File -ErrorAction SilentlyContinue | ForEach-Object {
1205
+ Remove-Item $_.FullName -Force -ErrorAction SilentlyContinue
1206
+ $purged++
1207
+ }
1208
+ if ($purged -gt 0) {
1209
+ Write-Info "Cache purgado: versao alterada de $oldVersion para $pkgVersion ($purged arquivo(s))"
1210
+ }
1211
+ }
1212
+ }
1213
+
1214
+ # Write version file
1215
+ Write-Utf8NoBom -Path "$script:DestDir\.version" -Content $pkgVersion
1216
+ }
1217
+
1218
+ if ($LOCAL_MODE) {
1219
+ Write-Ok "Core instalado (standards, templates, scripts) [modo local]"
1220
+ } else {
1221
+ Write-Ok "Remote mode configured (thin client ready)"
1222
+ }
1223
+ return $true
1224
+ }
1225
+
1226
+ # =============================================================================
1227
+ # INSTALACAO DE SKILLS
1228
+ # =============================================================================
1229
+
1230
+ function Install-Skills {
1231
+ $script:SkillsDir = "$script:ClaudeDir\skills"
1232
+ $script:SkillsDest = "$script:SkillsDir\neocortex"
1233
+ $skillsSource = "$script:SourceDir\core\skills"
1234
+
1235
+ # Tier 3 Remote Mode: skills vem do server, nao copiar
1236
+ if (-not $LOCAL_MODE) {
1237
+ Write-Ok "Skills: delivered by remote server"
1238
+ return $true
1239
+ }
1240
+
1241
+ if (-not (Test-Path $skillsSource -PathType Container)) { return $true }
1242
+
1243
+ New-Item -ItemType Directory -Path $script:SkillsDir -Force | Out-Null
1244
+
1245
+ if (Test-Path $script:SkillsDest) {
1246
+ Remove-Item -Path $script:SkillsDest -Recurse -Force -ErrorAction SilentlyContinue
1247
+ }
1248
+
1249
+ try {
1250
+ Copy-Item -Path $skillsSource -Destination $script:SkillsDest -Recurse -Force
1251
+
1252
+ # Count skills
1253
+ $skillCount = 0
1254
+ Get-ChildItem "$script:SkillsDest\step-skills\*\*.md", "$script:SkillsDest\domain-skills\*\*.md" -File -ErrorAction SilentlyContinue |
1255
+ Where-Object { $_.Name -ne "_template.md" } | ForEach-Object { $skillCount++ }
1256
+
1257
+ Write-Ok "Skills instaladas ($skillCount skills) [modo local]"
1258
+ return $true
1259
+ } catch {
1260
+ Write-Fail "Falha ao copiar skills"
1261
+ return $false
1262
+ }
1263
+ }
1264
+
1265
+ # =============================================================================
1266
+ # INSTALACAO DE AGENT (Claude Code)
1267
+ # =============================================================================
1268
+
1269
+ function Install-Agent {
1270
+ $claudeTargetDir = Join-Path $script:SourceDir "targets-stubs\claude-code"
1271
+ if ((-not (Test-Path $claudeTargetDir)) -and (Test-NeocortexSourceProject $script:SourceDir) -and (Test-Path (Join-Path $script:SourceDir "targets\claude-code") -PathType Container)) {
1272
+ $claudeTargetDir = Join-Path $script:SourceDir "targets\claude-code"
1273
+ }
1274
+ if (-not (Test-Path $claudeTargetDir)) {
1275
+ $claudeTargetDir = $script:SourceDir
1276
+ }
1277
+
1278
+ # Epic 64 (Story 64.6): Debug diagnostics for Install-Agent
1279
+ Write-Dbg "Install-Agent: SourceDir=$($script:SourceDir)"
1280
+ Write-Dbg "Install-Agent: DestDir=$($script:DestDir)"
1281
+ Write-Dbg "Install-Agent: claudeTargetDir=$claudeTargetDir"
1282
+ $srcMdExists = Test-Path "$claudeTargetDir\neocortex.md"
1283
+ $srcYamlExists = Test-Path "$claudeTargetDir\neocortex.agent.yaml"
1284
+ Write-Dbg "Install-Agent: neocortex.md exists=$srcMdExists"
1285
+ Write-Dbg "Install-Agent: neocortex.agent.yaml exists=$srcYamlExists"
1286
+
1287
+ # Epic 65: Early validation -- if NEITHER source file exists, emit clear error
1288
+ if (-not $srcMdExists -and -not $srcYamlExists) {
1289
+ Write-Fail "Arquivos fonte nao encontrados para target claude-code em: $claudeTargetDir"
1290
+ Write-Fail "Diretorio de origem ($($script:SourceDir)) pode estar incompleto"
1291
+ Write-Fail "Tente reinstalar: npm install -g @ornexus/neocortex"
1292
+ $script:Errors += 2
1293
+ return $false
1294
+ }
1295
+
1296
+ # Tier 3: Copy only 2 stub files
1297
+ # Epic 64 (Story 64.2): Capture Copy-Silent return and check for errors
1298
+ $stubResult1 = Copy-Silent "$claudeTargetDir\neocortex.md" "$script:DestDir\"
1299
+ if (-not $stubResult1) { Write-Fail "Falha ao copiar neocortex.md de $claudeTargetDir" }
1300
+ Write-Dbg "Install-Agent: Copy neocortex.md result=$stubResult1"
1301
+ $stubResult2 = Copy-Silent "$claudeTargetDir\neocortex.agent.yaml" "$script:DestDir\"
1302
+ if (-not $stubResult2) { Write-Fail "Falha ao copiar neocortex.agent.yaml de $claudeTargetDir" }
1303
+ Write-Dbg "Install-Agent: Copy neocortex.agent.yaml result=$stubResult2"
1304
+
1305
+ # Epic 65: Post-copy verification -- belt and suspenders
1306
+ $destMd = Join-Path $script:DestDir "neocortex.md"
1307
+ $destYaml = Join-Path $script:DestDir "neocortex.agent.yaml"
1308
+ if (-not (Test-Path $destMd -PathType Leaf)) {
1309
+ Write-Fail "neocortex.md nao encontrado no destino apos copia: $destMd"
1310
+ if (-not $stubResult1) {} else { $script:Errors++ }
1311
+ }
1312
+ if (-not (Test-Path $destYaml -PathType Leaf)) {
1313
+ Write-Fail "neocortex.agent.yaml nao encontrado no destino apos copia: $destYaml"
1314
+ if (-not $stubResult2) {} else { $script:Errors++ }
1315
+ }
1316
+
1317
+ # Cleanup workflow.md from previous versions
1318
+ if (Test-Path "$script:DestDir\workflow.md") {
1319
+ Remove-Item "$script:DestDir\workflow.md" -Force -ErrorAction SilentlyContinue
1320
+ }
1321
+
1322
+ if ($LOCAL_MODE) {
1323
+ # Modo local: copiar IP completa (comportamento legado para devs)
1324
+ $r = Copy-Silent "$($script:SourceDir)\package.json" "$script:DestDir\"
1325
+ if (-not $r) { Write-Fail "Falha ao copiar package.json" }
1326
+ $r = Copy-Silent "$($script:SourceDir)\README.md" "$script:DestDir\"
1327
+ if (-not $r) { Write-Fail "Falha ao copiar README.md" }
1328
+
1329
+ $coreSource = Join-Path $script:SourceDir "core"
1330
+ if (Test-Path $coreSource -PathType Container) {
1331
+ Copy-Item -Path $coreSource -Destination "$script:DestDir\" -Recurse -Force -ErrorAction SilentlyContinue
1332
+ }
1333
+
1334
+ foreach ($folder in @("steps-c", "steps-e", "steps-p", "steps-r", "steps-u")) {
1335
+ $stepSrc = "$($script:SourceDir)\core\steps\$folder"
1336
+ if (Test-Path $stepSrc -PathType Container) {
1337
+ Copy-Item -Path $stepSrc -Destination "$script:DestDir\" -Recurse -Force -ErrorAction SilentlyContinue
1338
+ }
1339
+ }
1340
+ }
1341
+ # Tier 3 Remote Mode: apenas 2 arquivos de interface (stubs)
1342
+
1343
+ return ($script:Errors -eq 0)
1344
+ }
1345
+
1346
+ # =============================================================================
1347
+ # CARREGAMENTO DE .ENV
1348
+ # =============================================================================
1349
+
1350
+ function Import-EnvFile {
1351
+ $envFile = $null
1352
+ @(".\.env", "$script:SourceDir\.env") | ForEach-Object {
1353
+ if ((Test-Path $_ -PathType Leaf) -and -not $envFile) { $envFile = $_ }
1354
+ }
1355
+ if (-not $envFile) { return $false }
1356
+
1357
+ $loaded = 0
1358
+ Get-Content $envFile | ForEach-Object {
1359
+ $line = $_.Trim()
1360
+ if (-not $line -or $line.StartsWith("#")) { return }
1361
+ if ($line -match "^([^=]+)=(.*)$") {
1362
+ $key = $matches[1].Trim()
1363
+ $value = $matches[2].Trim() -replace '^["'']|["'']$', ''
1364
+ if ($value -and -not [Environment]::GetEnvironmentVariable($key)) {
1365
+ [Environment]::SetEnvironmentVariable($key, $value, "Process")
1366
+ Set-Item -Path "env:$key" -Value $value -ErrorAction SilentlyContinue
1367
+ $loaded++
1368
+ }
1369
+ }
1370
+ }
1371
+ if ($loaded -gt 0) { Write-Info "$loaded variavel(eis) carregada(s) do .env" }
1372
+ return $true
1373
+ }
1374
+
1375
+ # =============================================================================
1376
+ # CONFIGURACAO DE TOKENS
1377
+ # =============================================================================
1378
+
1379
+ function Request-Tokens {
1380
+ if ($QUIET_MODE -or $AUTO_YES -or $env:CONTEXT7_API_KEY) { return }
1381
+
1382
+ Write-Host ""
1383
+ Write-Info "MCP Context7 nao configurado (opcional)"
1384
+ Write-Host " Configurar agora? [s/N]: " -NoNewline -ForegroundColor White
1385
+ $response = Read-HostWithTimeout -TimeoutSeconds 15 -Default "n"
1386
+
1387
+ if ($response -match "^[sS]([iI][mM]?)?$|^[yY]([eE][sS]?)?$") {
1388
+ Write-Host ""
1389
+ Write-Info "Obtenha sua API Key em: https://context7.com"
1390
+ Write-Host " Cole sua CONTEXT7_API_KEY: " -NoNewline -ForegroundColor White
1391
+ try {
1392
+ $apiKey = Read-Host
1393
+ if ($apiKey -match "^ctx7sk-") {
1394
+ $env:CONTEXT7_API_KEY = $apiKey
1395
+ Write-Ok "CONTEXT7_API_KEY configurada"
1396
+ Write-Info "Para persistir: `$env:CONTEXT7_API_KEY = `"$apiKey`" em `$PROFILE"
1397
+ } elseif ($apiKey) {
1398
+ Write-Warn "Formato invalido (esperado: ctx7sk-...)"
1399
+ }
1400
+ } catch {}
1401
+ }
1402
+ }
1403
+
1404
+ # =============================================================================
1405
+ # INSTALACAO DE MCP SERVERS
1406
+ # =============================================================================
1407
+
1408
+ function Install-MCPs {
1409
+ $claudeCmd = Get-Command claude -ErrorAction SilentlyContinue
1410
+ if (-not $claudeCmd) {
1411
+ Write-Warn "Claude CLI nao encontrado (MCPs Claude user-scope serao instalados depois; outros targets continuam)"
1412
+ return
1413
+ }
1414
+
1415
+ $mcpOk = @()
1416
+ $mcpSkip = @()
1417
+ $browserUseEnvArgs = @()
1418
+ if ($env:OPENAI_API_KEY) { $browserUseEnvArgs += @('-e', "OPENAI_API_KEY=$env:OPENAI_API_KEY") }
1419
+ if ($env:ANTHROPIC_API_KEY) { $browserUseEnvArgs += @('-e', "ANTHROPIC_API_KEY=$env:ANTHROPIC_API_KEY") }
1420
+
1421
+ # Playwright
1422
+ try {
1423
+ & claude mcp add --scope user playwright -- npx -y @playwright/mcp@latest 2>$null
1424
+ if ($LASTEXITCODE -eq 0) { $mcpOk += "playwright" }
1425
+ } catch {}
1426
+
1427
+ # Context7
1428
+ if ($env:CONTEXT7_API_KEY) {
1429
+ try {
1430
+ & claude mcp add --scope user --transport http context7 https://mcp.context7.com/mcp --header "CONTEXT7_API_KEY: $env:CONTEXT7_API_KEY" 2>$null
1431
+ if ($LASTEXITCODE -eq 0) { $mcpOk += "context7" }
1432
+ } catch {}
1433
+ } else {
1434
+ $mcpSkip += "context7"
1435
+ }
1436
+
1437
+ # browser_use: optional (requires uvx and at least one supported LLM API key).
1438
+ # Supported env vars are referenced by name only in logs: OPENAI_API_KEY,
1439
+ # ANTHROPIC_API_KEY.
1440
+ if ($browserUseEnvArgs.Count -gt 0) {
1441
+ if (-not (Get-Command uvx -ErrorAction SilentlyContinue)) {
1442
+ Write-Warn "uvx nao encontrado (Browser Use MCP requer uvx; instale uv/uvx e tente novamente)"
1443
+ $mcpSkip += "browser_use"
1444
+ } else {
1445
+ try {
1446
+ & claude mcp add --scope user browser_use @browserUseEnvArgs -- uvx --from 'browser-use[cli]' browser-use --mcp 2>$null
1447
+ if ($LASTEXITCODE -eq 0) { $mcpOk += "browser_use" }
1448
+ } catch {}
1449
+ }
1450
+ } else {
1451
+ $mcpSkip += "browser_use"
1452
+ }
1453
+
1454
+ # Design/component MCPs follow the same baseline install pattern as
1455
+ # Playwright because public target fragments include them by default.
1456
+ try {
1457
+ & claude mcp add --scope user --transport http figma https://mcp.figma.com/mcp 2>$null
1458
+ if ($LASTEXITCODE -eq 0) { $mcpOk += "figma" }
1459
+ } catch {}
1460
+
1461
+ try {
1462
+ & claude mcp add --scope user shadcn -- npx shadcn@latest mcp 2>$null
1463
+ if ($LASTEXITCODE -eq 0) { $mcpOk += "shadcn" }
1464
+ } catch {}
1465
+
1466
+ try {
1467
+ & claude mcp add --scope user chrome-devtools -- npx -y chrome-devtools-mcp@latest 2>$null
1468
+ if ($LASTEXITCODE -eq 0) { $mcpOk += "chrome-devtools" }
1469
+ } catch {}
1470
+
1471
+ if ($mcpOk.Count -gt 0) { Write-Ok "MCPs: $($mcpOk -join ', ')" }
1472
+ if ($mcpSkip.Count -gt 0) { Write-Info "MCPs pendentes: $($mcpSkip -join ', ')" }
1473
+ }
1474
+
1475
+ # =============================================================================
1476
+ # STATUS DO CODERABBIT CLI (OPCIONAL / MANUAL)
1477
+ # =============================================================================
1478
+
1479
+ function Test-CiEnvironment {
1480
+ return ($env:CI -eq "true" -or $env:GITHUB_ACTIONS -eq "true" -or $env:TF_BUILD -eq "true")
1481
+ }
1482
+
1483
+ function Get-CodeRabbitSkipReason {
1484
+ if (Test-CiEnvironment) { return "P177_OPTIONAL_TOOL_SKIPPED_CI" }
1485
+ if ((-not (Test-Interactive)) -or $AUTO_YES -or $QUIET_MODE) { return "P177_OPTIONAL_TOOL_SKIPPED_NONINTERACTIVE" }
1486
+ return "P177_OPTIONAL_TOOL_OPT_IN_REQUIRED"
1487
+ }
1488
+
1489
+ function Write-OptionalToolInfo {
1490
+ param([string]$Message)
1491
+ Write-Host " -> " -ForegroundColor DarkGray -NoNewline
1492
+ Write-Host $Message -ForegroundColor DarkGray
1493
+ }
1494
+
1495
+ function Report-CodeRabbitStatus {
1496
+ $coderabbitCmd = Get-Command coderabbit -ErrorAction SilentlyContinue
1497
+ if ($coderabbitCmd) {
1498
+ Write-InstallerDiagnostic -Tool "coderabbit" -Phase "optional-tool-setup" -Status "detected" -ReasonCode "P177_OPTIONAL_TOOL_DETECTED_MANUAL" -Code "0" -Summary "manual_optional_tool_detected"
1499
+ Write-Ok "CodeRabbit CLI (opcional/manual detectado)"
1500
+ $script:CodeRabbitInstalled = $true
1501
+ return
1502
+ }
1503
+
1504
+ $reasonCode = Get-CodeRabbitSkipReason
1505
+ Write-InstallerDiagnostic -Tool "coderabbit" -Phase "optional-tool-setup" -Status "skipped" -ReasonCode $reasonCode -Code "null" -Summary "optional_tool_setup_skipped"
1506
+ Write-OptionalToolInfo "CodeRabbit CLI (opcional; setup manual em docs/install/coderabbit-manual-setup.md; $reasonCode)"
1507
+ $script:CodeRabbitInstalled = $false
1508
+ }
1509
+
1510
+ # =============================================================================
1511
+ # MULTI-TARGET SUPPORT
1512
+ # =============================================================================
1513
+
1514
+ $script:ValidTargets = @("claude-code", "cursor", "vscode", "gemini", "gemini-cli", "codex", "antigravity", "opencode", "kimi", "openclaw")
1515
+ $script:SelectedTargets = @()
1516
+ $script:TargetResults = @()
1517
+ $script:TargetCount = 0
1518
+
1519
+ function Install-Targets {
1520
+ param([string[]]$TargetList)
1521
+
1522
+ $homeDir = $env:USERPROFILE
1523
+
1524
+ foreach ($target in $TargetList) {
1525
+ switch ($target) {
1526
+ "claude-code" {
1527
+ $success = Install-Agent
1528
+ if ($success) {
1529
+ $script:TargetResults += "$target`:OK"
1530
+ $script:TargetCount++
1531
+ Write-Ok "claude-code (thin client)"
1532
+ } else {
1533
+ $script:TargetResults += "$target`:FAIL"
1534
+ Write-Fail "claude-code"
1535
+ }
1536
+ }
1537
+ "cursor" {
1538
+ $cursorTargetDir = Resolve-TargetDir -Target "cursor"
1539
+ if ($cursorTargetDir -and (Test-Path $cursorTargetDir -PathType Container)) {
1540
+ # Clean previous (NOTE: mcp.json is NOT deleted -- it is merged additively below)
1541
+ Remove-Item -Path "$homeDir\.cursor\agents\neocortex.md" -Force -ErrorAction SilentlyContinue
1542
+ if (Test-Path "$homeDir\.cursor\skills") { Remove-Item -Path "$homeDir\.cursor\skills" -Recurse -Force -ErrorAction SilentlyContinue }
1543
+
1544
+ # Agent
1545
+ if (Test-Path "$cursorTargetDir\agent.md") {
1546
+ New-Item -ItemType Directory -Path "$homeDir\.cursor\agents" -Force | Out-Null
1547
+ Copy-Item -Path "$cursorTargetDir\agent.md" -Destination "$homeDir\.cursor\agents\neocortex.md" -Force
1548
+ }
1549
+ # Rules
1550
+ if (Test-Path "$cursorTargetDir\rules" -PathType Container) {
1551
+ New-Item -ItemType Directory -Path "$homeDir\.cursor\rules" -Force | Out-Null
1552
+ Copy-Item -Path "$cursorTargetDir\rules\*.mdc" -Destination "$homeDir\.cursor\rules\" -Force -ErrorAction SilentlyContinue
1553
+ }
1554
+ # Commands
1555
+ if (Test-Path "$cursorTargetDir\commands" -PathType Container) {
1556
+ New-Item -ItemType Directory -Path "$homeDir\.cursor\commands" -Force | Out-Null
1557
+ Copy-Item -Path "$cursorTargetDir\commands\*.md" -Destination "$homeDir\.cursor\commands\" -Force -ErrorAction SilentlyContinue
1558
+ }
1559
+ # MCP config (additive merge -- preserves user-defined servers)
1560
+ if (Test-Path "$cursorTargetDir\mcp.json") {
1561
+ Merge-JsonMcpConfig -StubFile "$cursorTargetDir\mcp.json" -DestFile "$homeDir\.cursor\mcp.json" -RootKey "mcpServers"
1562
+ }
1563
+ # Skills
1564
+ $skillsSource = Join-Path $script:SourceDir "core\skills"
1565
+ if (Test-Path $skillsSource -PathType Container) {
1566
+ New-Item -ItemType Directory -Path "$homeDir\.cursor\skills" -Force | Out-Null
1567
+ Copy-Item -Path "$skillsSource\*" -Destination "$homeDir\.cursor\skills\" -Recurse -Force -ErrorAction SilentlyContinue
1568
+ }
1569
+
1570
+ Write-Ok "cursor"
1571
+ $script:TargetResults += "$target`:OK"
1572
+ $script:TargetCount++
1573
+ } else {
1574
+ Write-Warn "cursor (adapter nao encontrado)"
1575
+ $script:TargetResults += "$target`:SKIP"
1576
+ }
1577
+ }
1578
+ "vscode" {
1579
+ $vscodeTargetDir = Resolve-TargetDir -Target "vscode"
1580
+ if ($vscodeTargetDir -and (Test-Path $vscodeTargetDir -PathType Container)) {
1581
+ # Clean previous
1582
+ Remove-Item -Path "$homeDir\.github\agents\neocortex.md" -Force -ErrorAction SilentlyContinue
1583
+ Remove-Item -Path "$homeDir\.github\agents\neocortex.agent.md" -Force -ErrorAction SilentlyContinue
1584
+ Remove-Item -Path "$homeDir\.github\copilot-instructions.md" -Force -ErrorAction SilentlyContinue
1585
+ if (Test-Path "$homeDir\.github\skills") { Remove-Item -Path "$homeDir\.github\skills" -Recurse -Force -ErrorAction SilentlyContinue }
1586
+
1587
+ # P124.02: source filename is neocortex.agent.md (canonical Custom Agents extension).
1588
+ if (Test-Path "$vscodeTargetDir\neocortex.agent.md") {
1589
+ New-Item -ItemType Directory -Path "$homeDir\.github\agents" -Force | Out-Null
1590
+ Copy-Item -Path "$vscodeTargetDir\neocortex.agent.md" -Destination "$homeDir\.github\agents\neocortex.agent.md" -Force
1591
+ Copy-Item -Path "$vscodeTargetDir\neocortex.agent.md" -Destination "$homeDir\.github\agents\neocortex.md" -Force
1592
+ New-Item -ItemType Directory -Path "$homeDir\.copilot\agents" -Force | Out-Null
1593
+ Copy-Item -Path "$vscodeTargetDir\neocortex.agent.md" -Destination "$homeDir\.copilot\agents\neocortex.agent.md" -Force
1594
+ }
1595
+ if (Test-Path "$vscodeTargetDir\instructions" -PathType Container) {
1596
+ New-Item -ItemType Directory -Path "$homeDir\.github\instructions" -Force | Out-Null
1597
+ Copy-Item -Path "$vscodeTargetDir\instructions\*.instructions.md" -Destination "$homeDir\.github\instructions\" -Force -ErrorAction SilentlyContinue
1598
+ }
1599
+ if (Test-Path "$vscodeTargetDir\prompts" -PathType Container) {
1600
+ New-Item -ItemType Directory -Path "$homeDir\.github\prompts" -Force | Out-Null
1601
+ Copy-Item -Path "$vscodeTargetDir\prompts\*.prompt.md" -Destination "$homeDir\.github\prompts\" -Force -ErrorAction SilentlyContinue
1602
+ }
1603
+ if (Test-Path "$vscodeTargetDir\mcp.json") {
1604
+ # User-level additive merge -- preserves user-defined servers (root key is "servers" in VS Code)
1605
+ Merge-JsonMcpConfig -StubFile "$vscodeTargetDir\mcp.json" -DestFile (Get-VsCodeUserMcpPath) -RootKey "servers"
1606
+ }
1607
+ if (Test-Path "$vscodeTargetDir\copilot-instructions.md") {
1608
+ Copy-Item -Path "$vscodeTargetDir\copilot-instructions.md" -Destination "$homeDir\.github\copilot-instructions.md" -Force
1609
+ }
1610
+ $skillsSource = Join-Path $script:SourceDir "core\skills"
1611
+ if (Test-Path $skillsSource -PathType Container) {
1612
+ New-Item -ItemType Directory -Path "$homeDir\.github\skills" -Force | Out-Null
1613
+ Copy-Item -Path "$skillsSource\*" -Destination "$homeDir\.github\skills\" -Recurse -Force -ErrorAction SilentlyContinue
1614
+ }
1615
+
1616
+ Write-Ok "vscode"
1617
+ $script:TargetResults += "$target`:OK"
1618
+ $script:TargetCount++
1619
+ } else {
1620
+ Write-Warn "vscode (adapter nao encontrado)"
1621
+ $script:TargetResults += "$target`:SKIP"
1622
+ }
1623
+ }
1624
+ { $_ -in @("gemini", "gemini-cli") } {
1625
+ $geminiTargetDir = Resolve-TargetDir -Target "gemini-cli"
1626
+ $geminiHome = if ($env:GEMINI_HOME) { $env:GEMINI_HOME } else { "$homeDir\.gemini" }
1627
+ if ($geminiTargetDir -and (Test-Path $geminiTargetDir -PathType Container)) {
1628
+ Remove-Item -Path "$geminiHome\agents\neocortex.md" -Force -ErrorAction SilentlyContinue
1629
+ if (Test-Path "$geminiHome\skills") { Remove-Item -Path "$geminiHome\skills" -Recurse -Force -ErrorAction SilentlyContinue }
1630
+
1631
+ if (Test-Path "$geminiTargetDir\agents\neocortex.md") {
1632
+ New-Item -ItemType Directory -Path "$geminiHome\agents" -Force | Out-Null
1633
+ Copy-Item -Path "$geminiTargetDir\agents\neocortex.md" -Destination "$geminiHome\agents\neocortex.md" -Force
1634
+ } elseif (Test-Path "$geminiTargetDir\agent.md") {
1635
+ New-Item -ItemType Directory -Path "$geminiHome\agents" -Force | Out-Null
1636
+ Copy-Item -Path "$geminiTargetDir\agent.md" -Destination "$geminiHome\agents\neocortex.md" -Force
1637
+ }
1638
+ if (Test-Path "$geminiTargetDir\gemini.md") {
1639
+ Copy-Item -Path "$geminiTargetDir\gemini.md" -Destination "$homeDir\GEMINI.md" -Force
1640
+ }
1641
+ if (Test-Path "$geminiTargetDir\commands" -PathType Container) {
1642
+ New-Item -ItemType Directory -Path "$geminiHome\commands" -Force | Out-Null
1643
+ Copy-Item -Path "$geminiTargetDir\commands\*.toml" -Destination "$geminiHome\commands\" -Force -ErrorAction SilentlyContinue
1644
+ }
1645
+ if (Test-Path "$geminiTargetDir\settings-mcp.json") {
1646
+ # Additive merge into ~/.gemini/settings.json (root key is "mcpServers")
1647
+ Merge-JsonMcpConfig -StubFile "$geminiTargetDir\settings-mcp.json" -DestFile "$geminiHome\settings.json" -RootKey "mcpServers"
1648
+ }
1649
+ $skillsSource = Join-Path $script:SourceDir "core\skills"
1650
+ if (Test-Path $skillsSource -PathType Container) {
1651
+ New-Item -ItemType Directory -Path "$geminiHome\skills" -Force | Out-Null
1652
+ Copy-Item -Path "$skillsSource\*" -Destination "$geminiHome\skills\" -Recurse -Force -ErrorAction SilentlyContinue
1653
+ }
1654
+
1655
+ Write-Ok "gemini"
1656
+ $script:TargetResults += "$target`:OK"
1657
+ $script:TargetCount++
1658
+ } else {
1659
+ Write-Warn "gemini (adapter nao encontrado)"
1660
+ $script:TargetResults += "$target`:SKIP"
1661
+ }
1662
+ }
1663
+ "codex" {
1664
+ $codexTargetDir = Resolve-TargetDir -Target "codex"
1665
+ $codexHome = if ($env:CODEX_HOME) { $env:CODEX_HOME } else { "$homeDir\.codex" }
1666
+ if ($codexTargetDir -and (Test-Path $codexTargetDir -PathType Container)) {
1667
+ Remove-Item -Path "$codexHome\AGENTS.md" -Force -ErrorAction SilentlyContinue
1668
+ New-Item -ItemType Directory -Path $codexHome -Force | Out-Null
1669
+ New-Item -ItemType Directory -Path "$codexHome\agents" -Force | Out-Null
1670
+
1671
+ if (Test-Path "$codexTargetDir\AGENTS.md") {
1672
+ Copy-Item -Path "$codexTargetDir\AGENTS.md" -Destination "$codexHome\AGENTS.md" -Force
1673
+ }
1674
+ if (Test-Path "$codexTargetDir\neocortex.toml") {
1675
+ Copy-Item -Path "$codexTargetDir\neocortex.toml" -Destination "$codexHome\agents\neocortex.toml" -Force
1676
+ }
1677
+ if (Test-Path "$codexTargetDir\config-mcp.toml") {
1678
+ $configFile = "$codexHome\config.toml"
1679
+ Merge-CodexMcpToml -StubFile "$codexTargetDir\config-mcp.toml" -DestFile $configFile
1680
+ }
1681
+
1682
+ Write-Ok "codex"
1683
+ $script:TargetResults += "$target`:OK"
1684
+ $script:TargetCount++
1685
+ } else {
1686
+ Write-Warn "codex (adapter nao encontrado)"
1687
+ $script:TargetResults += "$target`:SKIP"
1688
+ }
1689
+ }
1690
+ "antigravity" {
1691
+ $antiTargetDir = Resolve-TargetDir -Target "antigravity"
1692
+ $geminiHome = if ($env:GEMINI_HOME) { $env:GEMINI_HOME } else { "$homeDir\.gemini" }
1693
+ if ($antiTargetDir -and (Test-Path $antiTargetDir -PathType Container)) {
1694
+ # NOTE: Do NOT recursively delete $geminiHome\antigravity -- merge mcp_config.json instead
1695
+ if (Test-Path "$antiTargetDir\skill\SKILL.md") {
1696
+ New-Item -ItemType Directory -Path "$homeDir\.agent\skills\neocortex" -Force | Out-Null
1697
+ Copy-Item -Path "$antiTargetDir\skill\SKILL.md" -Destination "$homeDir\.agent\skills\neocortex\SKILL.md" -Force
1698
+ }
1699
+ if (Test-Path "$antiTargetDir\rules" -PathType Container) {
1700
+ New-Item -ItemType Directory -Path "$homeDir\.agent\rules" -Force | Out-Null
1701
+ Copy-Item -Path "$antiTargetDir\rules\*.md" -Destination "$homeDir\.agent\rules\" -Force -ErrorAction SilentlyContinue
1702
+ }
1703
+ if (Test-Path "$antiTargetDir\workflows" -PathType Container) {
1704
+ New-Item -ItemType Directory -Path "$homeDir\.agent\workflows" -Force | Out-Null
1705
+ Copy-Item -Path "$antiTargetDir\workflows\*.md" -Destination "$homeDir\.agent\workflows\" -Force -ErrorAction SilentlyContinue
1706
+ }
1707
+ if (Test-Path "$antiTargetDir\mcp-config.json") {
1708
+ New-Item -ItemType Directory -Path "$geminiHome\antigravity" -Force | Out-Null
1709
+ # Antigravity uses root mcpServers; preserve custom entries while migrating managed legacy top-level entries.
1710
+ Merge-JsonMcpConfig -StubFile "$antiTargetDir\mcp-config.json" -DestFile "$geminiHome\antigravity\mcp_config.json" -RootKey "mcpServers" -ManagedTopLevelNames @("playwright", "context7", "browser_use", "figma", "shadcn", "chrome-devtools")
1711
+ }
1712
+ if (Test-Path "$antiTargetDir\gemini.md") {
1713
+ Copy-Item -Path "$antiTargetDir\gemini.md" -Destination "$homeDir\GEMINI.md" -Force
1714
+ }
1715
+ $skillsSource = Join-Path $script:SourceDir "core\skills"
1716
+ if (Test-Path $skillsSource -PathType Container) {
1717
+ $skillsDest = "$homeDir\.agent\skills\neocortex\skills"
1718
+ New-Item -ItemType Directory -Path $skillsDest -Force | Out-Null
1719
+ Copy-Item -Path "$skillsSource\*" -Destination "$skillsDest\" -Recurse -Force -ErrorAction SilentlyContinue
1720
+ }
1721
+
1722
+ Write-Ok "antigravity"
1723
+ $script:TargetResults += "$target`:OK"
1724
+ $script:TargetCount++
1725
+ } else {
1726
+ Write-Warn "antigravity (adapter nao encontrado)"
1727
+ $script:TargetResults += "$target`:SKIP"
1728
+ }
1729
+ }
1730
+ "opencode" {
1731
+ $opencodeTargetDir = Resolve-TargetDir -Target "opencode"
1732
+ $opencodeHome = Get-OpenCodeHome
1733
+ if ($opencodeTargetDir -and (Test-Path "$opencodeTargetDir\neocortex.md") -and (Test-Path "$opencodeTargetDir\neocortex-root.md")) {
1734
+ New-Item -ItemType Directory -Path "$opencodeHome\agents" -Force | Out-Null
1735
+ Copy-Item -Path "$opencodeTargetDir\neocortex.md" -Destination "$opencodeHome\agents\neocortex.md" -Force -ErrorAction SilentlyContinue
1736
+ Copy-Item -Path "$opencodeTargetDir\neocortex-root.md" -Destination "$opencodeHome\agents\neocortex-root.md" -Force -ErrorAction SilentlyContinue
1737
+ if (Test-Path "$opencodeTargetDir\opencode-mcp.json") {
1738
+ Merge-JsonMcpConfig -StubFile "$opencodeTargetDir\opencode-mcp.json" -DestFile "$opencodeHome\opencode.json" -RootKey "mcp"
1739
+ }
1740
+ Write-Ok "opencode"
1741
+ $script:TargetResults += "$target`:OK"
1742
+ $script:TargetCount++
1743
+ } else {
1744
+ Write-Warn "opencode (root/subagent stubs nao encontrados)"
1745
+ $script:TargetResults += "opencode`:SKIP"
1746
+ }
1747
+ }
1748
+ "kimi" {
1749
+ $kimiTargetDir = Resolve-TargetDir -Target "kimi"
1750
+ $kimiHome = Get-KimiHome
1751
+ if ($kimiTargetDir -and (Test-Path "$kimiTargetDir\neocortex.md")) {
1752
+ New-Item -ItemType Directory -Path "$kimiHome\skills\neocortex" -Force | Out-Null
1753
+ New-Item -ItemType Directory -Path "$homeDir\.config\agents\skills\neocortex" -Force | Out-Null
1754
+ Copy-Item -Path "$kimiTargetDir\neocortex.md" -Destination "$kimiHome\skills\neocortex\SKILL.md" -Force -ErrorAction SilentlyContinue
1755
+ Copy-Item -Path "$kimiTargetDir\neocortex.md" -Destination "$homeDir\.config\agents\skills\neocortex\SKILL.md" -Force -ErrorAction SilentlyContinue
1756
+ if (Test-Path "$kimiTargetDir\mcp.json") {
1757
+ Merge-JsonMcpConfig -StubFile "$kimiTargetDir\mcp.json" -DestFile "$kimiHome\mcp.json" -RootKey "mcpServers"
1758
+ }
1759
+ Write-Ok "kimi"
1760
+ $script:TargetResults += "$target`:OK"
1761
+ $script:TargetCount++
1762
+ } else {
1763
+ Write-Warn "kimi (adapter nao encontrado)"
1764
+ $script:TargetResults += "$target`:SKIP"
1765
+ }
1766
+ }
1767
+ "openclaw" {
1768
+ $openclawTargetDir = Resolve-TargetDir -Target "openclaw"
1769
+ $openclawHome = if ($env:OPENCLAW_HOME) { $env:OPENCLAW_HOME } else { Join-Path $homeDir ".openclaw" }
1770
+ $skillSource = if ($openclawTargetDir) { Join-Path $openclawTargetDir "SKILL.md" } else { $null }
1771
+ if ($skillSource -and (Test-Path $skillSource -PathType Leaf)) {
1772
+ $skillDestDir = Join-Path $openclawHome "skills\neocortex"
1773
+ New-Item -ItemType Directory -Path $skillDestDir -Force | Out-Null
1774
+ Copy-Item -Path $skillSource -Destination (Join-Path $skillDestDir "SKILL.md") -Force -ErrorAction SilentlyContinue
1775
+ Write-Ok "openclaw (Skill thin-client; openclaw.json preservado)"
1776
+ $script:TargetResults += "$target`:OK"
1777
+ $script:TargetCount++
1778
+ } else {
1779
+ Write-Warn "openclaw (Skill stub nao encontrado; path tentado: $skillSource)"
1780
+ $script:TargetResults += "$target`:SKIP"
1781
+ }
1782
+ }
1783
+ default {
1784
+ Write-Warn "'$target' nao reconhecido"
1785
+ $script:TargetResults += "$target`:SKIP"
1786
+ }
1787
+ }
1788
+ }
1789
+ }
1790
+
1791
+ # =============================================================================
1792
+ # CRIACAO DE DIRETORIOS DO PROJETO
1793
+ # =============================================================================
1794
+
1795
+ function New-ProjectDirectories {
1796
+ if ($QUIET_MODE -or $SKIP_PROJECT_DIRS) { return }
1797
+
1798
+ $shouldCreate = $false
1799
+
1800
+ if ($CreateProject -or $AUTO_YES) {
1801
+ $shouldCreate = $true
1802
+ } else {
1803
+ Write-Host " Instalar estrutura no projeto atual? [s/N]: " -NoNewline -ForegroundColor White
1804
+ $response = Read-HostWithTimeout -TimeoutSeconds 30 -Default "n"
1805
+ if ($response -match "^[sS]([iI][mM]?)?$|^[yY]([eE][sS]?)?$") { $shouldCreate = $true }
1806
+ }
1807
+
1808
+ if (-not $shouldCreate) { return }
1809
+
1810
+ $projectDir = Get-Location
1811
+
1812
+ if (Test-NeocortexSourceProject -Path $projectDir) {
1813
+ Write-Warn "Recusando -CreateProject dentro do repo fonte do Neocortex"
1814
+ Write-Warn "Isso preserva core/ e arquivos de desenvolvimento locais"
1815
+ return
1816
+ }
1817
+
1818
+ # Clean previous
1819
+ @("$projectDir\core", "$projectDir\targets\claude-code", "$projectDir\.cursor\skills",
1820
+ "$projectDir\.github\skills", "$projectDir\.agent\skills\neocortex",
1821
+ "$projectDir\.agents\skills", "$projectDir\.claude\agents\neocortex",
1822
+ "$projectDir\.claude\skills\neocortex", "$projectDir\.kimi\skills\neocortex",
1823
+ "$projectDir\.openclaw\skills\neocortex") | ForEach-Object {
1824
+ if (Test-Path $_) { Remove-Item -Path $_ -Recurse -Force -ErrorAction SilentlyContinue }
1825
+ }
1826
+ @("$projectDir\.cursor\agents\neocortex.md", "$projectDir\.github\agents\neocortex.md",
1827
+ "$projectDir\.github\copilot-instructions.md", "$projectDir\AGENTS.md",
1828
+ "$projectDir\GEMINI.md", "$projectDir\.opencode\agents\neocortex.md") | ForEach-Object {
1829
+ if (Test-Path $_) { Remove-Item -Path $_ -Force -ErrorAction SilentlyContinue }
1830
+ }
1831
+
1832
+ # Create base dirs
1833
+ @("$projectDir\.neocortex\specs", "$projectDir\.neocortex\planning",
1834
+ "$projectDir\docs\stories", "$projectDir\docs\epics", "$projectDir\docs\proposals") | ForEach-Object {
1835
+ New-Item -ItemType Directory -Path $_ -Force | Out-Null
1836
+ }
1837
+
1838
+ # State template
1839
+ $stateTemplate = Join-Path $script:SourceDir "core\data\state-template.json"
1840
+ $stateTarget = "$projectDir\.neocortex\state.json"
1841
+ if (-not (Test-Path $stateTarget) -and (Test-Path $stateTemplate)) {
1842
+ Copy-Item -Path $stateTemplate -Destination $stateTarget -Force
1843
+ }
1844
+
1845
+ # Core (ONLY in local mode)
1846
+ $coreSource = Join-Path $script:SourceDir "core"
1847
+ if ($LOCAL_MODE -and (Test-Path $coreSource -PathType Container)) {
1848
+ New-Item -ItemType Directory -Path "$projectDir\core" -Force | Out-Null
1849
+ Copy-Item -Path "$coreSource\*" -Destination "$projectDir\core\" -Recurse -Force
1850
+ }
1851
+
1852
+ # Target-specific files
1853
+ $targetSummary = @()
1854
+ foreach ($target in $script:SelectedTargets) {
1855
+ switch ($target) {
1856
+ "claude-code" {
1857
+ $claudeTargetDir = Resolve-TargetDir -Target "claude-code"
1858
+ if ($claudeTargetDir -and (Test-Path $claudeTargetDir -PathType Container)) {
1859
+ $agentDest = "$projectDir\.claude\agents\neocortex"
1860
+ New-Item -ItemType Directory -Path $agentDest -Force | Out-Null
1861
+
1862
+ # SEMPRE copiar os 3 arquivos de interface
1863
+ # Tier 3: Copy only 2 stub files
1864
+ # Epic 64 (Story 64.2): Capture Copy-Silent return
1865
+ $r = Copy-Silent "$claudeTargetDir\neocortex.md" "$agentDest\"
1866
+ if (-not $r) { Write-Fail "Falha ao copiar neocortex.md (projeto)" }
1867
+ $r = Copy-Silent "$claudeTargetDir\neocortex.agent.yaml" "$agentDest\"
1868
+ if (-not $r) { Write-Fail "Falha ao copiar neocortex.agent.yaml (projeto)" }
1869
+ # Cleanup workflow.md from previous versions
1870
+ if (Test-Path "$agentDest\workflow.md") {
1871
+ Remove-Item "$agentDest\workflow.md" -Force -ErrorAction SilentlyContinue
1872
+ }
1873
+
1874
+ if ($LOCAL_MODE) {
1875
+ # Modo local: copiar IP completa (comportamento legado)
1876
+ $r = Copy-Silent "$($script:SourceDir)\package.json" "$agentDest\"
1877
+ if (-not $r) { Write-Fail "Falha ao copiar package.json (projeto)" }
1878
+ $r = Copy-Silent "$($script:SourceDir)\README.md" "$agentDest\"
1879
+ if (-not $r) { Write-Fail "Falha ao copiar README.md (projeto)" }
1880
+ New-Item -ItemType Directory -Path "$agentDest\core" -Force | Out-Null
1881
+ Copy-Item -Path "$coreSource\*" -Destination "$agentDest\core\" -Recurse -Force -ErrorAction SilentlyContinue
1882
+ foreach ($folder in @("steps-c", "steps-e", "steps-p", "steps-r", "steps-u")) {
1883
+ $stepSrc = "$($script:SourceDir)\core\steps\$folder"
1884
+ if (Test-Path $stepSrc -PathType Container) {
1885
+ New-Item -ItemType Directory -Path "$agentDest\$folder" -Force | Out-Null
1886
+ Copy-Item -Path "$stepSrc\*" -Destination "$agentDest\$folder\" -Recurse -Force -ErrorAction SilentlyContinue
1887
+ }
1888
+ }
1889
+ $skillsSource = Join-Path $script:SourceDir "core\skills"
1890
+ if (Test-Path $skillsSource -PathType Container) {
1891
+ New-Item -ItemType Directory -Path "$projectDir\.claude\skills\neocortex" -Force | Out-Null
1892
+ Copy-Item -Path "$skillsSource\*" -Destination "$projectDir\.claude\skills\neocortex\" -Recurse -Force -ErrorAction SilentlyContinue
1893
+ }
1894
+ } else {
1895
+ # Tier 3 Remote Mode: limpar IP de versoes anteriores no projeto
1896
+ Invoke-AutoCleanupLegacyProject -ProjectDir $projectDir
1897
+ }
1898
+ $targetSummary += "claude-code"
1899
+ }
1900
+ }
1901
+ "cursor" {
1902
+ $cursorTargetDir = Resolve-TargetDir -Target "cursor"
1903
+ if ($cursorTargetDir -and (Test-Path $cursorTargetDir -PathType Container)) {
1904
+ if (Test-Path "$cursorTargetDir\agent.md") {
1905
+ New-Item -ItemType Directory -Path "$projectDir\.cursor\agents" -Force | Out-Null
1906
+ Copy-Item -Path "$cursorTargetDir\agent.md" -Destination "$projectDir\.cursor\agents\neocortex.md" -Force
1907
+ }
1908
+ if (Test-Path "$cursorTargetDir\rules" -PathType Container) {
1909
+ New-Item -ItemType Directory -Path "$projectDir\.cursor\rules" -Force | Out-Null
1910
+ Copy-Item -Path "$cursorTargetDir\rules\*.mdc" -Destination "$projectDir\.cursor\rules\" -Force -ErrorAction SilentlyContinue
1911
+ }
1912
+ if (Test-Path "$cursorTargetDir\commands" -PathType Container) {
1913
+ New-Item -ItemType Directory -Path "$projectDir\.cursor\commands" -Force | Out-Null
1914
+ Copy-Item -Path "$cursorTargetDir\commands\*.md" -Destination "$projectDir\.cursor\commands\" -Force -ErrorAction SilentlyContinue
1915
+ }
1916
+ if (Test-Path "$cursorTargetDir\mcp.json") {
1917
+ New-Item -ItemType Directory -Path "$projectDir\.cursor" -Force | Out-Null
1918
+ Merge-JsonMcpConfig -StubFile "$cursorTargetDir\mcp.json" -DestFile "$projectDir\.cursor\mcp.json" -RootKey "mcpServers"
1919
+ }
1920
+ $skillsSource = Join-Path $script:SourceDir "core\skills"
1921
+ if (Test-Path $skillsSource -PathType Container) {
1922
+ New-Item -ItemType Directory -Path "$projectDir\.cursor\skills" -Force | Out-Null
1923
+ Copy-Item -Path "$skillsSource\*" -Destination "$projectDir\.cursor\skills\" -Recurse -Force -ErrorAction SilentlyContinue
1924
+ }
1925
+ $targetSummary += "cursor"
1926
+ }
1927
+ }
1928
+ "vscode" {
1929
+ $vscodeTargetDir = Resolve-TargetDir -Target "vscode"
1930
+ if ($vscodeTargetDir -and (Test-Path $vscodeTargetDir -PathType Container)) {
1931
+ # P124.02: source filename is neocortex.agent.md (canonical Custom Agents extension).
1932
+ if (Test-Path "$vscodeTargetDir\neocortex.agent.md") {
1933
+ New-Item -ItemType Directory -Path "$projectDir\.github\agents" -Force | Out-Null
1934
+ Copy-Item -Path "$vscodeTargetDir\neocortex.agent.md" -Destination "$projectDir\.github\agents\neocortex.agent.md" -Force
1935
+ Copy-Item -Path "$vscodeTargetDir\neocortex.agent.md" -Destination "$projectDir\.github\agents\neocortex.md" -Force
1936
+ }
1937
+ if (Test-Path "$vscodeTargetDir\instructions" -PathType Container) {
1938
+ New-Item -ItemType Directory -Path "$projectDir\.github\instructions" -Force | Out-Null
1939
+ Copy-Item -Path "$vscodeTargetDir\instructions\*.instructions.md" -Destination "$projectDir\.github\instructions\" -Force -ErrorAction SilentlyContinue
1940
+ }
1941
+ if (Test-Path "$vscodeTargetDir\prompts" -PathType Container) {
1942
+ New-Item -ItemType Directory -Path "$projectDir\.github\prompts" -Force | Out-Null
1943
+ Copy-Item -Path "$vscodeTargetDir\prompts\*.prompt.md" -Destination "$projectDir\.github\prompts\" -Force -ErrorAction SilentlyContinue
1944
+ }
1945
+ if (Test-Path "$vscodeTargetDir\mcp.json") {
1946
+ New-Item -ItemType Directory -Path "$projectDir\.vscode" -Force | Out-Null
1947
+ Merge-JsonMcpConfig -StubFile "$vscodeTargetDir\mcp.json" -DestFile "$projectDir\.vscode\mcp.json" -RootKey "servers"
1948
+ }
1949
+ if (Test-Path "$vscodeTargetDir\copilot-instructions.md") {
1950
+ Copy-Item -Path "$vscodeTargetDir\copilot-instructions.md" -Destination "$projectDir\.github\copilot-instructions.md" -Force
1951
+ }
1952
+ $skillsSource = Join-Path $script:SourceDir "core\skills"
1953
+ if (Test-Path $skillsSource -PathType Container) {
1954
+ New-Item -ItemType Directory -Path "$projectDir\.github\skills" -Force | Out-Null
1955
+ Copy-Item -Path "$skillsSource\*" -Destination "$projectDir\.github\skills\" -Recurse -Force -ErrorAction SilentlyContinue
1956
+ }
1957
+ $targetSummary += "vscode"
1958
+ }
1959
+ }
1960
+ { $_ -in @("gemini", "gemini-cli") } {
1961
+ $geminiTargetDir = Resolve-TargetDir -Target "gemini-cli"
1962
+ if ($geminiTargetDir -and (Test-Path $geminiTargetDir -PathType Container)) {
1963
+ if (Test-Path "$geminiTargetDir\gemini.md") { Copy-Item -Path "$geminiTargetDir\gemini.md" -Destination "$projectDir\GEMINI.md" -Force }
1964
+ if (Test-Path "$geminiTargetDir\commands" -PathType Container) {
1965
+ New-Item -ItemType Directory -Path "$projectDir\.gemini\commands" -Force | Out-Null
1966
+ Copy-Item -Path "$geminiTargetDir\commands\*.toml" -Destination "$projectDir\.gemini\commands\" -Force -ErrorAction SilentlyContinue
1967
+ }
1968
+ if (Test-Path "$geminiTargetDir\agents\neocortex.md") {
1969
+ New-Item -ItemType Directory -Path "$projectDir\.gemini\agents" -Force | Out-Null
1970
+ Copy-Item -Path "$geminiTargetDir\agents\neocortex.md" -Destination "$projectDir\.gemini\agents\neocortex.md" -Force
1971
+ } elseif (Test-Path "$geminiTargetDir\agent.md") {
1972
+ New-Item -ItemType Directory -Path "$projectDir\.gemini\agents" -Force | Out-Null
1973
+ Copy-Item -Path "$geminiTargetDir\agent.md" -Destination "$projectDir\.gemini\agents\neocortex.md" -Force
1974
+ }
1975
+ if (Test-Path "$geminiTargetDir\settings-mcp.json") {
1976
+ Copy-Item -Path "$geminiTargetDir\settings-mcp.json" -Destination "$projectDir\.gemini\settings-mcp.json" -Force
1977
+ }
1978
+ $targetSummary += "gemini"
1979
+ }
1980
+ }
1981
+ "codex" {
1982
+ $codexTargetDir = Resolve-TargetDir -Target "codex"
1983
+ if ($codexTargetDir -and (Test-Path $codexTargetDir -PathType Container)) {
1984
+ if (Test-Path "$codexTargetDir\AGENTS.md") { Copy-Item -Path "$codexTargetDir\AGENTS.md" -Destination "$projectDir\AGENTS.md" -Force }
1985
+ if (Test-Path "$codexTargetDir\neocortex.toml") {
1986
+ New-Item -ItemType Directory -Path "$projectDir\.codex\agents" -Force | Out-Null
1987
+ Copy-Item -Path "$codexTargetDir\neocortex.toml" -Destination "$projectDir\.codex\agents\neocortex.toml" -Force
1988
+ }
1989
+ $skillsSource = Join-Path $script:SourceDir "core\skills"
1990
+ if (Test-Path $skillsSource -PathType Container) {
1991
+ New-Item -ItemType Directory -Path "$projectDir\.agents\skills" -Force | Out-Null
1992
+ Copy-Item -Path "$skillsSource\*" -Destination "$projectDir\.agents\skills\" -Recurse -Force -ErrorAction SilentlyContinue
1993
+ }
1994
+ $targetSummary += "codex"
1995
+ }
1996
+ }
1997
+ "antigravity" {
1998
+ $antiTargetDir = Resolve-TargetDir -Target "antigravity"
1999
+ if ($antiTargetDir -and (Test-Path $antiTargetDir -PathType Container)) {
2000
+ if (Test-Path "$antiTargetDir\skill\SKILL.md") {
2001
+ New-Item -ItemType Directory -Path "$projectDir\.agent\skills\neocortex" -Force | Out-Null
2002
+ Copy-Item -Path "$antiTargetDir\skill\SKILL.md" -Destination "$projectDir\.agent\skills\neocortex\SKILL.md" -Force
2003
+ }
2004
+ if (Test-Path "$antiTargetDir\rules" -PathType Container) {
2005
+ New-Item -ItemType Directory -Path "$projectDir\.agent\rules" -Force | Out-Null
2006
+ Copy-Item -Path "$antiTargetDir\rules\*.md" -Destination "$projectDir\.agent\rules\" -Force -ErrorAction SilentlyContinue
2007
+ }
2008
+ if (Test-Path "$antiTargetDir\workflows" -PathType Container) {
2009
+ New-Item -ItemType Directory -Path "$projectDir\.agent\workflows" -Force | Out-Null
2010
+ Copy-Item -Path "$antiTargetDir\workflows\*.md" -Destination "$projectDir\.agent\workflows\" -Force -ErrorAction SilentlyContinue
2011
+ }
2012
+ if (Test-Path "$antiTargetDir\gemini.md") { Copy-Item -Path "$antiTargetDir\gemini.md" -Destination "$projectDir\GEMINI.md" -Force }
2013
+ $skillsSource = Join-Path $script:SourceDir "core\skills"
2014
+ if (Test-Path $skillsSource -PathType Container) {
2015
+ $skillsDest = "$projectDir\.agent\skills\neocortex\skills"
2016
+ New-Item -ItemType Directory -Path $skillsDest -Force | Out-Null
2017
+ Copy-Item -Path "$skillsSource\*" -Destination "$skillsDest\" -Recurse -Force -ErrorAction SilentlyContinue
2018
+ }
2019
+ $targetSummary += "antigravity"
2020
+ }
2021
+ }
2022
+ "opencode" {
2023
+ $opencodeTargetDir = Resolve-TargetDir -Target "opencode"
2024
+ if ($opencodeTargetDir -and (Test-Path "$opencodeTargetDir\neocortex.md") -and (Test-Path "$opencodeTargetDir\neocortex-root.md")) {
2025
+ New-Item -ItemType Directory -Path "$projectDir\.opencode\agents" -Force | Out-Null
2026
+ Copy-Item -Path "$opencodeTargetDir\neocortex.md" -Destination "$projectDir\.opencode\agents\neocortex.md" -Force
2027
+ Copy-Item -Path "$opencodeTargetDir\neocortex-root.md" -Destination "$projectDir\.opencode\agents\neocortex-root.md" -Force
2028
+ $targetSummary += "opencode"
2029
+ }
2030
+ }
2031
+ "kimi" {
2032
+ $kimiTargetDir = Resolve-TargetDir -Target "kimi"
2033
+ if ($kimiTargetDir -and (Test-Path "$kimiTargetDir\neocortex.md")) {
2034
+ New-Item -ItemType Directory -Path "$projectDir\.kimi\skills\neocortex" -Force | Out-Null
2035
+ Copy-Item -Path "$kimiTargetDir\neocortex.md" -Destination "$projectDir\.kimi\skills\neocortex\SKILL.md" -Force
2036
+ $targetSummary += "kimi"
2037
+ }
2038
+ }
2039
+ "openclaw" {
2040
+ $openclawTargetDir = Resolve-TargetDir -Target "openclaw"
2041
+ $skillSource = if ($openclawTargetDir) { Join-Path $openclawTargetDir "SKILL.md" } else { $null }
2042
+ if ($skillSource -and (Test-Path $skillSource -PathType Leaf)) {
2043
+ $skillDestDir = "$projectDir\.openclaw\skills\neocortex"
2044
+ New-Item -ItemType Directory -Path $skillDestDir -Force | Out-Null
2045
+ Copy-Item -Path $skillSource -Destination "$skillDestDir\SKILL.md" -Force
2046
+ $targetSummary += "openclaw"
2047
+ }
2048
+ }
2049
+ }
2050
+ }
2051
+
2052
+ Write-Host ""
2053
+ Write-Ok "Estrutura do projeto instalada ($($targetSummary -join ', '))"
2054
+ Write-Host ""
2055
+ Write-Info "Proximo passo: @neocortex *init @docs/epics.md"
2056
+ }
2057
+
2058
+ # =============================================================================
2059
+ # RESULTADO
2060
+ # =============================================================================
2061
+
2062
+ function Show-Result {
2063
+ param([bool]$Success)
2064
+ if ($QUIET_MODE) { return $Success }
2065
+
2066
+ Write-Host ""
2067
+ Write-Host " ----------------------------------------" -ForegroundColor DarkGray
2068
+
2069
+ if ($Success) {
2070
+ Write-Host ""
2071
+
2072
+ # Success logo (brain/cortex shape, text centered vertically)
2073
+ Write-Host " #######" -ForegroundColor Cyan
2074
+ Write-Host " ### ########" -ForegroundColor Cyan
2075
+ Write-Host " ######### #####" -ForegroundColor Cyan
2076
+ Write-Host -NoNewline " ## ############## " -ForegroundColor Cyan
2077
+ Write-Host "N E O C O R T E X" -ForegroundColor White
2078
+ Write-Host -NoNewline " ## ### ###### ## " -ForegroundColor Cyan
2079
+ Write-Host "v$VERSION" -ForegroundColor White
2080
+ Write-Host " ## ### ### ##" -ForegroundColor Cyan
2081
+ Write-Host -NoNewline " ## ###### ### ## " -ForegroundColor Cyan
2082
+ Write-Host "Installation complete!" -ForegroundColor Green
2083
+ Write-Host -NoNewline " ############### ## " -ForegroundColor Cyan
2084
+ Write-Host "OrNexus Team" -ForegroundColor DarkGray
2085
+ Write-Host " ##### ########" -ForegroundColor Cyan
2086
+ Write-Host " ######## ##" -ForegroundColor Cyan
2087
+ Write-Host " #######" -ForegroundColor Cyan
2088
+ Write-Host ""
2089
+
2090
+ # Story 66.4 AC6: Target count
2091
+ $okTargets = ($script:TargetResults | Where-Object { $_ -match ":OK$" }).Count
2092
+ if ($okTargets -gt 0) {
2093
+ Write-Host " $okTargets plataforma(s) instalada(s)" -ForegroundColor DarkGray
2094
+ }
2095
+
2096
+ if ($LOCAL_MODE) {
2097
+ Write-Host " Modo: Local (IP completa copiada)" -ForegroundColor DarkGray
2098
+ Write-Host " Locais:" -ForegroundColor DarkGray
2099
+ Write-Host " Agente: ~\.claude\agents\neocortex\" -ForegroundColor DarkGray
2100
+ Write-Host " Skills: ~\.claude\skills\neocortex\" -ForegroundColor DarkGray
2101
+ } else {
2102
+ Write-Host " Mode: Remote (thin client)" -ForegroundColor DarkGray
2103
+ Write-Host " Status: Ready to activate" -ForegroundColor DarkGray
2104
+ Write-Host ""
2105
+ Write-Host " Activate your license:" -ForegroundColor DarkGray
2106
+ Write-Host " neocortex activate YOUR-LICENSE-KEY" -ForegroundColor Cyan
2107
+ Write-Host ""
2108
+ Write-Host " Get your key at: https://neocortex.sh/portal/login" -ForegroundColor DarkGray
2109
+ Write-Host ""
2110
+ Write-Host " After activation:" -ForegroundColor DarkGray
2111
+ Write-Host " @neocortex *menu" -ForegroundColor Cyan
2112
+ }
2113
+ Write-Host ""
2114
+ Write-Host " Proximo passo:" -ForegroundColor DarkGray
2115
+ Write-Host " @neocortex *init @docs/epics.md" -ForegroundColor Cyan
2116
+ Write-Host ""
2117
+ return $true
2118
+ } else {
2119
+ Write-Host ""
2120
+ Write-Host " Instalacao com problemas" -ForegroundColor Red
2121
+ Write-Host ""
2122
+ # Story 66.4 AC5: Windows troubleshooting
2123
+ Write-Host " Execute novamente com " -NoNewline
2124
+ Write-Host "-Debug" -ForegroundColor White -NoNewline
2125
+ Write-Host " para detalhes:"
2126
+ Write-Host " npx @ornexus/neocortex -Debug" -ForegroundColor Yellow
2127
+ Write-Host ""
2128
+ return $false
2129
+ }
2130
+ }
2131
+
2132
+ # =============================================================================
2133
+ # MAIN
2134
+ # =============================================================================
2135
+
2136
+ Show-Banner
2137
+ Get-SourceDirectory
2138
+ Test-OldInstallation
2139
+
2140
+ # Determine targets
2141
+ if ($Targets) {
2142
+ $script:SelectedTargets = $Targets -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ }
2143
+ $invalid = $script:SelectedTargets | Where-Object { $_ -notin $script:ValidTargets }
2144
+ if ($invalid) {
2145
+ Write-Fail "Plataformas invalidas: $($invalid -join ', ')"
2146
+ Write-Host " Validas: $($script:ValidTargets -join ', ')"
2147
+ exit 1
2148
+ }
2149
+ } elseif ($Yes) {
2150
+ $script:SelectedTargets = @("claude-code")
2151
+ } else {
2152
+ $script:SelectedTargets = @("claude-code")
2153
+ }
2154
+
2155
+ Write-Dbg "Targets: $($script:SelectedTargets -join ', ')"
2156
+
2157
+ if ($DryRun) {
2158
+ Show-TargetResolutionDryRun -TargetList $script:SelectedTargets
2159
+ exit 0
2160
+ }
2161
+
2162
+ $totalSteps = 4
2163
+ if ($script:SelectedTargets -contains "claude-code") { $totalSteps = 6 }
2164
+
2165
+ # Step 1: Limpeza automatica de versoes anteriores
2166
+ Write-Step 1 $totalSteps "Limpeza de versoes anteriores"
2167
+ Invoke-AutoCleanupLegacy
2168
+
2169
+ # Step 2: Core
2170
+ Write-Step 2 $totalSteps "Instalando core"
2171
+ $coreSuccess = Install-Core
2172
+
2173
+ if (-not $coreSuccess) {
2174
+ Write-Fail "Falha na instalacao do core"
2175
+ exit 1
2176
+ }
2177
+
2178
+ # Step 3: Skills
2179
+ Write-Step 3 $totalSteps "Instalando skills"
2180
+ Install-Skills | Out-Null
2181
+
2182
+ # Step 4: Targets
2183
+ Write-Step 4 $totalSteps "Instalando $($script:SelectedTargets.Count) plataforma(s)"
2184
+ Install-Targets -TargetList $script:SelectedTargets
2185
+
2186
+ # Step 5-6: Claude Code extras
2187
+ if ($script:SelectedTargets -contains "claude-code") {
2188
+ Import-EnvFile | Out-Null
2189
+ Request-Tokens
2190
+
2191
+ Write-Step 5 $totalSteps "Configurando MCPs"
2192
+ Install-MCPs
2193
+
2194
+ Write-Step 6 $totalSteps "Verificando ferramentas"
2195
+ Report-CodeRabbitStatus
2196
+ }
2197
+
2198
+ $installSuccess = ($script:TargetResults | Where-Object { $_ -match ":FAIL$" }).Count -eq 0
2199
+ $verifySuccess = Verify-Installation
2200
+ $installSuccess = $installSuccess -and $verifySuccess
2201
+
2202
+ if (Show-Result $installSuccess) {
2203
+ $projectSources = Test-ProjectMigrationNeeds
2204
+ if ($projectSources) {
2205
+ Write-Warn "Migracao detectada"
2206
+ Write-Info "Execute: @neocortex *init @docs/epics.md para migrar"
2207
+ }
2208
+ New-ProjectDirectories
2209
+ Write-Host " Desenvolvido por OrNexus Team" -ForegroundColor DarkGray
2210
+ Write-Host ""
2211
+ exit 0
2212
+ } else {
2213
+ exit 1
2214
+ }