noiton 2.0.0 → 2.0.2

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/lib/detect.js CHANGED
@@ -1,18 +1,15 @@
1
1
  /**
2
2
  * NOITON Agent Detection
3
3
  *
4
- * Detects which AI coding agents are installed on the system by:
5
- * 1. Checking if the CLI binary is on PATH (which)
6
- * 2. Checking if the config file exists
7
- *
8
- * Returns a list of detected agents with metadata.
4
+ * Detects which AI coding agents are installed on the system.
5
+ * Detection requires the CLI binary to be on PATH (not just config files,
6
+ * which can be leftover from uninstalled tools).
9
7
  */
10
8
 
11
9
  import { existsSync } from "node:fs";
12
10
  import { join } from "node:path";
13
11
  import { homedir } from "node:os";
14
12
  import { execSync } from "node:child_process";
15
- import { c } from "../constants.js";
16
13
 
17
14
  const HOME = homedir();
18
15
  const XDG = process.env.XDG_CONFIG_HOME || join(HOME, ".config");
@@ -24,7 +21,7 @@ const XDG = process.env.XDG_CONFIG_HOME || join(HOME, ".config");
24
21
  */
25
22
  function hasBin(bin) {
26
23
  try {
27
- execSync(`which ${bin} 2>/dev/null`, { stdio: "ignore" });
24
+ execSync(`command -v ${bin} 2>/dev/null`, { stdio: "ignore" });
28
25
  return true;
29
26
  } catch {
30
27
  return false;
@@ -44,7 +41,6 @@ function getBinVersion(bin) {
44
41
  encoding: "utf8",
45
42
  timeout: 5000,
46
43
  }).trim();
47
- // Extract first version-like token
48
44
  const match = out.match(/(\d+\.\d+(?:\.\d+)?(?:-\w+)?)/);
49
45
  if (match) return match[1];
50
46
  return out.split("\n")[0];
@@ -76,8 +72,6 @@ function getNpmLatest(pkg) {
76
72
  /**
77
73
  * Compare two semver-like version strings.
78
74
  * Returns: 1 if a > b, -1 if a < b, 0 if equal.
79
- * @param {string} a
80
- * @param {string} b
81
75
  */
82
76
  function compareVersions(a, b) {
83
77
  const pa = a.split(".").map(Number);
@@ -93,10 +87,6 @@ function compareVersions(a, b) {
93
87
 
94
88
  // ============= AGENT DEFINITIONS =============
95
89
 
96
- /**
97
- * Registry of all supported agents.
98
- * Each entry describes how to detect the agent and where its config lives.
99
- */
100
90
  export const AGENT_REGISTRY = [
101
91
  {
102
92
  id: "opencode",
@@ -125,109 +115,42 @@ export const AGENT_REGISTRY = [
125
115
  description: "Assistente que faz tarefas e se integra com apps",
126
116
  useCase: "assistant",
127
117
  },
128
- {
129
- id: "openclaw",
130
- name: "OpenClaw",
131
- type: "CLI",
132
- bin: "openclaw",
133
- npmPkg: null,
134
- installMethod: null,
135
- installCmd: null,
136
- configPath: join(HOME, ".openclaw", "openclaw.json"),
137
- supportsMultipleModels: true,
138
- description: "Agente de IA open-source com providers customizáveis",
139
- useCase: "programming",
140
- },
141
- {
142
- id: "cline",
143
- name: "Cline",
144
- type: "VSCode Extension",
145
- bin: null,
146
- npmPkg: null,
147
- installMethod: null,
148
- installCmd: null,
149
- configPath: join(XDG, "Code", "User", "settings.json"),
150
- supportsMultipleModels: true,
151
- description: "Extensão VS Code para programação com IA",
152
- useCase: "programming",
153
- },
154
- {
155
- id: "aider",
156
- name: "Aider",
157
- type: "CLI",
158
- bin: "aider",
159
- npmPkg: null,
160
- installMethod: "pip",
161
- installCmd: "pip install aider-chat",
162
- configPath: join(HOME, ".aider.conf.yml"),
163
- supportsMultipleModels: false,
164
- description: "Pair programmer de terminal que edita código com você",
165
- useCase: "programming",
166
- },
167
- {
168
- id: "kilo",
169
- name: "Kilo Code",
170
- type: "CLI + VSCode",
171
- bin: "kilo",
172
- npmPkg: "@kilocode/cli",
173
- installMethod: "npm",
174
- installCmd: "npm install -g @kilocode/cli",
175
- configPath: join(XDG, "kilo", "kilo.jsonc"),
176
- supportsMultipleModels: true,
177
- description: "Agente de IA com CLI e extensão VS Code",
178
- useCase: "programming",
179
- },
180
- {
181
- id: "kimi",
182
- name: "Kimi Code",
183
- type: "CLI + VSCode",
184
- bin: "kimi",
185
- npmPkg: null,
186
- installMethod: null,
187
- installCmd: null,
188
- configPath: join(HOME, ".kimi-code", "config.toml"),
189
- supportsMultipleModels: false,
190
- description: "Agente de IA da Moonshot com CLI e VS Code",
191
- useCase: "programming",
192
- },
193
118
  ];
194
119
 
195
120
  /**
196
121
  * Detect all installed agents.
122
+ * An agent is "installed" only if its binary is on PATH.
123
+ * Config files alone are NOT enough (could be leftover from uninstalled tools).
197
124
  * @returns {Array} detected agents with status info
198
125
  */
199
126
  export function detectAgents() {
200
127
  const detected = [];
201
128
  for (const agent of AGENT_REGISTRY) {
202
- const binFound = agent.bin ? hasBin(agent.bin) : false;
203
- const configFound = existsSync(agent.configPath);
204
- const installed = binFound || configFound;
205
-
206
- if (installed) {
207
- const version = binFound ? getBinVersion(agent.bin) : null;
208
- detected.push({
209
- ...agent,
210
- installed: true,
211
- version,
212
- binFound,
213
- configFound,
214
- });
215
- }
129
+ if (!agent.bin) continue;
130
+ const binFound = hasBin(agent.bin);
131
+ if (!binFound) continue;
132
+
133
+ const version = getBinVersion(agent.bin);
134
+ detected.push({
135
+ ...agent,
136
+ installed: true,
137
+ version,
138
+ binFound: true,
139
+ configFound: existsSync(agent.configPath),
140
+ });
216
141
  }
217
142
  return detected;
218
143
  }
219
144
 
220
145
  /**
221
- * Check if a specific agent is installed.
146
+ * Check if a specific agent is installed (binary on PATH).
222
147
  * @param {string} id
223
148
  * @returns {boolean}
224
149
  */
225
150
  export function isAgentInstalled(id) {
226
151
  const agent = AGENT_REGISTRY.find((a) => a.id === id);
227
- if (!agent) return false;
228
- const binFound = agent.bin ? hasBin(agent.bin) : false;
229
- const configFound = existsSync(agent.configPath);
230
- return binFound || configFound;
152
+ if (!agent || !agent.bin) return false;
153
+ return hasBin(agent.bin);
231
154
  }
232
155
 
233
156
  /**
@@ -240,13 +163,10 @@ export function getOpenCodeStatus() {
240
163
  if (!agent) return null;
241
164
 
242
165
  const binFound = hasBin("opencode");
243
- const configFound = existsSync(agent.configPath);
244
- const installed = binFound || configFound;
166
+ if (!binFound) return null;
245
167
 
246
- if (!installed) return null;
247
-
248
- const version = binFound ? getBinVersion("opencode") : null;
249
- const latest = binFound ? getNpmLatest("opencode-ai") : null;
168
+ const version = getBinVersion("opencode");
169
+ const latest = getNpmLatest("opencode-ai");
250
170
  const updateAvailable =
251
171
  version && latest ? compareVersions(latest, version) > 0 : false;
252
172
 
@@ -256,8 +176,29 @@ export function getOpenCodeStatus() {
256
176
  version,
257
177
  latest,
258
178
  updateAvailable,
259
- binFound,
260
- configFound,
179
+ binFound: true,
180
+ configFound: existsSync(agent.configPath),
181
+ };
182
+ }
183
+
184
+ /**
185
+ * Get the Hermes agent status.
186
+ * @returns {object|null}
187
+ */
188
+ export function getHermesStatus() {
189
+ const agent = AGENT_REGISTRY.find((a) => a.id === "hermes");
190
+ if (!agent) return null;
191
+
192
+ const binFound = hasBin("hermes");
193
+ if (!binFound) return null;
194
+
195
+ const version = getBinVersion("hermes");
196
+ return {
197
+ ...agent,
198
+ installed: true,
199
+ version,
200
+ binFound: true,
201
+ configFound: existsSync(agent.configPath),
261
202
  };
262
203
  }
263
204
 
@@ -275,7 +216,6 @@ export function getInstallableAgents() {
275
216
  * @returns {object|null}
276
217
  */
277
218
  export function getRecommendedAgent(useCase) {
278
- // Prefer OpenCode for programming, Hermes for assistant
279
219
  if (useCase === "programming") {
280
220
  return AGENT_REGISTRY.find((a) => a.id === "opencode");
281
221
  }
@@ -80,8 +80,6 @@ export async function updateAgent(agentId) {
80
80
 
81
81
  const updateCmds = {
82
82
  opencode: "curl -fsSL https://opencode.ai/install | bash",
83
- kilo: "npm update -g @kilocode/cli",
84
- aider: "pip install --upgrade aider-chat",
85
83
  hermes: "pip install --upgrade nous-hermes",
86
84
  };
87
85