nex-app 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # nex-app
2
+
3
+ Crie projetos NEX com interface interativa (CLI + Web UI).
4
+
5
+ ## 🚀 Uso
6
+
7
+ ```bash
8
+ # Uso básico (detecção automática)
9
+ npx nex-app
10
+
11
+ # Forçar Web UI
12
+ npx nex-app --web
13
+
14
+ # Forçar CLI
15
+ npx nex-app --no-browser
16
+ ```
17
+
18
+ ## ✨ Características
19
+
20
+ - **Modo Híbrido**: CLI rápido ou Web UI visual
21
+ - **Detecção Automática**: Escolhe o melhor modo automaticamente
22
+ - **Templates**: Blank, Full Stack, API Only
23
+ - **Integração Cursor**: Configuração automática
24
+ - **Marketplace**: Seleção de agentes durante criação
25
+
26
+ ## 📋 Requisitos
27
+
28
+ - Node.js >= 18.0.0
29
+ - npm, pnpm ou yarn
30
+
31
+ ## 🏗️ Arquitetura
32
+
33
+ Este pacote implementa a **Opção 3 (Híbrida)**:
34
+ - CLI como padrão (rápido, confiável)
35
+ - Web installer como opção (`--web`)
36
+ - Detecção automática de ambiente
37
+
38
+ ## 📖 Documentação
39
+
40
+ - [Arquitetura Completa](../docs/ARQUITETURA_NPX_WEB_INSTALLER_NEX.md)
41
+ - [Implementação Detalhada](../docs/IMPLEMENTACAO_HIBRIDA_NPX.md)
42
+ - [Brainstorm](../docs/BRAINSTORM_NPX_WEB_INSTALLER.md)
43
+
44
+ ## 🔧 Desenvolvimento
45
+
46
+ ```bash
47
+ # Instalar dependências
48
+ npm install
49
+
50
+ # Testar localmente
51
+ node cli/create.js
52
+
53
+ # Testar modo web
54
+ node cli/create.js --web
55
+ ```
56
+
57
+ ## 📝 Status
58
+
59
+ 🟡 **Em Desenvolvimento** - MVP funcional
60
+
61
+ - [x] Estrutura base
62
+ - [x] CLI entrypoint
63
+ - [x] Servidor web
64
+ - [x] UI básica
65
+ - [x] Generator
66
+ - [x] Templates básicos
67
+ - [ ] Integração completa com Marketplace
68
+ - [ ] Integração completa com Cursor
69
+ - [ ] Testes
70
+ - [ ] Publicação no npm
71
+
72
+ ## 📄 Licença
73
+
74
+ PROPRIETARY - INOSX
package/cli/create.js ADDED
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * NEX App Creator - Entrypoint principal
5
+ * Suporta modo CLI e Web UI (híbrido)
6
+ */
7
+
8
+ import { startServer } from '../server/index.js'
9
+ import { openBrowser } from '../utils/browser.js'
10
+ import { detectEnvironment } from '../utils/environment.js'
11
+ import { runCLIFallback } from './fallback.js'
12
+ import chalk from 'chalk'
13
+
14
+ async function main() {
15
+ // Verificar flags especiais
16
+ const args = process.argv.slice(2)
17
+
18
+ // --version ou -v
19
+ if (args.includes('--version') || args.includes('-v')) {
20
+ const pkg = await import('../package.json', { assert: { type: 'json' } })
21
+ console.log(pkg.default.version)
22
+ process.exit(0)
23
+ }
24
+
25
+ // --help ou -h
26
+ if (args.includes('--help') || args.includes('-h')) {
27
+ console.log(chalk.blue('🚀 nex-app'))
28
+ console.log(chalk.gray('Create NEX projects with interactive installer\n'))
29
+ console.log('Usage:')
30
+ console.log(' npx nex-app # Auto-detect mode')
31
+ console.log(' npx nex-app --web # Force Web UI')
32
+ console.log(' npx nex-app --no-browser # Force CLI')
33
+ console.log(' npx nex-app --version # Show version')
34
+ process.exit(0)
35
+ }
36
+
37
+ // Validações
38
+ const nodeVersion = process.version
39
+ if (nodeVersion < 'v18.0.0') {
40
+ console.error(chalk.red('❌ Node.js >= 18.0.0 required'))
41
+ console.error(chalk.gray(` Current: ${nodeVersion}`))
42
+ process.exit(1)
43
+ }
44
+
45
+ // Detectar modo de operação
46
+ const useWeb = args.includes('--web')
47
+ const forceCLI = args.includes('--no-browser')
48
+
49
+ const env = detectEnvironment()
50
+
51
+ // Decisão: Web UI ou CLI?
52
+ let shouldUseWeb = false
53
+
54
+ if (forceCLI) {
55
+ shouldUseWeb = false
56
+ console.log(chalk.gray('ℹ️ Modo CLI forçado\n'))
57
+ } else if (useWeb) {
58
+ shouldUseWeb = true
59
+ console.log(chalk.blue('🌐 Modo Web UI forçado\n'))
60
+ } else {
61
+ // Detecção automática: usar web se ambiente suporta
62
+ shouldUseWeb = !env.isHeadless && !env.isCI && env.hasDisplay
63
+ if (shouldUseWeb) {
64
+ console.log(chalk.blue('🌐 Ambiente detectado: usando Web UI\n'))
65
+ } else {
66
+ console.log(chalk.gray('ℹ️ Ambiente headless/CI detectado: usando modo CLI\n'))
67
+ }
68
+ }
69
+
70
+ // Capturar diretório de trabalho inicial (onde o usuário executou o comando)
71
+ const initialCwd = process.cwd()
72
+
73
+ if (shouldUseWeb) {
74
+ // Modo Web UI
75
+ try {
76
+ const { port, server } = await startServer(initialCwd)
77
+ const url = `http://localhost:${port}`
78
+
79
+ await openBrowser(url)
80
+
81
+ console.log(chalk.green(`✅ Installer aberto em ${chalk.cyan(url)}`))
82
+ console.log(chalk.gray(` Diretório de trabalho: ${chalk.cyan(initialCwd)}`))
83
+ console.log(chalk.gray(' Pressione Ctrl+C para cancelar\n'))
84
+
85
+ // Aguardar finalização
86
+ process.on('SIGINT', () => {
87
+ console.log(chalk.yellow('\n\n⏹️ Cancelando...'))
88
+ server.close()
89
+ process.exit(0)
90
+ })
91
+
92
+ // Timeout de segurança (10 minutos)
93
+ setTimeout(() => {
94
+ console.log(chalk.yellow('\n⏱️ Timeout: fechando servidor...'))
95
+ server.close()
96
+ process.exit(0)
97
+ }, 10 * 60 * 1000)
98
+
99
+ } catch (error) {
100
+ console.error(chalk.red(`\n❌ Erro ao iniciar servidor web: ${error.message}`))
101
+ console.log(chalk.yellow('\n🔄 Fallback para modo CLI...\n'))
102
+ await runCLIFallback(initialCwd)
103
+ }
104
+ } else {
105
+ // Modo CLI Fallback
106
+ await runCLIFallback(initialCwd)
107
+ }
108
+ }
109
+
110
+ main().catch((error) => {
111
+ console.error(chalk.red(`\n❌ Erro fatal: ${error.message}`))
112
+ if (error.stack) {
113
+ console.error(chalk.gray(error.stack))
114
+ }
115
+ process.exit(1)
116
+ })
@@ -0,0 +1,96 @@
1
+ /**
2
+ * CLI Fallback - Modo interativo via terminal
3
+ */
4
+
5
+ import inquirer from 'inquirer'
6
+ import chalk from 'chalk'
7
+ import path from 'path'
8
+ import fs from 'fs-extra'
9
+ import { generateProject } from '../generator/index.js'
10
+ import { searchAgents } from '../lib/marketplace-client.js'
11
+
12
+ export async function runCLIFallback(initialCwd = process.cwd()) {
13
+ console.log(chalk.blue('🚀 Create NEX App - Modo CLI\n'))
14
+ console.log(chalk.gray(`Diretório de trabalho: ${chalk.cyan(initialCwd)}\n`))
15
+
16
+ // Buscar agentes disponíveis
17
+ const availableAgents = await searchAgents({})
18
+
19
+ // Detectar nome do projeto (da pasta ou package.json)
20
+ let currentProjectName = path.basename(initialCwd)
21
+ try {
22
+ const packageJsonPath = path.join(initialCwd, 'package.json')
23
+ if (await fs.pathExists(packageJsonPath)) {
24
+ const packageJson = await fs.readJSON(packageJsonPath)
25
+ if (packageJson.name) {
26
+ currentProjectName = packageJson.name
27
+ }
28
+ }
29
+ } catch (error) {
30
+ // Ignore
31
+ }
32
+
33
+ const answers = await inquirer.prompt([
34
+ {
35
+ type: 'list',
36
+ name: 'template',
37
+ message: 'Template:',
38
+ choices: [
39
+ { name: 'Blank - Projeto vazio', value: 'blank' },
40
+ { name: 'Full Stack - Frontend + Backend', value: 'full-stack' },
41
+ { name: 'API Only - Apenas backend', value: 'api-only' }
42
+ ],
43
+ default: 'blank'
44
+ },
45
+ {
46
+ type: 'checkbox',
47
+ name: 'agents',
48
+ message: 'Agentes iniciais (opcional):',
49
+ choices: availableAgents.map(agent => ({
50
+ name: `${agent.icon} ${agent.name} - ${agent.tagline}`,
51
+ value: agent.agent_id
52
+ })),
53
+ default: []
54
+ },
55
+ {
56
+ type: 'confirm',
57
+ name: 'cursorIntegration',
58
+ message: 'Configurar integração com Cursor IDE?',
59
+ default: true
60
+ },
61
+ {
62
+ type: 'list',
63
+ name: 'packageManager',
64
+ message: 'Package Manager:',
65
+ choices: ['npm', 'pnpm', 'yarn'],
66
+ default: 'npm'
67
+ }
68
+ ])
69
+
70
+ console.log(chalk.blue('\n📦 Gerando projeto...\n'))
71
+
72
+ try {
73
+ const projectPath = await generateProject({
74
+ template: answers.template,
75
+ agents: answers.agents || [],
76
+ packageManager: answers.packageManager,
77
+ cursorIntegration: answers.cursorIntegration,
78
+ options: {},
79
+ targetDir: initialCwd
80
+ })
81
+
82
+ console.log(chalk.green(`\n✅ NEX inicializado em: ${chalk.cyan(projectPath)}\n`))
83
+ console.log(chalk.gray('Próximos passos:'))
84
+ console.log(chalk.cyan(` ${answers.packageManager} install`))
85
+ console.log(chalk.cyan(` ${answers.packageManager} start\n`))
86
+ console.log(chalk.gray('Para instalar agentes:'))
87
+ console.log(chalk.cyan(` nex agent list --all`))
88
+ console.log(chalk.cyan(` nex agent install <agent-id>\n`))
89
+ } catch (error) {
90
+ console.error(chalk.red(`\n❌ Erro: ${error.message}\n`))
91
+ if (error.stack) {
92
+ console.error(chalk.gray(error.stack))
93
+ }
94
+ process.exit(1)
95
+ }
96
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Instalador de agentes
3
+ * TODO: Integrar com nex-installer service real
4
+ */
5
+
6
+ import fs from 'fs-extra'
7
+ import path from 'path'
8
+
9
+ export async function installAgents(projectPath, agentIds) {
10
+ // Por enquanto, apenas criar estrutura
11
+ // TODO: Integrar com AgentInstaller real do NEX
12
+
13
+ const agentsDir = path.join(projectPath, '.nex-core', 'agents')
14
+ await fs.ensureDir(agentsDir)
15
+
16
+ // Criar arquivo de manifest dos agentes instalados
17
+ const manifest = {
18
+ agents: agentIds.map(id => ({
19
+ agent_id: id,
20
+ installed_at: new Date().toISOString()
21
+ }))
22
+ }
23
+
24
+ await fs.writeJSON(
25
+ path.join(agentsDir, 'manifest.json'),
26
+ manifest,
27
+ { spaces: 2 }
28
+ )
29
+
30
+ // TODO: Instalar agentes reais do registry
31
+ console.log(`📦 Agentes selecionados: ${agentIds.join(', ')}`)
32
+ console.log(' (Instalação real será implementada na próxima fase)')
33
+ }
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Integração com Cursor IDE
3
+ * Segue o padrão BMAD: .cursor/rules/nex/{category}/agents/{agentId}.mdc
4
+ */
5
+
6
+ import fs from 'fs-extra'
7
+ import path from 'path'
8
+ import { fileURLToPath } from 'url'
9
+ import yaml from 'yaml'
10
+
11
+ const __filename = fileURLToPath(import.meta.url)
12
+ const __dirname = path.dirname(__filename)
13
+
14
+ /**
15
+ * Carregar configuração global do NEX CLI
16
+ */
17
+ async function getGlobalConfig() {
18
+ const os = await import('os')
19
+ const configPath = process.platform === 'win32'
20
+ ? path.join(os.default.homedir(), 'AppData', 'Roaming', 'nex', 'config.json')
21
+ : path.join(os.default.homedir(), '.nex', 'config.json')
22
+
23
+ try {
24
+ if (await fs.pathExists(configPath)) {
25
+ return await fs.readJSON(configPath)
26
+ }
27
+ } catch (error) {
28
+ // Ignore
29
+ }
30
+
31
+ return {
32
+ user: {
33
+ name: 'Developer',
34
+ conversationLanguage: 'pt-br',
35
+ documentLanguage: 'pt-br'
36
+ },
37
+ cursor: {
38
+ enabled: true
39
+ }
40
+ }
41
+ }
42
+
43
+ export async function integrateCursor(projectPath, agents) {
44
+ if (!agents || agents.length === 0) {
45
+ // Ainda assim criar estrutura básica
46
+ await createBasicCursorStructure(projectPath)
47
+ return
48
+ }
49
+
50
+ const globalConfig = await getGlobalConfig()
51
+ const userConfig = globalConfig.user || {}
52
+
53
+ // 1. Criar nex/core/config.yaml (similar ao BMAD bmad/core/config.yaml)
54
+ const nexCoreDir = path.join(projectPath, 'nex', 'core')
55
+ await fs.ensureDir(nexCoreDir)
56
+
57
+ const configYaml = {
58
+ user_name: userConfig.name || 'Developer',
59
+ communication_language: userConfig.conversationLanguage || 'pt-br',
60
+ document_output_language: userConfig.documentLanguage || 'pt-br',
61
+ output_folder: '{project-root}/docs'
62
+ }
63
+
64
+ const configPath = path.join(nexCoreDir, 'config.yaml')
65
+ if (!await fs.pathExists(configPath)) {
66
+ const configYamlContent = yaml.stringify(configYaml)
67
+ await fs.writeFile(configPath, configYamlContent, 'utf8')
68
+ }
69
+
70
+ // 2. Criar .cursor/rules/nex/ (seguindo padrão BMAD .cursor/rules/bmad/)
71
+ const cursorRulesDir = path.join(projectPath, '.cursor', 'rules', 'nex')
72
+ await fs.ensureDir(cursorRulesDir)
73
+
74
+ // 3. Criar index.mdc (similar ao BMAD index.mdc)
75
+ const indexContent = `---
76
+ description: NEX Framework - Master Index
77
+ globs:
78
+ alwaysApply: true
79
+ ---
80
+
81
+ # NEX Framework - Cursor Rules Index
82
+
83
+ This is the master index for all NEX agents installed in your project.
84
+
85
+ ## Installation Complete!
86
+
87
+ NEX rules have been installed to: \`.cursor/rules/nex/\`
88
+
89
+ **Note:** NEX does not modify your \`.cursorrules\` file. You manage that separately.
90
+
91
+ ## How to Use
92
+
93
+ - Reference specific agents: @nex/{category}/agents/{agent-name}
94
+ - Reference this index: @nex/index
95
+
96
+ ## Installed Agents
97
+
98
+ ${agents.length > 0
99
+ ? agents.map(agentId => `- @nex/${agentId} - ${agentId}`).join('\n')
100
+ : 'No agents installed yet. Use: nex agent install <agent-id>'
101
+ }
102
+
103
+ ## Quick Reference
104
+
105
+ - All NEX rules are Manual type - reference them explicitly when needed
106
+ - Agents provide persona-based assistance with specific expertise
107
+ - Each agent includes an activation block for proper initialization
108
+
109
+ ## Configuration
110
+
111
+ NEX rules are configured as Manual rules (alwaysApply: false) to give you control
112
+ over when they're included in your context. Reference them explicitly when you need
113
+ specific agent expertise.
114
+
115
+ ---
116
+
117
+ **Project:** ${path.basename(projectPath)}
118
+ **Created:** ${new Date().toISOString()}
119
+ `
120
+
121
+ await fs.writeFile(
122
+ path.join(cursorRulesDir, 'index.mdc'),
123
+ indexContent,
124
+ 'utf8'
125
+ )
126
+
127
+ console.log('🔗 Integração com Cursor configurada (padrão BMAD)')
128
+ console.log(` Config: nex/core/config.yaml`)
129
+ console.log(` Rules: .cursor/rules/nex/index.mdc`)
130
+ console.log(` Agentes selecionados: ${agents.length}`)
131
+ console.log(' (Para instalar agentes reais, execute: nex agent install <agent-id>)')
132
+ }
133
+
134
+ /**
135
+ * Criar estrutura básica do Cursor mesmo sem agentes
136
+ */
137
+ async function createBasicCursorStructure(projectPath) {
138
+ const globalConfig = await getGlobalConfig()
139
+ const userConfig = globalConfig.user || {}
140
+
141
+ // Criar nex/core/config.yaml
142
+ const nexCoreDir = path.join(projectPath, 'nex', 'core')
143
+ await fs.ensureDir(nexCoreDir)
144
+
145
+ const configYaml = {
146
+ user_name: userConfig.name || 'Developer',
147
+ communication_language: userConfig.conversationLanguage || 'pt-br',
148
+ document_output_language: userConfig.documentLanguage || 'pt-br',
149
+ output_folder: '{project-root}/docs'
150
+ }
151
+
152
+ const configPath = path.join(nexCoreDir, 'config.yaml')
153
+ const configYamlContent = yaml.stringify(configYaml)
154
+ await fs.writeFile(configPath, configYamlContent, 'utf8')
155
+
156
+ // Criar .cursor/rules/nex/index.mdc básico
157
+ const cursorRulesDir = path.join(projectPath, '.cursor', 'rules', 'nex')
158
+ await fs.ensureDir(cursorRulesDir)
159
+
160
+ const indexContent = `---
161
+ description: NEX Framework - Master Index
162
+ globs:
163
+ alwaysApply: true
164
+ ---
165
+
166
+ # NEX Framework - Cursor Rules Index
167
+
168
+ This is the master index for all NEX agents installed in your project.
169
+
170
+ ## Installation Complete!
171
+
172
+ NEX rules have been installed to: \`.cursor/rules/nex/\`
173
+
174
+ **Note:** NEX does not modify your \`.cursorrules\` file. You manage that separately.
175
+
176
+ ## How to Use
177
+
178
+ - Reference specific agents: @nex/{category}/agents/{agent-name}
179
+ - Reference this index: @nex/index
180
+
181
+ ## Installed Agents
182
+
183
+ No agents installed yet. Use: nex agent install <agent-id>
184
+
185
+ ## Quick Reference
186
+
187
+ - All NEX rules are Manual type - reference them explicitly when needed
188
+ - Agents provide persona-based assistance with specific expertise
189
+ - Each agent includes an activation block for proper initialization
190
+
191
+ ---
192
+
193
+ **Project:** ${path.basename(projectPath)}
194
+ **Created:** ${new Date().toISOString()}
195
+ `
196
+
197
+ await fs.writeFile(
198
+ path.join(cursorRulesDir, 'index.mdc'),
199
+ indexContent,
200
+ 'utf8'
201
+ )
202
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Generator de projetos NEX
3
+ */
4
+
5
+ import fs from 'fs-extra'
6
+ import path from 'path'
7
+ import { fileURLToPath } from 'url'
8
+ import { renderTemplate } from './template-engine.js'
9
+ import { installAgents } from './agent-installer.js'
10
+ import { integrateCursor } from './cursor-integration.js'
11
+ import { installDependencies } from './package-manager.js'
12
+
13
+ const __filename = fileURLToPath(import.meta.url)
14
+ const __dirname = path.dirname(__filename)
15
+
16
+ /**
17
+ * Obter todos os arquivos de um template recursivamente
18
+ */
19
+ async function getTemplateFiles(templatePath) {
20
+ const files = []
21
+
22
+ async function walkDir(dir) {
23
+ const entries = await fs.readdir(dir, { withFileTypes: true })
24
+
25
+ for (const entry of entries) {
26
+ const fullPath = path.join(dir, entry.name)
27
+
28
+ if (entry.isDirectory()) {
29
+ await walkDir(fullPath)
30
+ } else {
31
+ files.push(fullPath)
32
+ }
33
+ }
34
+ }
35
+
36
+ await walkDir(templatePath)
37
+ return files
38
+ }
39
+
40
+ export async function generateProject(config) {
41
+ const {
42
+ template,
43
+ agents = [],
44
+ packageManager = 'npm',
45
+ cursorIntegration = false,
46
+ options = {},
47
+ targetDir
48
+ } = config
49
+
50
+ // 1. Carregar template
51
+ const templatePath = path.join(__dirname, '../templates', template)
52
+ if (!await fs.pathExists(templatePath)) {
53
+ throw new Error(`Template "${template}" not found`)
54
+ }
55
+
56
+ // 2. Usar diretório atual (padrão BMAD/NEX - não cria nova pasta)
57
+ const projectPath = targetDir
58
+
59
+ // Detectar nome do projeto (da pasta ou package.json existente)
60
+ let detectedProjectName = path.basename(projectPath)
61
+ try {
62
+ const packageJsonPath = path.join(projectPath, 'package.json')
63
+ if (await fs.pathExists(packageJsonPath)) {
64
+ const packageJson = await fs.readJSON(packageJsonPath)
65
+ if (packageJson.name) {
66
+ detectedProjectName = packageJson.name
67
+ }
68
+ }
69
+ } catch (error) {
70
+ // Ignore
71
+ }
72
+
73
+ // Verificar se diretório está vazio ou pode ser inicializado
74
+ const files = await fs.readdir(projectPath).catch(() => [])
75
+ const importantFiles = files.filter(f =>
76
+ !f.startsWith('.') &&
77
+ f !== 'node_modules' &&
78
+ f !== 'package-lock.json' &&
79
+ f !== 'pnpm-lock.yaml' &&
80
+ f !== 'yarn.lock'
81
+ )
82
+
83
+ if (importantFiles.length > 0) {
84
+ // Diretório não está vazio, mas podemos inicializar NEX nele (como nex init)
85
+ console.log(`⚠️ Diretório não está vazio. Inicializando NEX no diretório atual...`)
86
+ }
87
+
88
+ // 3. Processar arquivos do template
89
+ const templateFiles = await getTemplateFiles(templatePath)
90
+
91
+ for (const file of templateFiles) {
92
+ const content = await fs.readFile(file, 'utf-8')
93
+ const relativePath = path.relative(templatePath, file)
94
+ const targetPath = path.join(projectPath, relativePath.replace('.tpl', ''))
95
+
96
+ // Não sobrescrever arquivos existentes (exceto se for .nex-core, .cursor ou nex/)
97
+ const isNexFile = relativePath.includes('.nex-core') ||
98
+ relativePath.includes('.cursor') ||
99
+ relativePath.includes('nex/')
100
+ if (!isNexFile && await fs.pathExists(targetPath)) {
101
+ console.log(`⏭️ Pulando ${relativePath} (já existe)`)
102
+ continue
103
+ }
104
+
105
+ const rendered = renderTemplate(content, {
106
+ projectName: detectedProjectName,
107
+ packageManager,
108
+ ...options
109
+ })
110
+
111
+ await fs.ensureDir(path.dirname(targetPath))
112
+ await fs.writeFile(targetPath, rendered)
113
+ }
114
+
115
+ // 4. Criar estrutura .nex-core (sempre, padrão NEX)
116
+ const nexCoreDir = path.join(projectPath, '.nex-core')
117
+ await fs.ensureDir(path.join(nexCoreDir, 'agents'))
118
+ await fs.ensureDir(path.join(nexCoreDir, 'data'))
119
+ await fs.ensureDir(path.join(nexCoreDir, 'documents'))
120
+
121
+ // Criar installed.json vazio (registry de agentes)
122
+ const installedJsonPath = path.join(nexCoreDir, 'agents', 'installed.json')
123
+ if (!await fs.pathExists(installedJsonPath)) {
124
+ await fs.writeJSON(installedJsonPath, {}, { spaces: 2 })
125
+ }
126
+
127
+ // 5. Instalar agentes (se selecionados)
128
+ if (agents.length > 0) {
129
+ await installAgents(projectPath, agents)
130
+ }
131
+
132
+ // 6. Integrar com Cursor (se habilitado)
133
+ if (cursorIntegration) {
134
+ await integrateCursor(projectPath, agents)
135
+ }
136
+
137
+ // 7. Instalar dependências
138
+ await installDependencies(projectPath, packageManager)
139
+
140
+ return projectPath
141
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Instalador de dependências (npm/pnpm/yarn)
3
+ */
4
+
5
+ import { exec } from 'child_process'
6
+ import { promisify } from 'util'
7
+
8
+ const execAsync = promisify(exec)
9
+
10
+ export async function installDependencies(projectPath, packageManager) {
11
+ const commands = {
12
+ npm: 'npm install',
13
+ pnpm: 'pnpm install',
14
+ yarn: 'yarn install'
15
+ }
16
+
17
+ const command = commands[packageManager]
18
+ if (!command) {
19
+ throw new Error(`Package manager "${packageManager}" não suportado`)
20
+ }
21
+
22
+ console.log(`📦 Instalando dependências com ${packageManager}...`)
23
+
24
+ try {
25
+ const { stdout, stderr } = await execAsync(command, {
26
+ cwd: projectPath,
27
+ stdio: 'inherit'
28
+ })
29
+
30
+ if (stderr && !stderr.includes('npm warn')) {
31
+ console.warn(stderr)
32
+ }
33
+
34
+ console.log('✅ Dependências instaladas')
35
+ } catch (error) {
36
+ console.warn(`⚠️ Erro ao instalar dependências: ${error.message}`)
37
+ console.warn(' Você pode instalar manualmente depois com:')
38
+ console.warn(` cd ${projectPath} && ${command}`)
39
+ }
40
+ }