squidclaw 0.2.2 → 0.3.1

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/squidclaw.js CHANGED
@@ -24,8 +24,9 @@ program
24
24
 
25
25
  // ── Setup ──────────────────────────────────────────────
26
26
  program
27
- .command('setup')
28
- .description('Interactive first-time setup wizard')
27
+ .command('hatch')
28
+ .alias('setup')
29
+ .description('🥚 Hatch your first Squidclaw agent')
29
30
  .action(async () => {
30
31
  const { setup } = await import('../lib/cli/setup.js');
31
32
  await setup();
@@ -492,7 +493,7 @@ if (process.argv.length <= 2) {
492
493
  🦑 Squidclaw v${pkg.version}
493
494
  AI agent platform — human-like agents for WhatsApp & more
494
495
 
495
- ${chalk.gray('Get started:')} squidclaw setup
496
+ ${chalk.gray('Get started:')} squidclaw hatch
496
497
  ${chalk.gray('Help:')} squidclaw --help
497
498
  ${chalk.gray('Docs:')} https://squidclaw.dev
498
499
  `));
package/lib/cli/setup.js CHANGED
@@ -1,90 +1,110 @@
1
1
  /**
2
2
  * 🦑 Setup Wizard
3
- * Interactive first-time setup
3
+ * One command to install everything — AI, agent, WhatsApp, Telegram
4
4
  */
5
5
 
6
6
  import * as p from '@clack/prompts';
7
7
  import chalk from 'chalk';
8
8
  import { loadConfig, saveConfig, ensureHome, getHome } from '../core/config.js';
9
- import { writeFileSync, mkdirSync } from 'fs';
9
+ import { writeFileSync, mkdirSync, existsSync } from 'fs';
10
10
  import { join } from 'path';
11
11
  import crypto from 'crypto';
12
12
 
13
13
  export async function setup() {
14
14
  console.clear();
15
- p.intro(chalk.cyan('🦑 Welcome to Squidclaw!'));
15
+ console.log(chalk.cyan('\n ╔══════════════════════════════════════╗'));
16
+ console.log(chalk.cyan(' ║ 🥚 Squidclaw Hatch ║'));
17
+ console.log(chalk.cyan(' ║ Hatching your AI agent... ║'));
18
+ console.log(chalk.cyan(' ╚══════════════════════════════════════╝\n'));
19
+ p.intro(chalk.gray("Let's get you up and running"));
16
20
 
17
21
  const config = loadConfig();
18
22
  ensureHome();
19
23
 
20
- // Step 1: AI Provider
24
+ // ═══ Step 1: AI Provider ═══
25
+ p.note('Step 1 of 5: AI Brain', '🧠');
26
+
21
27
  const provider = await p.select({
22
28
  message: 'Choose your AI provider:',
23
29
  options: [
24
30
  { value: 'anthropic', label: '🟣 Anthropic (Claude)', hint: 'recommended' },
25
31
  { value: 'openai', label: '🟢 OpenAI (GPT-4o)' },
26
- { value: 'google', label: '🔵 Google (Gemini)', hint: 'free tier available' },
32
+ { value: 'google', label: '🔵 Google (Gemini)', hint: 'free tier' },
27
33
  { value: 'groq', label: '⚡ Groq (Llama)', hint: 'free, very fast' },
28
34
  { value: 'cerebras', label: '🧠 Cerebras (Llama)', hint: 'free, fastest' },
29
- { value: 'ollama', label: '🏠 Ollama (Local)', hint: 'runs on your machine, no key needed' },
35
+ { value: 'together', label: '🤝 Together AI' },
36
+ { value: 'mistral', label: '🇫🇷 Mistral' },
37
+ { value: 'openrouter', label: '🔀 OpenRouter', hint: 'access all models' },
38
+ { value: 'ollama', label: '🏠 Ollama (Local)', hint: 'no key needed' },
39
+ { value: 'lmstudio', label: '🖥️ LM Studio (Local)', hint: 'no key needed' },
30
40
  ],
31
41
  });
32
42
  if (p.isCancel(provider)) return p.cancel('Setup cancelled');
33
43
 
34
- // Step 2: API Key
35
44
  let apiKey = 'local';
36
- if (provider !== 'ollama' && provider !== 'lmstudio') {
37
- const keyHint = {
38
- anthropic: 'Get key at console.anthropic.com',
39
- openai: 'Get key at platform.openai.com',
40
- google: 'Get key at aistudio.google.dev',
41
- groq: 'Get key at console.groq.com (free)',
42
- cerebras: 'Get key at cloud.cerebras.ai (free)',
45
+ if (!['ollama', 'lmstudio'].includes(provider)) {
46
+ const keyHints = {
47
+ anthropic: 'console.anthropic.com/keys',
48
+ openai: 'platform.openai.com/api-keys',
49
+ google: 'aistudio.google.dev/apikey',
50
+ groq: 'console.groq.com/keys (free)',
51
+ cerebras: 'cloud.cerebras.ai (free)',
52
+ together: 'api.together.ai/settings',
53
+ mistral: 'console.mistral.ai',
54
+ openrouter: 'openrouter.ai/keys',
43
55
  };
44
-
45
56
  apiKey = await p.text({
46
- message: `API Key: ${chalk.gray(keyHint[provider] || '')}`,
47
- placeholder: 'sk-...',
57
+ message: 'API Key:',
58
+ placeholder: 'Get yours at ' + (keyHints[provider] || 'provider website'),
48
59
  validate: (v) => v.length < 5 ? 'Key is too short' : undefined,
49
60
  });
50
61
  if (p.isCancel(apiKey)) return p.cancel('Setup cancelled');
51
62
  }
52
63
 
53
- // Step 3: Model
54
- const modelOptions = {
64
+ const modelOpts = {
55
65
  anthropic: [
56
- { value: 'claude-sonnet-4', label: 'Claude Sonnet 4', hint: 'balanced — recommended' },
66
+ { value: 'claude-sonnet-4', label: 'Claude Sonnet 4', hint: 'balanced ' },
57
67
  { value: 'claude-opus-4', label: 'Claude Opus 4', hint: 'most powerful' },
58
68
  { value: 'claude-haiku-3.5', label: 'Claude Haiku 3.5', hint: 'fastest, cheapest' },
59
69
  ],
60
70
  openai: [
61
- { value: 'gpt-4o', label: 'GPT-4o', hint: 'recommended' },
71
+ { value: 'gpt-4o', label: 'GPT-4o', hint: 'recommended' },
62
72
  { value: 'gpt-4o-mini', label: 'GPT-4o Mini', hint: 'cheaper' },
63
73
  ],
64
74
  google: [
65
- { value: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash', hint: 'recommended' },
66
- { value: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro', hint: 'most powerful' },
75
+ { value: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash', hint: 'fast ⭐' },
76
+ { value: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro', hint: 'powerful' },
67
77
  ],
68
78
  groq: [
69
- { value: 'groq/llama-4-scout', label: 'Llama 4 Scout', hint: 'free, fast' },
79
+ { value: 'groq/llama-4-scout', label: 'Llama 4 Scout', hint: 'free ' },
70
80
  { value: 'groq/llama-4-maverick', label: 'Llama 4 Maverick', hint: 'free, powerful' },
71
81
  ],
72
- cerebras: [
73
- { value: 'cerebras/llama-3.3-70b', label: 'Llama 3.3 70B', hint: 'free, fastest' },
74
- ],
75
- ollama: [
76
- { value: 'ollama/llama3.3', label: 'Llama 3.3 (local)' },
82
+ cerebras: [{ value: 'cerebras/llama-3.3-70b', label: 'Llama 3.3 70B', hint: 'free ⭐' }],
83
+ together: [{ value: 'together/llama-4-maverick', label: 'Llama 4 Maverick ' }],
84
+ mistral: [{ value: 'mistral/mistral-large', label: 'Mistral Large ⭐' }],
85
+ openrouter: [
86
+ { value: 'openrouter/anthropic/claude-sonnet-4', label: 'Claude Sonnet 4 ⭐' },
87
+ { value: 'openrouter/openai/gpt-4o', label: 'GPT-4o' },
77
88
  ],
89
+ ollama: [{ value: 'ollama/llama3.3', label: 'Llama 3.3' }],
90
+ lmstudio: [{ value: 'lmstudio/default', label: 'Currently loaded model' }],
78
91
  };
79
92
 
80
93
  const model = await p.select({
81
94
  message: 'Choose your model:',
82
- options: modelOptions[provider] || modelOptions.openai,
95
+ options: modelOpts[provider] || modelOpts.openai,
83
96
  });
84
97
  if (p.isCancel(model)) return p.cancel('Setup cancelled');
85
98
 
86
- // Step 4: Create first agent
87
- p.note('Let\'s create your first AI agent');
99
+ config.ai = config.ai || { providers: {} };
100
+ config.ai.defaultProvider = provider;
101
+ config.ai.defaultModel = model;
102
+ config.ai.providers = config.ai.providers || {};
103
+ config.ai.providers[provider] = config.ai.providers[provider] || {};
104
+ config.ai.providers[provider].key = apiKey;
105
+
106
+ // ═══ Step 2: Agent ═══
107
+ p.note('Step 2 of 5: Your Agent', '🤖');
88
108
 
89
109
  const agentName = await p.text({
90
110
  message: 'What should your agent be called?',
@@ -102,125 +122,172 @@ export async function setup() {
102
122
  const language = await p.select({
103
123
  message: 'Language:',
104
124
  options: [
125
+ { value: 'bilingual', label: '🌍 Bilingual (Arabic + English)', hint: 'auto-detects' },
105
126
  { value: 'en', label: '🇬🇧 English' },
106
127
  { value: 'ar', label: '🇸🇦 Arabic' },
107
- { value: 'bilingual', label: '🌍 Bilingual (Arabic + English)' },
128
+ { value: 'fr', label: '🇫🇷 French' },
129
+ { value: 'tr', label: '🇹🇷 Turkish' },
130
+ { value: 'ur', label: '🇵🇰 Urdu' },
108
131
  ],
109
132
  });
110
133
  if (p.isCancel(language)) return p.cancel('Setup cancelled');
111
134
 
112
135
  const tone = await p.select({
113
- message: 'Tone:',
136
+ message: 'Personality:',
114
137
  options: [
115
- { value: 30, label: '👔 Formal' },
116
- { value: 50, label: '🤝 Professional but friendly' },
138
+ { value: 50, label: '🤝 Professional but friendly', hint: 'recommended' },
117
139
  { value: 80, label: '😊 Casual and warm' },
140
+ { value: 30, label: '👔 Formal and polished' },
141
+ { value: 90, label: '🔥 Fun and sarcastic' },
118
142
  ],
119
143
  });
120
144
  if (p.isCancel(tone)) return p.cancel('Setup cancelled');
121
145
 
122
- // Step 5: WhatsApp
123
- const connectWA = await p.confirm({
124
- message: 'Connect WhatsApp now?',
125
- initialValue: true,
126
- });
127
- if (p.isCancel(connectWA)) return p.cancel('Setup cancelled');
128
-
129
- // Save config
130
- config.ai.defaultProvider = provider;
131
- config.ai.defaultModel = model;
132
- config.ai.providers[provider].key = apiKey;
133
-
134
- if (connectWA) {
135
- config.channels.whatsapp.enabled = true;
136
- }
137
-
138
- saveConfig(config);
139
-
140
- // Create agent files
141
146
  const agentId = crypto.randomUUID().slice(0, 8);
142
147
  const agentDir = join(getHome(), 'agents', agentId);
143
148
  mkdirSync(join(agentDir, 'memory'), { recursive: true });
149
+ mkdirSync(join(agentDir, 'knowledge'), { recursive: true });
144
150
 
145
- const soul = `# ${agentName}
151
+ const langText = { bilingual: 'Bilingual — match the customer language', en: 'English', ar: 'Arabic', fr: 'French', tr: 'Turkish', ur: 'Urdu' };
152
+ const toneText = { 30: 'Formal and polished.', 50: 'Professional but warm.', 80: 'Casual and friendly.', 90: 'Fun and sarcastic.' };
146
153
 
147
- ## Who I Am
148
- I'm ${agentName}. ${agentPurpose || 'I\'m a helpful AI assistant.'}
154
+ const soul = '# ' + agentName + '\n\n## Who I Am\nI\'m ' + agentName + '. ' + (agentPurpose || 'A helpful AI assistant.') + '\n\n## How I Speak\n- Language: ' + (langText[language] || 'English') + '\n- Tone: ' + (toneText[tone] || toneText[50]) + '\n- Short messages, natural, human-like\n- Emojis: natural but not overdone\n\n## What I Do\n' + (agentPurpose || 'Help people.') + '\n\n## Never\n- Say "As an AI" or "I\'d be happy to help!"\n- Send walls of text\n- Make things up\n- Over-respond to "thanks" or "ok" — just react ❤️\n';
149
155
 
150
- ## How I Speak
151
- - Language: ${language === 'ar' ? 'Arabic' : language === 'bilingual' ? 'Bilingual I match whatever language the person uses' : 'English'}
152
- - Tone: ${tone > 60 ? 'Casual and friendly. I text like a real person.' : tone > 30 ? 'Professional but warm. Friendly without being too casual.' : 'Formal and polished. I maintain professionalism.'}
153
- - Platform: WhatsApp short messages, natural, human-like
154
- - I use emojis naturally but don't overdo it
156
+ writeFileSync(join(agentDir, 'SOUL.md'), soul);
157
+ writeFileSync(join(agentDir, 'IDENTITY.md'), '# ' + agentName + '\n- Name: ' + agentName + '\n- Language: ' + language + '\n- Emoji: 🦑\n');
158
+ writeFileSync(join(agentDir, 'MEMORY.md'), '# Memory\n\n_Learning about people._\n');
159
+ writeFileSync(join(agentDir, 'RULES.md'), '# Rules\n\n1. Be helpful, be human, be honest\n2. Keep messages short\n3. Don\'t make things up\n4. Match customer language\n5. Respect privacy\n');
160
+ writeFileSync(join(agentDir, 'BEHAVIOR.md'), JSON.stringify({ splitMessages: true, maxChunkLength: 200, reactBeforeReply: true, reactOnlyEndings: true, autoDetectLanguage: true, avoidPhrases: ["Is there anything else I can help with?", "I'd be happy to help!", "Great question!", "As an AI"], handoff: { enabled: false }, heartbeat: '30m' }, null, 2));
155
161
 
156
- ## What I Do
157
- ${agentPurpose || 'Help people with their questions and tasks.'}
162
+ const manifest = { id: agentId, name: agentName, language, tone, model, status: 'active', createdAt: new Date().toISOString() };
158
163
 
159
- ## What I Never Do
160
- - Say "As an AI" or "I'd be happy to help" or "Great question!"
161
- - Send walls of text — I keep it short and conversational
162
- - Make things up when I don't know — I say "let me check" instead
163
- `;
164
+ // ═══ Step 3: WhatsApp ═══
165
+ p.note('Step 3 of 5: WhatsApp', '📱');
164
166
 
165
- writeFileSync(join(agentDir, 'SOUL.md'), soul);
166
- writeFileSync(join(agentDir, 'IDENTITY.md'), `# ${agentName}\n- Name: ${agentName}\n- Language: ${language}\n- Emoji: 🦑\n`);
167
- writeFileSync(join(agentDir, 'MEMORY.md'), `# ${agentName}'s Memory\n\n_Learning about people and building relationships._\n`);
168
- writeFileSync(join(agentDir, 'RULES.md'), `# Rules\n\n1. Be helpful, be human, be honest\n2. Keep messages short — this is WhatsApp, not email\n3. If unsure, say so instead of making things up\n`);
169
- writeFileSync(join(agentDir, 'BEHAVIOR.md'), JSON.stringify({
170
- splitMessages: true,
171
- maxChunkLength: 200,
172
- reactBeforeReply: true,
173
- reactOnlyEndings: true,
174
- autoDetectLanguage: true,
175
- avoidPhrases: [
176
- "Is there anything else I can help with?",
177
- "I'd be happy to help!",
178
- "Great question!",
179
- "As an AI",
167
+ const connectWA = await p.select({
168
+ message: 'Connect WhatsApp?',
169
+ options: [
170
+ { value: 'pair', label: '📲 Pairing Code (easier)', hint: 'enter phone number' },
171
+ { value: 'qr', label: '📷 QR Code', hint: 'scan with phone' },
172
+ { value: 'skip', label: '⏭️ Skip for now' },
180
173
  ],
181
- handoff: { enabled: false },
182
- heartbeat: '30m',
183
- }, null, 2));
184
-
185
- // Save agent to a manifest file for the engine to pick up
186
- const manifest = {
187
- id: agentId,
188
- name: agentName,
189
- soul,
190
- language,
191
- tone,
192
- model,
193
- status: 'active',
194
- createdAt: new Date().toISOString(),
195
- };
196
- writeFileSync(join(agentDir, 'agent.json'), JSON.stringify(manifest, null, 2));
197
-
198
- p.note(`Agent "${agentName}" created in ${agentDir}`);
174
+ });
175
+ if (p.isCancel(connectWA)) return p.cancel('Setup cancelled');
199
176
 
200
- // WhatsApp pairing
201
- if (connectWA) {
202
- const phone = await p.text({
203
- message: '📱 Your WhatsApp phone number:',
177
+ if (connectWA === 'pair') {
178
+ const waPhone = await p.text({
179
+ message: 'WhatsApp phone number (with country code):',
204
180
  placeholder: '+966 5XX XXX XXXX',
205
181
  validate: (v) => v.replace(/[^0-9+]/g, '').length < 8 ? 'Enter a valid phone number' : undefined,
206
182
  });
183
+ if (!p.isCancel(waPhone)) {
184
+ manifest.whatsappNumber = waPhone.replace(/[^0-9]/g, '');
185
+ manifest.whatsappLoginMethod = 'pair';
186
+ }
187
+ } else if (connectWA === 'qr') {
188
+ manifest.whatsappLoginMethod = 'qr';
189
+ }
190
+
191
+ if (connectWA !== 'skip') {
192
+ config.channels = config.channels || {};
193
+ config.channels.whatsapp = config.channels.whatsapp || {};
194
+ config.channels.whatsapp.enabled = true;
195
+ }
196
+
197
+ // ═══ Step 4: Telegram ═══
198
+ p.note('Step 4 of 5: Telegram', '✈️');
199
+
200
+ const connectTG = await p.confirm({ message: 'Connect Telegram bot?', initialValue: false });
201
+ if (p.isCancel(connectTG)) return p.cancel('Setup cancelled');
202
+
203
+ if (connectTG) {
204
+ console.log(chalk.gray('\n To create a Telegram bot:\n 1. Open @BotFather on Telegram\n 2. Send /newbot\n 3. Copy the token\n'));
205
+ const tgToken = await p.text({
206
+ message: 'Telegram Bot Token:',
207
+ placeholder: '123456789:ABCdefGhIjKlMnOpQrStUvWxYz',
208
+ validate: (v) => !v.includes(':') ? 'Invalid token (should contain ":")' : undefined,
209
+ });
210
+ if (!p.isCancel(tgToken)) {
211
+ manifest.telegramToken = tgToken;
212
+ config.channels = config.channels || {};
213
+ config.channels.telegram = { enabled: true };
214
+ }
215
+ }
216
+
217
+ // ═══ Step 5: Knowledge ═══
218
+ p.note('Step 5 of 5: Knowledge (optional)', '📚');
219
+
220
+ const addKnowledge = await p.confirm({
221
+ message: 'Upload docs now? (menu, FAQ, etc — you can add later too)',
222
+ initialValue: false,
223
+ });
224
+ if (p.isCancel(addKnowledge)) return p.cancel('Setup cancelled');
225
+
226
+ const knowledgeFiles = [];
227
+ if (addKnowledge) {
228
+ let addMore = true;
229
+ while (addMore) {
230
+ const ft = await p.select({
231
+ message: 'What to add?',
232
+ options: [
233
+ { value: 'file', label: '📄 Local file (PDF, TXT, MD)' },
234
+ { value: 'url', label: '🌐 Website URL' },
235
+ { value: 'text', label: '✏️ Type it directly' },
236
+ ],
237
+ });
238
+ if (p.isCancel(ft)) break;
207
239
 
208
- if (!p.isCancel(phone)) {
209
- p.note(`WhatsApp will connect when you run ${chalk.cyan('squidclaw start')}\nThen run ${chalk.cyan('squidclaw channels login whatsapp')} to link your account.`);
210
-
211
- // Save phone to agent manifest
212
- manifest.whatsappNumber = phone.replace(/[^0-9+]/g, '');
213
- writeFileSync(join(agentDir, 'agent.json'), JSON.stringify(manifest, null, 2));
240
+ if (ft === 'file') {
241
+ const fp = await p.text({ message: 'File path:', placeholder: '/path/to/menu.pdf', validate: (v) => !existsSync(v) ? 'File not found' : undefined });
242
+ if (!p.isCancel(fp)) knowledgeFiles.push({ type: 'file', path: fp });
243
+ } else if (ft === 'url') {
244
+ const url = await p.text({ message: 'URL:', placeholder: 'https://mysite.com/menu', validate: (v) => !v.startsWith('http') ? 'Enter a valid URL' : undefined });
245
+ if (!p.isCancel(url)) knowledgeFiles.push({ type: 'url', path: url });
246
+ } else {
247
+ const txt = await p.text({ message: 'Paste your text:', placeholder: 'Business hours: 9am-11pm...' });
248
+ if (!p.isCancel(txt)) knowledgeFiles.push({ type: 'text', content: txt });
249
+ }
250
+
251
+ const more = await p.confirm({ message: 'Add more?', initialValue: false });
252
+ addMore = !p.isCancel(more) && more;
214
253
  }
215
254
  }
216
255
 
217
- // Done!
218
- p.outro(chalk.green(`
219
- 🦑 Setup complete!
256
+ // ═══ Save everything ═══
257
+ saveConfig(config);
258
+ writeFileSync(join(agentDir, 'agent.json'), JSON.stringify(manifest, null, 2));
259
+ if (knowledgeFiles.length > 0) {
260
+ writeFileSync(join(agentDir, 'knowledge', 'pending.json'), JSON.stringify(knowledgeFiles, null, 2));
261
+ }
262
+
263
+ // ═══ Summary ═══
264
+ const waStatus = connectWA === 'pair' ? 'Pairing code (on start)' : connectWA === 'qr' ? 'QR code (on start)' : 'skipped';
265
+ const tgStatus = connectTG ? 'bot ready' : 'skipped';
220
266
 
221
- Start: ${chalk.cyan('squidclaw start')}
222
- Status: ${chalk.cyan('squidclaw status')}
223
- Chat: ${chalk.cyan(`squidclaw agent chat ${agentId}`)}
224
- Docs: ${chalk.cyan('https://squidclaw.dev')}
225
- `));
267
+ console.log(chalk.green('\n ════════════════════════════════════'));
268
+ console.log(chalk.green(' Setup Complete!'));
269
+ console.log(chalk.green(' ════════════════════════════════════\n'));
270
+ console.log(' ' + chalk.bold('Agent:') + ' ' + agentName + ' (' + agentId + ')');
271
+ console.log(' ' + chalk.bold('Brain:') + ' ' + model + ' via ' + provider);
272
+ console.log(' ' + chalk.bold('Language:') + ' ' + (langText[language] || language));
273
+ console.log(' ' + chalk.bold('WhatsApp:') + ' ' + waStatus);
274
+ console.log(' ' + chalk.bold('Telegram:') + ' ' + tgStatus);
275
+ if (knowledgeFiles.length > 0) console.log(' ' + chalk.bold('Knowledge:') + ' ' + knowledgeFiles.length + ' items queued');
276
+ console.log('\n ────────────────────────────────────');
277
+ console.log('\n ' + chalk.bold('Now just run:\n'));
278
+ console.log(' ' + chalk.cyan('squidclaw start'));
279
+ if (connectWA === 'pair') {
280
+ console.log('\n A pairing code will appear. Enter it in:');
281
+ console.log(' WhatsApp → ⚙️ → Linked Devices → Link → Link with phone number');
282
+ } else if (connectWA === 'qr') {
283
+ console.log('\n A QR code will appear. Scan it in:');
284
+ console.log(' WhatsApp → ⚙️ → Linked Devices → Link a Device');
285
+ }
286
+ console.log('\n ' + chalk.bold('Other commands:\n'));
287
+ console.log(' ' + chalk.cyan('squidclaw status') + ' — check what\'s running');
288
+ console.log(' ' + chalk.cyan('squidclaw tui') + ' — chat in terminal');
289
+ console.log(' ' + chalk.cyan('squidclaw agent chat ' + agentId) + ' — test your agent');
290
+ console.log(' ' + chalk.cyan('squidclaw knowledge upload') + ' — add documents');
291
+ console.log(' ' + chalk.cyan('squidclaw help') + ' — all commands');
292
+ console.log('\n ════════════════════════════════════\n');
226
293
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squidclaw",
3
- "version": "0.2.2",
3
+ "version": "0.3.1",
4
4
  "description": "🦑 AI agent platform — human-like agents for WhatsApp, Telegram & more",
5
5
  "main": "lib/engine.js",
6
6
  "bin": {