@soleri/cli 7.0.0 → 8.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soleri/cli",
3
- "version": "7.0.0",
3
+ "version": "8.1.0",
4
4
  "description": "Developer CLI for creating and managing Soleri AI agents.",
5
5
  "keywords": [
6
6
  "agent",
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@clack/prompts": "^1.0.0",
44
- "@soleri/core": "^2.0.0",
44
+ "@soleri/core": "^8.0.0",
45
45
  "@soleri/forge": "^5.0.0",
46
46
  "commander": "^13.0.0"
47
47
  },
@@ -8,7 +8,14 @@
8
8
  */
9
9
 
10
10
  import { join } from 'node:path';
11
- import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, unlinkSync } from 'node:fs';
11
+ import {
12
+ existsSync,
13
+ readFileSync,
14
+ writeFileSync,
15
+ mkdirSync,
16
+ copyFileSync,
17
+ unlinkSync,
18
+ } from 'node:fs';
12
19
  import { homedir } from 'node:os';
13
20
  import type { Command } from 'commander';
14
21
  import * as p from '@clack/prompts';
@@ -77,7 +84,9 @@ export function registerCognee(program: Command): void {
77
84
  copyFileSync(sourceCompose, targetCompose);
78
85
  p.log.info(`Copied ${COGNEE_COMPOSE_FILE} to agent project`);
79
86
  } else if (!existsSync(targetCompose)) {
80
- p.log.warn(`${COGNEE_COMPOSE_FILE} not found — create one manually or run Cognee externally`);
87
+ p.log.warn(
88
+ `${COGNEE_COMPOSE_FILE} not found — create one manually or run Cognee externally`,
89
+ );
81
90
  }
82
91
 
83
92
  // 3. Update package.json
