arsenal-agent 0.1.3 → 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/README.md +60 -16
- package/dist/index.js +8 -1
- package/dist/setup.js +133 -0
- package/package.json +9 -2
- package/src/index.tsx +8 -0
- package/src/setup.ts +142 -0
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# arsenal-agent
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Tasks simples vão pro Nemotron via OpenCode (grátis). Tasks complexas vão pro Claude Sonnet.
|
|
3
|
+
Launcher do Claude Code com gerenciamento de múltiplas contas via OAuth.
|
|
6
4
|
|
|
7
5
|
## Instalação
|
|
8
6
|
|
|
@@ -10,27 +8,73 @@ Tasks simples vão pro Nemotron via OpenCode (grátis). Tasks complexas vão pro
|
|
|
10
8
|
npm install -g arsenal-agent
|
|
11
9
|
```
|
|
12
10
|
|
|
11
|
+
**Dependências:**
|
|
12
|
+
- [`claude`](https://claude.ai/code) — Claude Code CLI
|
|
13
|
+
- [`opencode`](https://opencode.ai) — OpenCode CLI (opcional, para delegação de tasks)
|
|
14
|
+
- Node.js >= 20
|
|
15
|
+
|
|
16
|
+
## Configuração inicial
|
|
17
|
+
|
|
18
|
+
Após instalar, cadastre suas contas (abre o navegador para cada uma):
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
aa profile add pessoal
|
|
22
|
+
aa profile add trabalho
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Adicione aliases rápidos no `~/.bashrc`:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
alias aa-p='aa --profile pessoal'
|
|
29
|
+
alias aa-z='aa --profile trabalho' # adapte a letra para cada conta
|
|
30
|
+
```
|
|
31
|
+
|
|
13
32
|
## Uso
|
|
14
33
|
|
|
15
34
|
```bash
|
|
16
|
-
|
|
17
|
-
#
|
|
18
|
-
aa
|
|
35
|
+
aa # abre Claude com o perfil ativo
|
|
36
|
+
aa-p # conta pessoal
|
|
37
|
+
aa-z # conta trabalho
|
|
38
|
+
|
|
39
|
+
aa profile list # ver contas cadastradas
|
|
40
|
+
aa profile use trabalho # trocar conta padrão
|
|
19
41
|
```
|
|
20
42
|
|
|
21
|
-
##
|
|
43
|
+
## Onde ficam as credenciais
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
~/.arsenal-agent/
|
|
47
|
+
profiles.json ← índice de perfis (chmod 600)
|
|
48
|
+
credentials/
|
|
49
|
+
pessoal.json ← token OAuth da conta pessoal
|
|
50
|
+
trabalho.json ← token OAuth da conta trabalho
|
|
51
|
+
```
|
|
22
52
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- `OPENROUTER_API_KEY` no ambiente
|
|
53
|
+
**As credenciais são locais — nunca vão para o git.**
|
|
54
|
+
Em uma máquina nova você precisa rodar `aa profile add` novamente para cada conta.
|
|
26
55
|
|
|
27
|
-
##
|
|
56
|
+
## Instalar em outra máquina
|
|
28
57
|
|
|
58
|
+
```bash
|
|
59
|
+
# 1. Instala o CLI
|
|
60
|
+
npm install -g arsenal-agent
|
|
61
|
+
|
|
62
|
+
# 2. Autentica as contas (processo manual — abre navegador)
|
|
63
|
+
aa profile add pessoal
|
|
64
|
+
aa profile add trabalho
|
|
65
|
+
|
|
66
|
+
# 3. Adiciona aliases no ~/.bashrc
|
|
67
|
+
echo "alias aa-p='aa --profile pessoal'" >> ~/.bashrc
|
|
68
|
+
echo "alias aa-z='aa --profile trabalho'" >> ~/.bashrc
|
|
69
|
+
source ~/.bashrc
|
|
29
70
|
```
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
71
|
+
|
|
72
|
+
## Integração com tmux
|
|
73
|
+
|
|
74
|
+
Copie o `claude-picker.sh` para `~/projetos/arsenal/scripts/` e adicione ao `~/.tmux.conf`:
|
|
75
|
+
|
|
76
|
+
```tmux
|
|
77
|
+
bind-key S run-shell "bash ~/projetos/arsenal/scripts/claude-picker.sh"
|
|
34
78
|
```
|
|
35
79
|
|
|
36
|
-
|
|
80
|
+
`Ctrl+B S` abre o picker de sessões. `Ctrl+N` cria nova sessão e pergunta qual conta usar.
|
package/dist/index.js
CHANGED
|
@@ -3,13 +3,20 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import { loadProfile, applyProfile, addProfile, listProfiles, useProfile } from "./profiles.js";
|
|
6
|
+
import { setup } from "./setup.js";
|
|
6
7
|
const program = new Command();
|
|
7
8
|
program
|
|
8
9
|
.name("arsenal-agent")
|
|
9
10
|
.description("Launcher do Claude Code com perfil de conta configurado")
|
|
10
11
|
.version("0.1.3")
|
|
11
12
|
.option("-p, --profile <name>", "perfil a usar")
|
|
12
|
-
.argument("[args...]", "argumentos passados direto ao claude")
|
|
13
|
+
.argument("[args...]", "argumentos passados direto ao claude")
|
|
14
|
+
.allowUnknownOption(true)
|
|
15
|
+
.passThroughOptions(true);
|
|
16
|
+
program
|
|
17
|
+
.command("setup")
|
|
18
|
+
.description("instala e configura tudo: brain, opencode, plugins, MCPs, aliases")
|
|
19
|
+
.action(async () => { await setup(); process.exit(0); });
|
|
13
20
|
const profileCmd = program.command("profile").description("gerenciar perfis");
|
|
14
21
|
profileCmd.command("add [name]").description("adicionar perfil").action(async (name) => { await addProfile(name); process.exit(0); });
|
|
15
22
|
profileCmd.command("list").alias("ls").description("listar perfis").action(() => { listProfiles(); process.exit(0); });
|
package/dist/setup.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
const HOME = homedir();
|
|
7
|
+
const CLAUDE_DIR = join(HOME, ".claude");
|
|
8
|
+
const BRAIN_DIR = join(HOME, ".claude", "brain");
|
|
9
|
+
const VENV = join(HOME, "projetos", "arsenal", "venv");
|
|
10
|
+
const ARSENAL_DIR = join(HOME, "projetos", "arsenal");
|
|
11
|
+
function step(msg) { console.log(chalk.cyan("→ ") + msg); }
|
|
12
|
+
function ok(msg) { console.log(chalk.green("✓ ") + msg); }
|
|
13
|
+
function warn(msg) { console.log(chalk.yellow("⚠ ") + msg); }
|
|
14
|
+
function run(cmd, opts = {}) {
|
|
15
|
+
return spawnSync(cmd, { shell: true, stdio: "inherit", ...opts });
|
|
16
|
+
}
|
|
17
|
+
// ── 1. claude-brain ──────────────────────────────────────────────────────────
|
|
18
|
+
function setupBrain() {
|
|
19
|
+
step("Clonando claude-brain...");
|
|
20
|
+
if (existsSync(BRAIN_DIR)) {
|
|
21
|
+
run(`git -C ${BRAIN_DIR} pull --recurse-submodules`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
run(`git clone --recurse-submodules https://github.com/dbezerra95/claude-brain.git ${BRAIN_DIR}`);
|
|
25
|
+
}
|
|
26
|
+
step("Instalando dependências do arsenal-mcp...");
|
|
27
|
+
const req = join(BRAIN_DIR, "arsenal-mcp", "requirements.txt");
|
|
28
|
+
if (existsSync(req)) {
|
|
29
|
+
run(`pip3 install -r ${req} -q`);
|
|
30
|
+
}
|
|
31
|
+
step("Copiando agentes, commands e memórias...");
|
|
32
|
+
run(`cp ${BRAIN_DIR}/agents/*.md ${CLAUDE_DIR}/agents/ 2>/dev/null || true`);
|
|
33
|
+
run(`cp ${BRAIN_DIR}/agents/*.py ${ARSENAL_DIR}/agents/ 2>/dev/null || true`);
|
|
34
|
+
step("Copiando CLAUDE.md e settings.json...");
|
|
35
|
+
run(`cp ${BRAIN_DIR}/CLAUDE.md ${CLAUDE_DIR}/CLAUDE.md`);
|
|
36
|
+
run(`cp ${BRAIN_DIR}/settings.json ${CLAUDE_DIR}/settings.json`);
|
|
37
|
+
ok("claude-brain sincronizado");
|
|
38
|
+
}
|
|
39
|
+
// ── 2. opencode ──────────────────────────────────────────────────────────────
|
|
40
|
+
function setupOpencode() {
|
|
41
|
+
step("Verificando opencode...");
|
|
42
|
+
const result = spawnSync("which", ["opencode"], { encoding: "utf-8" });
|
|
43
|
+
if (result.status !== 0) {
|
|
44
|
+
step("Instalando opencode...");
|
|
45
|
+
run("npm install -g opencode-ai");
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
ok("opencode já instalado");
|
|
49
|
+
}
|
|
50
|
+
step("Configurando ~/.opencode.json...");
|
|
51
|
+
const config = {
|
|
52
|
+
$schema: "https://opencode.ai/config.json",
|
|
53
|
+
model: "anthropic/claude-sonnet-4-6",
|
|
54
|
+
providers: {
|
|
55
|
+
anthropic: { name: "Anthropic", apiKey: { env: "ANTHROPIC_API_KEY" } },
|
|
56
|
+
openrouter: { name: "OpenRouter", apiKey: { env: "OPENROUTER_API_KEY" } },
|
|
57
|
+
},
|
|
58
|
+
plugin: [
|
|
59
|
+
"@tarquinen/opencode-dcp",
|
|
60
|
+
"oh-my-opencode",
|
|
61
|
+
"opencode-vibeguard",
|
|
62
|
+
"opencode-antigravity-auth",
|
|
63
|
+
"opencode-background-agents",
|
|
64
|
+
"opencode-supermemory",
|
|
65
|
+
],
|
|
66
|
+
mcp: {
|
|
67
|
+
arsenal: {
|
|
68
|
+
type: "local",
|
|
69
|
+
command: join(HOME, "venv", "bin", "python3"),
|
|
70
|
+
args: [join(BRAIN_DIR, "arsenal-mcp", "server.py")],
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
models: {
|
|
74
|
+
"openrouter/nvidia/nemotron-3-super-120b-a12b:free": { name: "Nemotron 120B (Free)", temperature: 0.3 },
|
|
75
|
+
"openrouter/nvidia/nemotron-nano-12b-v2-vl:free": { name: "Nemotron Nano (Free)", temperature: 0.3 },
|
|
76
|
+
"anthropic/claude-haiku-4-5": { name: "Haiku 4.5 (Econômico)", temperature: 0.3 },
|
|
77
|
+
"anthropic/claude-sonnet-4-6": { name: "Sonnet 4.6 (Principal)", temperature: 0.5 },
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
writeFileSync(join(HOME, ".opencode.json"), JSON.stringify(config, null, 2));
|
|
81
|
+
step("Instalando plugins do opencode...");
|
|
82
|
+
const plugins = ["@tarquinen/opencode-dcp", "oh-my-opencode", "opencode-vibeguard",
|
|
83
|
+
"opencode-antigravity-auth", "opencode-background-agents", "opencode-supermemory"];
|
|
84
|
+
for (const p of plugins) {
|
|
85
|
+
const r = spawnSync("opencode", ["plugin", p], { stdio: "pipe", cwd: HOME });
|
|
86
|
+
if (r.status === 0)
|
|
87
|
+
ok(`plugin: ${p}`);
|
|
88
|
+
else
|
|
89
|
+
warn(`plugin falhou (pode já estar instalado): ${p}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// ── 3. venv python ───────────────────────────────────────────────────────────
|
|
93
|
+
function setupVenv() {
|
|
94
|
+
step("Verificando venv Python...");
|
|
95
|
+
if (!existsSync(VENV)) {
|
|
96
|
+
mkdirSync(join(HOME, "projetos", "arsenal"), { recursive: true });
|
|
97
|
+
run(`python3 -m venv ${VENV}`);
|
|
98
|
+
}
|
|
99
|
+
run(`${VENV}/bin/pip install mcp openrouter-py 2>/dev/null || ${VENV}/bin/pip install mcp -q`);
|
|
100
|
+
ok("venv ok");
|
|
101
|
+
}
|
|
102
|
+
// ── 4. aliases ───────────────────────────────────────────────────────────────
|
|
103
|
+
function setupAliases() {
|
|
104
|
+
step("Adicionando aliases ao ~/.bashrc...");
|
|
105
|
+
const bashrc = join(HOME, ".bashrc");
|
|
106
|
+
const content = existsSync(bashrc) ? readFileSync(bashrc, "utf-8") : "";
|
|
107
|
+
const aliases = `
|
|
108
|
+
# arsenal-agent — perfis rápidos
|
|
109
|
+
alias aa-p='aa --profile pessoal'
|
|
110
|
+
alias aa-z='aa --profile trabalho'
|
|
111
|
+
`;
|
|
112
|
+
if (!content.includes("arsenal-agent — perfis rápidos")) {
|
|
113
|
+
writeFileSync(bashrc, content + aliases);
|
|
114
|
+
ok("aliases adicionados (reabra o terminal ou rode: source ~/.bashrc)");
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
ok("aliases já existem");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// ── Entry point ───────────────────────────────────────────────────────────────
|
|
121
|
+
export async function setup() {
|
|
122
|
+
console.log(chalk.bold("\narsenal-cli setup\n"));
|
|
123
|
+
setupVenv();
|
|
124
|
+
setupBrain();
|
|
125
|
+
setupOpencode();
|
|
126
|
+
setupAliases();
|
|
127
|
+
console.log(chalk.bold.green("\n✓ Setup completo!\n"));
|
|
128
|
+
console.log("Próximos passos:");
|
|
129
|
+
console.log(" 1. " + chalk.white("aa profile add pessoal") + chalk.dim(" → autenticar conta pessoal"));
|
|
130
|
+
console.log(" 2. " + chalk.white("aa profile add trabalho") + chalk.dim(" → autenticar conta trabalho"));
|
|
131
|
+
console.log(" 3. " + chalk.white("source ~/.bashrc") + chalk.dim(" → ativar aliases"));
|
|
132
|
+
console.log(" 4. " + chalk.white("aa-p") + chalk.dim(" → abrir Claude\n"));
|
|
133
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arsenal-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Task router: classifies and launches claude or opencode automatically",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "DBC Tech",
|
|
@@ -26,7 +26,14 @@
|
|
|
26
26
|
"engines": {
|
|
27
27
|
"node": ">=20"
|
|
28
28
|
},
|
|
29
|
-
"keywords": [
|
|
29
|
+
"keywords": [
|
|
30
|
+
"ai",
|
|
31
|
+
"claude",
|
|
32
|
+
"opencode",
|
|
33
|
+
"nemotron",
|
|
34
|
+
"cli",
|
|
35
|
+
"router"
|
|
36
|
+
],
|
|
30
37
|
"repository": {
|
|
31
38
|
"type": "git",
|
|
32
39
|
"url": "git+https://github.com/dbezerra95/arsenal-cli.git"
|
package/src/index.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { createInterface } from "node:readline"
|
|
|
4
4
|
import { Command } from "commander"
|
|
5
5
|
import chalk from "chalk"
|
|
6
6
|
import { loadProfile, applyProfile, addProfile, listProfiles, useProfile } from "./profiles.js"
|
|
7
|
+
import { setup } from "./setup.js"
|
|
7
8
|
|
|
8
9
|
const program = new Command()
|
|
9
10
|
|
|
@@ -13,6 +14,13 @@ program
|
|
|
13
14
|
.version("0.1.3")
|
|
14
15
|
.option("-p, --profile <name>", "perfil a usar")
|
|
15
16
|
.argument("[args...]", "argumentos passados direto ao claude")
|
|
17
|
+
.allowUnknownOption(true)
|
|
18
|
+
.passThroughOptions(true)
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.command("setup")
|
|
22
|
+
.description("instala e configura tudo: brain, opencode, plugins, MCPs, aliases")
|
|
23
|
+
.action(async () => { await setup(); process.exit(0) })
|
|
16
24
|
|
|
17
25
|
const profileCmd = program.command("profile").description("gerenciar perfis")
|
|
18
26
|
profileCmd.command("add [name]").description("adicionar perfil").action(async (name) => { await addProfile(name); process.exit(0) })
|
package/src/setup.ts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { execSync, spawnSync } from "node:child_process"
|
|
2
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, chmodSync } from "node:fs"
|
|
3
|
+
import { homedir } from "node:os"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
import chalk from "chalk"
|
|
6
|
+
|
|
7
|
+
const HOME = homedir()
|
|
8
|
+
const CLAUDE_DIR = join(HOME, ".claude")
|
|
9
|
+
const BRAIN_DIR = join(HOME, ".claude", "brain")
|
|
10
|
+
const VENV = join(HOME, "projetos", "arsenal", "venv")
|
|
11
|
+
const ARSENAL_DIR = join(HOME, "projetos", "arsenal")
|
|
12
|
+
|
|
13
|
+
function step(msg: string) { console.log(chalk.cyan("→ ") + msg) }
|
|
14
|
+
function ok(msg: string) { console.log(chalk.green("✓ ") + msg) }
|
|
15
|
+
function warn(msg: string) { console.log(chalk.yellow("⚠ ") + msg) }
|
|
16
|
+
function run(cmd: string, opts: object = {}) {
|
|
17
|
+
return spawnSync(cmd, { shell: true, stdio: "inherit", ...opts })
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ── 1. claude-brain ──────────────────────────────────────────────────────────
|
|
21
|
+
function setupBrain() {
|
|
22
|
+
step("Clonando claude-brain...")
|
|
23
|
+
if (existsSync(BRAIN_DIR)) {
|
|
24
|
+
run(`git -C ${BRAIN_DIR} pull --recurse-submodules`)
|
|
25
|
+
} else {
|
|
26
|
+
run(`git clone --recurse-submodules https://github.com/dbezerra95/claude-brain.git ${BRAIN_DIR}`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
step("Instalando dependências do arsenal-mcp...")
|
|
30
|
+
const req = join(BRAIN_DIR, "arsenal-mcp", "requirements.txt")
|
|
31
|
+
if (existsSync(req)) {
|
|
32
|
+
run(`pip3 install -r ${req} -q`)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
step("Copiando agentes, commands e memórias...")
|
|
36
|
+
run(`cp ${BRAIN_DIR}/agents/*.md ${CLAUDE_DIR}/agents/ 2>/dev/null || true`)
|
|
37
|
+
run(`cp ${BRAIN_DIR}/agents/*.py ${ARSENAL_DIR}/agents/ 2>/dev/null || true`)
|
|
38
|
+
|
|
39
|
+
step("Copiando CLAUDE.md e settings.json...")
|
|
40
|
+
run(`cp ${BRAIN_DIR}/CLAUDE.md ${CLAUDE_DIR}/CLAUDE.md`)
|
|
41
|
+
run(`cp ${BRAIN_DIR}/settings.json ${CLAUDE_DIR}/settings.json`)
|
|
42
|
+
ok("claude-brain sincronizado")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ── 2. opencode ──────────────────────────────────────────────────────────────
|
|
46
|
+
function setupOpencode() {
|
|
47
|
+
step("Verificando opencode...")
|
|
48
|
+
const result = spawnSync("which", ["opencode"], { encoding: "utf-8" })
|
|
49
|
+
if (result.status !== 0) {
|
|
50
|
+
step("Instalando opencode...")
|
|
51
|
+
run("npm install -g opencode-ai")
|
|
52
|
+
} else {
|
|
53
|
+
ok("opencode já instalado")
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
step("Configurando ~/.opencode.json...")
|
|
57
|
+
const config = {
|
|
58
|
+
$schema: "https://opencode.ai/config.json",
|
|
59
|
+
model: "anthropic/claude-sonnet-4-6",
|
|
60
|
+
providers: {
|
|
61
|
+
anthropic: { name: "Anthropic", apiKey: { env: "ANTHROPIC_API_KEY" } },
|
|
62
|
+
openrouter: { name: "OpenRouter", apiKey: { env: "OPENROUTER_API_KEY" } },
|
|
63
|
+
},
|
|
64
|
+
plugin: [
|
|
65
|
+
"@tarquinen/opencode-dcp",
|
|
66
|
+
"oh-my-opencode",
|
|
67
|
+
"opencode-vibeguard",
|
|
68
|
+
"opencode-antigravity-auth",
|
|
69
|
+
"opencode-background-agents",
|
|
70
|
+
"opencode-supermemory",
|
|
71
|
+
],
|
|
72
|
+
mcp: {
|
|
73
|
+
arsenal: {
|
|
74
|
+
type: "local",
|
|
75
|
+
command: join(HOME, "venv", "bin", "python3"),
|
|
76
|
+
args: [join(BRAIN_DIR, "arsenal-mcp", "server.py")],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
models: {
|
|
80
|
+
"openrouter/nvidia/nemotron-3-super-120b-a12b:free": { name: "Nemotron 120B (Free)", temperature: 0.3 },
|
|
81
|
+
"openrouter/nvidia/nemotron-nano-12b-v2-vl:free": { name: "Nemotron Nano (Free)", temperature: 0.3 },
|
|
82
|
+
"anthropic/claude-haiku-4-5": { name: "Haiku 4.5 (Econômico)", temperature: 0.3 },
|
|
83
|
+
"anthropic/claude-sonnet-4-6": { name: "Sonnet 4.6 (Principal)", temperature: 0.5 },
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
writeFileSync(join(HOME, ".opencode.json"), JSON.stringify(config, null, 2))
|
|
87
|
+
|
|
88
|
+
step("Instalando plugins do opencode...")
|
|
89
|
+
const plugins = ["@tarquinen/opencode-dcp", "oh-my-opencode", "opencode-vibeguard",
|
|
90
|
+
"opencode-antigravity-auth", "opencode-background-agents", "opencode-supermemory"]
|
|
91
|
+
for (const p of plugins) {
|
|
92
|
+
const r = spawnSync("opencode", ["plugin", p], { stdio: "pipe", cwd: HOME })
|
|
93
|
+
if (r.status === 0) ok(`plugin: ${p}`)
|
|
94
|
+
else warn(`plugin falhou (pode já estar instalado): ${p}`)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ── 3. venv python ───────────────────────────────────────────────────────────
|
|
99
|
+
function setupVenv() {
|
|
100
|
+
step("Verificando venv Python...")
|
|
101
|
+
if (!existsSync(VENV)) {
|
|
102
|
+
mkdirSync(join(HOME, "projetos", "arsenal"), { recursive: true })
|
|
103
|
+
run(`python3 -m venv ${VENV}`)
|
|
104
|
+
}
|
|
105
|
+
run(`${VENV}/bin/pip install mcp openrouter-py 2>/dev/null || ${VENV}/bin/pip install mcp -q`)
|
|
106
|
+
ok("venv ok")
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ── 4. aliases ───────────────────────────────────────────────────────────────
|
|
110
|
+
function setupAliases() {
|
|
111
|
+
step("Adicionando aliases ao ~/.bashrc...")
|
|
112
|
+
const bashrc = join(HOME, ".bashrc")
|
|
113
|
+
const content = existsSync(bashrc) ? readFileSync(bashrc, "utf-8") : ""
|
|
114
|
+
const aliases = `
|
|
115
|
+
# arsenal-agent — perfis rápidos
|
|
116
|
+
alias aa-p='aa --profile pessoal'
|
|
117
|
+
alias aa-z='aa --profile trabalho'
|
|
118
|
+
`
|
|
119
|
+
if (!content.includes("arsenal-agent — perfis rápidos")) {
|
|
120
|
+
writeFileSync(bashrc, content + aliases)
|
|
121
|
+
ok("aliases adicionados (reabra o terminal ou rode: source ~/.bashrc)")
|
|
122
|
+
} else {
|
|
123
|
+
ok("aliases já existem")
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── Entry point ───────────────────────────────────────────────────────────────
|
|
128
|
+
export async function setup() {
|
|
129
|
+
console.log(chalk.bold("\narsenal-cli setup\n"))
|
|
130
|
+
|
|
131
|
+
setupVenv()
|
|
132
|
+
setupBrain()
|
|
133
|
+
setupOpencode()
|
|
134
|
+
setupAliases()
|
|
135
|
+
|
|
136
|
+
console.log(chalk.bold.green("\n✓ Setup completo!\n"))
|
|
137
|
+
console.log("Próximos passos:")
|
|
138
|
+
console.log(" 1. " + chalk.white("aa profile add pessoal") + chalk.dim(" → autenticar conta pessoal"))
|
|
139
|
+
console.log(" 2. " + chalk.white("aa profile add trabalho") + chalk.dim(" → autenticar conta trabalho"))
|
|
140
|
+
console.log(" 3. " + chalk.white("source ~/.bashrc") + chalk.dim(" → ativar aliases"))
|
|
141
|
+
console.log(" 4. " + chalk.white("aa-p") + chalk.dim(" → abrir Claude\n"))
|
|
142
|
+
}
|