noiton 1.0.0 → 2.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/README.md CHANGED
@@ -1,6 +1,8 @@
1
- # NOITON
1
+ # 🐙 NOITON
2
2
 
3
- OpenCode authenticator - configure providers with a single key.
3
+ **Universal AI Agent Connector** one key, many agents.
4
+
5
+ NOITON is a CLI wizard that connects your API key to multiple AI coding agents at once. Run it once, pick your agents, paste your key — done.
4
6
 
5
7
  ## Quick Start
6
8
 
@@ -8,51 +10,64 @@ OpenCode authenticator - configure providers with a single key.
8
10
  npx noiton
9
11
  ```
10
12
 
11
- The wizard will:
12
- 1. Ask for your NOITON API key
13
- 2. Validate it against the API
14
- 3. Fetch available models
15
- 4. Write the `noiton` provider to your `opencode.json`
13
+ The octopus will:
14
+ 1. 🔍 Detect which AI agents are installed (OpenCode, Hermes, OpenClaw, Cline, Aider, Kilo Code, Kimi Code)
15
+ 2. 📦 If none found, offer to install OpenCode or Hermes based on your use case
16
+ 3. 🔄 If OpenCode is outdated, offer to update it
17
+ 4. 🔑 Ask for your NOITON API key (one key serves all agents)
18
+ 5. 📋 Fetch available models from the API
19
+ 6. ⚙️ Configure each selected agent in its native format
20
+ 7. 🎉 Show a funny success message (with easter eggs on holidays!)
21
+
22
+ ## Supported Agents
23
+
24
+ | Agent | Type | Config Format | Multiple Models |
25
+ |-------|------|---------------|-----------------|
26
+ | **OpenCode** | CLI | JSON (`opencode.json`) | ✅ |
27
+ | **Hermes** | CLI | YAML (`config.yaml` + `.env`) | Single default |
28
+ | **OpenClaw** | CLI | JSON5 (`openclaw.json`) | ✅ |
29
+ | **Cline** | VS Code ext | JSON (VS Code settings) | Single |
30
+ | **Aider** | CLI | YAML (`.aider.conf.yml` + `.env`) | Single |
31
+ | **Kilo Code** | CLI + VS Code | JSONC (`kilo.jsonc`) | ✅ |
32
+ | **Kimi Code** | CLI + VS Code | TOML (`config.toml`) | Single |
16
33
 
17
34
  ## Commands
18
35
 
19
36
  ```bash
20
37
  npx noiton # Setup wizard (default)
21
- npx noiton status # Show current configuration
38
+ npx noiton status # Show configuration across all agents
22
39
  npx noiton refresh # Re-fetch models from API
23
- npx noiton change # Change API key
24
- npx noiton remove # Remove NOITON from config
40
+ npx noiton change # Change API key in all agents
41
+ npx noiton remove # Remove NOITON from all agents
25
42
  npx noiton help # Show help
43
+ npx noiton --version # Show version
26
44
  ```
27
45
 
28
46
  ## How It Works
29
47
 
30
- NOITON adds a provider to your `~/.config/opencode/opencode.json`:
31
-
32
- ```json
33
- {
34
- "provider": {
35
- "noiton": {
36
- "npm": "@ai-sdk/openai-compatible",
37
- "name": "NOITON",
38
- "options": {
39
- "baseURL": "https://blogs-litellm-app.rwezkp.easypanel.host",
40
- "apiKey": "sk-your-key-here"
41
- },
42
- "models": {
43
- "model-id": { "name": "Model Name", "limit": { ... } }
44
- }
45
- }
46
- }
47
- }
48
- ```
48
+ NOITON detects your installed agents and writes to each one's native config format:
49
+
50
+ - **OpenCode** → `~/.config/opencode/opencode.json` (provider block)
51
+ - **Hermes** → `~/.hermes/config.yaml` (model block) + `~/.hermes/.env` (API key)
52
+ - **OpenClaw** → `~/.openclaw/openclaw.json` (models.providers block)
53
+ - **Cline** → VS Code `settings.json` (cline.* keys)
54
+ - **Aider** → `~/.aider.conf.yml` (openai-api-base) + `~/.env` (OPENAI_API_KEY)
55
+ - **Kilo Code** → `~/.config/kilo/kilo.jsonc` (provider.openai-compatible block)
56
+ - **Kimi Code** → `~/.kimi-code/config.toml` (providers + models blocks)
57
+
58
+ Existing configurations are preserved — NOITON only adds/removes its own entries.
59
+
60
+ ## Auto-Install
61
+
62
+ If no agents are detected, NOITON asks what you want to do:
49
63
 
50
- After running `npx noiton`, restart OpenCode and the models will be available.
64
+ - 💻 **Programar** installs OpenCode (`curl -fsSL https://opencode.ai/install | bash`)
65
+ - 🤖 **Assistente** → installs Hermes (`pip install nous-hermes`)
51
66
 
