noiton 2.0.1 → 2.0.3

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/src/octopus.js CHANGED
@@ -1,16 +1,18 @@
1
1
  /**
2
- * NOITON Octopus Mascot - "O Polvo NOITON"
2
+ * NOITON Banner - Clean OpenCode-style design
3
3
  *
4
- * Design philosophy:
5
- * - Smooth organic curves using ╭╮╰╯ ◠ ◡ ⌒ ~ characters
6
- * - Round bulbous head with expressive eyes
7
- * - 8 graceful flowing tentacles (not blocky)
8
- * - Brand gradient: purple magenta → pink → cyan
4
+ * Features:
5
+ * - Big 🐙 emoji prominently displayed
6
+ * - Large "NOITON" wordmark in figlet (ANSI Shadow)
7
+ * - Clean subtitle line
8
+ * - No more ASCII art octopus
9
9
  */
10
10
 
11
+ import figlet from "figlet";
11
12
  import gradient from "gradient-string";
13
+ import { VERSION, c } from "./constants.js";
12
14
 
13
- // NOITON brand gradient
15
+ // NOITON brand gradient (purple → magenta → cyan)
14
16
  const brandGradient = gradient([
15
17
  "#A78BFA", // light purple
16
18
  "#C084FC", // purple
@@ -22,229 +24,77 @@ const brandGradient = gradient([
22
24
  const successGradient = gradient(["#34D399", "#22D3EE", "#A78BFA"]);
23
25
  const errorGradient = gradient(["#F87171", "#EF4444"]);
24
26
 
25
- /**
26
- * Main happy octopus - graceful, friendly, ready to help
27
- * Clean organic curves, expressive eyes, flowing tentacles
28
- */
29
- export function octopusHappy() {
30
- return brandGradient.multiline(`
31
- ╭━━━━━━━━━╮
32
- ╱ ╲
33
- │ ◉ ◉ │
34
- │ ◡ │
35
- ╲___________╱
36
- ╲___╱
37
- ╭┤ ├╮
38
- ╭┘╰┐ ╭━┘╰┐
39
- ╭┘ ╰╮ ╭┘ ╰┐
40
- ╭┘ ◠ ╰━╯ ◠ ╰┐
41
- ╭┘ ╰┐
42
- ╭┘ ♥ ✦ ♥ ╰┐
43
- │ │
44
- ╰╮ ╭╯
45
- ╰╮ ✦ ♥ ╭╯
46
- ╰╮ ╭╯
47
- ╰─✦───♥─✦─╯
48
- `);
27
+ // Cache the figlet rendering for performance
28
+ let cachedFiglet = null;
29
+ function getFiglet() {
30
+ if (!cachedFiglet) {
31
+ try {
32
+ cachedFiglet = figlet.textSync("NOITON", {
33
+ font: "ANSI Shadow",
34
+ width: 80,
35
+ whitespaceBreak: true,
36
+ });
37
+ } catch {
38
+ // Fallback if figlet fails
39
+ cachedFiglet = `N O I T O N`;
40
+ }
41
+ }
42
+ return cachedFiglet;
49
43
  }
50
44
 
51
45
  /**
52
- * Compact octopus for tight spaces
46
+ * Print the big NOITON banner with octopus emoji.
47
+ * OpenCode-style: big emoji + big text + subtitle
53
48
  */
54
- export function octopusMini() {
55
- return brandGradient.multiline(`
56
- ╭━━━━━╮
57
- ╱ ◉ ◉ ╲
58
- │ ◡ │
59
- ╲_______╱
60
- ╭╯ ╰╮
61
- ╭┘ ◠ ◠ ╰┐
62
- ╰╮ ♥ ╭╯
63
- ╰───╯
64
- `);
65
- }
49
+ export function printBanner() {
50
+ console.log();
51
+ // Big octopus emoji on its own line, centered
52
+ console.log(c.cyan(" 🐙"));
53
+ console.log();
66
54
 
67
- /**
68
- * Curious octopus - looking around with question marks
69
- */
70
- export function octopusCurious() {
71
- return brandGradient.multiline(`
72
- ╭━━━━━━━━━╮
73
- ╱ ╲
74
- │ ? ? │
75
- │ ◡ │
76
- ╲___________╱
77
- │ ╲___╱ │
78
- ╭┤ ├╮
79
- ╭┘╰┐ ╭━┘╰┐
80
- ╭┘ ╰╮ ╭┘ ╰┐
81
- ╭┘ ╰━╯ ╰┐
82
- ╭┘ ╰┐
83
- ╭┘ ? ? ? ╰┐
84
- │ │
85
- ╰╮ ╭╯
86
- ╰╮ ╭╯
87
- ╰╮ ? ? ╭╯
88
- ╰───────────╯
89
- `);
90
- }
55
+ // Big NOITON wordmark with gradient
56
+ console.log(brandGradient.multiline(getFiglet()));
91
57
 
92
- /**
93
- * Working octopus - focused, with swirly eyes
94
- */
95
- export function octopusWorking() {
96
- return brandGradient.multiline(`
97
- ╭━━━━━━━━━╮
98
- ╱ ╲
99
- │ @ @ │
100
- │ ▬ │
101
- ╲___________╱
102
- │ ╲___╱ │
103
- ╭┤ ├╮
104
- ╭┘╰┐ ╭━┘╰┐
105
- ╭┘ ╰╮ ╭┘ ╰┐
106
- ╭┘ ◠ ╰━╯ ◠ ╰┐
107
- ╭┘ ⚙ ╰┐
108
- ╭┘ ⚙ ⚙ ⚙ ╰┐
109
- │ ⚙ ⚙ │
110
- ╰╮ ⚙ ⚙ ╭╯
111
- ╰╮ ╭╯
112
- ╰╮──⚙───⚙──⚙─╭╯
113
- ╰───────────╯
114
- `);
58
+ // Subtitle line
59
+ console.log(
60
+ " " +
61
+ c.bold(c.white("🐙 Universal AI Agent Connector")) +
62
+ c.dim(` · v${VERSION}`)
63
+ );
64
+ console.log();
115
65
  }
116
66
 
117
67
  /**
118
- * Dancing/celebrating octopus - happy eyes, hearts and stars
68
+ * Smaller success banner (used at end of init flow)
119
69
  */
120
- export function octopusDancing() {
121
- return successGradient.multiline(`
122
- ╭━━━━━━━━━╮
123
- ╱ ╲
124
- ◠ ◠ │
125
- │ ◡ │
126
- ╲___________╱
127
- ╲___╱
128
- ╭┤ ├╮
129
- ✦ ╭┘╰┐ ╭━┘╰┐ ✦
130
- ╭━━━╭┘ ╰╮ ╭┘ ╰┐━━━╮
131
- ╭━━━╭┘ ◠ ╰━╯ ◠ ╰┐━━━╮
132
- ╭━━━╭┘ ╰┐━━━╮
133
- ╭━━━╭┘ ♥ ★ ♥ ╰┐━━━╮
134
- │ │
135
- ╰╮ ★ ♥ ★ ♥ ╭╯
136
- ╰╮ ╭╯
137
- ★ ╰╮ ♥ ★ ♥ ╭╯ ★
138
- ╰─★──♥───★──♥──★╯
139
- ♪ ♥ ♪
140
- `);
70
+ export function successBanner() {
71
+ console.log();
72
+ console.log(c.green(" 🐙"));
73
+ console.log();
74
+ console.log(successGradient.multiline(getFiglet()));
75
+ console.log(
76
+ " " +
77
+ c.bold(c.green("✨ Tudo conectado! ✨"))
78
+ );
79
+ console.log();
141
80
  }
142
81
 
143
82
  /**
144
- * Sad octopus - drooping tentacles
83
+ * Sad/small banner
145
84
  */
146
- export function octopusSad() {
147
- return errorGradient.multiline(`
148
- ╭━━━━━━━━━╮
149
- ╱ ╲
150
- . .
151
- │ ╲ │
152
- ╲___________╱
153
- │ ╲___╱ │
154
- ╭┤ ├╮
155
- ╭┘╰┐ ╭━┘╰┐
156
- ╭━┘ ╰╮ ╭┘ ╰━╗
157
- ╭┘ ╰━╯ ╰┐
158
- ╭┘ ╰┐
159
- │ │
160
- ╰╮ ╭╯
161
- ╰╮ ╭╯
162
- ╰╮ ╭╯
163
- ╰╮ ╭╯
164
- ╰──────╯
165
- `);
166
- }
167
-
168
- /**
169
- * Sleeping octopus - zzz floating
170
- */
171
- export function octopusSleeping() {
172
- return brandGradient.multiline(`
173
- ╭━━━━━━━━━╮
174
- ╱ ╲
175
- │ - - │
176
- │ ▬ │
177
- ╲___________╱ z
178
- │ ╲___╱ │ zZ
179
- ╭┤ ├╮ zZZ
180
- ╭┘╰┐ ╭━┘╰┐
181
- ╭┘ ╰╮ ╭┘ ╰┐
182
- ╭┘ ╰━╯ ╰┐
183
- ╭┘ z ╰┐
184
- ╭┘ zZ ╰┐
185
- │ zZZ │
186
- ╰╮ ╭╯
187
- ╰╮ ╭╯
188
- ╰──────────────╯
189
- `);
190
- }
191
-
192
- /**
193
- * Coding octopus - typing on keyboard
194
- */
195
- export function octopusCoding() {
196
- return brandGradient.multiline(`
197
- ╭━━━━━━━━━╮
198
- ╱ ╲
199
- │ ◉ ◉ │
200
- │ ◡ │
201
- ╲___________╱
202
- │ ╲___╱ │
203
- ╭┤ ├╮
204
- ╭───╭┘╰┐ ╭━┘╰┐───╮
205
- ╭┘ ╭┘ ╰╮ ╭┘ ╰┐ ╰┐
206
- ╭┘ ╭┘ ◠ ╰━╯ ◠ ╰┐ ╰┐
207
- ╭┘ ╭┘ ╰┐ ╰┐
208
- ╭┘ ╭┘ ╰┐ ╰┐
209
- │ │ [ / / / / ] │ │
210
- ╰╮ ╰╮ ╭╯ ╭╯
211
- ╰╮ ╰╮ ▓▓▓▓▓▓▓ ╭╯ ╭╯
212
- ╰╮ ╰╮ ╭╯ ╭╯
213
- ╰╮╰──────────╯ ╭╯
214
- ╰╮ ▓▓ ╭╯
215
- ╰──────────╯
216
- `);
217
- }
218
-
219
- /**
220
- * NOITON wordmark logo with small octopus on top
221
- */
222
- export function octopusLogo() {
223
- return brandGradient.multiline(`
224
- ╭━━━━━╮
225
- ╱ ◉ ◉ ╲
226
- │ ◡ │
227
- ╲_______╱
228
- ╭╯ ╰╮
229
- ╭┘ ◠ ◠ ╰┐
230
- ╰╮ ♥ ╭╯
231
- ╰───╯
232
-
233
- ███╗ ██╗ ██████╗ ██╗████████╗ ██████╗ ███╗ ██╗
234
- ████╗ ██║██╔═══██╗██║╚══██╔══╝██╔═══██╗████╗ ██║
235
- ██╔██╗ ██║██║ ██║██║ ██║ ██║ ██║██╔██╗ ██║
236
- ██║╚██╗██║██║ ██║██║ ██║ ██║ ██║██║╚██╗██║
237
- ██║ ╚████║╚██████╔╝██║ ██║ ╚██████╔╝██║ ╚████║
238
- ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝
239
- `);
85
+ export function errorBanner() {
86
+ console.log();
87
+ console.log(c.red(" 🐙"));
88
+ console.log();
89
+ console.log(errorGradient.multiline(getFiglet()));
90
+ console.log();
240
91
  }
241
92
 
242
93
  /**
243
- * Get a random octopus pose
94
+ * Get just the big octopus emoji (for inline use)
244
95
  */
245
- export function randomOctopus() {
246
- const poses = [octopusHappy, octopusDancing, octopusCoding];
247
- return poses[Math.floor(Math.random() * poses.length)]();
96
+ export function bigOctopus() {
97
+ return c.cyan("🐙");
248
98
  }
249
99
 
250
100
  export {
package/src/ui.js CHANGED
@@ -9,30 +9,9 @@ import picocolors from "picocolors";
9
9
  import ora from "ora";
10
10
  import boxen from "boxen";
11
11
  import { VERSION, c } from "./constants.js";
12
- import { octopusHappy, octopusCurious, octopusDancing, octopusSad, octopusSleeping, octopusWorking, octopusLogo, brandGradient } from "./octopus.js";
12
+ import { printBanner, successBanner, errorBanner, bigOctopus, brandGradient } from "./octopus.js";
13
13
 
14
- // ============= BANNER =============
15
-
16
- /**
17
- * Print the main NOITON banner with octopus mascot.
18
- * @param {"happy"|"curious"|"dancing"|"sad"|"sleeping"|"working"} mood
19
- */
20
- export function printBanner(mood = "happy") {
21
- const octopus = {
22
- happy: octopusHappy,
23
- curious: octopusCurious,
24
- dancing: octopusDancing,
25
- sad: octopusSad,
26
- sleeping: octopusSleeping,
27
- working: octopusWorking,
28
- }[mood] || octopusHappy;
29
-
30
- console.log();
31
- console.log(octopus());
32
- console.log(brandGradient(` N O I T O N v${VERSION}`));
33
- console.log(c.dim(` 🐙 Universal AI Agent Connector 🐙`));
34
- console.log();
35
- }
14
+ export { printBanner, successBanner, errorBanner, bigOctopus, brandGradient };
36
15
 
37
16
  // ============= BOXES =============
38
17
 
@@ -1,141 +0,0 @@
1
- /**
2
- * Aider Agent Adapter
3
- *
4
- * Config: ~/.aider.conf.yml (YAML) + env vars
5
- * Format:
6
- * openai-api-base: <BASE_URL>
7
- * model: <model-id>
8
- * .env: OPENAI_API_KEY=<key>
9
- */
10
-
11
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
12
- import { join, dirname } from "node:path";
13
- import { homedir } from "node:os";
14
- import YAML from "yaml";
15
- import { BASE_URL } from "../../constants.js";
16
-
17
- const HOME = homedir();
18
- const CONFIG_PATH = join(HOME, ".aider.conf.yml");
19
- const ENV_PATH = join(HOME, ".env");
20
-
21
- function readYaml() {
22
- if (!existsSync(CONFIG_PATH)) return { config: {}, path: CONFIG_PATH };
23
- try {
24
- const raw = readFileSync(CONFIG_PATH, "utf8");
25
- return { config: YAML.parse(raw) || {}, path: CONFIG_PATH };
26
- } catch {
27
- return { config: {}, path: CONFIG_PATH };
28
- }
29
- }
30
-
31
- function writeYaml(config) {
32
- if (!existsSync(dirname(CONFIG_PATH))) {
33
- mkdirSync(dirname(CONFIG_PATH), { recursive: true });
34
- }
35
- writeFileSync(CONFIG_PATH, YAML.stringify(config, { indent: 2 }), "utf8");
36
- }
37
-
38
- function readEnvFile() {
39
- if (!existsSync(ENV_PATH)) return {};
40
- const raw = readFileSync(ENV_PATH, "utf8");
41
- const env = {};
42
- for (const line of raw.split("\n")) {
43
- const m = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
44
- if (m) env[m[1]] = m[2];
45
- }
46
- return env;
47
- }
48
-
49
- function writeEnvFile(env) {
50
- const lines = Object.entries(env).map(([k, v]) => `${k}=${v}`);
51
- writeFileSync(ENV_PATH, lines.join("\n") + "\n", "utf8");
52
- }
53
-
54
- // ============= ADAPTER INTERFACE =============
55
-
56
- export const aiderAgent = {
57
- id: "aider",
58
- name: "Aider",
59
- supportsMultipleModels: false,
60
-
61
- detect() {
62
- return existsSync(CONFIG_PATH);
63
- },
64
-
65
- isConfigured() {
66
- const { config } = readYaml();
67
- return Boolean(config?.["openai-api-base"] === BASE_URL);
68
- },
69
-
70
- getStatus() {
71
- const { config, path } = readYaml();
72
- const env = readEnvFile();
73
- const apiKey = env.OPENAI_API_KEY;
74
- const configured = config?.["openai-api-base"] === BASE_URL;
75
- const models = config?.model ? [config.model] : [];
76
- return {
77
- configured,
78
- path,
79
- models,
80
- apiKey: apiKey ? `${apiKey.slice(0, 6)}...${apiKey.slice(-4)}` : null,
81
- envPath: ENV_PATH,
82
- };
83
- },
84
-
85
- configure({ apiKey, models, primaryModel }) {
86
- const { config } = readYaml();
87
- config["openai-api-base"] = `${BASE_URL}/v1`;
88
- config.model = primaryModel || models[0]?.id || "gpt-4";
89
- config["weak-model"] = primaryModel || models[0]?.id || "gpt-4";
90
- writeYaml(config);
91
-
92
- // Set API key in .env
93
- const env = readEnvFile();
94
- env.OPENAI_API_KEY = apiKey;
95
- writeEnvFile(env);
96
-
97
- return { path: CONFIG_PATH, envPath: ENV_PATH, modelsCount: 1 };
98
- },
99
-
100
- changeKey(apiKey) {
101
- if (!this.isConfigured()) {
102
- throw new Error("NOITON não está configurado no Aider");
103
- }
104
- const env = readEnvFile();
105
- env.OPENAI_API_KEY = apiKey;
106
- writeEnvFile(env);
107
- return { path: ENV_PATH };
108
- },
109
-
110
- refresh(models) {
111
- const { config } = readYaml();
112
- if (!config?.["openai-api-base"]) {
113
- throw new Error("NOITON não está configurado no Aider");
114
- }
115
- if (models.length > 0) {
116
- config.model = models[0].id;
117
- config["weak-model"] = models[0].id;
118
- }
119
- writeYaml(config);
120
- return { path: CONFIG_PATH, modelsCount: 1 };
121
- },
122
-
123
- remove() {
124
- const { config } = readYaml();
125
- let removed = false;
126
- if (config?.["openai-api-base"] === BASE_URL) {
127
- delete config["openai-api-base"];
128
- delete config.model;
129
- delete config["weak-model"];
130
- removed = true;
131
- writeYaml(config);
132
- }
133
- const env = readEnvFile();
134
- if (env.OPENAI_API_KEY) {
135
- delete env.OPENAI_API_KEY;
136
- writeEnvFile(env);
137
- removed = true;
138
- }
139
- return { path: CONFIG_PATH, envPath: ENV_PATH, removed };
140
- },
141
- };
@@ -1,128 +0,0 @@
1
- /**
2
- * Cline Agent Adapter
3
- *
4
- * Config: VS Code settings.json with cline config block.
5
- * Format (under `cline` key):
6
- * "cline.provider": "openai",
7
- * "cline.openAiBaseUrl": "<BASE_URL>",
8
- * "cline.openAiApiKey": "<key>",
9
- * "cline.openAiModelId": "<model>"
10
- *
11
- * VS Code stores multiple providers via cline.provider entries.
12
- */
13
-
14
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
15
- import { join, dirname } from "node:path";
16
- import { homedir } from "node:os";
17
- import JSON5 from "json5";
18
- import { BASE_URL, PROVIDER_ID } from "../../constants.js";
19
-
20
- const HOME = homedir();
21
- const XDG = process.env.XDG_CONFIG_HOME || join(HOME, ".config");
22
- const CONFIG_PATH = join(XDG, "Code", "User", "settings.json");
23
-
24
- function readConfig() {
25
- if (!existsSync(CONFIG_PATH)) return { config: {}, path: CONFIG_PATH };
26
- try {
27
- // VS Code settings support JSON with comments (JSONC)
28
- const raw = readFileSync(CONFIG_PATH, "utf8");
29
- const cleaned = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
30
- return { config: JSON5.parse(cleaned), path: CONFIG_PATH };
31
- } catch {
32
- return { config: {}, path: CONFIG_PATH };
33
- }
34
- }
35
-
36
- function writeConfig(config) {
37
- if (!existsSync(dirname(CONFIG_PATH))) {
38
- mkdirSync(dirname(dirname(CONFIG_PATH)), { recursive: true });
39
- }
40
- writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf8");
41
- }
42
-
43
- // ============= ADAPTER INTERFACE =============
44
-
45
- export const clineAgent = {
46
- id: "cline",
47
- name: "Cline",
48
- supportsMultipleModels: true,
49
-
50
- detect() {
51
- return existsSync(CONFIG_PATH);
52
- },
53
-
54
- isConfigured() {
55
- const { config } = readConfig();
56
- const result = Boolean(
57
- config?.["cline.openAiBaseUrl"] === BASE_URL ||
58
- config?.["cline.apiProvider"] === "openai" && config?.["cline.openAiApiKey"]
59
- );
60
- return result;
61
- },
62
-
63
- getStatus() {
64
- const { config, path } = readConfig();
65
- const baseUrl = config?.["cline.openAiBaseUrl"];
66
- const apiKey = config?.["cline.openAiApiKey"];
67
- const modelId = config?.["cline.openAiModelId"];
68
- const configured = Boolean(baseUrl === BASE_URL || (apiKey && baseUrl));
69
-
70
- const models = modelId ? [modelId] : [];
71
-
72
- return {
73
- configured,
74
- path,
75
- models,
76
- apiKey: apiKey ? `${apiKey.slice(0, 6)}...${apiKey.slice(-4)}` : null,
77
- };
78
- },
79
-
80
- configure({ apiKey, models, primaryModel }) {
81
- const { config } = readConfig();
82
- config["cline.apiProvider"] = "openai";
83
- config["cline.openAiBaseUrl"] = `${BASE_URL}/v1`;
84
- config["cline.openAiApiKey"] = apiKey;
85
- config["cline.openAiModelId"] = primaryModel || models[0]?.id;
86
- // Save list of available models as a JSON array string
87
- config["cline.noitonModels"] = JSON.stringify(models.map((m) => m.id));
88
- writeConfig(config);
89
- return { path: CONFIG_PATH, modelsCount: 1 };
90
- },
91
-
92
- changeKey(apiKey) {
93
- const { config } = readConfig();
94
- if (!config?.["cline.openAiApiKey"]) {
95
- throw new Error("NOITON não está configurado no Cline");
96
- }
97
- config["cline.openAiApiKey"] = apiKey;
98
- writeConfig(config);
99
- return { path: CONFIG_PATH };
100
- },
101
-
102
- refresh(models) {
103
- const { config } = readConfig();
104
- if (!config?.["cline.openAiBaseUrl"]) {
105
- throw new Error("NOITON não está configurado no Cline");
106
- }
107
- config["cline.openAiModelId"] = models[0]?.id || config["cline.openAiModelId"];
108
- config["cline.noitonModels"] = JSON.stringify(models.map((m) => m.id));
109
- writeConfig(config);
110
- return { path: CONFIG_PATH, modelsCount: 1 };
111
- },
112
-
113
- remove() {
114
- const { config } = readConfig();
115
- let removed = false;
116
- if (config?.["cline.openAiBaseUrl"] === BASE_URL ||
117
- config?.["cline.openAiApiKey"]) {
118
- delete config["cline.apiProvider"];
119
- delete config["cline.openAiBaseUrl"];
120
- delete config["cline.openAiApiKey"];
121
- delete config["cline.openAiModelId"];
122
- delete config["cline.noitonModels"];
123
- removed = true;
124
- }
125
- if (removed) writeConfig(config);
126
- return { path: CONFIG_PATH, removed };
127
- },
128
- };