overmind-mcp 2.8.6 → 2.8.8
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/bin/launch.bat +40 -0
- package/bin/launch.js +78 -0
- package/bin/launch.sh +46 -0
- package/bin/restart_mcp.bat +3 -0
- package/bin/start_server.bat +3 -0
- package/bin/test_mcp.bat +4 -0
- package/dist/bin/cli.js +13 -0
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/launch.js +78 -0
- package/dist/bridge/BridgeProxy.d.ts +52 -0
- package/dist/bridge/BridgeProxy.d.ts.map +1 -0
- package/dist/bridge/BridgeProxy.js +265 -0
- package/dist/bridge/BridgeProxy.js.map +1 -0
- package/dist/bridge/OverBridgeService.d.ts +96 -0
- package/dist/bridge/OverBridgeService.d.ts.map +1 -0
- package/dist/bridge/OverBridgeService.js +334 -0
- package/dist/bridge/OverBridgeService.js.map +1 -0
- package/dist/bridge/index.d.ts +11 -0
- package/dist/bridge/index.d.ts.map +1 -0
- package/dist/bridge/index.js +11 -0
- package/dist/bridge/index.js.map +1 -0
- package/dist/bridge/types.d.ts +132 -0
- package/dist/bridge/types.d.ts.map +1 -0
- package/dist/bridge/types.js +19 -0
- package/dist/bridge/types.js.map +1 -0
- package/dist/bridge/utils.d.ts +48 -0
- package/dist/bridge/utils.d.ts.map +1 -0
- package/dist/bridge/utils.js +128 -0
- package/dist/bridge/utils.js.map +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +11 -4
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/envUtils.d.ts +2 -0
- package/dist/lib/envUtils.d.ts.map +1 -1
- package/dist/lib/envUtils.js +13 -2
- package/dist/lib/envUtils.js.map +1 -1
- package/dist/lib/logger.js +1 -1
- package/dist/lib/processRegistry.d.ts.map +1 -1
- package/dist/lib/processRegistry.js +34 -21
- package/dist/lib/processRegistry.js.map +1 -1
- package/dist/memory/PostgresMemoryProvider.d.ts.map +1 -1
- package/dist/memory/PostgresMemoryProvider.js +14 -3
- package/dist/memory/PostgresMemoryProvider.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/dist/services/AgentManager.d.ts.map +1 -1
- package/dist/services/AgentManager.js +473 -97
- package/dist/services/AgentManager.js.map +1 -1
- package/dist/services/GeminiRunner.d.ts +18 -16
- package/dist/services/GeminiRunner.d.ts.map +1 -1
- package/dist/services/GeminiRunner.js +112 -126
- package/dist/services/GeminiRunner.js.map +1 -1
- package/dist/services/NousHermesRunner.d.ts.map +1 -1
- package/dist/services/NousHermesRunner.js +172 -56
- package/dist/services/NousHermesRunner.js.map +1 -1
- package/dist/tools/agent_control.d.ts +1 -1
- package/dist/tools/agent_control.d.ts.map +1 -1
- package/dist/tools/agent_control.js +4 -3
- package/dist/tools/agent_control.js.map +1 -1
- package/dist/tools/config_example.d.ts +1 -1
- package/dist/tools/create_agent.d.ts +1 -1
- package/dist/tools/manage_agents.d.ts +1 -1
- package/dist/tools/run_agent.d.ts +1 -1
- package/dist/tools/run_agent.d.ts.map +1 -1
- package/dist/tools/run_agent.js +3 -0
- package/dist/tools/run_agent.js.map +1 -1
- package/dist/tools/run_agents_parallel.d.ts +1 -1
- package/dist/tools/run_hermes.d.ts +1 -0
- package/dist/tools/run_hermes.d.ts.map +1 -1
- package/dist/tools/run_hermes.js +22 -15
- package/dist/tools/run_hermes.js.map +1 -1
- package/docs/provider-config-map.md +170 -105
- package/package.json +8 -10
- package/scripts/_db_check.py +10 -0
- package/scripts/postgres-manager.mjs +1 -1
- package/scripts/postinstall.mjs +10 -20
- package/scripts/setup.mjs +14 -16
- package/scripts/status_check.py +20 -0
- package/dist/services/AntigravityRunner.d.ts +0 -42
- package/dist/services/AntigravityRunner.d.ts.map +0 -1
- package/dist/services/AntigravityRunner.js +0 -404
- package/dist/services/AntigravityRunner.js.map +0 -1
|
@@ -35,30 +35,36 @@ export class AgentManager {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
async listAgents(details = false) {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
38
|
+
const workspaceDir = getWorkspaceDir();
|
|
39
|
+
const claudeAgentsDir = path.join(this.claudeDir, 'agents');
|
|
40
|
+
await fs.mkdir(claudeAgentsDir, { recursive: true });
|
|
41
|
+
// 1. Scan .claude/agents/*.md
|
|
42
|
+
const claudeFiles = await fs.readdir(claudeAgentsDir).catch(() => []);
|
|
43
|
+
const claudeAgentNames = claudeFiles.filter((f) => f.endsWith('.md')).map((f) => f.replace('.md', ''));
|
|
44
|
+
// 2. Scan .overmind/hermes/agent_*
|
|
45
|
+
const hermesDir = path.join(workspaceDir, '.overmind', 'hermes');
|
|
46
|
+
const hermesDirs = await fs.readdir(hermesDir).catch(() => []);
|
|
47
|
+
const hermesAgentNames = hermesDirs
|
|
48
|
+
.filter((d) => d.startsWith('agent_'))
|
|
49
|
+
.map((d) => d.replace('agent_', ''));
|
|
50
|
+
const agentsMap = new Map();
|
|
43
51
|
const availableServers = await this.getAvailableMcpServers();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
let
|
|
51
|
-
const settingsPath = path.join(this.claudeDir, `settings_${
|
|
52
|
+
// Process Claude agents
|
|
53
|
+
for (const name of claudeAgentNames) {
|
|
54
|
+
let runner = 'claude';
|
|
55
|
+
let model = 'settings-default';
|
|
56
|
+
let provider = 'auto';
|
|
57
|
+
let mcpServers = [];
|
|
58
|
+
let missingConfig = false;
|
|
59
|
+
const settingsPath = path.join(this.claudeDir, `settings_${name}.json`);
|
|
52
60
|
try {
|
|
53
61
|
const settingsContent = await fs.readFile(settingsPath, 'utf-8');
|
|
54
62
|
let settings = JSON.parse(settingsContent);
|
|
55
|
-
// --- New interpolation logic ---
|
|
56
63
|
settings = interpolateEnvVars(settings);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
let provider = settings.provider || settings.env?.PROVIDER || 'auto';
|
|
64
|
+
model = settings.env?.ANTHROPIC_MODEL || settings.model || 'settings-default';
|
|
65
|
+
runner = settings.runner || 'claude';
|
|
66
|
+
mcpServers = settings.enabledMcpjsonServers || [];
|
|
67
|
+
provider = settings.provider || settings.env?.PROVIDER || 'auto';
|
|
62
68
|
if (provider === 'auto') {
|
|
63
69
|
if (settings.env?.MISTRAL_API_KEY)
|
|
64
70
|
provider = 'mistral';
|
|
@@ -68,9 +74,7 @@ export class AgentManager {
|
|
|
68
74
|
provider = 'nvidia';
|
|
69
75
|
else if (settings.env?.GEMINI_API_KEY)
|
|
70
76
|
provider = 'google';
|
|
71
|
-
else if (model.includes('mistral') ||
|
|
72
|
-
model.includes('codestral') ||
|
|
73
|
-
model.includes('devstral'))
|
|
77
|
+
else if (model.includes('mistral') || model.includes('codestral') || model.includes('devstral'))
|
|
74
78
|
provider = 'mistral';
|
|
75
79
|
else if (model.includes('gpt-'))
|
|
76
80
|
provider = 'openai';
|
|
@@ -79,44 +83,189 @@ export class AgentManager {
|
|
|
79
83
|
else if (model.includes('claude'))
|
|
80
84
|
provider = 'anthropic';
|
|
81
85
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
missingConfig = true;
|
|
89
|
+
}
|
|
90
|
+
let promptSize = 0;
|
|
91
|
+
try {
|
|
92
|
+
const stat = await fs.stat(path.join(claudeAgentsDir, `${name}.md`));
|
|
93
|
+
promptSize = stat.size;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Ignored
|
|
97
|
+
}
|
|
98
|
+
agentsMap.set(name, {
|
|
99
|
+
name,
|
|
100
|
+
runner,
|
|
101
|
+
model,
|
|
102
|
+
provider,
|
|
103
|
+
mcpServers,
|
|
104
|
+
origin: 'claude',
|
|
105
|
+
missingConfig,
|
|
106
|
+
promptSize,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
// Process Hermes agents
|
|
110
|
+
for (const name of hermesAgentNames) {
|
|
111
|
+
let model = 'MiniMax-M2.7';
|
|
112
|
+
let provider = 'minimax-cn';
|
|
113
|
+
let mcpServers = ['memory-server', 'postgresql-server', 'twilio-mcp'];
|
|
114
|
+
let promptSize = 0;
|
|
115
|
+
const agentHermesDir = path.join(hermesDir, `agent_${name}`, '.hermes');
|
|
116
|
+
try {
|
|
117
|
+
const soulStat = await fs.stat(path.join(agentHermesDir, 'SOUL.md'));
|
|
118
|
+
promptSize = soulStat.size;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
// Ignored
|
|
122
|
+
}
|
|
123
|
+
// Try reading .env to get actual model and provider
|
|
124
|
+
const envPath = path.join(agentHermesDir, '.env');
|
|
125
|
+
try {
|
|
126
|
+
const content = await fs.readFile(envPath, 'utf-8');
|
|
127
|
+
content.split('\n').forEach((line) => {
|
|
128
|
+
const trimmed = line.trim();
|
|
129
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
130
|
+
return;
|
|
131
|
+
const eqIdx = trimmed.indexOf('=');
|
|
132
|
+
if (eqIdx === -1)
|
|
133
|
+
return;
|
|
134
|
+
const k = trimmed.slice(0, eqIdx).trim();
|
|
135
|
+
let v = trimmed.slice(eqIdx + 1).trim();
|
|
136
|
+
if (v.startsWith('"') && v.endsWith('"'))
|
|
137
|
+
v = v.slice(1, -1);
|
|
138
|
+
else if (v.startsWith("'") && v.endsWith("'"))
|
|
139
|
+
v = v.slice(1, -1);
|
|
140
|
+
if (k === 'MODEL' || k === 'ANTHROPIC_MODEL') {
|
|
141
|
+
model = v;
|
|
142
|
+
}
|
|
143
|
+
else if (k === 'PROVIDER' || k === 'ANTHROPIC_PROVIDER') {
|
|
144
|
+
provider = v;
|
|
145
|
+
}
|
|
91
146
|
});
|
|
92
|
-
info += `\n - Runner : ${runner}`;
|
|
93
|
-
info += `\n - Model : ${model}`;
|
|
94
|
-
info += `\n - Provider: ${provider}`;
|
|
95
|
-
info += `\n - Serveurs MCP : ${servers.length > 0 ? serverStatus.join(', ') : 'Aucun'}`;
|
|
96
147
|
}
|
|
97
|
-
catch
|
|
98
|
-
|
|
148
|
+
catch {
|
|
149
|
+
// Ignored
|
|
99
150
|
}
|
|
151
|
+
// Try reading config.yaml to get actual MCP servers
|
|
152
|
+
const yamlPath = path.join(agentHermesDir, 'config.yaml');
|
|
100
153
|
try {
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
|
|
154
|
+
const yamlContent = await fs.readFile(yamlPath, 'utf-8');
|
|
155
|
+
const parsedMcp = [];
|
|
156
|
+
const serverBlocks = yamlContent.split('\n ');
|
|
157
|
+
serverBlocks.forEach((block) => {
|
|
158
|
+
const lines = block.split('\n');
|
|
159
|
+
const header = lines[0].trim();
|
|
160
|
+
if (header && header.endsWith(':') && header !== 'mcp_servers:') {
|
|
161
|
+
parsedMcp.push(header.slice(0, -1).trim());
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
if (parsedMcp.length > 0)
|
|
165
|
+
mcpServers = parsedMcp;
|
|
104
166
|
}
|
|
105
|
-
catch
|
|
106
|
-
//
|
|
167
|
+
catch {
|
|
168
|
+
// Ignored
|
|
107
169
|
}
|
|
108
|
-
|
|
170
|
+
if (!agentsMap.has(name)) {
|
|
171
|
+
agentsMap.set(name, {
|
|
172
|
+
name,
|
|
173
|
+
runner: 'hermes',
|
|
174
|
+
model,
|
|
175
|
+
provider,
|
|
176
|
+
mcpServers,
|
|
177
|
+
origin: 'hermes',
|
|
178
|
+
missingConfig: false,
|
|
179
|
+
promptSize,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
const existing = agentsMap.get(name);
|
|
184
|
+
if (existing.runner === 'claude' && existing.missingConfig) {
|
|
185
|
+
existing.runner = 'hermes';
|
|
186
|
+
existing.missingConfig = false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} // Group by runner
|
|
190
|
+
const runners = ['hermes', 'claude', 'kilo', 'gemini', 'qwencli', 'openclaw', 'cline', 'opencode'];
|
|
191
|
+
const grouped = new Map();
|
|
192
|
+
for (const r of runners) {
|
|
193
|
+
grouped.set(r, []);
|
|
109
194
|
}
|
|
110
|
-
|
|
195
|
+
grouped.set('unknown/other', []);
|
|
196
|
+
for (const agent of agentsMap.values()) {
|
|
197
|
+
const r = agent.runner.toLowerCase();
|
|
198
|
+
if (grouped.has(r)) {
|
|
199
|
+
grouped.get(r).push(agent);
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
grouped.get('unknown/other').push(agent);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const outputLines = [];
|
|
206
|
+
for (const [runName, list] of grouped.entries()) {
|
|
207
|
+
if (list.length === 0)
|
|
208
|
+
continue;
|
|
209
|
+
let sectionText = `### 🏃 Runner/Harnais : **${runName.toUpperCase()}** (${list.length} agent(s))\n`;
|
|
210
|
+
const items = [];
|
|
211
|
+
for (const agent of list) {
|
|
212
|
+
if (!details) {
|
|
213
|
+
const warnText = agent.missingConfig ? ' ⚠️ (Config settings manquante)' : '';
|
|
214
|
+
items.push(` - **${agent.name}**${warnText}`);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
let agentDesc = ` - **${agent.name}**`;
|
|
218
|
+
if (agent.missingConfig) {
|
|
219
|
+
agentDesc += `\n ⚠️ Config settings manquante (settings_${agent.name}.json)`;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
agentDesc += `\n - Modèle : ${agent.model}`;
|
|
223
|
+
agentDesc += `\n - Provider : ${agent.provider}`;
|
|
224
|
+
const serverStatus = agent.mcpServers.map((s) => {
|
|
225
|
+
if (availableServers.includes(s))
|
|
226
|
+
return s;
|
|
227
|
+
const sugg = availableServers.find(v => v.toLowerCase().includes(s.toLowerCase()) || s.toLowerCase().includes(v.toLowerCase()));
|
|
228
|
+
return sugg ? `${s} (⚠️ Suggestion: ${sugg})` : `${s} (⚠️ Absent)`;
|
|
229
|
+
});
|
|
230
|
+
agentDesc += `\n - MCPs : ${serverStatus.length > 0 ? serverStatus.join(', ') : 'Aucun'}`;
|
|
231
|
+
}
|
|
232
|
+
agentDesc += `\n - Prompt : ${agent.promptSize} bytes (${agent.origin === 'hermes' ? 'SOUL.md' : 'agents/' + agent.name + '.md'})`;
|
|
233
|
+
items.push(agentDesc);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
sectionText += items.join('\n');
|
|
237
|
+
outputLines.push(sectionText);
|
|
238
|
+
}
|
|
239
|
+
return outputLines;
|
|
111
240
|
}
|
|
112
241
|
async deleteAgent(name) {
|
|
113
242
|
validateAgentName(name);
|
|
243
|
+
const workspaceDir = getWorkspaceDir();
|
|
244
|
+
const hermesAgentDir = path.join(workspaceDir, '.overmind', 'hermes', `agent_${name}`);
|
|
245
|
+
let isHermes = false;
|
|
246
|
+
try {
|
|
247
|
+
const stat = await fs.stat(hermesAgentDir);
|
|
248
|
+
isHermes = stat.isDirectory();
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
// Ignored
|
|
252
|
+
}
|
|
253
|
+
const deletedFiles = [];
|
|
254
|
+
const errors = [];
|
|
255
|
+
if (isHermes) {
|
|
256
|
+
try {
|
|
257
|
+
await fs.rm(hermesAgentDir, { recursive: true, force: true });
|
|
258
|
+
deletedFiles.push(hermesAgentDir);
|
|
259
|
+
}
|
|
260
|
+
catch (e) {
|
|
261
|
+
errors.push(`hermes_dir: ${e instanceof Error ? e.message : String(e)}`);
|
|
262
|
+
}
|
|
263
|
+
return { deletedFiles, errors };
|
|
264
|
+
}
|
|
114
265
|
const agentsDir = path.join(this.claudeDir, 'agents');
|
|
115
266
|
const promptPath = path.join(agentsDir, `${name}.md`);
|
|
116
267
|
const settingsPath = path.join(this.claudeDir, `settings_${name}.json`);
|
|
117
268
|
const tmpMcpPath = path.join(this.claudeDir, `mcp_${name}_tmp.json`);
|
|
118
|
-
const deletedFiles = [];
|
|
119
|
-
const errors = [];
|
|
120
269
|
for (const file of [promptPath, settingsPath, tmpMcpPath]) {
|
|
121
270
|
try {
|
|
122
271
|
await fs.unlink(file);
|
|
@@ -134,33 +283,154 @@ export class AgentManager {
|
|
|
134
283
|
validateAgentName(name);
|
|
135
284
|
const changes = [];
|
|
136
285
|
const claudeDir = this.claudeDir;
|
|
286
|
+
const workspaceDir = getWorkspaceDir();
|
|
287
|
+
const hermesAgentDir = path.join(workspaceDir, '.overmind', 'hermes', `agent_${name}`);
|
|
288
|
+
const hermesSubDir = path.join(hermesAgentDir, '.hermes');
|
|
289
|
+
let isHermes = false;
|
|
290
|
+
try {
|
|
291
|
+
const stat = await fs.stat(hermesAgentDir);
|
|
292
|
+
isHermes = stat.isDirectory();
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
// Ignored
|
|
296
|
+
}
|
|
137
297
|
// --- MODE RÉÉCRITURE DE FICHIER COMPLET ---
|
|
138
298
|
if (updates.file && updates.content) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
299
|
+
if (isHermes) {
|
|
300
|
+
let filePath;
|
|
301
|
+
switch (updates.file) {
|
|
302
|
+
case 'prompt.md':
|
|
303
|
+
filePath = path.join(hermesSubDir, 'SOUL.md');
|
|
304
|
+
await fs.writeFile(filePath, updates.content, 'utf-8');
|
|
305
|
+
changes.push(`✅ Fichier **SOUL.md** réécrit pour l'agent Hermes **${name}**.`);
|
|
306
|
+
break;
|
|
307
|
+
case 'settings.json': {
|
|
308
|
+
let envStr = '';
|
|
309
|
+
try {
|
|
310
|
+
const s = JSON.parse(updates.content);
|
|
311
|
+
if (s.env) {
|
|
312
|
+
for (const [k, v] of Object.entries(s.env)) {
|
|
313
|
+
if (typeof v === 'string')
|
|
314
|
+
envStr += `${k}=${v}\n`;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (s.model && !envStr.includes('MODEL='))
|
|
318
|
+
envStr += `MODEL=${s.model}\n`;
|
|
319
|
+
if (s.runner && !envStr.includes('RUNNER='))
|
|
320
|
+
envStr += `RUNNER=${s.runner}\n`;
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
envStr = updates.content;
|
|
324
|
+
}
|
|
325
|
+
filePath = path.join(hermesSubDir, '.env');
|
|
326
|
+
await fs.writeFile(filePath, envStr, 'utf-8');
|
|
327
|
+
changes.push(`✅ Fichier **.env** réécrit pour l'agent Hermes **${name}**.`);
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
case '.mcp.json': {
|
|
331
|
+
let yamlContent = 'mcp_servers:\n';
|
|
332
|
+
try {
|
|
333
|
+
const mc = JSON.parse(updates.content);
|
|
334
|
+
for (const [sName, srv] of Object.entries(mc.mcpServers || {})) {
|
|
335
|
+
const s = srv;
|
|
336
|
+
yamlContent += ` ${sName}:\n`;
|
|
337
|
+
if (s.url)
|
|
338
|
+
yamlContent += ` url: "${s.url}"\n`;
|
|
339
|
+
if (s.command)
|
|
340
|
+
yamlContent += ` command: "${s.command}"\n`;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
yamlContent = updates.content;
|
|
345
|
+
}
|
|
346
|
+
filePath = path.join(hermesSubDir, 'config.yaml');
|
|
347
|
+
await fs.writeFile(filePath, yamlContent, 'utf-8');
|
|
348
|
+
changes.push(`✅ Fichier **config.yaml** réécrit pour l'agent Hermes **${name}**.`);
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
case 'skill.md':
|
|
352
|
+
filePath = path.join(hermesSubDir, 'skills', 'skill.md');
|
|
353
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
354
|
+
await fs.writeFile(filePath, updates.content, 'utf-8');
|
|
355
|
+
changes.push(`✅ Fichier **skill.md** réécrit pour l'agent Hermes **${name}**.`);
|
|
356
|
+
break;
|
|
357
|
+
default:
|
|
358
|
+
throw new Error(`Fichier non supporté: ${updates.file}`);
|
|
359
|
+
}
|
|
161
360
|
return changes;
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
let filePath;
|
|
364
|
+
switch (updates.file) {
|
|
365
|
+
case 'prompt.md':
|
|
366
|
+
filePath = path.join(claudeDir, 'agents', `${name}.md`);
|
|
367
|
+
break;
|
|
368
|
+
case 'settings.json':
|
|
369
|
+
filePath = path.join(claudeDir, `settings_${name}.json`);
|
|
370
|
+
break;
|
|
371
|
+
case '.mcp.json':
|
|
372
|
+
filePath = path.join(claudeDir, `.mcp.${name}.json`);
|
|
373
|
+
break;
|
|
374
|
+
case 'skill.md':
|
|
375
|
+
filePath = path.join(claudeDir, 'agents', `${name}_skill.md`);
|
|
376
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
377
|
+
break;
|
|
378
|
+
default:
|
|
379
|
+
throw new Error(`Fichier non supporté: ${updates.file}`);
|
|
380
|
+
}
|
|
381
|
+
await fs.writeFile(filePath, updates.content, 'utf-8');
|
|
382
|
+
changes.push(`✅ Fichier **${updates.file}** réécrit pour l'agent **${name}**.`);
|
|
383
|
+
if (updates.file === 'settings.json')
|
|
384
|
+
return changes;
|
|
385
|
+
}
|
|
162
386
|
}
|
|
163
|
-
|
|
387
|
+
if (isHermes) {
|
|
388
|
+
const envVars = {};
|
|
389
|
+
const envPath = path.join(hermesSubDir, '.env');
|
|
390
|
+
try {
|
|
391
|
+
const content = await fs.readFile(envPath, 'utf-8');
|
|
392
|
+
content.split('\n').forEach((line) => {
|
|
393
|
+
const trimmed = line.trim();
|
|
394
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
395
|
+
return;
|
|
396
|
+
const eqIdx = trimmed.indexOf('=');
|
|
397
|
+
if (eqIdx === -1)
|
|
398
|
+
return;
|
|
399
|
+
const k = trimmed.slice(0, eqIdx).trim();
|
|
400
|
+
let v = trimmed.slice(eqIdx + 1).trim();
|
|
401
|
+
if (v.startsWith('"') && v.endsWith('"'))
|
|
402
|
+
v = v.slice(1, -1);
|
|
403
|
+
else if (v.startsWith("'") && v.endsWith("'"))
|
|
404
|
+
v = v.slice(1, -1);
|
|
405
|
+
if (k)
|
|
406
|
+
envVars[k] = v;
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
// Ignored
|
|
411
|
+
}
|
|
412
|
+
if (updates.model) {
|
|
413
|
+
const oldModel = envVars.MODEL || envVars.ANTHROPIC_MODEL;
|
|
414
|
+
envVars.MODEL = updates.model;
|
|
415
|
+
changes.push(`- Modèle : ${oldModel} -> ${updates.model}`);
|
|
416
|
+
}
|
|
417
|
+
if (updates.env) {
|
|
418
|
+
for (const [key, value] of Object.entries(updates.env)) {
|
|
419
|
+
const oldVal = envVars[key] ? '***' : '(undefined)';
|
|
420
|
+
envVars[key] = value;
|
|
421
|
+
changes.push(`- Env Var '${key}' : ${oldVal} -> ${value ? '***' : '(vide)'}`);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (changes.length > 0) {
|
|
425
|
+
let newEnvContent = '';
|
|
426
|
+
for (const [k, v] of Object.entries(envVars)) {
|
|
427
|
+
newEnvContent += `${k}=${v}\n`;
|
|
428
|
+
}
|
|
429
|
+
await fs.writeFile(envPath, newEnvContent, 'utf-8');
|
|
430
|
+
}
|
|
431
|
+
return changes;
|
|
432
|
+
}
|
|
433
|
+
// --- MODE MISE À JOUR UNITAIRE (settings.json) pour Claude/Kilo/etc. ---
|
|
164
434
|
const settingsPath = path.join(claudeDir, `settings_${name}.json`);
|
|
165
435
|
let settings;
|
|
166
436
|
try {
|
|
@@ -168,7 +438,6 @@ export class AgentManager {
|
|
|
168
438
|
settings = JSON.parse(content);
|
|
169
439
|
}
|
|
170
440
|
catch (_e) {
|
|
171
|
-
// Si on veut faire des updates unitaires, le settings doit exister
|
|
172
441
|
if (updates.model ||
|
|
173
442
|
updates.mcpServers ||
|
|
174
443
|
updates.env ||
|
|
@@ -177,7 +446,7 @@ export class AgentManager {
|
|
|
177
446
|
updates.cliPath) {
|
|
178
447
|
throw new Error(`Impossible de modifier les paramètres unitaires : settings_${name}.json est introuvable.`, { cause: _e });
|
|
179
448
|
}
|
|
180
|
-
return changes;
|
|
449
|
+
return changes;
|
|
181
450
|
}
|
|
182
451
|
if (updates.model) {
|
|
183
452
|
settings.env = settings.env || {};
|
|
@@ -194,7 +463,6 @@ export class AgentManager {
|
|
|
194
463
|
}
|
|
195
464
|
settings.enabledMcpjsonServers = updates.mcpServers;
|
|
196
465
|
changes.push(`- Serveurs MCP : [${oldServers.join(', ')}] -> [${updates.mcpServers.join(', ')}]`);
|
|
197
|
-
// 📂 UPDATE .mcp.<name>.json
|
|
198
466
|
try {
|
|
199
467
|
const globalMcpPath = resolveConfigPath(CONFIG.CLAUDE.PATHS.MCP);
|
|
200
468
|
const globalMcpContent = await fs.readFile(globalMcpPath, 'utf-8');
|
|
@@ -245,9 +513,6 @@ export class AgentManager {
|
|
|
245
513
|
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
246
514
|
return { promptPath: '', settingsPath: '', error: 'INVALID_NAME' };
|
|
247
515
|
}
|
|
248
|
-
const agentsDir = path.join(this.claudeDir, 'agents');
|
|
249
|
-
await fs.mkdir(this.claudeDir, { recursive: true });
|
|
250
|
-
await fs.mkdir(agentsDir, { recursive: true });
|
|
251
516
|
const memoryInstructions = `
|
|
252
517
|
|
|
253
518
|
---
|
|
@@ -272,12 +537,9 @@ Tu es jugé sur ta capacité à transmettre ton savoir. FOUILLIE ta mémoire et
|
|
|
272
537
|
Tu es conçu pour être exécuté par différents runners (Claude, Kilo, Gemini, Hermes, etc.).
|
|
273
538
|
- **Compatibilité** : Ton prompt et tes compétences sont agnostiques au runner.
|
|
274
539
|
- **Orchestration** : Tu peux être sollicité dans des workflows parallèles (\`run_agents_parallel\`) ou séquentiels.
|
|
275
|
-
- **Auto
|
|
540
|
+
- **Auto-Évaluation** : Adapte tes réponses au format attendu par le runner actuel (JSON pour Claude Code, texte brut pour Kilo, etc.).`;
|
|
276
541
|
const finalPrompt = prompt + memoryInstructions;
|
|
277
|
-
const promptPath = path.join(agentsDir, `${name}.md`);
|
|
278
|
-
await fs.writeFile(promptPath, finalPrompt, 'utf-8');
|
|
279
542
|
// Default mandatory environment variables according to user request
|
|
280
|
-
// SECURITY: Never default to placeholder tokens — require real env vars
|
|
281
543
|
const authToken = process.env.ANTHROPIC_AUTH_TOKEN;
|
|
282
544
|
if (!authToken) {
|
|
283
545
|
return {
|
|
@@ -316,7 +578,6 @@ Tu es conçu pour être exécuté par différents runners (Claude, Kilo, Gemini,
|
|
|
316
578
|
if (copyEnvFrom && projectRoot) {
|
|
317
579
|
try {
|
|
318
580
|
const sourceSettingsPath = path.resolve(projectRoot, copyEnvFrom);
|
|
319
|
-
// Prevent path traversal: source must be inside projectRoot
|
|
320
581
|
if (!sourceSettingsPath.startsWith(path.resolve(projectRoot))) {
|
|
321
582
|
throw new Error(`copyEnvFrom path escapes project root: ${copyEnvFrom}`);
|
|
322
583
|
}
|
|
@@ -332,6 +593,63 @@ Tu es conçu pour être exécuté par différents runners (Claude, Kilo, Gemini,
|
|
|
332
593
|
}
|
|
333
594
|
}
|
|
334
595
|
envVars.ANTHROPIC_MODEL = model; // Ensure model is correctly set
|
|
596
|
+
if (runner === 'hermes') {
|
|
597
|
+
const workspaceDir = getWorkspaceDir();
|
|
598
|
+
const hermesAgentDir = path.join(workspaceDir, '.overmind', 'hermes', `agent_${name}`);
|
|
599
|
+
const hermesSubDir = path.join(hermesAgentDir, '.hermes');
|
|
600
|
+
const hermesSkillsDir = path.join(hermesSubDir, 'skills');
|
|
601
|
+
await fs.mkdir(hermesSubDir, { recursive: true });
|
|
602
|
+
await fs.mkdir(hermesSkillsDir, { recursive: true });
|
|
603
|
+
const promptPath = path.join(hermesSubDir, 'SOUL.md');
|
|
604
|
+
await fs.writeFile(promptPath, finalPrompt, 'utf-8');
|
|
605
|
+
// Write .env
|
|
606
|
+
const envPath = path.join(hermesSubDir, '.env');
|
|
607
|
+
let envContent = '';
|
|
608
|
+
for (const [k, v] of Object.entries(envVars)) {
|
|
609
|
+
envContent += `${k}=${v}\n`;
|
|
610
|
+
}
|
|
611
|
+
await fs.writeFile(envPath, envContent, 'utf-8');
|
|
612
|
+
// Write config.yaml (MCP config)
|
|
613
|
+
const yamlPath = path.join(hermesSubDir, 'config.yaml');
|
|
614
|
+
let yamlContent = 'mcp_servers:\n';
|
|
615
|
+
const globalMcpPath = resolveConfigPath(CONFIG.CLAUDE.PATHS.MCP);
|
|
616
|
+
try {
|
|
617
|
+
const globalMcpContent = await fs.readFile(globalMcpPath, 'utf-8');
|
|
618
|
+
const globalMcp = JSON.parse(globalMcpContent);
|
|
619
|
+
mcpServers.forEach((serverName) => {
|
|
620
|
+
if (globalMcp.mcpServers && globalMcp.mcpServers[serverName]) {
|
|
621
|
+
const s = globalMcp.mcpServers[serverName];
|
|
622
|
+
yamlContent += ` ${serverName}:\n`;
|
|
623
|
+
if (s.url)
|
|
624
|
+
yamlContent += ` url: "${s.url}"\n`;
|
|
625
|
+
if (s.command)
|
|
626
|
+
yamlContent += ` command: "${s.command}"\n`;
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
catch {
|
|
631
|
+
// Ignored
|
|
632
|
+
}
|
|
633
|
+
await fs.writeFile(yamlPath, yamlContent, 'utf-8');
|
|
634
|
+
// Mémoriser la création de l'agent
|
|
635
|
+
try {
|
|
636
|
+
const memory = getMemoryProvider();
|
|
637
|
+
await memory.storeKnowledge({
|
|
638
|
+
text: `Nouvel agent IA créé : '${name}'. Runner : hermes.`,
|
|
639
|
+
source: 'agent',
|
|
640
|
+
agentName: name,
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
catch {
|
|
644
|
+
// Ignored
|
|
645
|
+
}
|
|
646
|
+
return { promptPath, settingsPath: envPath };
|
|
647
|
+
}
|
|
648
|
+
const agentsDir = path.join(this.claudeDir, 'agents');
|
|
649
|
+
await fs.mkdir(this.claudeDir, { recursive: true });
|
|
650
|
+
await fs.mkdir(agentsDir, { recursive: true });
|
|
651
|
+
const promptPath = path.join(agentsDir, `${name}.md`);
|
|
652
|
+
await fs.writeFile(promptPath, finalPrompt, 'utf-8');
|
|
335
653
|
const settings = {
|
|
336
654
|
env: envVars,
|
|
337
655
|
enableAllProjectMcpServers: false,
|
|
@@ -339,7 +657,6 @@ Tu es conçu pour être exécuté par différents runners (Claude, Kilo, Gemini,
|
|
|
339
657
|
agent: name,
|
|
340
658
|
runner: runner || 'claude',
|
|
341
659
|
};
|
|
342
|
-
// Add runner specific metadata
|
|
343
660
|
if (mode)
|
|
344
661
|
settings.mode = mode;
|
|
345
662
|
if (cliPath)
|
|
@@ -347,7 +664,6 @@ Tu es conçu pour être exécuté par différents runners (Claude, Kilo, Gemini,
|
|
|
347
664
|
const settingsFileName = `settings_${name}.json`;
|
|
348
665
|
const settingsPath = path.join(this.claudeDir, settingsFileName);
|
|
349
666
|
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
|
|
350
|
-
// 📂 ENSURE .mcp.<agent>.json CREATION
|
|
351
667
|
try {
|
|
352
668
|
const globalMcpPath = resolveConfigPath(CONFIG.CLAUDE.PATHS.MCP);
|
|
353
669
|
const globalMcpContent = await fs.readFile(globalMcpPath, 'utf-8');
|
|
@@ -360,37 +676,36 @@ Tu es conçu pour être exécuté par différents runners (Claude, Kilo, Gemini,
|
|
|
360
676
|
});
|
|
361
677
|
const agentMcpPath = path.join(this.claudeDir, `.mcp.${name}.json`);
|
|
362
678
|
await fs.writeFile(agentMcpPath, JSON.stringify({ mcpServers: agentMcpServers }, null, 2), 'utf-8');
|
|
363
|
-
console.error(`[AgentManager] ✅ .mcp.${name}.json created with ${Object.keys(agentMcpServers).length} servers.`);
|
|
364
679
|
}
|
|
365
680
|
catch (e) {
|
|
366
681
|
console.error(`[AgentManager] ⚠️ Failed to create .mcp.${name}.json:`, e.message);
|
|
367
682
|
}
|
|
368
|
-
// Mémoriser la création de l'agent dans Overmind
|
|
369
683
|
try {
|
|
370
684
|
const memory = getMemoryProvider();
|
|
371
685
|
await memory.storeKnowledge({
|
|
372
|
-
text: `Nouvel agent IA créé : '${name}'.
|
|
373
|
-
Runner : ${runner || 'claude'}.
|
|
374
|
-
Modèle : ${model}.
|
|
375
|
-
Capacités définies : ${prompt.slice(0, 300)}...
|
|
376
|
-
Serveurs MCP activés : ${mcpServers.join(', ')}.`,
|
|
686
|
+
text: `Nouvel agent IA créé : '${name}'. Runner : ${runner || 'claude'}.`,
|
|
377
687
|
source: 'agent',
|
|
378
688
|
agentName: name,
|
|
379
689
|
});
|
|
380
690
|
}
|
|
381
691
|
catch (_e) {
|
|
382
|
-
//
|
|
692
|
+
// Ignored
|
|
383
693
|
}
|
|
384
694
|
return { promptPath, settingsPath };
|
|
385
695
|
}
|
|
386
696
|
async getDetailedConfigs(name) {
|
|
387
697
|
validateAgentName(name);
|
|
388
|
-
const
|
|
389
|
-
const
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
698
|
+
const workspaceDir = getWorkspaceDir();
|
|
699
|
+
const hermesAgentDir = path.join(workspaceDir, '.overmind', 'hermes', `agent_${name}`);
|
|
700
|
+
const hermesSubDir = path.join(hermesAgentDir, '.hermes');
|
|
701
|
+
let isHermes = false;
|
|
702
|
+
try {
|
|
703
|
+
const stat = await fs.stat(hermesAgentDir);
|
|
704
|
+
isHermes = stat.isDirectory();
|
|
705
|
+
}
|
|
706
|
+
catch {
|
|
707
|
+
// Ignored
|
|
708
|
+
}
|
|
394
709
|
const result = {};
|
|
395
710
|
const readFileSafe = async (filePath) => {
|
|
396
711
|
try {
|
|
@@ -400,8 +715,69 @@ Serveurs MCP activés : ${mcpServers.join(', ')}.`,
|
|
|
400
715
|
return 'MISSING';
|
|
401
716
|
}
|
|
402
717
|
};
|
|
718
|
+
if (isHermes) {
|
|
719
|
+
result['prompt.md'] = await readFileSafe(path.join(hermesSubDir, 'SOUL.md'));
|
|
720
|
+
const envVars = {};
|
|
721
|
+
const envContent = await readFileSafe(path.join(hermesSubDir, '.env'));
|
|
722
|
+
if (envContent !== 'MISSING') {
|
|
723
|
+
envContent.split('\n').forEach((line) => {
|
|
724
|
+
const trimmed = line.trim();
|
|
725
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
726
|
+
return;
|
|
727
|
+
const eqIdx = trimmed.indexOf('=');
|
|
728
|
+
if (eqIdx === -1)
|
|
729
|
+
return;
|
|
730
|
+
const k = trimmed.slice(0, eqIdx).trim();
|
|
731
|
+
let v = trimmed.slice(eqIdx + 1).trim();
|
|
732
|
+
if (v.startsWith('"') && v.endsWith('"'))
|
|
733
|
+
v = v.slice(1, -1);
|
|
734
|
+
else if (v.startsWith("'") && v.endsWith("'"))
|
|
735
|
+
v = v.slice(1, -1);
|
|
736
|
+
if (k)
|
|
737
|
+
envVars[k] = v;
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
result['settings.json'] = JSON.stringify({
|
|
741
|
+
env: envVars,
|
|
742
|
+
model: envVars.MODEL || envVars.ANTHROPIC_MODEL || 'MiniMax-M2.7',
|
|
743
|
+
runner: 'hermes'
|
|
744
|
+
}, null, 2);
|
|
745
|
+
const mcpServers = {};
|
|
746
|
+
const yamlContent = await readFileSafe(path.join(hermesSubDir, 'config.yaml'));
|
|
747
|
+
if (yamlContent !== 'MISSING') {
|
|
748
|
+
const serverBlocks = yamlContent.split('\n ');
|
|
749
|
+
serverBlocks.forEach((block) => {
|
|
750
|
+
const lines = block.split('\n');
|
|
751
|
+
const header = lines[0].trim();
|
|
752
|
+
if (header && header.endsWith(':') && header !== 'mcp_servers:') {
|
|
753
|
+
const srvName = header.slice(0, -1).trim();
|
|
754
|
+
const srvObj = {};
|
|
755
|
+
lines.slice(1).forEach((l) => {
|
|
756
|
+
const parts = l.trim().split(':');
|
|
757
|
+
if (parts.length >= 2) {
|
|
758
|
+
const key = parts[0].trim();
|
|
759
|
+
let val = parts.slice(1).join(':').trim();
|
|
760
|
+
if (val.startsWith('"') && val.endsWith('"'))
|
|
761
|
+
val = val.slice(1, -1);
|
|
762
|
+
srvObj[key] = val;
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
mcpServers[srvName] = srvObj;
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
result['.mcp.json'] = JSON.stringify({ mcpServers }, null, 2);
|
|
770
|
+
const skillPath = path.join(hermesSubDir, 'skills', 'skill.md');
|
|
771
|
+
result['skill.md'] = await readFileSafe(skillPath);
|
|
772
|
+
return result;
|
|
773
|
+
}
|
|
774
|
+
const agentsDir = path.join(this.claudeDir, 'agents');
|
|
775
|
+
const promptPath = path.join(agentsDir, `${name}.md`);
|
|
776
|
+
const settingsPath = path.join(this.claudeDir, `settings_${name}.json`);
|
|
777
|
+
const mcpPath = path.join(this.claudeDir, `.mcp.${name}.json`);
|
|
778
|
+
const skillPath = path.join(this.claudeDir, `agents/${name}_skill.md`);
|
|
779
|
+
const alternativeSkillPath = path.join(this.claudeDir, `skills/${name}.md`);
|
|
403
780
|
// Fallback paths using workspace from OVERMIND_WORKSPACE env var
|
|
404
|
-
const workspaceDir = getWorkspaceDir();
|
|
405
781
|
const fallbackAgentsDir = path.join(workspaceDir, '.overmind', 'agents');
|
|
406
782
|
const fallbackPromptPath = path.join(fallbackAgentsDir, `${name}.md`);
|
|
407
783
|
const fallbackSettingsDir = path.join(workspaceDir, '.overmind', 'agents', name);
|