nex-framework-cli 1.0.17 → 1.0.19

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/cli/nex-cli.js CHANGED
@@ -28,7 +28,7 @@ program.configureHelp({
28
28
  program
29
29
  .name('nex')
30
30
  .description('NEX Framework - Framework completo de agentes AI')
31
- .version('1.0.16', '-v, --version', 'Mostra a versão')
31
+ .version('1.0.18', '-v, --version', 'Mostra a versão')
32
32
  .addHelpText('before', chalk.bold.cyan(`
33
33
  ╔════════════════════════════════════════════════════════════════╗
34
34
  ║ NEX Framework - CLI v1.0.9 ║
@@ -62,6 +62,7 @@ Exemplos:
62
62
  `))
63
63
  .action(async (options) => {
64
64
  console.log(chalk.blue('🚀 Inicializando projeto NEX...'))
65
+ console.log()
65
66
 
66
67
  const answers = await inquirer.prompt([
67
68
  {
@@ -84,10 +85,11 @@ Exemplos:
84
85
  const projectName = options.name || answers.projectName
85
86
  const template = options.template || answers.template
86
87
 
87
- // Criar estrutura do projeto
88
+ // Criar estrutura do projeto (sem config, usa global)
88
89
  await createProjectStructure(projectName, template)
89
90
 
90
- console.log(chalk.green(`✅ Projeto ${projectName} criado com sucesso!`))
91
+ console.log(chalk.green(`\n✅ Projeto ${projectName} criado com sucesso!`))
92
+ console.log()
91
93
  })
92
94
 
93
95
  // Comando: plan
@@ -340,20 +342,37 @@ program
340
342
  })
341
343
 
342
344
  // Comando: config
343
- program
345
+ const configCmd = program
344
346
  .command('config')
345
- .description('Gerencia configurações')
346
- .option('get <key>', 'Obtém valor de configuração')
347
- .option('set <key> <value>', 'Define valor de configuração')
348
- .option('list', 'Lista todas as configurações')
349
- .action(async (options) => {
350
- if (options.get) {
351
- // Obter configuração
352
- } else if (options.set) {
353
- // Definir configuração
354
- } else if (options.list) {
355
- // Listar configurações
356
- }
347
+ .description('Gerencia configurações do NEX CLI')
348
+
349
+ configCmd
350
+ .command('reset')
351
+ .description('Reconfigura o NEX CLI (nome, idiomas, integração Cursor)')
352
+ .action(async () => {
353
+ const { runSetup } = await import('../scripts/postinstall.js')
354
+ await runSetup()
355
+ })
356
+
357
+ configCmd
358
+ .command('show')
359
+ .alias('list')
360
+ .description('Mostra a configuração atual')
361
+ .action(async () => {
362
+ const { default: NEXMarketplace } = await import('../src/services/nex-marketplace/NEXMarketplace.js')
363
+ const marketplace = new NEXMarketplace()
364
+ const config = marketplace.getGlobalConfig()
365
+
366
+ console.log(chalk.blue('\n⚙️ Configuração do NEX CLI\n'))
367
+ console.log(chalk.gray('Usuário:'))
368
+ console.log(chalk.white(` Nome: ${config.user.name}`))
369
+ console.log(chalk.white(` Idioma de conversação: ${config.user.conversationLanguage}`))
370
+ console.log(chalk.white(` Idioma de documentos: ${config.user.documentLanguage}`))
371
+ console.log(chalk.gray('\nCursor:'))
372
+ console.log(chalk.white(` Integração: ${config.cursor.enabled ? chalk.green('Habilitada') : chalk.red('Desabilitada')}`))
373
+
374
+ const configPath = marketplace.getGlobalConfigPath()
375
+ console.log(chalk.gray(`\nArquivo: ${configPath}\n`))
357
376
  })
358
377
 
359
378
  // Funções auxiliares
@@ -391,6 +410,11 @@ Projeto criado com NEX Framework.
391
410
  npm install
392
411
  npm start
393
412
  \`\`\`
413
+
414
+ ## NEX Configuration
415
+
416
+ As configurações do NEX (idioma, integração com Cursor) são gerenciadas globalmente.
417
+ Para ver ou alterar: \`nex config\`
394
418
  `
395
419
  await fs.writeFile(path.join(projectDir, 'README.md'), readme)
396
420
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nex-framework-cli",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "NEX CLI - Command-line interface for NEX Framework and Agent Marketplace",
5
5
  "type": "module",
6
6
  "main": "cli/nex-cli.js",
@@ -11,12 +11,14 @@
11
11
  "cli/",
12
12
  "src/services/nex-marketplace/",
13
13
  "src/services/nex-installer/",
14
+ "scripts/postinstall.js",
14
15
  "registry/",
15
16
  "README.md",
16
17
  "LICENSE"
17
18
  ],
18
19
  "scripts": {
19
20
  "prepublishOnly": "npm run test",
21
+ "postinstall": "node scripts/postinstall.js",
20
22
  "test": "node cli/nex-cli.js --version",
21
23
  "test:api": "node scripts/test-marketplace-api.js",
22
24
  "publish:cli": "node scripts/publish-cli.js",
@@ -0,0 +1,196 @@
1
+ # 📦 Scripts do NEX Framework
2
+
3
+ Scripts utilitários para gerenciar o projeto NEX.
4
+
5
+ ---
6
+
7
+ ## 🗄️ Scripts de Banco de Dados
8
+
9
+ ### `check-database.js`
10
+
11
+ **Descrição:** Verifica o estado atual do banco de dados Supabase
12
+
13
+ **Uso:**
14
+ ```bash
15
+ npm run db:check
16
+ ```
17
+
18
+ **O que faz:**
19
+ - ✅ Conecta ao Supabase
20
+ - ✅ Verifica se as 7 tabelas do NEX Marketplace existem
21
+ - ✅ Mostra status de cada tabela (✅ OK, ❌ Faltando, ⚠️ Erro)
22
+ - ✅ Conta quantos agentes estão cadastrados
23
+ - ✅ Lista os agentes existentes
24
+
25
+ **Pré-requisitos:**
26
+ ```powershell
27
+ $env:NEXT_PUBLIC_SUPABASE_URL="https://seu-projeto.supabase.co"
28
+ $env:NEXT_PUBLIC_SUPABASE_ANON_KEY="sua-anon-key"
29
+ ```
30
+
31
+ **Exemplo de saída:**
32
+ ```
33
+ 🔗 Connecting to Supabase...
34
+ 📍 URL: https://auqfubbpxjzuzzqfazdp.supabase.co
35
+
36
+ 🔍 Checking for NEX Marketplace tables...
37
+
38
+ 📊 Database Status:
39
+
40
+ ✅ nex_marketplace_agents OK
41
+ ✅ nex_marketplace_versions OK
42
+ ✅ nex_marketplace_installs OK
43
+ ✅ nex_marketplace_reviews OK
44
+ ✅ nex_marketplace_bundles OK
45
+ ✅ nex_marketplace_analytics OK
46
+ ✅ nex_marketplace_stars OK
47
+
48
+ 🎉 All tables exist! Database is ready.
49
+
50
+ 📊 Checking data...
51
+ ✅ Found 3 agents:
52
+ - anx: ANX - Analysis Expert (v1.0.0)
53
+ - pmx: PMX - Product Management Expert (v1.0.0)
54
+ - arx: ARX - Architecture Expert (v1.0.0)
55
+ ```
56
+
57
+ ---
58
+
59
+ ### `run-migration.js`
60
+
61
+ **Descrição:** Helper para executar migrations do Supabase
62
+
63
+ **Uso:**
64
+ ```bash
65
+ npm run db:migrate
66
+ ```
67
+
68
+ **O que faz:**
69
+ - ✅ Lê o arquivo `supabase/migrations/002_nex_agent_marketplace.sql`
70
+ - ✅ Tenta executar via API do Supabase
71
+ - ✅ Se falhar, fornece instruções para execução manual
72
+
73
+ **Pré-requisitos:**
74
+ ```powershell
75
+ $env:NEXT_PUBLIC_SUPABASE_URL="https://seu-projeto.supabase.co"
76
+ $env:SUPABASE_SERVICE_ROLE_KEY="sua-service-role-key"
77
+ ```
78
+
79
+ **Nota:** Na maioria dos casos, é melhor executar a migration **manualmente** via SQL Editor do Supabase. Este script é um helper que orienta o processo.
80
+
81
+ ---
82
+
83
+ ## ⚙️ Script de Setup do Supabase
84
+
85
+ ### `supabase-setup.ps1`
86
+
87
+ **Descrição:** Script PowerShell para configurar o Supabase localmente (Docker)
88
+
89
+ **Uso:**
90
+ ```powershell
91
+ .\scripts\supabase-setup.ps1
92
+ ```
93
+
94
+ **O que faz:**
95
+ - ✅ Verifica se Docker está instalado
96
+ - ✅ Inicializa projeto Supabase local
97
+ - ✅ Inicia containers Docker
98
+ - ✅ Aplica migrations
99
+ - ✅ Configura ambiente local
100
+
101
+ **Quando usar:** Para desenvolvimento local com Supabase rodando em Docker.
102
+
103
+ ---
104
+
105
+ ## 🔧 Variáveis de Ambiente
106
+
107
+ Todos os scripts de banco de dados precisam de:
108
+
109
+ ### Para Verificação (read-only):
110
+ ```powershell
111
+ $env:NEXT_PUBLIC_SUPABASE_URL="https://seu-projeto.supabase.co"
112
+ $env:NEXT_PUBLIC_SUPABASE_ANON_KEY="sua-anon-key"
113
+ ```
114
+
115
+ ### Para Migrations (write):
116
+ ```powershell
117
+ $env:NEXT_PUBLIC_SUPABASE_URL="https://seu-projeto.supabase.co"
118
+ $env:SUPABASE_SERVICE_ROLE_KEY="sua-service-role-key"
119
+ ```
120
+
121
+ **Onde encontrar:**
122
+ 👉 https://app.supabase.com/project/SEU_PROJECT_ID/settings/api
123
+
124
+ ---
125
+
126
+ ## 📝 Comandos NPM
127
+
128
+ Adicionados em `package.json`:
129
+
130
+ ```json
131
+ {
132
+ "scripts": {
133
+ "db:check": "node scripts/check-database.js",
134
+ "db:migrate": "node scripts/run-migration.js"
135
+ }
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ ## 🚀 Fluxo Recomendado
142
+
143
+ ### Primeira vez (setup inicial):
144
+
145
+ 1. **Configurar variáveis:**
146
+ ```powershell
147
+ $env:NEXT_PUBLIC_SUPABASE_URL="https://auqfubbpxjzuzzqfazdp.supabase.co"
148
+ $env:NEXT_PUBLIC_SUPABASE_ANON_KEY="sua-key"
149
+ ```
150
+
151
+ 2. **Verificar estado:**
152
+ ```bash
153
+ npm run db:check
154
+ ```
155
+
156
+ 3. **Executar migration (manualmente):**
157
+ - Abrir: https://app.supabase.com/project/auqfubbpxjzuzzqfazdp/sql
158
+ - Copiar: `supabase/migrations/002_nex_agent_marketplace.sql`
159
+ - Colar e executar (Ctrl+Enter)
160
+
161
+ 4. **Verificar novamente:**
162
+ ```bash
163
+ npm run db:check
164
+ ```
165
+
166
+ 5. **Popular com dados de teste:**
167
+ - Ver: `EXECUTAR_SUPABASE.md` (seção "Popular com dados")
168
+
169
+ ---
170
+
171
+ ## 📚 Documentação Relacionada
172
+
173
+ - **`EXECUTAR_SUPABASE.md`** - Guia completo de setup do banco
174
+ - **`RODAR_MIGRATION_SUPABASE.md`** - Guia rápido de migrations
175
+ - **`SUPABASE_DATABASE_SETUP_SUMMARY.md`** - Resumo executivo
176
+ - **`SUPABASE_SETUP_INSTRUCTIONS.md`** - Instruções gerais do Supabase
177
+
178
+ ---
179
+
180
+ ## ⚠️ Troubleshooting
181
+
182
+ ### Erro: "Missing environment variables"
183
+ **Solução:** Configure as variáveis de ambiente (ver acima)
184
+
185
+ ### Erro: "404 Not Found" na API
186
+ **Solução:** As tabelas não existem. Execute a migration primeiro.
187
+
188
+ ### Erro: "Unauthorized"
189
+ **Solução:** Use a `SUPABASE_SERVICE_ROLE_KEY` (não a anon key)
190
+
191
+ ### Erro: "function does not exist"
192
+ **Solução:** A função RPC não está disponível. Use execução manual via SQL Editor.
193
+
194
+ ---
195
+
196
+ **Precisa de ajuda?** Veja os guias completos nos arquivos `.md` na raiz do projeto! 🚀
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * NEX CLI Post-Install Script
4
+ * Runs after npm install to configure the CLI
5
+ */
6
+
7
+ import inquirer from 'inquirer'
8
+ import fs from 'fs-extra'
9
+ import path from 'path'
10
+ import os from 'os'
11
+ import chalk from 'chalk'
12
+ import { fileURLToPath } from 'url'
13
+
14
+ const __filename = fileURLToPath(import.meta.url)
15
+ const __dirname = path.dirname(__filename)
16
+
17
+ // Get global config path
18
+ export function getGlobalConfigPath() {
19
+ const homeDir = os.homedir()
20
+
21
+ if (process.platform === 'win32') {
22
+ // Windows: %APPDATA%\nex\config.json
23
+ return path.join(process.env.APPDATA || path.join(homeDir, 'AppData', 'Roaming'), 'nex', 'config.json')
24
+ } else {
25
+ // Unix-like: ~/.nex/config.json
26
+ return path.join(homeDir, '.nex', 'config.json')
27
+ }
28
+ }
29
+
30
+ export async function runSetup() {
31
+ // Check if config already exists
32
+ const configPath = getGlobalConfigPath()
33
+ const configDir = path.dirname(configPath)
34
+
35
+ if (await fs.pathExists(configPath)) {
36
+ try {
37
+ const existingConfig = await fs.readJSON(configPath)
38
+ // If config exists and is valid, skip setup
39
+ if (existingConfig.user && existingConfig.user.name) {
40
+ console.log(chalk.green('\n✅ NEX CLI já está configurado!'))
41
+ console.log(chalk.gray(` Config: ${configPath}`))
42
+ console.log(chalk.gray(` Para reconfigurar, delete o arquivo ou execute: nex config reset\n`))
43
+ return
44
+ }
45
+ } catch (error) {
46
+ // Config file exists but is invalid, continue with setup
47
+ }
48
+ }
49
+
50
+ console.log(chalk.blue('\n╔══════════════════════════════════════════════════════════╗'))
51
+ console.log(chalk.blue('║ 🚀 Configuração do NEX CLI ║'))
52
+ console.log(chalk.blue('╚══════════════════════════════════════════════════════════╝\n'))
53
+
54
+ const answers = await inquirer.prompt([
55
+ {
56
+ type: 'input',
57
+ name: 'userName',
58
+ message: 'Seu nome:',
59
+ default: process.env.USER || process.env.USERNAME || 'Developer',
60
+ validate: (input) => {
61
+ if (input.trim().length === 0) {
62
+ return 'Por favor, informe seu nome'
63
+ }
64
+ return true
65
+ }
66
+ },
67
+ {
68
+ type: 'list',
69
+ name: 'conversationLanguage',
70
+ message: 'Idioma de conversação:',
71
+ choices: [
72
+ { name: 'Português (PT-BR)', value: 'pt-BR' },
73
+ { name: 'English (EN-US)', value: 'en-US' },
74
+ { name: 'Español (ES)', value: 'es' },
75
+ { name: 'Français (FR)', value: 'fr' }
76
+ ],
77
+ default: 'pt-BR'
78
+ },
79
+ {
80
+ type: 'list',
81
+ name: 'documentLanguage',
82
+ message: 'Idioma para criação de documentos:',
83
+ choices: [
84
+ { name: 'Português (PT-BR)', value: 'pt-BR' },
85
+ { name: 'English (EN-US)', value: 'en-US' },
86
+ { name: 'Español (ES)', value: 'es' },
87
+ { name: 'Français (FR)', value: 'fr' }
88
+ ],
89
+ default: 'pt-BR'
90
+ },
91
+ {
92
+ type: 'confirm',
93
+ name: 'cursorIntegration',
94
+ message: 'Deseja habilitar integração com Cursor IDE?',
95
+ default: true
96
+ }
97
+ ])
98
+
99
+ const config = {
100
+ user: {
101
+ name: answers.userName.trim(),
102
+ conversationLanguage: answers.conversationLanguage,
103
+ documentLanguage: answers.documentLanguage
104
+ },
105
+ cursor: {
106
+ enabled: answers.cursorIntegration
107
+ },
108
+ installedAt: new Date().toISOString(),
109
+ version: '1.0.18'
110
+ }
111
+
112
+ // Save config
113
+ await fs.ensureDir(configDir)
114
+ await fs.writeJSON(configPath, config, { spaces: 2 })
115
+
116
+ console.log(chalk.green('\n✅ NEX CLI configurado com sucesso!'))
117
+ console.log(chalk.gray(` Config salvo em: ${configPath}`))
118
+ console.log(chalk.gray(` Nome: ${config.user.name}`))
119
+ console.log(chalk.gray(` Idioma de conversação: ${config.user.conversationLanguage}`))
120
+ console.log(chalk.gray(` Idioma de documentos: ${config.user.documentLanguage}`))
121
+ console.log(chalk.gray(` Integração Cursor: ${config.cursor.enabled ? 'Habilitada' : 'Desabilitada'}`))
122
+ console.log(chalk.cyan('\n💡 Dica: Para reconfigurar, execute: nex config reset\n'))
123
+ }
124
+
125
+ // Check if running in interactive mode (TTY available)
126
+ const isInteractive = process.stdin.isTTY && process.stdout.isTTY && process.stderr.isTTY
127
+ const isCI = process.env.CI === 'true'
128
+ const skipSetup = process.env.NEX_SKIP_SETUP === 'true'
129
+
130
+ // Only run interactive setup if:
131
+ // 1. Not in CI
132
+ // 2. Not explicitly skipped
133
+ // 3. Running in interactive mode (TTY)
134
+ if (!isCI && !skipSetup && isInteractive) {
135
+ runSetup().catch((error) => {
136
+ console.error(chalk.red('\n❌ Erro ao configurar NEX CLI:'))
137
+ console.error(chalk.red(error.message))
138
+ console.log(chalk.yellow('\n💡 Você pode configurar manualmente depois executando: nex config reset\n'))
139
+ process.exit(0) // Don't fail the install
140
+ })
141
+ } else {
142
+ // Non-interactive mode: create default config silently
143
+ const configPath = getGlobalConfigPath()
144
+ const configDir = path.dirname(configPath)
145
+ const defaultConfig = {
146
+ user: {
147
+ name: process.env.USER || process.env.USERNAME || 'Developer',
148
+ conversationLanguage: 'pt-BR',
149
+ documentLanguage: 'pt-BR'
150
+ },
151
+ cursor: {
152
+ enabled: false
153
+ },
154
+ installedAt: new Date().toISOString(),
155
+ version: '1.0.18'
156
+ }
157
+
158
+ fs.ensureDir(configDir)
159
+ .then(() => fs.writeJSON(configPath, defaultConfig, { spaces: 2 }))
160
+ .then(() => {
161
+ if (!isCI && isInteractive) {
162
+ console.log(chalk.green('\n✅ NEX CLI instalado com configuração padrão'))
163
+ console.log(chalk.gray(' Para configurar: nex config reset\n'))
164
+ }
165
+ })
166
+ .catch(() => {
167
+ // Silently fail in non-interactive mode
168
+ })
169
+ }
@@ -6,10 +6,12 @@
6
6
  import fs from 'fs-extra'
7
7
  import { existsSync } from 'fs'
8
8
  import path from 'path'
9
+ import os from 'os'
9
10
  import yaml from 'yaml'
10
11
  import chalk from 'chalk'
11
12
  import ora from 'ora'
12
13
  import semver from 'semver'
14
+ import inquirer from 'inquirer'
13
15
  import { createClient } from '@supabase/supabase-js'
14
16
  import { fileURLToPath } from 'url'
15
17
 
@@ -57,11 +59,16 @@ export default class NEXMarketplace {
57
59
 
58
60
  // Supabase client
59
61
  this.supabase = null
62
+ this.apiUrl = null
63
+ this.anonKey = null
60
64
  this.initializeSupabase()
61
65
 
62
66
  // Config
63
67
  this.config = null
68
+ this.globalConfig = null
69
+ this.projectConfig = null
64
70
  this.loadConfig()
71
+ this.loadGlobalConfig()
65
72
  }
66
73
 
67
74
  /**
@@ -128,6 +135,103 @@ export default class NEXMarketplace {
128
135
  return this.config
129
136
  }
130
137
 
138
+ /**
139
+ * Get global config path (user's home directory)
140
+ */
141
+ getGlobalConfigPath() {
142
+ const homeDir = os.homedir()
143
+
144
+ if (process.platform === 'win32') {
145
+ // Windows: %APPDATA%\nex\config.json
146
+ return path.join(process.env.APPDATA || path.join(homeDir, 'AppData', 'Roaming'), 'nex', 'config.json')
147
+ } else {
148
+ // Unix-like: ~/.nex/config.json
149
+ return path.join(homeDir, '.nex', 'config.json')
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Load global configuration from user's home directory
155
+ */
156
+ async loadGlobalConfig() {
157
+ try {
158
+ const configPath = this.getGlobalConfigPath()
159
+
160
+ if (await fs.pathExists(configPath)) {
161
+ this.globalConfig = await fs.readJSON(configPath)
162
+ return this.globalConfig
163
+ }
164
+ } catch (error) {
165
+ // Config file doesn't exist or is invalid
166
+ }
167
+
168
+ this.globalConfig = null
169
+ return null
170
+ }
171
+
172
+ /**
173
+ * Get global configuration with defaults
174
+ */
175
+ getGlobalConfig() {
176
+ if (!this.globalConfig) {
177
+ return {
178
+ user: {
179
+ name: process.env.USER || process.env.USERNAME || 'Developer',
180
+ conversationLanguage: 'pt-BR',
181
+ documentLanguage: 'pt-BR'
182
+ },
183
+ cursor: {
184
+ enabled: false
185
+ }
186
+ }
187
+ }
188
+ return this.globalConfig
189
+ }
190
+
191
+ /**
192
+ * Load project configuration (.nex-core/config.json)
193
+ * @deprecated Use getGlobalConfig() instead - project config is now optional
194
+ */
195
+ async loadProjectConfig() {
196
+ const configPath = path.join(this.projectRoot, '.nex-core', 'config.json')
197
+
198
+ if (await fs.pathExists(configPath)) {
199
+ try {
200
+ this.projectConfig = await fs.readJSON(configPath)
201
+ } catch (error) {
202
+ this.projectConfig = null
203
+ }
204
+ } else {
205
+ this.projectConfig = null
206
+ }
207
+
208
+ return this.projectConfig
209
+ }
210
+
211
+ /**
212
+ * Get project configuration with defaults
213
+ * @deprecated Use getGlobalConfig() instead
214
+ */
215
+ getProjectConfig() {
216
+ // Prefer global config, fallback to project config
217
+ const global = this.getGlobalConfig()
218
+ if (this.projectConfig) {
219
+ return {
220
+ ...global,
221
+ ...this.projectConfig,
222
+ user: {
223
+ ...global.user,
224
+ ...(this.projectConfig.user || {})
225
+ },
226
+ cursor: {
227
+ ...global.cursor,
228
+ ...(this.projectConfig.cursor || {})
229
+ }
230
+ }
231
+ }
232
+ return global
233
+ }
234
+
131
235
  /**
132
236
  * Default configuration
133
237
  */
@@ -844,12 +948,32 @@ export default class NEXMarketplace {
844
948
  // 7. Track installation
845
949
  await this.trackInstallation(agentId, version, method)
846
950
 
951
+ // 8. Integrate with Cursor if BMAD agent
952
+ if (manifest.bmad?.cursor_integration === true) {
953
+ // Use global config (set during CLI installation)
954
+ const globalConfig = this.getGlobalConfig()
955
+ const shouldIntegrate = globalConfig.cursor?.enabled === true
956
+
957
+ if (shouldIntegrate) {
958
+ await this.integrateWithCursor(agentId, manifest, sourcePath)
959
+ } else {
960
+ console.log(chalk.gray(`\n💡 Integração com Cursor desabilitada. Para habilitar, execute: nex config reset\n`))
961
+ }
962
+ }
963
+
847
964
  spinner.succeed(chalk.green(`✅ ${manifest.icon} ${manifest.name} v${version} installed!`))
848
965
 
849
- // 8. Show quick start
966
+ // 9. Show quick start
850
967
  console.log(chalk.cyan('\n📖 Quick Start:'))
851
- console.log(chalk.gray(` @${agentId}`))
852
- console.log(chalk.gray(` nex agent run ${agentId} <command>\n`))
968
+ if (manifest.bmad?.cursor_integration === true) {
969
+ const trigger = manifest.bmad?.activation_trigger || `@${agentId}`
970
+ console.log(chalk.gray(` ${trigger} <command>`))
971
+ console.log(chalk.gray(` (No Cursor, use: nex agent run ${agentId} <command>)`))
972
+ } else {
973
+ console.log(chalk.gray(` @${agentId}`))
974
+ console.log(chalk.gray(` nex agent run ${agentId} <command>`))
975
+ }
976
+ console.log()
853
977
 
854
978
  return true
855
979
 
@@ -896,6 +1020,107 @@ export default class NEXMarketplace {
896
1020
  throw new Error(errorMsg)
897
1021
  }
898
1022
 
1023
+ /**
1024
+ * Integrate BMAD agent with Cursor
1025
+ */
1026
+ async integrateWithCursor(agentId, manifest, sourcePath) {
1027
+ try {
1028
+ const rulesFile = manifest.bmad?.rules_file
1029
+ if (!rulesFile) {
1030
+ return // No rules file specified
1031
+ }
1032
+
1033
+ // Path to .cursorrules file in agent directory
1034
+ let agentRulesPath = path.join(sourcePath, rulesFile)
1035
+
1036
+ // Check if rules file exists
1037
+ if (!await fs.pathExists(agentRulesPath)) {
1038
+ // Try alternative locations
1039
+ const alternativePaths = [
1040
+ path.join(sourcePath, '..', rulesFile), // Parent directory
1041
+ path.join(this.registryPath, manifest.category || 'bmad', agentId, rulesFile), // Registry root
1042
+ path.join(this.registryPath, 'bmad', agentId, rulesFile) // BMAD category
1043
+ ]
1044
+
1045
+ let foundPath = null
1046
+ for (const altPath of alternativePaths) {
1047
+ if (await fs.pathExists(altPath)) {
1048
+ foundPath = altPath
1049
+ break
1050
+ }
1051
+ }
1052
+
1053
+ if (!foundPath) {
1054
+ // Create a basic .cursorrules file from manifest
1055
+ const trigger = manifest.bmad?.activation_trigger || `@${agentId}`
1056
+ const basicRules = `# ${manifest.name}
1057
+ # ${manifest.tagline || manifest.description?.substring(0, 100)}
1058
+
1059
+ # Activation: ${trigger}
1060
+
1061
+ # ${manifest.name} - ${manifest.description || 'BMAD Expert Agent'}
1062
+
1063
+ # Usage in Cursor:
1064
+ # ${trigger} <command>
1065
+
1066
+ # Example:
1067
+ # ${trigger} analyze-page https://example.com
1068
+ `
1069
+
1070
+ // Write basic rules file to agent directory
1071
+ await fs.writeFile(agentRulesPath, basicRules, 'utf8')
1072
+ console.log(chalk.cyan(`\n📝 Created basic Cursor rules file: ${rulesFile}`))
1073
+ } else {
1074
+ agentRulesPath = foundPath
1075
+ }
1076
+ }
1077
+
1078
+ // Create .cursorrules directory in project (for storing individual agent rules)
1079
+ const cursorRulesDir = path.join(this.projectRoot, '.cursorrules')
1080
+ await fs.ensureDir(cursorRulesDir)
1081
+
1082
+ // Copy rules file to .cursorrules directory
1083
+ const targetRulesPath = path.join(cursorRulesDir, rulesFile)
1084
+ await fs.copy(agentRulesPath, targetRulesPath)
1085
+
1086
+ // Update main .cursorrules file (in project root)
1087
+ const mainCursorRulesPath = path.join(this.projectRoot, '.cursorrules')
1088
+ const importLine = `import .cursorrules/${rulesFile}`
1089
+ const commentLine = `# ${manifest.name} - ${manifest.tagline || manifest.description?.substring(0, 50)}`
1090
+ const trigger = manifest.bmad?.activation_trigger || `@${agentId}`
1091
+ const agentComment = `# Use: ${trigger} <command>`
1092
+
1093
+ let cursorRulesContent = ''
1094
+ if (await fs.pathExists(mainCursorRulesPath)) {
1095
+ cursorRulesContent = await fs.readFile(mainCursorRulesPath, 'utf8')
1096
+ }
1097
+
1098
+ // Check if already imported
1099
+ if (cursorRulesContent.includes(importLine) || cursorRulesContent.includes(rulesFile)) {
1100
+ // Already integrated, skip
1101
+ return
1102
+ }
1103
+
1104
+ // Add import to .cursorrules
1105
+ const newContent = cursorRulesContent +
1106
+ (cursorRulesContent ? '\n\n' : '') +
1107
+ commentLine + '\n' +
1108
+ agentComment + '\n' +
1109
+ importLine + '\n'
1110
+
1111
+ await fs.writeFile(mainCursorRulesPath, newContent, 'utf8')
1112
+
1113
+ console.log(chalk.green(`\n✅ Cursor integration complete!`))
1114
+ console.log(chalk.gray(` Added ${rulesFile} to .cursorrules`))
1115
+ const trigger = manifest.bmad?.activation_trigger || `@${agentId}`
1116
+ console.log(chalk.gray(` Use: ${trigger} <command> in Cursor\n`))
1117
+
1118
+ } catch (error) {
1119
+ console.log(chalk.yellow(`\n⚠️ Cursor integration failed: ${error.message}`))
1120
+ console.log(chalk.gray(` Agent installed but Cursor integration skipped.\n`))
1121
+ }
1122
+ }
1123
+
899
1124
  /**
900
1125
  * Get latest version of agent
901
1126
  */