sincron-auto 1.0.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/LICENSE +20 -0
- package/README.md +195 -0
- package/agents/avaliador.md +290 -0
- package/agents/construtor.md +149 -0
- package/agents/estrutura.md +79 -0
- package/agents/gestor-projeto.md +195 -0
- package/agents/orquestrador.md +109 -0
- package/bin/install.js +449 -0
- package/commands/sincron-auto.md +406 -0
- package/docs/arquitetura.md +397 -0
- package/docs/cenarios-uso.md +292 -0
- package/docs/duvidas.md +170 -0
- package/docs/estruturas-dados.md +377 -0
- package/docs/exemplos-playwright.md +497 -0
- package/docs/fluxo-mensagens.md +180 -0
- package/docs/installation-guide.md +535 -0
- package/docs/mcp-instalacao.md +118 -0
- package/docs/metricas-performance.md +371 -0
- package/docs/relatorio-final-exemplo.md +200 -0
- package/docs/skills-vs-agents.md +107 -0
- package/docs/template-permissoes.json +83 -0
- package/package.json +34 -0
- package/skills/autorizacao/SKILL.md +78 -0
- package/skills/avaliacao/SKILL.md +55 -0
- package/skills/gerenciar/SKILL.md +60 -0
- package/skills/instalar-mcp/SKILL.md +59 -0
- package/skills/projetar/SKILL.md +53 -0
- package/skills/relatorio-final/SKILL.md +269 -0
- package/templates/settings-nenhuma.json +5 -0
- package/templates/settings-parcial.json +17 -0
- package/templates/settings-total.json +23 -0
package/bin/install.js
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const readline = require('readline');
|
|
7
|
+
|
|
8
|
+
// Colors
|
|
9
|
+
const cyan = '\x1b[36m';
|
|
10
|
+
const green = '\x1b[32m';
|
|
11
|
+
const yellow = '\x1b[33m';
|
|
12
|
+
const dim = '\x1b[2m';
|
|
13
|
+
const reset = '\x1b[0m';
|
|
14
|
+
|
|
15
|
+
// Get version from package.json
|
|
16
|
+
const pkg = require('../package.json');
|
|
17
|
+
|
|
18
|
+
// Parse args
|
|
19
|
+
const args = process.argv.slice(2);
|
|
20
|
+
const hasGlobal = args.includes('--global') || args.includes('-g');
|
|
21
|
+
const hasLocal = args.includes('--local') || args.includes('-l');
|
|
22
|
+
const hasUninstall = args.includes('--uninstall') || args.includes('-u');
|
|
23
|
+
const hasHelp = args.includes('--help') || args.includes('-h');
|
|
24
|
+
|
|
25
|
+
// Parse --config-dir argument
|
|
26
|
+
function parseConfigDirArg() {
|
|
27
|
+
const configDirIndex = args.findIndex(arg => arg === '--config-dir' || arg === '-c');
|
|
28
|
+
if (configDirIndex !== -1) {
|
|
29
|
+
const nextArg = args[configDirIndex + 1];
|
|
30
|
+
if (!nextArg || nextArg.startsWith('-')) {
|
|
31
|
+
console.error(` ${yellow}--config-dir requires a path argument${reset}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
return nextArg;
|
|
35
|
+
}
|
|
36
|
+
const configDirArg = args.find(arg => arg.startsWith('--config-dir=') || arg.startsWith('-c='));
|
|
37
|
+
if (configDirArg) {
|
|
38
|
+
const value = configDirArg.split('=')[1];
|
|
39
|
+
if (!value) {
|
|
40
|
+
console.error(` ${yellow}--config-dir requires a non-empty path${reset}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const explicitConfigDir = parseConfigDirArg();
|
|
48
|
+
|
|
49
|
+
const banner = `
|
|
50
|
+
${cyan} ███████╗ ██╗ ███╗ ██╗ ██████╗ ██████╗ ██████╗ ███╗ ██╗
|
|
51
|
+
██╔════╝ ██║ ████╗ ██║ ██╔════╝ ██╔══██╗ ██╔═══██╗ ████╗ ██║
|
|
52
|
+
███████╗ ██║ ██╔██╗ ██║ ██║ ██████╔╝ ██║ ██║ ██╔██╗ ██║
|
|
53
|
+
╚════██║ ██║ ██║╚██╗██║ ██║ ██╔══██╗ ██║ ██║ ██║╚██╗██║
|
|
54
|
+
███████║ ██║ ██║ ╚████║ ╚██████╗ ██║ ██║ ╚██████╔╝ ██║ ╚████║
|
|
55
|
+
╚══════╝ ╚═╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝${reset}
|
|
56
|
+
|
|
57
|
+
${green}Sincron-Auto${reset} ${dim}v${pkg.version}${reset}
|
|
58
|
+
Plugin de automação multi-agente para Claude Code.
|
|
59
|
+
Do planejamento à execução, com inteligência distribuída.
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
console.log(banner);
|
|
63
|
+
|
|
64
|
+
// Show help if requested
|
|
65
|
+
if (hasHelp) {
|
|
66
|
+
console.log(` ${yellow}Uso:${reset} npx sincron-auto [opções]
|
|
67
|
+
|
|
68
|
+
${yellow}Opções:${reset}
|
|
69
|
+
${cyan}-g, --global${reset} Instalar globalmente (~/.claude)
|
|
70
|
+
${cyan}-l, --local${reset} Instalar localmente (./.claude)
|
|
71
|
+
${cyan}-u, --uninstall${reset} Desinstalar Sincron-Auto
|
|
72
|
+
${cyan}-c, --config-dir <path>${reset} Especificar diretório de config
|
|
73
|
+
${cyan}-h, --help${reset} Mostrar esta ajuda
|
|
74
|
+
|
|
75
|
+
${yellow}Exemplos:${reset}
|
|
76
|
+
${dim}# Instalação interativa${reset}
|
|
77
|
+
npx sincron-auto
|
|
78
|
+
|
|
79
|
+
${dim}# Instalar globalmente${reset}
|
|
80
|
+
npx sincron-auto --global
|
|
81
|
+
|
|
82
|
+
${dim}# Instalar apenas no projeto atual${reset}
|
|
83
|
+
npx sincron-auto --local
|
|
84
|
+
|
|
85
|
+
${dim}# Desinstalar globalmente${reset}
|
|
86
|
+
npx sincron-auto --global --uninstall
|
|
87
|
+
|
|
88
|
+
${yellow}Comandos disponíveis após instalação:${reset}
|
|
89
|
+
${cyan}/sincron-auto${reset} - Iniciar workflow de automação multi-agente
|
|
90
|
+
`);
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Expand ~ to home directory
|
|
96
|
+
*/
|
|
97
|
+
function expandTilde(filePath) {
|
|
98
|
+
if (filePath && filePath.startsWith('~/')) {
|
|
99
|
+
return path.join(os.homedir(), filePath.slice(2));
|
|
100
|
+
}
|
|
101
|
+
return filePath;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get the global config directory for Claude Code
|
|
106
|
+
*/
|
|
107
|
+
function getGlobalDir(explicitDir = null) {
|
|
108
|
+
if (explicitDir) {
|
|
109
|
+
return expandTilde(explicitDir);
|
|
110
|
+
}
|
|
111
|
+
if (process.env.CLAUDE_CONFIG_DIR) {
|
|
112
|
+
return expandTilde(process.env.CLAUDE_CONFIG_DIR);
|
|
113
|
+
}
|
|
114
|
+
return path.join(os.homedir(), '.claude');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Read and parse settings.json
|
|
119
|
+
*/
|
|
120
|
+
function readSettings(settingsPath) {
|
|
121
|
+
if (fs.existsSync(settingsPath)) {
|
|
122
|
+
try {
|
|
123
|
+
return JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
124
|
+
} catch (e) {
|
|
125
|
+
return {};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return {};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Write settings.json with proper formatting
|
|
133
|
+
*/
|
|
134
|
+
function writeSettings(settingsPath, settings) {
|
|
135
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Recursively copy directory, replacing paths in .md files
|
|
140
|
+
*/
|
|
141
|
+
function copyWithPathReplacement(srcDir, destDir, pathPrefix) {
|
|
142
|
+
if (fs.existsSync(destDir)) {
|
|
143
|
+
fs.rmSync(destDir, { recursive: true });
|
|
144
|
+
}
|
|
145
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
146
|
+
|
|
147
|
+
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
148
|
+
|
|
149
|
+
for (const entry of entries) {
|
|
150
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
151
|
+
const destPath = path.join(destDir, entry.name);
|
|
152
|
+
|
|
153
|
+
if (entry.isDirectory()) {
|
|
154
|
+
copyWithPathReplacement(srcPath, destPath, pathPrefix);
|
|
155
|
+
} else if (entry.name.endsWith('.md')) {
|
|
156
|
+
let content = fs.readFileSync(srcPath, 'utf8');
|
|
157
|
+
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
158
|
+
fs.writeFileSync(destPath, content);
|
|
159
|
+
} else {
|
|
160
|
+
fs.copyFileSync(srcPath, destPath);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Verify a directory exists and contains files
|
|
167
|
+
*/
|
|
168
|
+
function verifyInstalled(dirPath, description) {
|
|
169
|
+
if (!fs.existsSync(dirPath)) {
|
|
170
|
+
console.error(` ${yellow}✗${reset} Falha ao instalar ${description}: diretório não criado`);
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
const entries = fs.readdirSync(dirPath);
|
|
175
|
+
if (entries.length === 0) {
|
|
176
|
+
console.error(` ${yellow}✗${reset} Falha ao instalar ${description}: diretório vazio`);
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
} catch (e) {
|
|
180
|
+
console.error(` ${yellow}✗${reset} Falha ao instalar ${description}: ${e.message}`);
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Uninstall Sincron-Auto from the specified directory
|
|
188
|
+
*/
|
|
189
|
+
function uninstall(isGlobal) {
|
|
190
|
+
const targetDir = isGlobal
|
|
191
|
+
? getGlobalDir(explicitConfigDir)
|
|
192
|
+
: path.join(process.cwd(), '.claude');
|
|
193
|
+
|
|
194
|
+
const locationLabel = isGlobal
|
|
195
|
+
? targetDir.replace(os.homedir(), '~')
|
|
196
|
+
: targetDir.replace(process.cwd(), '.');
|
|
197
|
+
|
|
198
|
+
console.log(` Desinstalando Sincron-Auto de ${cyan}${locationLabel}${reset}\n`);
|
|
199
|
+
|
|
200
|
+
if (!fs.existsSync(targetDir)) {
|
|
201
|
+
console.log(` ${yellow}⚠${reset} Diretório não existe: ${locationLabel}`);
|
|
202
|
+
console.log(` Nada para desinstalar.\n`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
let removedCount = 0;
|
|
207
|
+
|
|
208
|
+
// 1. Remove sincron-auto command
|
|
209
|
+
const commandsDir = path.join(targetDir, 'commands');
|
|
210
|
+
if (fs.existsSync(commandsDir)) {
|
|
211
|
+
const cmdPath = path.join(commandsDir, 'sincron-auto.md');
|
|
212
|
+
if (fs.existsSync(cmdPath)) {
|
|
213
|
+
fs.unlinkSync(cmdPath);
|
|
214
|
+
removedCount++;
|
|
215
|
+
console.log(` ${green}✓${reset} Removido comando sincron-auto`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// 2. Remove sincron-auto agents
|
|
220
|
+
const agentsDir = path.join(targetDir, 'agents');
|
|
221
|
+
if (fs.existsSync(agentsDir)) {
|
|
222
|
+
const agentsToRemove = ['orquestrador.md', 'avaliador.md', 'construtor.md', 'estrutura.md', 'gestor-projeto.md'];
|
|
223
|
+
let agentCount = 0;
|
|
224
|
+
for (const agent of agentsToRemove) {
|
|
225
|
+
const agentPath = path.join(agentsDir, agent);
|
|
226
|
+
if (fs.existsSync(agentPath)) {
|
|
227
|
+
fs.unlinkSync(agentPath);
|
|
228
|
+
agentCount++;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (agentCount > 0) {
|
|
232
|
+
removedCount++;
|
|
233
|
+
console.log(` ${green}✓${reset} Removidos ${agentCount} agentes`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 3. Remove sincron-auto skills
|
|
238
|
+
const skillsDir = path.join(targetDir, 'skills');
|
|
239
|
+
if (fs.existsSync(skillsDir)) {
|
|
240
|
+
const skillsToRemove = ['autorizacao', 'avaliacao', 'gerenciar', 'instalar-mcp', 'projetar', 'relatorio-final'];
|
|
241
|
+
let skillCount = 0;
|
|
242
|
+
for (const skill of skillsToRemove) {
|
|
243
|
+
const skillPath = path.join(skillsDir, skill);
|
|
244
|
+
if (fs.existsSync(skillPath)) {
|
|
245
|
+
fs.rmSync(skillPath, { recursive: true });
|
|
246
|
+
skillCount++;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (skillCount > 0) {
|
|
250
|
+
removedCount++;
|
|
251
|
+
console.log(` ${green}✓${reset} Removidas ${skillCount} skills`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 4. Remove sincron-auto templates/docs
|
|
256
|
+
const sincronAutoDir = path.join(targetDir, 'sincron-auto');
|
|
257
|
+
if (fs.existsSync(sincronAutoDir)) {
|
|
258
|
+
fs.rmSync(sincronAutoDir, { recursive: true });
|
|
259
|
+
removedCount++;
|
|
260
|
+
console.log(` ${green}✓${reset} Removido sincron-auto/`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (removedCount === 0) {
|
|
264
|
+
console.log(` ${yellow}⚠${reset} Nenhum arquivo do Sincron-Auto encontrado.`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
console.log(`
|
|
268
|
+
${green}Pronto!${reset} Sincron-Auto foi desinstalado.
|
|
269
|
+
Seus outros arquivos e configurações foram preservados.
|
|
270
|
+
`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Install Sincron-Auto to the specified directory
|
|
275
|
+
*/
|
|
276
|
+
function install(isGlobal) {
|
|
277
|
+
const src = path.join(__dirname, '..');
|
|
278
|
+
const targetDir = isGlobal
|
|
279
|
+
? getGlobalDir(explicitConfigDir)
|
|
280
|
+
: path.join(process.cwd(), '.claude');
|
|
281
|
+
|
|
282
|
+
const locationLabel = isGlobal
|
|
283
|
+
? targetDir.replace(os.homedir(), '~')
|
|
284
|
+
: targetDir.replace(process.cwd(), '.');
|
|
285
|
+
|
|
286
|
+
const pathPrefix = isGlobal
|
|
287
|
+
? `${targetDir}/`
|
|
288
|
+
: './.claude/';
|
|
289
|
+
|
|
290
|
+
console.log(` Instalando em ${cyan}${locationLabel}${reset}\n`);
|
|
291
|
+
|
|
292
|
+
const failures = [];
|
|
293
|
+
|
|
294
|
+
// 1. Create directories
|
|
295
|
+
fs.mkdirSync(path.join(targetDir, 'commands'), { recursive: true });
|
|
296
|
+
fs.mkdirSync(path.join(targetDir, 'agents'), { recursive: true });
|
|
297
|
+
fs.mkdirSync(path.join(targetDir, 'skills'), { recursive: true });
|
|
298
|
+
|
|
299
|
+
// 2. Copy command
|
|
300
|
+
const commandsSrc = path.join(src, 'commands');
|
|
301
|
+
if (fs.existsSync(commandsSrc)) {
|
|
302
|
+
const commandFiles = fs.readdirSync(commandsSrc).filter(f => f.endsWith('.md'));
|
|
303
|
+
for (const file of commandFiles) {
|
|
304
|
+
let content = fs.readFileSync(path.join(commandsSrc, file), 'utf8');
|
|
305
|
+
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
306
|
+
fs.writeFileSync(path.join(targetDir, 'commands', file), content);
|
|
307
|
+
}
|
|
308
|
+
console.log(` ${green}✓${reset} Instalado ${commandFiles.length} comando(s)`);
|
|
309
|
+
} else {
|
|
310
|
+
failures.push('commands');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// 3. Copy agents
|
|
314
|
+
const agentsSrc = path.join(src, 'agents');
|
|
315
|
+
if (fs.existsSync(agentsSrc)) {
|
|
316
|
+
const agentFiles = fs.readdirSync(agentsSrc).filter(f => f.endsWith('.md'));
|
|
317
|
+
for (const file of agentFiles) {
|
|
318
|
+
let content = fs.readFileSync(path.join(agentsSrc, file), 'utf8');
|
|
319
|
+
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
320
|
+
fs.writeFileSync(path.join(targetDir, 'agents', file), content);
|
|
321
|
+
}
|
|
322
|
+
console.log(` ${green}✓${reset} Instalados ${agentFiles.length} agentes`);
|
|
323
|
+
} else {
|
|
324
|
+
failures.push('agents');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// 4. Copy skills
|
|
328
|
+
const skillsSrc = path.join(src, 'skills');
|
|
329
|
+
if (fs.existsSync(skillsSrc)) {
|
|
330
|
+
const skillDirs = fs.readdirSync(skillsSrc, { withFileTypes: true }).filter(d => d.isDirectory());
|
|
331
|
+
for (const dir of skillDirs) {
|
|
332
|
+
const srcSkillDir = path.join(skillsSrc, dir.name);
|
|
333
|
+
const destSkillDir = path.join(targetDir, 'skills', dir.name);
|
|
334
|
+
copyWithPathReplacement(srcSkillDir, destSkillDir, pathPrefix);
|
|
335
|
+
}
|
|
336
|
+
console.log(` ${green}✓${reset} Instaladas ${skillDirs.length} skills`);
|
|
337
|
+
} else {
|
|
338
|
+
failures.push('skills');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 5. Copy templates to sincron-auto directory
|
|
342
|
+
const templatesSrc = path.join(src, 'templates');
|
|
343
|
+
const templatesDest = path.join(targetDir, 'sincron-auto', 'templates');
|
|
344
|
+
if (fs.existsSync(templatesSrc)) {
|
|
345
|
+
copyWithPathReplacement(templatesSrc, templatesDest, pathPrefix);
|
|
346
|
+
if (verifyInstalled(templatesDest, 'templates')) {
|
|
347
|
+
console.log(` ${green}✓${reset} Instalados templates`);
|
|
348
|
+
} else {
|
|
349
|
+
failures.push('templates');
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// 6. Copy docs
|
|
354
|
+
const docsSrc = path.join(src, 'docs');
|
|
355
|
+
const docsDest = path.join(targetDir, 'sincron-auto', 'docs');
|
|
356
|
+
if (fs.existsSync(docsSrc)) {
|
|
357
|
+
copyWithPathReplacement(docsSrc, docsDest, pathPrefix);
|
|
358
|
+
if (verifyInstalled(docsDest, 'docs')) {
|
|
359
|
+
console.log(` ${green}✓${reset} Instalada documentação`);
|
|
360
|
+
} else {
|
|
361
|
+
failures.push('docs');
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// 7. Write VERSION file
|
|
366
|
+
const versionDest = path.join(targetDir, 'sincron-auto', 'VERSION');
|
|
367
|
+
fs.mkdirSync(path.dirname(versionDest), { recursive: true });
|
|
368
|
+
fs.writeFileSync(versionDest, pkg.version);
|
|
369
|
+
console.log(` ${green}✓${reset} Escrito VERSION (${pkg.version})`);
|
|
370
|
+
|
|
371
|
+
if (failures.length > 0) {
|
|
372
|
+
console.error(`\n ${yellow}Instalação incompleta!${reset} Falhou: ${failures.join(', ')}`);
|
|
373
|
+
process.exit(1);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
console.log(`
|
|
377
|
+
${green}Pronto!${reset} Abra o Claude Code e execute ${cyan}/sincron-auto${reset}.
|
|
378
|
+
|
|
379
|
+
${cyan}Comando disponível:${reset}
|
|
380
|
+
/sincron-auto - Iniciar workflow de automação multi-agente
|
|
381
|
+
|
|
382
|
+
${cyan}Dica:${reset} Sincron-Auto funciona junto com Sincron-Plan!
|
|
383
|
+
Use /sincron-plan para planejar e /sincron-auto para executar.
|
|
384
|
+
|
|
385
|
+
${cyan}Documentação:${reset} https://github.com/MLTCorp/sincron-auto
|
|
386
|
+
`);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Prompt for install location
|
|
391
|
+
*/
|
|
392
|
+
function promptLocation() {
|
|
393
|
+
if (!process.stdin.isTTY) {
|
|
394
|
+
console.log(` ${yellow}Terminal não-interativo detectado, usando instalação global${reset}\n`);
|
|
395
|
+
install(true);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const rl = readline.createInterface({
|
|
400
|
+
input: process.stdin,
|
|
401
|
+
output: process.stdout
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
let answered = false;
|
|
405
|
+
|
|
406
|
+
rl.on('close', () => {
|
|
407
|
+
if (!answered) {
|
|
408
|
+
answered = true;
|
|
409
|
+
console.log(`\n ${yellow}Instalação cancelada${reset}\n`);
|
|
410
|
+
process.exit(0);
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
const globalPath = getGlobalDir(explicitConfigDir).replace(os.homedir(), '~');
|
|
415
|
+
|
|
416
|
+
console.log(` ${yellow}Onde você quer instalar?${reset}
|
|
417
|
+
|
|
418
|
+
${cyan}1${reset}) Global ${dim}(${globalPath})${reset} - disponível em todos os projetos
|
|
419
|
+
${cyan}2${reset}) Local ${dim}(./.claude)${reset} - apenas neste projeto
|
|
420
|
+
`);
|
|
421
|
+
|
|
422
|
+
rl.question(` Escolha ${dim}[1]${reset}: `, (answer) => {
|
|
423
|
+
answered = true;
|
|
424
|
+
rl.close();
|
|
425
|
+
const choice = answer.trim() || '1';
|
|
426
|
+
const isGlobal = choice !== '2';
|
|
427
|
+
install(isGlobal);
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Main
|
|
432
|
+
if (hasGlobal && hasLocal) {
|
|
433
|
+
console.error(` ${yellow}Não é possível especificar --global e --local juntos${reset}`);
|
|
434
|
+
process.exit(1);
|
|
435
|
+
} else if (explicitConfigDir && hasLocal) {
|
|
436
|
+
console.error(` ${yellow}Não é possível usar --config-dir com --local${reset}`);
|
|
437
|
+
process.exit(1);
|
|
438
|
+
} else if (hasUninstall) {
|
|
439
|
+
if (!hasGlobal && !hasLocal) {
|
|
440
|
+
console.error(` ${yellow}--uninstall requer --global ou --local${reset}`);
|
|
441
|
+
console.error(` Exemplo: npx sincron-auto --global --uninstall`);
|
|
442
|
+
process.exit(1);
|
|
443
|
+
}
|
|
444
|
+
uninstall(hasGlobal);
|
|
445
|
+
} else if (hasGlobal || hasLocal) {
|
|
446
|
+
install(hasGlobal);
|
|
447
|
+
} else {
|
|
448
|
+
promptLocation();
|
|
449
|
+
}
|