52
67
  ## Requirements
53
68
 
54
69
  - Node.js >= 18
55
- - OpenCode CLI installed
70
+ - At least one supported agent (or let NOITON install one for you)
56
71
 
57
72
  ## License
58
73
 
package/package.json CHANGED
@@ -1,17 +1,26 @@
1
1
  {
2
2
  "name": "noiton",
3
- "version": "1.0.0",
4
- "description": "OpenCode authenticator - configure providers with a single key",
3
+ "version": "2.0.0",
4
+ "description": "🐙 Universal AI Agent Connector - one key, many agents (OpenCode, Hermes, OpenClaw, Cline, Aider, Kilo, Kimi)",
5
5
  "keywords": [
6
+ "noiton",
6
7
  "opencode",
7
- "auth",
8
+ "hermes",
9
+ "openclaw",
10
+ "cline",
11
+ "aider",
12
+ "kilo",
13
+ "kimi",
14
+ "ai",
8
15
  "cli",
9
- "noiton",
10
- "provider",
11
- "ai"
16
+ "agent",
17
+ "wizard",
18
+ "setup",
19
+ "auth"
12
20
  ],
13
21
  "license": "MIT",
14
22
  "author": "NOITON",
23
+ "homepage": "https://github.com/tokensilimitados/noiton",
15
24
  "type": "module",
16
25
  "bin": {
17
26
  "noiton": "./bin/noiton.js"
@@ -27,11 +36,19 @@
27
36
  "node": ">=18"
28
37
  },
29
38
  "dependencies": {
30
- "@inquirer/prompts": "^7.0.0"
39
+ "@inquirer/prompts": "^7.0.0",
40
+ "@iarna/toml": "^2.2.5",
41
+ "boxen": "^8.0.0",
42
+ "gradient-string": "^3.0.0",
43
+ "json5": "^2.2.3",
44
+ "ora": "^8.0.0",
45
+ "picocolors": "^1.1.0",
46
+ "yaml": "^2.8.4"
31
47
  },
32
48
  "scripts": {
33
49
  "test": "node --test tests/",
34
- "prepublishOnly": "echo 'no build step - pure JS'"
50
+ "prepublishOnly": "echo 'no build step - pure JS'",
51
+ "view-octopus": "node -e \"import('./src/octopus.js').then(m => console.log(m.octopusHappy()))\""
35
52
  },
36
53
  "publishConfig": {
37
54
  "access": "public",
@@ -1,77 +1,67 @@
1
+ /**
2
+ * NOITON change command - swap the API key across all configured agents
3
+ */
4
+
1
5
  import { input, confirm } from "@inquirer/prompts";
2
- import { BANNER, color } from "../constants.js";
6
+ import { c } from "../constants.js";
7
+ import { printHeader, spinner, ok, fail, info, box, dim } from "../ui.js";
8
+ import { getConfiguredAgents } from "../lib/agents/index.js";
3
9
  import { validateKey, maskKey } from "../lib/api.js";
4
- import {
5
- readConfig,
6
- writeConfig,
7
- hasProvider,
8
- getProvider,
9
- upsertProvider,
10
- buildModels,
11
- } from "../lib/config.js";
10
+ import { farewellMessage } from "../messages.js";
12
11
 
13
- /**
14
- * Change API key (re-runs wizard with new key).
15
- */
16
12
  export async function changeCommand() {
17
- console.log(BANNER);
13
+ printHeader("Trocar Chave API");
18
14
 
19
- const { config, path } = readConfig();
15
+ const configured = getConfiguredAgents();
20
16
 
21
- if (!hasProvider(config)) {
22
- console.log(color("NOITON não está configurado.\n", "red"));
23
- console.log(color(" Rode ", "dim") + color("npx noiton", "cyan") + color(" para configurar.\n", "dim"));
24
- process.exit(1);
17
+ if (configured.length === 0) {
18
+ box("NOITON não está configurado em nenhum agente.\n\nRode 'npx noiton' para configurar.", "warning");
19
+ return;
25
20
  }
26
21
 
27
- const provider = getProvider(config);
28
- const oldKey = provider?.options?.apiKey || "";
29
-
30
- console.log(color(" Chave atual: ", "dim") + maskKey(oldKey) + "\n");
22
+ info(`Agentes configurados: ${configured.map(a => a.name).join(", ")}`);
23
+ console.log();
31
24
 
32
25
  const newKey = await input({
33
- message: " Nova chave:",
26
+ message: " Nova chave NOITON:",
34
27
  validate: (v) => (v.trim().length >= 10 ? true : "Chave muito curta"),
35
28
  transformer: (v) => maskKey(v),
36
29
  });
37
30
 
38
- const trimmedKey = newKey.trim();
31
+ const trimmed = newKey.trim();
39
32
 
40
- console.log(color("\n ⏳ Validando nova chave...", "dim"));
41
-
42
- const { valid, models, error } = await validateKey(trimmedKey);
33
+ // Validate the new key
34
+ const s = spinner("Validando nova chave...");
35
+ const { valid, error } = await validateKey(trimmed);
43
36
 
44
37
  if (!valid) {
45
- console.log(color(`\n ❌ Erro: ${error}\n`, "red"));
46
- process.exit(1);
47
- }
48
-
49
- console.log(color(" ✓ Chave válida!\n", "green"));
50
-
51
- if (!models || models.length === 0) {
52
- console.log(color(" ⚠ Nenhum modelo disponível para esta chave.\n", "yellow"));
53
- process.exit(1);
54
- }
55
-
56
- console.log(color(` 📦 ${models.length} modelo(s) encontrado(s):\n`, "cyan"));
57
- for (const m of models) {
58
- console.log(` • ${m.id}`);
38
+ s.fail(`Chave inválida: ${error}`);
39
+ return;
59
40
  }
60
41
 
61
- const proceed = await confirm({
62
- message: "\n Atualizar configuração com a nova chave?",
63
- default: true,
64
- });
65
-
66
- if (!proceed) {
67
- console.log(color("\n ✋ Operação cancelada.\n", "dim"));
68
- return;
42
+ s.succeed("Nova chave válida!");
43
+ console.log();
44
+
45
+ let success = 0;
46
+ for (const agent of configured) {
47
+ const sub = spinner(`Atualizando ${agent.name}...`);
48
+ try {
49
+ agent.changeKey(trimmed);
50
+ sub.succeed(`${agent.name} atualizado!`);
51
+ success++;
52
+ } catch (err) {
53
+ sub.fail(`${agent.name}: ${err.message}`);
54
+ }
69
55
  }
70
56
 
71
- const modelsObj = buildModels(models);
72
- const newConfig = upsertProvider(config, trimmedKey, modelsObj);
73
- writeConfig(newConfig, path);
57
+ console.log();
58
+ box(
59
+ success > 0
60
+ ? `✨ Chave trocada em ${success} agente(s)!`
61
+ : "⚠️ Nenhum agente pôde ser atualizado.",
62
+ success > 0 ? "success" : "warning"
63
+ );
74
64
 
75
- console.log(color("\n ✓ Chave atualizada!\n", "green"));
76
- console.log(color(" 🚀 Reinicie o OpenCode para usar a nova chave.\n", "cyan"));
65
+ console.log();
66
+ console.log(c.dim(` ${farewellMessage()}`));
77
67
  }
@@ -1,85 +1,308 @@
1
- import { input, confirm } from "@inquirer/prompts";
2
- import { BANNER, color } from "../constants.js";
1
+ /**
2
+ * NOITON init command - the universal multi-agent wizard
3
+ *
4
+ * Flow:
5
+ * 1. Print banner with octopus mascot
6
+ * 2. Detect all installed agents
7
+ * 3. If OpenCode installed, check version & offer update
8
+ * 4. If no agents, offer to install OpenCode or Hermes (based on use case)
9
+ * 5. Show checklist of detected agents
10
+ * 6. Ask which agents to configure
11
+ * 7. Ask for NOITON API key (once)
12
+ * 8. Validate key + fetch models
13
+ * 9. Ask which model to use as primary (for single-model agents)
14
+ * 10. Configure each selected agent
15
+ * 11. Show funny success message + easter egg
16
+ */
17
+
18
+ import { input, confirm, select, checkbox } from "@inquirer/prompts";
19
+ import { c } from "../constants.js";
20
+ import {
21
+ printBanner,
22
+ box,
23
+ spinner,
24
+ ok,
25
+ warn,
26
+ fail,
27
+ info,
28
+ dim,
29
+ step,
30
+ checklist,
31
+ divider,
32
+ withSpinner,
33
+ } from "../ui.js";
34
+ import {
35
+ successMessage,
36
+ noAgentMessage,
37
+ thinkingMessage,
38
+ cancelMessage,
39
+ farewellMessage,
40
+ getEasterEgg,
41
+ } from "../messages.js";
3
42
  import { validateKey, maskKey } from "../lib/api.js";
43
+ import { getDetectedAgents, getAgent } from "../lib/agents/index.js";
4
44
  import {
5
- readConfig,
6
- writeConfig,
7
- hasProvider,
8
- upsertProvider,
9
- buildModels,
10
- } from "../lib/config.js";
45
+ detectAgents,
46
+ getOpenCodeStatus,
47
+ getRecommendedAgent,
48
+ } from "../lib/detect.js";
49
+ import { installAgent, updateAgent, offerInstall } from "../lib/install.js";
11
50
 
12
- /**
13
- * Main wizard command - interactive setup.
14
- */
15
51
  export async function initCommand() {
16
- console.log(BANNER);
52
+ // ============= STEP 1: BANNER =============
53
+ printBanner("happy");
54
+
55
+ // ============= STEP 2: DETECT AGENTS =============
56
+ step(1, "Procurando agentes de IA instalados...");
57
+
58
+ const detected = getDetectedAgents();
59
+ const opencodeStatus = getOpenCodeStatus();
60
+
61
+ if (detected.length === 0) {
62
+ // ============= STEP 2a: NO AGENTS - OFFER INSTALL =============
63
+ console.log();
64
+ box(noAgentMessage(), "warning");
65
+ console.log();
66
+
67
+ const installChoice = await offerInstall();
68
+ if (!installChoice) {
69
+ console.log();
70
+ info(cancelMessage());
71
+ console.log();
72
+ console.log(c.dim(" 💡 Dica: instale OpenCode ou Hermes e rode noiton novamente!"));
73
+ console.log(c.dim(" OpenCode: curl -fsSL https://opencode.ai/install | bash"));
74
+ console.log(c.dim(" Hermes: pip install nous-hermes"));
75
+ console.log();
76
+ return;
77
+ }
78
+
79
+ // Install the chosen agent
80
+ const result = await installAgent(installChoice);
81
+ if (!result.ok) {
82
+ console.log();
83
+ fail(`Não foi possível instalar: ${result.error}`);
84
+ console.log(c.dim(" Tente instalar manualmente e rode noiton novamente."));
85
+ return;
86
+ }
87
+
88
+ // Re-detect after install
89
+ const reDetected = getDetectedAgents();
90
+ if (reDetected.length === 0) {
91
+ console.log();
92
+ warn("Instalação concluída, mas não foi possível detectar o agente.");
93
+ info("Reinicie o terminal e rode 'npx noiton' novamente.");
94
+ return;
95
+ }
96
+ detected.push(...reDetected);
97
+ }
98
+
99
+ // ============= STEP 2b: CHECK OPENCODE VERSION =============
100
+ if (opencodeStatus?.updateAvailable) {
101
+ console.log();
102
+ box(
103
+ `🔄 OpenCode v${opencodeStatus.latest} disponível!\n` +
104
+ ` Você tem v${opencodeStatus.version}.\n\n` +
105
+ ` Atualizar agora?`,
106
+ "warning"
107
+ );
108
+ const wantUpdate = await confirm({
109
+ message: "Atualizar OpenCode?",
110
+ default: true,
111
+ });
112
+ if (wantUpdate) {
113
+ const updateResult = await updateAgent("opencode");
114
+ if (updateResult.ok) {
115
+ ok("OpenCode atualizado!");
116
+ } else {
117
+ warn(`Não foi possível atualizar: ${updateResult.error}`);
118
+ }
119
+ }
120
+ }
121
+
122
+ // ============= STEP 3: SHOW CHECKLIST =============
123
+ step(2, "Agentes detectados");
124
+
125
+ const items = detected.map((a) => ({
126
+ label: a.name,
127
+ ok: true,
128
+ detail: a.version ? `v${a.version}` : "(config encontrado)",
129
+ }));
130
+ checklist(items);
131
+ console.log();
17
132
 
18
- const { config, path } = readConfig();
133
+ // ============= STEP 4: CHOOSE AGENTS TO CONFIGURE =============
134
+ step(3, "Escolha quais agentes configurar");
19
135
 
20
- if (hasProvider(config)) {
21
- console.log(color(" ⚠ NOITON está configurado!\n", "yellow"));
136
+ const agentChoices = detected.map((a) => ({
137
+ name: `${a.name}${a.version ? ` (v${a.version})` : ""}`,
138
+ value: a.id,
139
+ checked: true,
140
+ }));
141
+
142
+ let selectedIds;
143
+ if (detected.length === 1) {
144
+ selectedIds = [detected[0].id];
145
+ info(`Configurando: ${detected[0].name}`);
146
+ } else {
147
+ selectedIds = await checkbox({
148
+ message: "Quais agentes você quer conectar ao NOITON?",
149
+ choices: agentChoices,
150
+ required: true,
151
+ });
152
+ }
153
+
154
+ const selectedAgents = selectedIds
155
+ .map((id) => getAgent(id))
156
+ .filter(Boolean);
157
+
158
+ // ============= STEP 5: CHECK EXISTING CONFIG =============
159
+ const alreadyConfigured = selectedAgents.filter((a) => {
160
+ try {
161
+ return a.isConfigured();
162
+ } catch {
163
+ return false;
164
+ }
165
+ });
166
+
167
+ if (alreadyConfigured.length > 0) {
168
+ console.log();
169
+ warn(
170
+ `NOITON já está configurado em: ${alreadyConfigured.map((a) => a.name).join(", ")}`
171
+ );
22
172
  const overwrite = await confirm({
23
- message: "Deseja sobrescrever a configuração existente?",
173
+ message: "Sobrescrever configuração existente?",
24
174
  default: false,
25
175
  });
26
176
  if (!overwrite) {
27
- console.log(color("\n ✋ Operação cancelada.\n", "dim"));
177
+ console.log();
178
+ info(cancelMessage());
28
179
  return;
29
180
  }
30
181
  }
31
182
 
32
- console.log(color(" Cole sua chave NOITON para começar.\n", "dim"));
183
+ // ============= STEP 6: ASK FOR API KEY =============
184
+ step(4, "Cole sua chave NOITON");
185
+ console.log(c.dim(" Uma chave só serve para todos os agentes! 🐙"));
186
+ console.log();
33
187
 
34
188
  const apiKey = await input({
35
- message: " Chave:",
189
+ message: " Chave NOITON:",
36
190
  validate: (v) => (v.trim().length >= 10 ? true : "Chave muito curta"),
37
191
  transformer: (v) => maskKey(v),
38
192
  });
39
193
 
40
194
  const trimmedKey = apiKey.trim();
41
195
 
42
- console.log(color("\n ⏳ Validando chave...", "dim"));
43
-
196
+ // ============= STEP 7: VALIDATE KEY + FETCH MODELS =============
197
+ console.log();
198
+ const s = spinner(`${thinkingMessage()} Validando chave...`);
44
199
  const { valid, models, error } = await validateKey(trimmedKey);
45
200
 
46
201
  if (!valid) {
47
- console.log(color(`\n ❌ Erro: ${error}\n`, "red"));
48
- process.exit(1);
202
+ s.fail(`Erro: ${error}`);
203
+ console.log();
204
+ fail("Não foi possível validar a chave. Verifique e tente novamente.");
205
+ return;
49
206
  }
50
207
 
51
- console.log(color("Chave válida!\n", "green"));
208
+ s.succeed("Chave válida!");
209
+ console.log();
52
210
 
53
211
  if (!models || models.length === 0) {
54
- console.log(color("Nenhum modelo disponível para esta chave.\n", "yellow"));
55
- process.exit(1);
212
+ warn("Nenhum modelo disponível para esta chave.");
213
+ return;
56
214
  }
57
215
 
58
- console.log(color(` 📦 ${models.length} modelo(s) encontrado(s):\n`, "cyan"));
216
+ // ============= STEP 8: SHOW MODELS =============
217
+ step(5, `Modelos disponíveis (${models.length})`);
218
+
59
219
  for (const m of models) {
60
220
  const ctx = m.context_window
61
- ? color(` (${Math.round(m.context_window / 1000)}K ctx)`, "dim")
221
+ ? c.dim(` (${Math.round(m.context_window / 1000)}K ctx)`)
62
222
  : "";
63
- console.log(` • ${m.id}${ctx}`);
223
+ console.log(` ${c.cyan("")} ${m.id}${ctx}`);
64
224
  }
225
+ console.log();
65
226
 
66
- const proceed = await confirm({
67
- message: "\n Configurar OpenCode com estes modelos?",
68
- default: true,
69
- });
227
+ // ============= STEP 9: CHOOSE PRIMARY MODEL =============
228
+ // For agents that only support one model, ask which to use
229
+ const needsPrimaryModel = selectedAgents.some((a) => !a.supportsMultipleModels);
70
230
 
71
- if (!proceed) {
72
- console.log(color("\n Operação cancelada.\n", "dim"));
73
- return;
231
+ let primaryModel = null;
232
+ if (needsPrimaryModel) {
233
+ step(6, "Escolha o modelo principal");
234
+ console.log(c.dim(" Alguns agentes usam apenas 1 modelo por vez."));
235
+ console.log();
236
+
237
+ const modelChoices = models.map((m) => ({
238
+ name: `${m.id}${m.context_window ? ` (${Math.round(m.context_window / 1000)}K ctx)` : ""}`,
239
+ value: m.id,
240
+ }));
241
+
242
+ primaryModel = await select({
243
+ message: "Qual modelo usar como principal?",
244
+ choices: modelChoices,
245
+ default: models[0]?.id,
246
+ });
247
+ console.log();
74
248
  }
75
249
 
76
- console.log(color("\n ⏳ Configurando OpenCode...", "dim"));
250
+ // ============= STEP 10: CONFIGURE EACH AGENT =============
251
+ step(7, "Configurando agentes...");
252
+
253
+ const results = [];
254
+ for (const agent of selectedAgents) {
255
+ const s = spinner(`Configurando ${agent.name}...`);
256
+ try {
257
+ const result = agent.configure({
258
+ apiKey: trimmedKey,
259
+ models,
260
+ primaryModel,
261
+ });
262
+ s.succeed(`${agent.name} configurado!`);
263
+ results.push({ agent: agent.name, ok: true, ...result });
264
+ } catch (err) {
265
+ s.fail(`Erro em ${agent.name}: ${err.message}`);
266
+ results.push({ agent: agent.name, ok: false, error: err.message });
267
+ }
268
+ }
77
269
 
78
- const modelsObj = buildModels(models);
79
- const newConfig = upsertProvider(config, trimmedKey, modelsObj);
80
- writeConfig(newConfig, path);
270
+ // ============= STEP 11: SUMMARY =============
271
+ console.log();
272
+ divider();
273
+ console.log();
274
+ printBanner("dancing");
275
+
276
+ const successCount = results.filter((r) => r.ok).length;
277
+ if (successCount > 0) {
278
+ box(
279
+ `${successMessage()}\n\n` +
280
+ `${successCount} de ${selectedAgents.length} agente(s) configurado(s):`,
281
+ "success"
282
+ );
283
+ console.log();
284
+ for (const r of results) {
285
+ if (r.ok) {
286
+ ok(`${r.agent} ${c.dim(`→ ${r.path}`)}`);
287
+ } else {
288
+ fail(`${r.agent} ${c.dim(`→ ${r.error}`)}`);
289
+ }
290
+ }
291
+ } else {
292
+ box("Nenhum agente foi configurado. Tente novamente!", "error");
293
+ return;
294
+ }
295
+
296
+ // ============= STEP 12: EASTER EGG + FAREWELL =============
297
+ const egg = getEasterEgg();
298
+ if (egg) {
299
+ console.log();
300
+ console.log(c.yellow(` 🎁 ${egg}`));
301
+ }
81
302
 
82
- console.log(color(" ✓ Provider 'noiton' adicionado!\n", "green"));
83
- console.log(color(` 📁 Arquivo: ${path}\n`, "dim"));
84
- console.log(color(" 🚀 Reinicie o OpenCode e use os modelos!\n", "cyan"));
303
+ console.log();
304
+ console.log(c.dim(" 💡 Reinicie os agentes para aplicar as mudanças."));
305
+ console.log();
306
+ console.log(c.purple(` ${farewellMessage()}`));
307
+ console.log();
85
308
  }