@@ -87,9 +96,9 @@ export function registerCognee(program: Command): void {
87
96
  pkgChanged = true;
88
97
 
89
98
  if (!pkg.scripts) pkg.scripts = {};
90
- for (const [name, cmd] of Object.entries(COGNEE_SCRIPTS)) {
99
+ for (const [name, script] of Object.entries(COGNEE_SCRIPTS)) {
91
100
  if (!pkg.scripts[name]) {
92
- pkg.scripts[name] = cmd;
101
+ pkg.scripts[name] = script;
93
102
  pkgChanged = true;
94
103
  }
95
104
  }
@@ -295,9 +304,7 @@ export function registerCognee(program: Command): void {
295
304
 
296
305
  // Check Docker Compose file
297
306
  const composePath = join(ctx.agentPath, COGNEE_COMPOSE_FILE);
298
- console.log(
299
- ` Docker Compose: ${existsSync(composePath) ? 'present' : 'missing'}`,
300
- );
307
+ console.log(` Docker Compose: ${existsSync(composePath) ? 'present' : 'missing'}`);
301
308
 
302
309
  // Check npm scripts
303
310
  const hasScripts = Object.keys(COGNEE_SCRIPTS).every((s) => !!pkg.scripts?.[s]);
@@ -333,18 +340,14 @@ export function registerCognee(program: Command): void {
333
340
  const timeout = setTimeout(() => controller.abort(), 5000);
334
341
  const res = await fetch(`${baseUrl}/`, { signal: controller.signal });
335
342
  clearTimeout(timeout);
336
- console.log(
337
- ` Sidecar: ${res.ok ? 'running' : `HTTP ${res.status}`} at ${baseUrl}`,
338
- );
343
+ console.log(` Sidecar: ${res.ok ? 'running' : `HTTP ${res.status}`} at ${baseUrl}`);
339
344
  } catch {
340
345
  console.log(` Sidecar: not reachable at ${baseUrl}`);
341
346
  }
342
347
 
343
348
  // Overall status
344
349
  const ready = enabled && existsSync(configPath);
345
- console.log(
346
- `\n Status: ${ready ? 'configured' : 'needs configuration'}`,
347
- );
350
+ console.log(`\n Status: ${ready ? 'configured' : 'needs configuration'}`);
348
351
  if (!existsSync(configPath)) {
349
352
  console.log(' Next: Run `soleri cognee setup`');
350
353
  } else {
@@ -85,7 +85,14 @@ function installCodex(agentId: string, agentDir: string, isFileTree: boolean): v
85
85
  }
86
86
 
87
87
  function installOpencode(agentId: string, agentDir: string, isFileTree: boolean): void {
88
- const configPath = join(homedir(), '.opencode.json');
88
+ // OpenCode uses ~/.config/opencode/opencode.json (not ~/.opencode.json)
89
+ // Config uses "mcp" (not "mcpServers"), type "local" (not "stdio"), command as array
90
+ const configDir = join(homedir(), '.config', 'opencode');
91
+ const configPath = join(configDir, 'opencode.json');
92
+
93
+ if (!existsSync(configDir)) {
94
+ mkdirSync(configDir, { recursive: true });
95
+ }
89
96
 
90
97
  let config: Record<string, unknown> = {};
91
98
  if (existsSync(configPath)) {
@@ -98,28 +105,77 @@ function installOpencode(agentId: string, agentDir: string, isFileTree: boolean)
98
105
  }
99
106
  }
100
107
 
101
- if (!config.mcpServers || typeof config.mcpServers !== 'object') {
102
- config.mcpServers = {};
108
+ if (!config.mcp || typeof config.mcp !== 'object') {
109
+ config.mcp = {};
103
110
  }
104
111
 
105
- const servers = config.mcpServers as Record<string, unknown>;
106
- servers[agentId] = isFileTree
107
- ? fileTreeMcpEntry(agentDir)
108
- : { type: 'stdio', command: 'node', args: [join(agentDir, 'dist', 'index.js')] };
112
+ const servers = config.mcp as Record<string, unknown>;
113
+ if (isFileTree) {
114
+ servers[agentId] = {
115
+ type: 'local',
116
+ command: [
117
+ 'node',
118
+ join(
119
+ agentDir,
120
+ '..',
121
+ 'soleri',
122
+ 'packages',
123
+ 'core',
124
+ 'dist',
125
+ 'engine',
126
+ 'bin',
127
+ 'soleri-engine.js',
128
+ ),
129
+ '--agent',
130
+ join(agentDir, 'agent.yaml'),
131
+ ],
132
+ };
133
+ } else {
134
+ servers[agentId] = {
135
+ type: 'local',
136
+ command: ['node', join(agentDir, 'dist', 'index.js')],
137
+ };
138
+ }
109
139
 
110
140
  writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
111
- p.log.success(`Registered ${agentId} in ~/.opencode.json`);
141
+ p.log.success(`Registered ${agentId} in ~/.config/opencode/opencode.json`);
112
142
  }
113
143
 
114
144
  function escapeRegExp(s: string): string {
115
145
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
116
146
  }
117
147
 
148
+ /**
149
+ * Create a global launcher script so the agent can be invoked by name from any directory.
150
+ * e.g., typing `ernesto` opens Claude Code with that agent's MCP config.
151
+ */
152
+ function installLauncher(agentId: string, agentDir: string): void {
153
+ const binPath = join('/usr/local/bin', agentId);
154
+
155
+ const script = [
156
+ '#!/bin/bash',
157
+ `# ${agentId} — Soleri second brain launcher`,
158
+ `# Type "${agentId}" from any directory to open Claude Code with this agent`,
159
+ `exec claude --mcp-config ${join(agentDir, '.mcp.json')}`,
160
+ '',
161
+ ].join('\n');
162
+
163
+ try {
164
+ writeFileSync(binPath, script, { mode: 0o755 });
165
+ p.log.success(`Launcher created: type "${agentId}" from any directory to start`);
166
+ } catch {
167
+ p.log.warn(`Could not create launcher at ${binPath} (may need sudo)`);
168
+ p.log.info(
169
+ `To create manually: sudo bash -c 'cat > ${binPath} << "EOF"\\n#!/bin/bash\\nexec claude --mcp-config ${join(agentDir, '.mcp.json')}\\nEOF' && chmod +x ${binPath}`,
170
+ );
171
+ }
172
+ }
173
+
118
174
  export function registerInstall(program: Command): void {
119
175
  program
120
176
  .command('install')
121
177
  .argument('[dir]', 'Agent directory (defaults to cwd)')
122
- .option('--target <target>', 'Registration target: opencode, claude, codex, or all', 'opencode')
178
+ .option('--target <target>', 'Registration target: claude, opencode, codex, or all', 'claude')
123
179
  .description('Register agent as MCP server in editor config')
124
180
  .action(async (dir?: string, opts?: { target?: string }) => {
125
181
  const resolvedDir = dir ? resolve(dir) : undefined;
@@ -130,7 +186,7 @@ export function registerInstall(program: Command): void {
130
186
  process.exit(1);
131
187
  }
132
188
 
133
- const target = (opts?.target ?? 'opencode') as Target;
189
+ const target = (opts?.target ?? 'claude') as Target;
134
190
  const validTargets: Target[] = ['claude', 'codex', 'opencode', 'both', 'all'];
135
191
  const isFileTree = ctx.format === 'filetree';
136
192
 
@@ -155,6 +211,9 @@ export function registerInstall(program: Command): void {
155
211
  installOpencode(ctx.agentId, ctx.agentPath, isFileTree);
156
212
  }
157
213
 
214
+ // Create global launcher script
215
+ installLauncher(ctx.agentId, ctx.agentPath);
216
+
158
217
  p.log.info(`Agent ${ctx.agentId} is now available as an MCP server.`);
159
218
  });
160
219
  }
@@ -65,11 +65,3 @@ export function detectAgent(dir?: string): AgentContext | null {
65
65
  return null;
66
66
  }
67
67
  }
68
-
69
- /**
70
- * Detect a file-tree agent specifically (agent.yaml only).
71
- */
72
- export function detectFileTreeAgent(dir?: string): AgentContext | null {
73
- const ctx = detectAgent(dir);
74
- return ctx?.format === 'filetree' ? ctx : null;
75
- }