@soleri/cli 1.12.5 → 8.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/dist/commands/add-pack.d.ts +2 -0
- package/dist/commands/add-pack.js +154 -0
- package/dist/commands/add-pack.js.map +1 -0
- package/dist/commands/agent.js +151 -2
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/cognee.d.ts +10 -0
- package/dist/commands/cognee.js +364 -0
- package/dist/commands/cognee.js.map +1 -0
- package/dist/commands/create.js +63 -5
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/dev.js +104 -17
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/install.js +70 -18
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/telegram.d.ts +10 -0
- package/dist/commands/telegram.js +423 -0
- package/dist/commands/telegram.js.map +1 -0
- package/dist/commands/uninstall.js +35 -6
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/main.js +6 -0
- package/dist/main.js.map +1 -1
- package/dist/prompts/create-wizard.js +87 -6
- package/dist/prompts/create-wizard.js.map +1 -1
- package/dist/utils/agent-context.d.ts +9 -2
- package/dist/utils/agent-context.js +32 -0
- package/dist/utils/agent-context.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/add-pack.ts +170 -0
- package/src/commands/agent.ts +174 -3
- package/src/commands/cognee.ts +419 -0
- package/src/commands/create.ts +90 -6
- package/src/commands/dev.ts +114 -18
- package/src/commands/install.ts +136 -18
- package/src/commands/telegram.ts +488 -0
- package/src/commands/uninstall.ts +41 -7
- package/src/main.ts +6 -0
- package/src/prompts/create-wizard.ts +93 -7
- package/src/utils/agent-context.ts +39 -2
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram transport management — enable, disable, setup, status.
|
|
3
|
+
*
|
|
4
|
+
* `soleri telegram enable` — Add Telegram files to the current agent
|
|
5
|
+
* `soleri telegram disable` — Remove Telegram files from the current agent
|
|
6
|
+
* `soleri telegram setup` — Interactive config wizard (bot token, API key, model)
|
|
7
|
+
* `soleri telegram status` — Check Telegram configuration status
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'node:fs';
|
|
12
|
+
import { homedir } from 'node:os';
|
|
13
|
+
import { execFileSync } from 'node:child_process';
|
|
14
|
+
import type { Command } from 'commander';
|
|
15
|
+
import * as p from '@clack/prompts';
|
|
16
|
+
import {
|
|
17
|
+
generateTelegramBot,
|
|
18
|
+
generateTelegramAgent,
|
|
19
|
+
generateTelegramConfig,
|
|
20
|
+
generateTelegramSupervisor,
|
|
21
|
+
} from '@soleri/forge/lib';
|
|
22
|
+
import type { AgentConfig } from '@soleri/forge/lib';
|
|
23
|
+
import { detectAgent } from '../utils/agent-context.js';
|
|
24
|
+
|
|
25
|
+
// ─── Telegram file paths relative to agent src/ ─────────────────────
|
|
26
|
+
|
|
27
|
+
const TELEGRAM_FILES = [
|
|
28
|
+
'src/telegram-bot.ts',
|
|
29
|
+
'src/telegram-agent.ts',
|
|
30
|
+
'src/telegram-config.ts',
|
|
31
|
+
'src/telegram-supervisor.ts',
|
|
32
|
+
] as const;
|
|
33
|
+
|
|
34
|
+
// ─── Registration ───────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
export function registerTelegram(program: Command): void {
|
|
37
|
+
const tg = program
|
|
38
|
+
.command('telegram')
|
|
39
|
+
.description('Manage Telegram transport for the current agent');
|
|
40
|
+
|
|
41
|
+
// ─── enable ─────────────────────────────────────────────────────
|
|
42
|
+
tg.command('enable')
|
|
43
|
+
.description('Add Telegram transport files to the current agent')
|
|
44
|
+
.action(() => {
|
|
45
|
+
const ctx = detectAgent();
|
|
46
|
+
if (!ctx) {
|
|
47
|
+
p.log.error('No agent project detected in current directory.');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check if already enabled
|
|
53
|
+
const existingFiles = TELEGRAM_FILES.filter((f) => existsSync(join(ctx.agentPath, f)));
|
|
54
|
+
if (existingFiles.length === TELEGRAM_FILES.length) {
|
|
55
|
+
p.log.warn('Telegram is already enabled for this agent.');
|
|
56
|
+
p.log.info('Run `soleri telegram setup` to configure it.');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (existingFiles.length > 0) {
|
|
60
|
+
p.log.warn(
|
|
61
|
+
`Partial Telegram setup detected (${existingFiles.length}/${TELEGRAM_FILES.length} files). Regenerating all files.`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Reconstruct AgentConfig
|
|
66
|
+
const config = readAgentConfig(ctx.agentPath, ctx.agentId);
|
|
67
|
+
if (!config) {
|
|
68
|
+
p.log.error('Could not read agent config from persona.ts and entry point.');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Generate the 4 Telegram files
|
|
74
|
+
const s = p.spinner();
|
|
75
|
+
s.start('Generating Telegram transport files...');
|
|
76
|
+
|
|
77
|
+
const telegramFiles: Array<[string, string]> = [
|
|
78
|
+
['src/telegram-bot.ts', generateTelegramBot(config)],
|
|
79
|
+
['src/telegram-agent.ts', generateTelegramAgent(config)],
|
|
80
|
+
['src/telegram-config.ts', generateTelegramConfig(config)],
|
|
81
|
+
['src/telegram-supervisor.ts', generateTelegramSupervisor(config)],
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
for (const [relPath, content] of telegramFiles) {
|
|
85
|
+
writeFileSync(join(ctx.agentPath, relPath), content, 'utf-8');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
s.stop('Generated 4 Telegram transport files');
|
|
89
|
+
|
|
90
|
+
// Add grammy to package.json if not present
|
|
91
|
+
const pkgPath = join(ctx.agentPath, 'package.json');
|
|
92
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
93
|
+
let pkgChanged = false;
|
|
94
|
+
|
|
95
|
+
if (!pkg.dependencies?.grammy) {
|
|
96
|
+
if (!pkg.dependencies) pkg.dependencies = {};
|
|
97
|
+
pkg.dependencies.grammy = '^1.35.0';
|
|
98
|
+
pkgChanged = true;
|
|
99
|
+
p.log.info('Added grammy dependency to package.json');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Add npm scripts if not present
|
|
103
|
+
if (!pkg.scripts) pkg.scripts = {};
|
|
104
|
+
if (!pkg.scripts['telegram:start']) {
|
|
105
|
+
pkg.scripts['telegram:start'] = 'node dist/telegram-supervisor.js';
|
|
106
|
+
pkgChanged = true;
|
|
107
|
+
}
|
|
108
|
+
if (!pkg.scripts['telegram:dev']) {
|
|
109
|
+
pkg.scripts['telegram:dev'] = 'tsx src/telegram-bot.ts';
|
|
110
|
+
pkgChanged = true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (pkgChanged) {
|
|
114
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
115
|
+
p.log.info('Updated package.json with telegram scripts');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Install grammy
|
|
119
|
+
const installSpinner = p.spinner();
|
|
120
|
+
installSpinner.start('Installing grammy...');
|
|
121
|
+
try {
|
|
122
|
+
execFileSync('npm', ['install', '--no-fund', '--no-audit'], {
|
|
123
|
+
cwd: ctx.agentPath,
|
|
124
|
+
stdio: 'pipe',
|
|
125
|
+
timeout: 120_000,
|
|
126
|
+
});
|
|
127
|
+
installSpinner.stop('Installed grammy');
|
|
128
|
+
} catch {
|
|
129
|
+
installSpinner.stop('npm install skipped — run `npm install` manually');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
p.log.success('Telegram enabled!');
|
|
133
|
+
p.log.info(`Run \`soleri telegram setup\` to configure bot token and API key.`);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// ─── setup ──────────────────────────────────────────────────────
|
|
137
|
+
tg.command('setup')
|
|
138
|
+
.description('Interactive Telegram configuration wizard')
|
|
139
|
+
.action(async () => {
|
|
140
|
+
const ctx = detectAgent();
|
|
141
|
+
if (!ctx) {
|
|
142
|
+
p.log.error('No agent project detected in current directory.');
|
|
143
|
+
process.exit(1);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Check that Telegram files exist
|
|
148
|
+
const missingFiles = TELEGRAM_FILES.filter((f) => !existsSync(join(ctx.agentPath, f)));
|
|
149
|
+
if (missingFiles.length > 0) {
|
|
150
|
+
p.log.error('Telegram is not enabled. Run `soleri telegram enable` first.');
|
|
151
|
+
process.exit(1);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
p.intro(`Telegram Setup for ${ctx.agentId}`);
|
|
156
|
+
|
|
157
|
+
// Step 1: Bot token
|
|
158
|
+
p.log.step('Step 1: Create a Telegram bot');
|
|
159
|
+
p.log.message(
|
|
160
|
+
[
|
|
161
|
+
' Open Telegram and talk to @BotFather',
|
|
162
|
+
' Send /newbot and follow the instructions',
|
|
163
|
+
' Copy the bot token when you get it',
|
|
164
|
+
].join('\n'),
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const botToken = await p.text({
|
|
168
|
+
message: 'Paste your bot token:',
|
|
169
|
+
placeholder: '123456789:ABCdefGHIjklMNOpqrsTUVwxyz',
|
|
170
|
+
validate: (val) => {
|
|
171
|
+
if (!val || val.trim().length === 0) return 'Bot token is required';
|
|
172
|
+
if (!val.includes(':')) return 'Invalid token format (expected number:string)';
|
|
173
|
+
return undefined;
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
if (p.isCancel(botToken)) {
|
|
177
|
+
p.cancel('Setup cancelled.');
|
|
178
|
+
process.exit(0);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Step 2: LLM API key
|
|
182
|
+
p.log.step('Step 2: LLM API Key');
|
|
183
|
+
p.log.message(' Your agent needs an API key to think');
|
|
184
|
+
|
|
185
|
+
const provider = await p.select({
|
|
186
|
+
message: 'Which provider?',
|
|
187
|
+
options: [
|
|
188
|
+
{ value: 'anthropic', label: 'Anthropic (Claude)' },
|
|
189
|
+
{ value: 'openai', label: 'OpenAI' },
|
|
190
|
+
{ value: 'env', label: 'Use environment variable (skip)' },
|
|
191
|
+
],
|
|
192
|
+
});
|
|
193
|
+
if (p.isCancel(provider)) {
|
|
194
|
+
p.cancel('Setup cancelled.');
|
|
195
|
+
process.exit(0);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let apiKey = '';
|
|
199
|
+
if (provider !== 'env') {
|
|
200
|
+
const keyPlaceholder = provider === 'anthropic' ? 'sk-ant-...' : 'sk-...';
|
|
201
|
+
const envHint = provider === 'anthropic' ? 'ANTHROPIC_API_KEY' : 'OPENAI_API_KEY';
|
|
202
|
+
|
|
203
|
+
const keyInput = await p.text({
|
|
204
|
+
message: `Paste your ${provider === 'anthropic' ? 'Anthropic' : 'OpenAI'} API key:`,
|
|
205
|
+
placeholder: keyPlaceholder,
|
|
206
|
+
validate: (val) => {
|
|
207
|
+
if (!val || val.trim().length === 0)
|
|
208
|
+
return `API key is required (or set ${envHint} env var)`;
|
|
209
|
+
return undefined;
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
if (p.isCancel(keyInput)) {
|
|
213
|
+
p.cancel('Setup cancelled.');
|
|
214
|
+
process.exit(0);
|
|
215
|
+
}
|
|
216
|
+
apiKey = keyInput;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Step 3: Security
|
|
220
|
+
p.log.step('Step 3: Security (optional)');
|
|
221
|
+
|
|
222
|
+
const passphrase = await p.text({
|
|
223
|
+
message: 'Set a passphrase? Users must send this to authenticate.',
|
|
224
|
+
placeholder: '(empty for open access)',
|
|
225
|
+
defaultValue: '',
|
|
226
|
+
});
|
|
227
|
+
if (p.isCancel(passphrase)) {
|
|
228
|
+
p.cancel('Setup cancelled.');
|
|
229
|
+
process.exit(0);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Step 4: Model selection
|
|
233
|
+
p.log.step('Step 4: Model selection');
|
|
234
|
+
|
|
235
|
+
const modelOptions =
|
|
236
|
+
provider === 'openai'
|
|
237
|
+
? [
|
|
238
|
+
{ value: 'gpt-4.1', label: 'gpt-4.1 (recommended)' },
|
|
239
|
+
{ value: 'gpt-4.1-mini', label: 'gpt-4.1-mini (fast)' },
|
|
240
|
+
{ value: 'o3', label: 'o3 (reasoning)' },
|
|
241
|
+
]
|
|
242
|
+
: [
|
|
243
|
+
{ value: 'claude-sonnet-4-20250514', label: 'claude-sonnet-4 (fast, recommended)' },
|
|
244
|
+
{ value: 'claude-opus-4-20250514', label: 'claude-opus-4 (powerful)' },
|
|
245
|
+
{ value: 'claude-haiku-3-5-20241022', label: 'claude-3.5-haiku (economical)' },
|
|
246
|
+
];
|
|
247
|
+
|
|
248
|
+
const model = await p.select({
|
|
249
|
+
message: 'Default model:',
|
|
250
|
+
options: modelOptions,
|
|
251
|
+
});
|
|
252
|
+
if (p.isCancel(model)) {
|
|
253
|
+
p.cancel('Setup cancelled.');
|
|
254
|
+
process.exit(0);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Save config
|
|
258
|
+
const configDir = join(homedir(), `.${ctx.agentId}`);
|
|
259
|
+
mkdirSync(configDir, { recursive: true });
|
|
260
|
+
const configPath = join(configDir, 'telegram.json');
|
|
261
|
+
|
|
262
|
+
const telegramConfig: Record<string, unknown> = {
|
|
263
|
+
botToken: (botToken as string).trim(),
|
|
264
|
+
...(apiKey ? { apiKey } : {}),
|
|
265
|
+
provider: provider === 'env' ? 'anthropic' : provider,
|
|
266
|
+
model,
|
|
267
|
+
...(passphrase ? { passphrase } : {}),
|
|
268
|
+
allowedUsers: [],
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
writeFileSync(configPath, JSON.stringify(telegramConfig, null, 2) + '\n', 'utf-8');
|
|
272
|
+
|
|
273
|
+
p.outro(`Configuration saved to ${configPath}`);
|
|
274
|
+
|
|
275
|
+
console.log('');
|
|
276
|
+
p.log.info(' Run: npm run telegram:start');
|
|
277
|
+
p.log.info(' Or: npm run telegram:dev (with auto-restart)');
|
|
278
|
+
console.log('');
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// ─── disable ────────────────────────────────────────────────────
|
|
282
|
+
tg.command('disable')
|
|
283
|
+
.description('Remove Telegram transport from the current agent')
|
|
284
|
+
.action(async () => {
|
|
285
|
+
const ctx = detectAgent();
|
|
286
|
+
if (!ctx) {
|
|
287
|
+
p.log.error('No agent project detected in current directory.');
|
|
288
|
+
process.exit(1);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Check if any Telegram files exist
|
|
293
|
+
const existingFiles = TELEGRAM_FILES.filter((f) => existsSync(join(ctx.agentPath, f)));
|
|
294
|
+
if (existingFiles.length === 0) {
|
|
295
|
+
p.log.warn('Telegram is not enabled for this agent.');
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const confirmed = await p.confirm({
|
|
300
|
+
message: `Remove ${existingFiles.length} Telegram files and related config from ${ctx.agentId}?`,
|
|
301
|
+
});
|
|
302
|
+
if (p.isCancel(confirmed) || !confirmed) {
|
|
303
|
+
p.cancel('Cancelled.');
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Remove Telegram source files
|
|
308
|
+
for (const relPath of TELEGRAM_FILES) {
|
|
309
|
+
const fullPath = join(ctx.agentPath, relPath);
|
|
310
|
+
if (existsSync(fullPath)) {
|
|
311
|
+
unlinkSync(fullPath);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
p.log.info(`Removed ${existingFiles.length} Telegram source files`);
|
|
315
|
+
|
|
316
|
+
// Remove grammy from package.json and telegram scripts
|
|
317
|
+
const pkgPath = join(ctx.agentPath, 'package.json');
|
|
318
|
+
if (existsSync(pkgPath)) {
|
|
319
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
320
|
+
let changed = false;
|
|
321
|
+
|
|
322
|
+
if (pkg.dependencies?.grammy) {
|
|
323
|
+
delete pkg.dependencies.grammy;
|
|
324
|
+
changed = true;
|
|
325
|
+
}
|
|
326
|
+
if (pkg.scripts?.['telegram:start']) {
|
|
327
|
+
delete pkg.scripts['telegram:start'];
|
|
328
|
+
changed = true;
|
|
329
|
+
}
|
|
330
|
+
if (pkg.scripts?.['telegram:dev']) {
|
|
331
|
+
delete pkg.scripts['telegram:dev'];
|
|
332
|
+
changed = true;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (changed) {
|
|
336
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
337
|
+
p.log.info('Removed grammy dependency and telegram scripts from package.json');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
p.log.success('Telegram disabled.');
|
|
342
|
+
p.log.info('Run `npm install` to clean up node_modules.');
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// ─── status ─────────────────────────────────────────────────────
|
|
346
|
+
tg.command('status')
|
|
347
|
+
.description('Check Telegram configuration status')
|
|
348
|
+
.action(() => {
|
|
349
|
+
const ctx = detectAgent();
|
|
350
|
+
if (!ctx) {
|
|
351
|
+
p.log.error('No agent project detected in current directory.');
|
|
352
|
+
process.exit(1);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
console.log(`\n Agent: ${ctx.agentId}`);
|
|
357
|
+
|
|
358
|
+
// Check source files
|
|
359
|
+
const presentFiles = TELEGRAM_FILES.filter((f) => existsSync(join(ctx.agentPath, f)));
|
|
360
|
+
const filesEnabled = presentFiles.length === TELEGRAM_FILES.length;
|
|
361
|
+
console.log(
|
|
362
|
+
` Files: ${filesEnabled ? 'all present' : `${presentFiles.length}/${TELEGRAM_FILES.length} present`}`,
|
|
363
|
+
);
|
|
364
|
+
if (!filesEnabled && presentFiles.length > 0) {
|
|
365
|
+
for (const f of TELEGRAM_FILES) {
|
|
366
|
+
const exists = existsSync(join(ctx.agentPath, f));
|
|
367
|
+
console.log(` ${exists ? '+' : '-'} ${f}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Check grammy dependency
|
|
372
|
+
const pkgPath = join(ctx.agentPath, 'package.json');
|
|
373
|
+
if (existsSync(pkgPath)) {
|
|
374
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
375
|
+
const hasGrammy = !!pkg.dependencies?.grammy;
|
|
376
|
+
console.log(
|
|
377
|
+
` Grammy: ${hasGrammy ? `installed (${pkg.dependencies.grammy})` : 'not in dependencies'}`,
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const hasStartScript = !!pkg.scripts?.['telegram:start'];
|
|
381
|
+
const hasDevScript = !!pkg.scripts?.['telegram:dev'];
|
|
382
|
+
console.log(
|
|
383
|
+
` Scripts: ${hasStartScript && hasDevScript ? 'telegram:start, telegram:dev' : hasStartScript ? 'telegram:start only' : hasDevScript ? 'telegram:dev only' : 'none'}`,
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Check config file
|
|
388
|
+
const configPath = join(homedir(), `.${ctx.agentId}`, 'telegram.json');
|
|
389
|
+
if (existsSync(configPath)) {
|
|
390
|
+
try {
|
|
391
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
392
|
+
const hasToken = !!config.botToken;
|
|
393
|
+
const hasKey = !!config.apiKey;
|
|
394
|
+
console.log(` Config: ${configPath}`);
|
|
395
|
+
console.log(` Bot token: ${hasToken ? 'set' : 'not set'}`);
|
|
396
|
+
console.log(` API key: ${hasKey ? 'set' : 'not set (check env vars)'}`);
|
|
397
|
+
if (config.model) console.log(` Model: ${config.model}`);
|
|
398
|
+
if (config.passphrase) console.log(` Passphrase: set`);
|
|
399
|
+
} catch {
|
|
400
|
+
console.log(` Config: ${configPath} (invalid JSON)`);
|
|
401
|
+
}
|
|
402
|
+
} else {
|
|
403
|
+
console.log(` Config: not found at ${configPath}`);
|
|
404
|
+
if (filesEnabled) {
|
|
405
|
+
console.log(' Run `soleri telegram setup` to configure.');
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Overall status
|
|
410
|
+
const ready = filesEnabled && existsSync(configPath);
|
|
411
|
+
console.log(`\n Status: ${ready ? 'ready to start' : 'needs configuration'}`);
|
|
412
|
+
if (!filesEnabled) {
|
|
413
|
+
console.log(' Next: Run `soleri telegram enable`');
|
|
414
|
+
} else if (!existsSync(configPath)) {
|
|
415
|
+
console.log(' Next: Run `soleri telegram setup`');
|
|
416
|
+
} else {
|
|
417
|
+
console.log(' Next: Run `npm run telegram:start` or `npm run telegram:dev`');
|
|
418
|
+
}
|
|
419
|
+
console.log('');
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ─── Helpers ──────────────────────────────────────────────────────────
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Reconstruct an AgentConfig from an existing scaffolded agent.
|
|
427
|
+
* Mirrors the logic in agent.ts but returns a config with telegram: true.
|
|
428
|
+
*/
|
|
429
|
+
function readAgentConfig(agentPath: string, agentId: string): AgentConfig | null {
|
|
430
|
+
// Try both locations: v6+ (src/identity/) and v5 (src/activation/)
|
|
431
|
+
const personaCandidates = [
|
|
432
|
+
join(agentPath, 'src', 'identity', 'persona.ts'),
|
|
433
|
+
join(agentPath, 'src', 'activation', 'persona.ts'),
|
|
434
|
+
];
|
|
435
|
+
const personaPath = personaCandidates.find((candidate) => existsSync(candidate));
|
|
436
|
+
if (!personaPath) return null;
|
|
437
|
+
const personaSrc = readFileSync(personaPath, 'utf-8');
|
|
438
|
+
|
|
439
|
+
const name = extractStringField(personaSrc, 'name') ?? agentId;
|
|
440
|
+
const role = extractStringField(personaSrc, 'role') ?? '';
|
|
441
|
+
const description = extractStringField(personaSrc, 'description') ?? '';
|
|
442
|
+
const tone =
|
|
443
|
+
(extractStringField(personaSrc, 'tone') as 'precise' | 'mentor' | 'pragmatic') ?? 'pragmatic';
|
|
444
|
+
const greeting = extractStringField(personaSrc, 'greeting') ?? `Hello! I'm ${name}.`;
|
|
445
|
+
const principles = extractArrayField(personaSrc, 'principles');
|
|
446
|
+
|
|
447
|
+
// Read domains from entry point
|
|
448
|
+
const indexPath = join(agentPath, 'src', 'index.ts');
|
|
449
|
+
const domains = existsSync(indexPath) ? extractDomains(readFileSync(indexPath, 'utf-8')) : [];
|
|
450
|
+
|
|
451
|
+
const pkg = JSON.parse(readFileSync(join(agentPath, 'package.json'), 'utf-8'));
|
|
452
|
+
|
|
453
|
+
return {
|
|
454
|
+
id: agentId,
|
|
455
|
+
name,
|
|
456
|
+
role,
|
|
457
|
+
description,
|
|
458
|
+
domains,
|
|
459
|
+
principles,
|
|
460
|
+
tone,
|
|
461
|
+
greeting,
|
|
462
|
+
outputDir: agentPath,
|
|
463
|
+
hookPacks: [],
|
|
464
|
+
model: pkg.soleri?.model ?? 'claude-code-sonnet-4',
|
|
465
|
+
setupTarget: pkg.soleri?.setupTarget ?? 'claude',
|
|
466
|
+
telegram: true, // Force true — we're enabling telegram
|
|
467
|
+
cognee: pkg.soleri?.cognee ?? false,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function extractStringField(src: string, field: string): string | undefined {
|
|
472
|
+
const re = new RegExp(`${field}:\\s*'([^']*)'`);
|
|
473
|
+
const m = src.match(re);
|
|
474
|
+
return m ? m[1].replace(/\\'/g, "'") : undefined;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function extractArrayField(src: string, field: string): string[] {
|
|
478
|
+
const re = new RegExp(`${field}:\\s*\\[([\\s\\S]*?)\\]`);
|
|
479
|
+
const m = src.match(re);
|
|
480
|
+
if (!m) return [];
|
|
481
|
+
return [...m[1].matchAll(/'([^']*)'/g)].map((x) => x[1]);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function extractDomains(indexSrc: string): string[] {
|
|
485
|
+
const m = indexSrc.match(/createDomainFacades\(runtime,\s*['"][^'"]+['"]\s*,\s*\[([\s\S]*?)\]\)/);
|
|
486
|
+
if (!m) return [];
|
|
487
|
+
return [...m[1].matchAll(/['"]([^'"]+)['"]/g)].map((x) => x[1]);
|
|
488
|
+
}
|
|
@@ -5,7 +5,7 @@ import { homedir } from 'node:os';
|
|
|
5
5
|
import * as p from '@clack/prompts';
|
|
6
6
|
import { detectAgent } from '../utils/agent-context.js';
|
|
7
7
|
|
|
8
|
-
type Target = 'claude' | 'codex' | 'both';
|
|
8
|
+
type Target = 'claude' | 'codex' | 'opencode' | 'both' | 'all';
|
|
9
9
|
|
|
10
10
|
function uninstallClaude(agentId: string): void {
|
|
11
11
|
const configPath = join(homedir(), '.claude.json');
|
|
@@ -56,6 +56,35 @@ function uninstallCodex(agentId: string): void {
|
|
|
56
56
|
p.log.success(`Removed ${agentId} from ~/.codex/config.toml`);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
function uninstallOpencode(agentId: string): void {
|
|
60
|
+
const configPath = join(homedir(), '.opencode.json');
|
|
61
|
+
|
|
62
|
+
if (!existsSync(configPath)) {
|
|
63
|
+
p.log.warn(`~/.opencode.json not found — nothing to remove.`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let config: Record<string, unknown>;
|
|
68
|
+
try {
|
|
69
|
+
const raw = readFileSync(configPath, 'utf-8');
|
|
70
|
+
const stripped = raw.replace(/^\s*\/\/.*$/gm, '');
|
|
71
|
+
config = JSON.parse(stripped);
|
|
72
|
+
} catch {
|
|
73
|
+
p.log.error(`Failed to parse ${configPath}.`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const servers = config.mcpServers as Record<string, unknown> | undefined;
|
|
78
|
+
if (!servers || !(agentId in servers)) {
|
|
79
|
+
p.log.warn(`${agentId} not found in ~/.opencode.json — nothing to remove.`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
delete servers[agentId];
|
|
84
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
85
|
+
p.log.success(`Removed ${agentId} from ~/.opencode.json`);
|
|
86
|
+
}
|
|
87
|
+
|
|
59
88
|
function escapeRegExp(s: string): string {
|
|
60
89
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
61
90
|
}
|
|
@@ -64,7 +93,7 @@ export function registerUninstall(program: Command): void {
|
|
|
64
93
|
program
|
|
65
94
|
.command('uninstall')
|
|
66
95
|
.argument('[dir]', 'Agent directory (defaults to cwd)')
|
|
67
|
-
.option('--target <target>', 'Registration target: claude, codex, or
|
|
96
|
+
.option('--target <target>', 'Registration target: opencode, claude, codex, or all', 'opencode')
|
|
68
97
|
.description('Remove agent MCP server entry from editor config')
|
|
69
98
|
.action(async (dir?: string, opts?: { target?: string }) => {
|
|
70
99
|
const resolvedDir = dir ? resolve(dir) : undefined;
|
|
@@ -75,19 +104,24 @@ export function registerUninstall(program: Command): void {
|
|
|
75
104
|
process.exit(1);
|
|
76
105
|
}
|
|
77
106
|
|
|
78
|
-
const target = (opts?.target ?? '
|
|
107
|
+
const target = (opts?.target ?? 'opencode') as Target;
|
|
108
|
+
const validTargets: Target[] = ['claude', 'codex', 'opencode', 'both', 'all'];
|
|
79
109
|
|
|
80
|
-
if (target
|
|
81
|
-
p.log.error(`Invalid target "${target}". Use:
|
|
110
|
+
if (!validTargets.includes(target)) {
|
|
111
|
+
p.log.error(`Invalid target "${target}". Use: ${validTargets.join(', ')}`);
|
|
82
112
|
process.exit(1);
|
|
83
113
|
}
|
|
84
114
|
|
|
85
|
-
if (target === 'claude' || target === 'both') {
|
|
115
|
+
if (target === 'claude' || target === 'both' || target === 'all') {
|
|
86
116
|
uninstallClaude(ctx.agentId);
|
|
87
117
|
}
|
|
88
118
|
|
|
89
|
-
if (target === 'codex' || target === 'both') {
|
|
119
|
+
if (target === 'codex' || target === 'both' || target === 'all') {
|
|
90
120
|
uninstallCodex(ctx.agentId);
|
|
91
121
|
}
|
|
122
|
+
|
|
123
|
+
if (target === 'opencode' || target === 'all') {
|
|
124
|
+
uninstallOpencode(ctx.agentId);
|
|
125
|
+
}
|
|
92
126
|
});
|
|
93
127
|
}
|
package/src/main.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { Command } from 'commander';
|
|
|
5
5
|
import { registerCreate } from './commands/create.js';
|
|
6
6
|
import { registerList } from './commands/list.js';
|
|
7
7
|
import { registerAddDomain } from './commands/add-domain.js';
|
|
8
|
+
import { registerAddPack } from './commands/add-pack.js';
|
|
8
9
|
import { registerInstallKnowledge } from './commands/install-knowledge.js';
|
|
9
10
|
import { registerDev } from './commands/dev.js';
|
|
10
11
|
import { registerDoctor } from './commands/doctor.js';
|
|
@@ -18,6 +19,8 @@ import { registerUninstall } from './commands/uninstall.js';
|
|
|
18
19
|
import { registerPack } from './commands/pack.js';
|
|
19
20
|
import { registerSkills } from './commands/skills.js';
|
|
20
21
|
import { registerAgent } from './commands/agent.js';
|
|
22
|
+
import { registerTelegram } from './commands/telegram.js';
|
|
23
|
+
import { registerCognee } from './commands/cognee.js';
|
|
21
24
|
|
|
22
25
|
const require = createRequire(import.meta.url);
|
|
23
26
|
const { version } = require('../package.json');
|
|
@@ -63,6 +66,7 @@ program
|
|
|
63
66
|
registerCreate(program);
|
|
64
67
|
registerList(program);
|
|
65
68
|
registerAddDomain(program);
|
|
69
|
+
registerAddPack(program);
|
|
66
70
|
registerInstallKnowledge(program);
|
|
67
71
|
registerDev(program);
|
|
68
72
|
registerDoctor(program);
|
|
@@ -76,5 +80,7 @@ registerUninstall(program);
|
|
|
76
80
|
registerPack(program);
|
|
77
81
|
registerSkills(program);
|
|
78
82
|
registerAgent(program);
|
|
83
|
+
registerTelegram(program);
|
|
84
|
+
registerCognee(program);
|
|
79
85
|
|
|
80
86
|
program.parse();
|