nex-app 0.1.1 → 0.2.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/cli/create.js +23 -4
- package/cli/fallback.js +18 -40
- package/generator/index.js +85 -81
- package/package.json +1 -1
- package/server/index.js +2 -0
- package/server/routes.js +17 -44
- package/ui/assets/app.js +27 -82
- package/ui/index.html +5 -55
package/cli/create.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* NEX App Creator - Entrypoint principal
|
|
@@ -82,18 +82,37 @@ async function main() {
|
|
|
82
82
|
console.log(chalk.gray(` Diretório de trabalho: ${chalk.cyan(initialCwd)}`))
|
|
83
83
|
console.log(chalk.gray(' Pressione Ctrl+C para cancelar\n'))
|
|
84
84
|
|
|
85
|
+
// Variável para controlar se deve fechar automaticamente
|
|
86
|
+
let shouldAutoClose = false
|
|
87
|
+
|
|
85
88
|
// Aguardar finalização
|
|
86
89
|
process.on('SIGINT', () => {
|
|
87
90
|
console.log(chalk.yellow('\n\n⏹️ Cancelando...'))
|
|
88
|
-
server.close()
|
|
91
|
+
server.close(() => {
|
|
92
|
+
process.exit(0)
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// Listener para fechar servidor quando projeto for gerado
|
|
97
|
+
server.on('close', () => {
|
|
98
|
+
if (shouldAutoClose) {
|
|
99
|
+
console.log(chalk.green('\n✅ Projeto criado! Servidor fechado.'))
|
|
100
|
+
}
|
|
89
101
|
process.exit(0)
|
|
90
102
|
})
|
|
91
103
|
|
|
104
|
+
// Expor função para fechar servidor via API
|
|
105
|
+
server.closeServer = () => {
|
|
106
|
+
shouldAutoClose = true
|
|
107
|
+
server.close()
|
|
108
|
+
}
|
|
109
|
+
|
|
92
110
|
// Timeout de segurança (10 minutos)
|
|
93
111
|
setTimeout(() => {
|
|
94
112
|
console.log(chalk.yellow('\n⏱️ Timeout: fechando servidor...'))
|
|
95
|
-
server.close()
|
|
96
|
-
|
|
113
|
+
server.close(() => {
|
|
114
|
+
process.exit(0)
|
|
115
|
+
})
|
|
97
116
|
}, 10 * 60 * 1000)
|
|
98
117
|
|
|
99
118
|
} catch (error) {
|
package/cli/fallback.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CLI Fallback - Modo interativo via terminal
|
|
2
|
+
* CLI Fallback - Modo interativo via terminal (Simplificado)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import inquirer from 'inquirer'
|
|
@@ -10,7 +10,7 @@ import { generateProject } from '../generator/index.js'
|
|
|
10
10
|
import { searchAgents } from '../lib/marketplace-client.js'
|
|
11
11
|
|
|
12
12
|
export async function runCLIFallback(initialCwd = process.cwd()) {
|
|
13
|
-
console.log(chalk.blue('🚀
|
|
13
|
+
console.log(chalk.blue('🚀 Instalando NEX Framework\n'))
|
|
14
14
|
console.log(chalk.gray(`Diretório de trabalho: ${chalk.cyan(initialCwd)}\n`))
|
|
15
15
|
|
|
16
16
|
// Buscar agentes disponíveis
|
|
@@ -30,62 +30,40 @@ export async function runCLIFallback(initialCwd = process.cwd()) {
|
|
|
30
30
|
// Ignore
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
console.log(chalk.gray(`Projeto detectado: ${chalk.cyan(currentProjectName)}\n`))
|
|
34
|
+
|
|
33
35
|
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
36
|
{
|
|
46
37
|
type: 'checkbox',
|
|
47
38
|
name: 'agents',
|
|
48
|
-
message: 'Agentes
|
|
39
|
+
message: 'Agentes para instalar (opcional):',
|
|
49
40
|
choices: availableAgents.map(agent => ({
|
|
50
|
-
name: `${agent.icon} ${agent.name} - ${agent.tagline}`,
|
|
41
|
+
name: `${agent.icon || '🤖'} ${agent.name} - ${agent.tagline}`,
|
|
51
42
|
value: agent.agent_id
|
|
52
43
|
})),
|
|
53
44
|
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
45
|
}
|
|
68
46
|
])
|
|
69
47
|
|
|
70
|
-
console.log(chalk.blue('\n📦
|
|
48
|
+
console.log(chalk.blue('\n📦 Instalando NEX...\n'))
|
|
71
49
|
|
|
72
50
|
try {
|
|
73
|
-
const
|
|
74
|
-
template: answers.template,
|
|
51
|
+
const result = await generateProject({
|
|
75
52
|
agents: answers.agents || [],
|
|
76
|
-
packageManager: answers.packageManager,
|
|
77
|
-
cursorIntegration: answers.cursorIntegration,
|
|
78
|
-
options: {},
|
|
79
53
|
targetDir: initialCwd
|
|
80
54
|
})
|
|
81
55
|
|
|
82
|
-
console.log(chalk.green(`\n✅ NEX
|
|
56
|
+
console.log(chalk.green(`\n✅ NEX instalado com sucesso!`))
|
|
57
|
+
|
|
58
|
+
if (result.bmadDetected) {
|
|
59
|
+
console.log(chalk.green(`✅ BMAD detectado e compatível\n`))
|
|
60
|
+
}
|
|
61
|
+
|
|
83
62
|
console.log(chalk.gray('Próximos passos:'))
|
|
84
|
-
console.log(chalk.cyan(`
|
|
85
|
-
console.log(chalk.cyan(`
|
|
86
|
-
console.log(chalk.
|
|
87
|
-
console.log(chalk.cyan(`
|
|
88
|
-
console.log(chalk.cyan(` nex agent install <agent-id>\n`))
|
|
63
|
+
console.log(chalk.cyan(` npm install -g nex-framework-cli # CLI global (opcional)`))
|
|
64
|
+
console.log(chalk.cyan(` nex agent list --all # Ver agentes disponíveis`))
|
|
65
|
+
console.log(chalk.cyan(` nex agent install <agent-id> # Instalar agentes`))
|
|
66
|
+
console.log(chalk.cyan(` @<agent-id> <comando> # Usar no Cursor\n`))
|
|
89
67
|
} catch (error) {
|
|
90
68
|
console.error(chalk.red(`\n❌ Erro: ${error.message}\n`))
|
|
91
69
|
if (error.stack) {
|
package/generator/index.js
CHANGED
|
@@ -1,62 +1,78 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generator de projetos NEX
|
|
2
|
+
* Generator de projetos NEX (Simplificado)
|
|
3
|
+
* Apenas instala estrutura NEX, detecta BMAD e integra com Cursor
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
import fs from 'fs-extra'
|
|
6
7
|
import path from 'path'
|
|
7
8
|
import { fileURLToPath } from 'url'
|
|
8
|
-
import
|
|
9
|
+
import yaml from 'yaml'
|
|
9
10
|
import { installAgents } from './agent-installer.js'
|
|
10
11
|
import { integrateCursor } from './cursor-integration.js'
|
|
11
|
-
import { installDependencies } from './package-manager.js'
|
|
12
12
|
|
|
13
13
|
const __filename = fileURLToPath(import.meta.url)
|
|
14
14
|
const __dirname = path.dirname(__filename)
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* Detectar se BMAD já está instalado
|
|
18
18
|
*/
|
|
19
|
-
async function
|
|
20
|
-
const
|
|
19
|
+
async function detectBMAD(projectPath) {
|
|
20
|
+
const bmadPaths = [
|
|
21
|
+
path.join(projectPath, 'bmad'),
|
|
22
|
+
path.join(projectPath, '.cursor', 'rules', 'bmad')
|
|
23
|
+
]
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
for (const bmadPath of bmadPaths) {
|
|
26
|
+
if (await fs.pathExists(bmadPath)) {
|
|
27
|
+
console.log(`✅ BMAD detectado em: ${bmadPath}`)
|
|
28
|
+
return true
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Carregar configuração global do NEX CLI
|
|
37
|
+
*/
|
|
38
|
+
async function getGlobalConfig() {
|
|
39
|
+
const os = await import('os')
|
|
40
|
+
const configPath = process.platform === 'win32'
|
|
41
|
+
? path.join(os.default.homedir(), 'AppData', 'Roaming', 'nex', 'config.json')
|
|
42
|
+
: path.join(os.default.homedir(), '.nex', 'config.json')
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
if (await fs.pathExists(configPath)) {
|
|
46
|
+
return await fs.readJSON(configPath)
|
|
33
47
|
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
// Ignore
|
|
34
50
|
}
|
|
35
51
|
|
|
36
|
-
|
|
37
|
-
|
|
52
|
+
return {
|
|
53
|
+
user: {
|
|
54
|
+
name: 'Developer',
|
|
55
|
+
conversationLanguage: 'pt-br',
|
|
56
|
+
documentLanguage: 'pt-br'
|
|
57
|
+
},
|
|
58
|
+
cursor: {
|
|
59
|
+
enabled: true
|
|
60
|
+
}
|
|
61
|
+
}
|
|
38
62
|
}
|
|
39
63
|
|
|
40
64
|
export async function generateProject(config) {
|
|
41
65
|
const {
|
|
42
|
-
template,
|
|
43
66
|
agents = [],
|
|
44
|
-
packageManager = 'npm',
|
|
45
|
-
cursorIntegration = false,
|
|
46
|
-
options = {},
|
|
47
67
|
targetDir
|
|
48
68
|
} = config
|
|
49
69
|
|
|
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
70
|
const projectPath = targetDir
|
|
58
71
|
|
|
59
|
-
// Detectar
|
|
72
|
+
// 1. Detectar BMAD
|
|
73
|
+
const bmadDetected = await detectBMAD(projectPath)
|
|
74
|
+
|
|
75
|
+
// 2. Detectar nome do projeto
|
|
60
76
|
let detectedProjectName = path.basename(projectPath)
|
|
61
77
|
try {
|
|
62
78
|
const packageJsonPath = path.join(projectPath, 'package.json')
|
|
@@ -70,49 +86,12 @@ export async function generateProject(config) {
|
|
|
70
86
|
// Ignore
|
|
71
87
|
}
|
|
72
88
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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)
|
|
89
|
+
console.log(`📦 Instalando NEX no projeto: ${detectedProjectName}`)
|
|
90
|
+
if (bmadDetected) {
|
|
91
|
+
console.log(`✅ BMAD detectado - mantendo compatibilidade`)
|
|
113
92
|
}
|
|
114
93
|
|
|
115
|
-
//
|
|
94
|
+
// 3. Criar estrutura .nex-core (sempre)
|
|
116
95
|
const nexCoreDir = path.join(projectPath, '.nex-core')
|
|
117
96
|
await fs.ensureDir(path.join(nexCoreDir, 'agents'))
|
|
118
97
|
await fs.ensureDir(path.join(nexCoreDir, 'data'))
|
|
@@ -123,19 +102,44 @@ export async function generateProject(config) {
|
|
|
123
102
|
if (!await fs.pathExists(installedJsonPath)) {
|
|
124
103
|
await fs.writeJSON(installedJsonPath, {}, { spaces: 2 })
|
|
125
104
|
}
|
|
105
|
+
|
|
106
|
+
console.log(`✅ Estrutura .nex-core/ criada`)
|
|
107
|
+
|
|
108
|
+
// 4. Criar nex/core/config.yaml (como BMAD bmad/core/config.yaml)
|
|
109
|
+
const globalConfig = await getGlobalConfig()
|
|
110
|
+
const userConfig = globalConfig.user || {}
|
|
111
|
+
|
|
112
|
+
const nexCoreConfigDir = path.join(projectPath, 'nex', 'core')
|
|
113
|
+
await fs.ensureDir(nexCoreConfigDir)
|
|
114
|
+
|
|
115
|
+
const configYaml = {
|
|
116
|
+
user_name: userConfig.name || 'Developer',
|
|
117
|
+
communication_language: userConfig.conversationLanguage || 'pt-br',
|
|
118
|
+
document_output_language: userConfig.documentLanguage || 'pt-br',
|
|
119
|
+
output_folder: '{project-root}/docs',
|
|
120
|
+
project_name: detectedProjectName,
|
|
121
|
+
bmad_detected: bmadDetected
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const configPath = path.join(nexCoreConfigDir, 'config.yaml')
|
|
125
|
+
const configYamlContent = yaml.stringify(configYaml)
|
|
126
|
+
await fs.writeFile(configPath, configYamlContent, 'utf8')
|
|
127
|
+
|
|
128
|
+
console.log(`✅ Configuração NEX criada: nex/core/config.yaml`)
|
|
126
129
|
|
|
127
|
-
// 5.
|
|
130
|
+
// 5. Integração com Cursor (sempre ativada)
|
|
131
|
+
await integrateCursor(projectPath, agents)
|
|
132
|
+
|
|
133
|
+
// 6. Instalar agentes selecionados
|
|
128
134
|
if (agents.length > 0) {
|
|
135
|
+
console.log(`📦 Agentes selecionados: ${agents.join(', ')}`)
|
|
129
136
|
await installAgents(projectPath, agents)
|
|
137
|
+
} else {
|
|
138
|
+
console.log(`ℹ️ Nenhum agente selecionado`)
|
|
130
139
|
}
|
|
131
140
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
141
|
+
return {
|
|
142
|
+
projectPath,
|
|
143
|
+
bmadDetected
|
|
135
144
|
}
|
|
136
|
-
|
|
137
|
-
// 7. Instalar dependências
|
|
138
|
-
await installDependencies(projectPath, packageManager)
|
|
139
|
-
|
|
140
|
-
return projectPath
|
|
141
145
|
}
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -45,6 +45,8 @@ export async function startServer(initialCwd) {
|
|
|
45
45
|
|
|
46
46
|
return new Promise((resolve, reject) => {
|
|
47
47
|
const server = app.listen(port, '127.0.0.1', () => {
|
|
48
|
+
// Armazenar referência do servidor no app para poder fechar depois
|
|
49
|
+
app.set('server', server)
|
|
48
50
|
resolve({ port, server })
|
|
49
51
|
})
|
|
50
52
|
|
package/server/routes.js
CHANGED
|
@@ -1,38 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* API Routes - Simplificado
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import express from 'express'
|
|
6
|
-
import path from 'path'
|
|
7
6
|
import { generateProject } from '../generator/index.js'
|
|
8
7
|
import { searchAgents } from '../lib/marketplace-client.js'
|
|
9
|
-
import { validateTemplate, validatePackageManager } from '../utils/validation.js'
|
|
10
8
|
|
|
11
9
|
const router = express.Router()
|
|
12
10
|
|
|
13
11
|
// Health check
|
|
14
12
|
router.get('/health', (req, res) => {
|
|
15
|
-
res.json({
|
|
16
|
-
status: 'ok',
|
|
17
|
-
timestamp: Date.now(),
|
|
18
|
-
version: '0.1.0'
|
|
19
|
-
})
|
|
13
|
+
res.json({ status: 'ok' })
|
|
20
14
|
})
|
|
21
15
|
|
|
22
|
-
// Buscar agentes
|
|
16
|
+
// Buscar agentes
|
|
23
17
|
router.get('/agents/search', async (req, res) => {
|
|
24
18
|
try {
|
|
25
|
-
const { q
|
|
26
|
-
|
|
19
|
+
const { q, category } = req.query
|
|
27
20
|
const agents = await searchAgents({ q, category })
|
|
28
|
-
|
|
29
21
|
res.json({ agents })
|
|
30
22
|
} catch (error) {
|
|
31
23
|
console.error('Erro ao buscar agentes:', error)
|
|
32
|
-
res.status(500).json({
|
|
33
|
-
error: error.message,
|
|
34
|
-
agents: [] // Fallback: retornar vazio
|
|
35
|
-
})
|
|
24
|
+
res.status(500).json({ error: error.message })
|
|
36
25
|
}
|
|
37
26
|
})
|
|
38
27
|
|
|
@@ -40,46 +29,30 @@ router.get('/agents/search', async (req, res) => {
|
|
|
40
29
|
router.post('/generate', async (req, res) => {
|
|
41
30
|
try {
|
|
42
31
|
const {
|
|
43
|
-
|
|
44
|
-
agents = [],
|
|
45
|
-
packageManager = 'npm',
|
|
46
|
-
cursorIntegration = false,
|
|
47
|
-
options = {}
|
|
32
|
+
agents = []
|
|
48
33
|
} = req.body
|
|
49
34
|
|
|
50
|
-
// No padrão BMAD/NEX, não pedimos nome do projeto
|
|
51
|
-
// Usamos o nome da pasta atual ou package.json existente
|
|
52
|
-
|
|
53
|
-
const templateValidation = validateTemplate(template)
|
|
54
|
-
if (!templateValidation.valid) {
|
|
55
|
-
return res.status(400).json({ error: templateValidation.error })
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const pmValidation = validatePackageManager(packageManager)
|
|
59
|
-
if (!pmValidation.valid) {
|
|
60
|
-
return res.status(400).json({ error: pmValidation.error })
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Obter diretório de trabalho inicial do app.locals
|
|
64
35
|
const initialCwd = req.app.locals.initialCwd || process.cwd()
|
|
65
36
|
|
|
66
|
-
|
|
67
|
-
const projectPath = await generateProject({
|
|
68
|
-
template,
|
|
37
|
+
const result = await generateProject({
|
|
69
38
|
agents,
|
|
70
|
-
packageManager,
|
|
71
|
-
cursorIntegration,
|
|
72
|
-
options,
|
|
73
39
|
targetDir: initialCwd
|
|
74
40
|
})
|
|
75
41
|
|
|
76
42
|
res.json({
|
|
77
43
|
success: true,
|
|
78
|
-
path: projectPath,
|
|
79
|
-
|
|
44
|
+
path: result.projectPath,
|
|
45
|
+
bmadDetected: result.bmadDetected,
|
|
46
|
+
message: `NEX instalado com sucesso no diretório atual!`
|
|
80
47
|
})
|
|
48
|
+
|
|
49
|
+
// Fechar o servidor após a geração do projeto
|
|
50
|
+
if (req.app.locals.server) {
|
|
51
|
+
setTimeout(() => req.app.locals.server.close(), 2000) // Fecha após 2 segundos
|
|
52
|
+
}
|
|
53
|
+
|
|
81
54
|
} catch (error) {
|
|
82
|
-
console.error('Erro ao
|
|
55
|
+
console.error('Erro ao instalar NEX:', error)
|
|
83
56
|
res.status(500).json({
|
|
84
57
|
error: error.message
|
|
85
58
|
})
|
package/ui/assets/app.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* JavaScript da UI do Installer
|
|
2
|
+
* JavaScript da UI do Installer (Simplificado)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
let currentStep = 1
|
|
6
|
-
const totalSteps = 4
|
|
7
5
|
let selectedAgents = []
|
|
8
6
|
let allAgents = []
|
|
9
7
|
|
|
@@ -11,7 +9,6 @@ let allAgents = []
|
|
|
11
9
|
document.addEventListener('DOMContentLoaded', async () => {
|
|
12
10
|
await loadAgents()
|
|
13
11
|
setupEventListeners()
|
|
14
|
-
updateStepVisibility()
|
|
15
12
|
})
|
|
16
13
|
|
|
17
14
|
// Carregar agentes do Marketplace
|
|
@@ -30,10 +27,6 @@ async function loadAgents() {
|
|
|
30
27
|
|
|
31
28
|
// Setup de event listeners
|
|
32
29
|
function setupEventListeners() {
|
|
33
|
-
// Navegação de steps
|
|
34
|
-
document.getElementById('next-btn').addEventListener('click', nextStep)
|
|
35
|
-
document.getElementById('prev-btn').addEventListener('click', prevStep)
|
|
36
|
-
|
|
37
30
|
// Busca de agentes
|
|
38
31
|
document.getElementById('agent-search').addEventListener('input', handleAgentSearch)
|
|
39
32
|
|
|
@@ -41,64 +34,6 @@ function setupEventListeners() {
|
|
|
41
34
|
document.getElementById('installer-form').addEventListener('submit', handleSubmit)
|
|
42
35
|
}
|
|
43
36
|
|
|
44
|
-
// Navegação entre steps
|
|
45
|
-
function nextStep() {
|
|
46
|
-
if (validateCurrentStep()) {
|
|
47
|
-
if (currentStep < totalSteps) {
|
|
48
|
-
currentStep++
|
|
49
|
-
updateStepVisibility()
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function prevStep() {
|
|
55
|
-
if (currentStep > 1) {
|
|
56
|
-
currentStep--
|
|
57
|
-
updateStepVisibility()
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Validar step atual
|
|
62
|
-
function validateCurrentStep() {
|
|
63
|
-
const currentStepEl = document.querySelector(`.step[data-step="${currentStep}"]`)
|
|
64
|
-
|
|
65
|
-
// Verificar se o step existe
|
|
66
|
-
if (!currentStepEl) {
|
|
67
|
-
console.warn(`Step ${currentStep} não encontrado`)
|
|
68
|
-
return true // Permite avançar se step não existe
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const requiredInputs = currentStepEl.querySelectorAll('input[required], select[required]')
|
|
72
|
-
|
|
73
|
-
for (const input of requiredInputs) {
|
|
74
|
-
if (!input.value.trim()) {
|
|
75
|
-
input.focus()
|
|
76
|
-
return false
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return true
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Atualizar visibilidade dos steps
|
|
84
|
-
function updateStepVisibility() {
|
|
85
|
-
// Esconder todos os steps
|
|
86
|
-
document.querySelectorAll('.step').forEach(step => {
|
|
87
|
-
step.classList.remove('active')
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
// Mostrar step atual
|
|
91
|
-
const currentStepEl = document.querySelector(`.step[data-step="${currentStep}"]`)
|
|
92
|
-
if (currentStepEl) {
|
|
93
|
-
currentStepEl.classList.add('active')
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Atualizar botões
|
|
97
|
-
document.getElementById('prev-btn').disabled = currentStep === 1
|
|
98
|
-
document.getElementById('next-btn').classList.toggle('hidden', currentStep === totalSteps)
|
|
99
|
-
document.getElementById('submit-btn').classList.toggle('hidden', currentStep !== totalSteps)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
37
|
// Busca de agentes
|
|
103
38
|
function handleAgentSearch(e) {
|
|
104
39
|
const query = e.target.value.toLowerCase()
|
|
@@ -184,13 +119,8 @@ async function handleSubmit(e) {
|
|
|
184
119
|
document.getElementById('installer-form').classList.add('hidden')
|
|
185
120
|
document.getElementById('progress').classList.remove('hidden')
|
|
186
121
|
|
|
187
|
-
const formData = new FormData(e.target)
|
|
188
122
|
const data = {
|
|
189
|
-
|
|
190
|
-
agents: selectedAgents,
|
|
191
|
-
packageManager: formData.get('packageManager'),
|
|
192
|
-
cursorIntegration: formData.get('cursorIntegration') === 'on',
|
|
193
|
-
options: {}
|
|
123
|
+
agents: selectedAgents
|
|
194
124
|
}
|
|
195
125
|
|
|
196
126
|
try {
|
|
@@ -206,8 +136,20 @@ async function handleSubmit(e) {
|
|
|
206
136
|
|
|
207
137
|
if (result.success) {
|
|
208
138
|
showSuccess(result)
|
|
139
|
+
|
|
140
|
+
// Fechar servidor após 3 segundos (tempo para ler a mensagem)
|
|
141
|
+
if (result.shouldClose) {
|
|
142
|
+
setTimeout(() => {
|
|
143
|
+
// Enviar sinal para fechar servidor
|
|
144
|
+
fetch('/api/close', { method: 'POST' }).catch(() => {})
|
|
145
|
+
// Fechar página após mais 1 segundo
|
|
146
|
+
setTimeout(() => {
|
|
147
|
+
window.close()
|
|
148
|
+
}, 1000)
|
|
149
|
+
}, 3000)
|
|
150
|
+
}
|
|
209
151
|
} else {
|
|
210
|
-
showError(result.error || 'Erro ao
|
|
152
|
+
showError(result.error || 'Erro ao instalar NEX')
|
|
211
153
|
}
|
|
212
154
|
} catch (error) {
|
|
213
155
|
showError(error.message)
|
|
@@ -219,23 +161,26 @@ function showSuccess(result) {
|
|
|
219
161
|
document.getElementById('progress').classList.add('hidden')
|
|
220
162
|
document.getElementById('success').classList.remove('hidden')
|
|
221
163
|
|
|
222
|
-
const formData = new FormData(document.getElementById('installer-form'))
|
|
223
|
-
const packageManager = formData.get('packageManager')
|
|
224
164
|
const projectPath = result.path || 'diretório atual'
|
|
165
|
+
const bmadDetected = result.bmadDetected ? '✅ BMAD detectado e compatível' : ''
|
|
225
166
|
|
|
226
167
|
document.getElementById('success-message').innerHTML = `
|
|
227
|
-
<p>NEX
|
|
228
|
-
|
|
168
|
+
<p>NEX instalado em: <code>${projectPath}</code></p>
|
|
169
|
+
${bmadDetected ? `<p class="hint">${bmadDetected}</p>` : ''}
|
|
170
|
+
<p class="hint">Estrutura NEX criada no diretório atual</p>
|
|
229
171
|
`
|
|
230
172
|
|
|
231
|
-
document.getElementById('next-steps-commands').textContent =
|
|
232
|
-
|
|
173
|
+
document.getElementById('next-steps-commands').textContent = `# Instalar CLI globalmente (opcional):
|
|
174
|
+
npm install -g nex-framework-cli
|
|
175
|
+
|
|
176
|
+
# Ver agentes disponíveis:
|
|
177
|
+
nex agent list --all
|
|
233
178
|
|
|
234
|
-
#
|
|
179
|
+
# Instalar agentes:
|
|
235
180
|
nex agent install <agent-id>
|
|
236
181
|
|
|
237
|
-
#
|
|
238
|
-
|
|
182
|
+
# Usar no Cursor:
|
|
183
|
+
@<agent-id> <comando>`
|
|
239
184
|
}
|
|
240
185
|
|
|
241
186
|
// Mostrar erro
|
package/ui/index.html
CHANGED
|
@@ -14,37 +14,9 @@
|
|
|
14
14
|
</header>
|
|
15
15
|
|
|
16
16
|
<form id="installer-form">
|
|
17
|
-
<!-- Step 1:
|
|
17
|
+
<!-- Step 1: Agentes -->
|
|
18
18
|
<div class="step active" data-step="1">
|
|
19
|
-
<h2>
|
|
20
|
-
<div class="template-grid">
|
|
21
|
-
<label class="template-card">
|
|
22
|
-
<input type="radio" name="template" value="blank" checked>
|
|
23
|
-
<div class="card-content">
|
|
24
|
-
<h3>Blank</h3>
|
|
25
|
-
<p>Projeto vazio para começar do zero</p>
|
|
26
|
-
</div>
|
|
27
|
-
</label>
|
|
28
|
-
<label class="template-card">
|
|
29
|
-
<input type="radio" name="template" value="full-stack">
|
|
30
|
-
<div class="card-content">
|
|
31
|
-
<h3>Full Stack</h3>
|
|
32
|
-
<p>Frontend + Backend completo</p>
|
|
33
|
-
</div>
|
|
34
|
-
</label>
|
|
35
|
-
<label class="template-card">
|
|
36
|
-
<input type="radio" name="template" value="api-only">
|
|
37
|
-
<div class="card-content">
|
|
38
|
-
<h3>API Only</h3>
|
|
39
|
-
<p>Apenas backend/API</p>
|
|
40
|
-
</div>
|
|
41
|
-
</label>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
|
|
45
|
-
<!-- Step 2: Agentes -->
|
|
46
|
-
<div class="step" data-step="2">
|
|
47
|
-
<h2>Agentes Iniciais</h2>
|
|
19
|
+
<h2>Instalar NEX Framework</h2>
|
|
48
20
|
<p class="subtitle">Selecione os agentes que deseja instalar (opcional)</p>
|
|
49
21
|
|
|
50
22
|
<div class="agent-search">
|
|
@@ -65,41 +37,19 @@
|
|
|
65
37
|
</div>
|
|
66
38
|
</div>
|
|
67
39
|
|
|
68
|
-
<!-- Step 3: Cursor -->
|
|
69
|
-
<div class="step" data-step="3">
|
|
70
|
-
<h2>Integração Cursor</h2>
|
|
71
|
-
<label class="checkbox-label">
|
|
72
|
-
<input type="checkbox" name="cursorIntegration" checked>
|
|
73
|
-
<span>Configurar integração automática com Cursor IDE</span>
|
|
74
|
-
</label>
|
|
75
|
-
<small class="hint">Cria arquivos de configuração para integração com Cursor</small>
|
|
76
|
-
</div>
|
|
77
|
-
|
|
78
|
-
<!-- Step 4: Package Manager -->
|
|
79
|
-
<div class="step" data-step="4">
|
|
80
|
-
<h2>Package Manager</h2>
|
|
81
|
-
<select name="packageManager" id="packageManager">
|
|
82
|
-
<option value="npm">npm</option>
|
|
83
|
-
<option value="pnpm">pnpm</option>
|
|
84
|
-
<option value="yarn">yarn</option>
|
|
85
|
-
</select>
|
|
86
|
-
</div>
|
|
87
|
-
|
|
88
40
|
<div class="actions">
|
|
89
|
-
<button type="
|
|
90
|
-
<button type="button" id="next-btn" class="btn btn-primary">Próximo</button>
|
|
91
|
-
<button type="submit" id="submit-btn" class="btn btn-primary hidden">Criar Projeto</button>
|
|
41
|
+
<button type="submit" id="submit-btn" class="btn btn-primary">Instalar NEX</button>
|
|
92
42
|
</div>
|
|
93
43
|
</form>
|
|
94
44
|
|
|
95
45
|
<div id="progress" class="progress hidden">
|
|
96
46
|
<div class="spinner"></div>
|
|
97
|
-
<p>
|
|
47
|
+
<p>Instalando NEX Framework...</p>
|
|
98
48
|
<small id="progress-message"></small>
|
|
99
49
|
</div>
|
|
100
50
|
|
|
101
51
|
<div id="success" class="success hidden">
|
|
102
|
-
<h2>✅
|
|
52
|
+
<h2>✅ NEX instalado com sucesso!</h2>
|
|
103
53
|
<div id="success-message"></div>
|
|
104
54
|
<div class="next-steps">
|
|
105
55
|
<h3>Próximos passos:</h3>
|