@cilow/cli 0.1.0 → 0.2.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.
Files changed (2) hide show
  1. package/dist/cli.js +473 -302
  2. package/package.json +5 -5
package/dist/cli.js CHANGED
@@ -4,6 +4,8 @@
4
4
  import { Command } from "commander";
5
5
  import chalk from "chalk";
6
6
  import ora from "ora";
7
+ import * as readline from "readline";
8
+ var VERSION = "0.2.0";
7
9
  var DEFAULT_API_URL = "https://cilow-gateway-c8ozzar9.uc.gateway.dev";
8
10
  function getConfig() {
9
11
  const apiKey = process.env.CILOW_API_KEY;
@@ -15,18 +17,8 @@ function getConfig() {
15
17
  }
16
18
  return { apiUrl, apiKey };
17
19
  }
18
- async function makeRequest(config, method, path, body, queryParams) {
19
- let url = `${config.apiUrl}${path}`;
20
- if (queryParams) {
21
- const params = new URLSearchParams();
22
- for (const [key, value] of Object.entries(queryParams)) {
23
- if (value !== void 0) {
24
- params.append(key, String(value));
25
- }
26
- }
27
- const qs = params.toString();
28
- if (qs) url += `?${qs}`;
29
- }
20
+ async function makeRequest(config, method, path, body) {
21
+ const url = `${config.apiUrl}${path}`;
30
22
  const headers = {
31
23
  "Content-Type": "application/json"
32
24
  };
@@ -47,18 +39,454 @@ async function makeRequest(config, method, path, body, queryParams) {
47
39
  const text = await response.text();
48
40
  return text ? JSON.parse(text) : null;
49
41
  }
42
+ async function startChat(config, options) {
43
+ const messages = [];
44
+ const provider = options.provider || "anthropic";
45
+ const model = options.model || "claude-3-5-haiku-20241022";
46
+ console.log(chalk.bold.cyan("\n\u{1F9E0} Cilow AI Chat"));
47
+ console.log(chalk.dim("Your AI assistant with persistent memory\n"));
48
+ console.log(chalk.dim("Commands: /help, /memories, /clear, /exit\n"));
49
+ const rl = readline.createInterface({
50
+ input: process.stdin,
51
+ output: process.stdout
52
+ });
53
+ const prompt = () => {
54
+ rl.question(chalk.green("You: "), async (input) => {
55
+ const trimmed = input.trim();
56
+ if (!trimmed) {
57
+ prompt();
58
+ return;
59
+ }
60
+ if (trimmed.startsWith("/")) {
61
+ await handleCommand(trimmed, config, messages);
62
+ prompt();
63
+ return;
64
+ }
65
+ messages.push({ role: "user", content: trimmed });
66
+ const spinner = ora({ text: "Thinking...", color: "cyan" }).start();
67
+ try {
68
+ const result = await makeRequest(config, "POST", "/api/v2/router", {
69
+ provider,
70
+ model,
71
+ messages,
72
+ auto_context: true
73
+ });
74
+ spinner.stop();
75
+ if (result.success && result.response) {
76
+ messages.push({ role: "assistant", content: result.response });
77
+ console.log(chalk.cyan("\nCilow: ") + result.response);
78
+ if (result.memories_used && result.memories_used > 0) {
79
+ console.log(chalk.dim(` [${result.memories_used} memories used]`));
80
+ }
81
+ console.log();
82
+ } else {
83
+ console.log(chalk.red("\nError: ") + (result.error || "Unknown error"));
84
+ console.log();
85
+ }
86
+ } catch (err) {
87
+ spinner.stop();
88
+ console.log(chalk.red("\nError: ") + String(err));
89
+ console.log();
90
+ }
91
+ prompt();
92
+ });
93
+ };
94
+ rl.on("close", () => {
95
+ console.log(chalk.dim("\nGoodbye! Your memories are saved.\n"));
96
+ process.exit(0);
97
+ });
98
+ prompt();
99
+ }
100
+ async function handleCommand(cmd, config, messages) {
101
+ const parts = cmd.slice(1).split(" ");
102
+ const command = parts[0].toLowerCase();
103
+ switch (command) {
104
+ case "help":
105
+ console.log(chalk.bold("\nCommands:"));
106
+ console.log(" /memories - Show relevant memories");
107
+ console.log(" /add <text> - Add a memory");
108
+ console.log(" /decide <decision> - Store a decision");
109
+ console.log(" /clear - Clear conversation");
110
+ console.log(" /exit - Exit chat");
111
+ console.log();
112
+ break;
113
+ case "memories":
114
+ const spinner = ora("Fetching memories...").start();
115
+ try {
116
+ const lastUserMsg = messages.filter((m) => m.role === "user").pop();
117
+ const query = lastUserMsg?.content || "recent memories";
118
+ const result = await makeRequest(config, "POST", "/api/v1/memory/search", {
119
+ query,
120
+ limit: 5
121
+ });
122
+ spinner.stop();
123
+ if (result.results && result.results.length > 0) {
124
+ console.log(chalk.bold("\nRelevant memories:"));
125
+ for (const m of result.results) {
126
+ console.log(chalk.dim(" \u2022 ") + m.content.substring(0, 100));
127
+ }
128
+ console.log();
129
+ } else {
130
+ console.log(chalk.yellow("\nNo memories found.\n"));
131
+ }
132
+ } catch (err) {
133
+ spinner.stop();
134
+ console.log(chalk.red("Failed to fetch memories\n"));
135
+ }
136
+ break;
137
+ case "add":
138
+ const content = parts.slice(1).join(" ");
139
+ if (content) {
140
+ try {
141
+ await makeRequest(config, "POST", "/api/v1/memory/add", { content });
142
+ console.log(chalk.green("Memory added!\n"));
143
+ } catch {
144
+ console.log(chalk.red("Failed to add memory\n"));
145
+ }
146
+ } else {
147
+ console.log(chalk.yellow("Usage: /add <memory content>\n"));
148
+ }
149
+ break;
150
+ case "decide":
151
+ const decision = parts.slice(1).join(" ");
152
+ if (decision) {
153
+ try {
154
+ await makeRequest(config, "POST", "/api/v2/memory", {
155
+ action: "add",
156
+ content: decision,
157
+ type: "decision",
158
+ tags: ["type:decision"]
159
+ });
160
+ console.log(chalk.green("Decision stored!\n"));
161
+ } catch {
162
+ console.log(chalk.red("Failed to store decision\n"));
163
+ }
164
+ } else {
165
+ console.log(chalk.yellow("Usage: /decide <your decision>\n"));
166
+ }
167
+ break;
168
+ case "clear":
169
+ messages.length = 0;
170
+ console.log(chalk.dim("Conversation cleared.\n"));
171
+ break;
172
+ case "exit":
173
+ case "quit":
174
+ console.log(chalk.dim("\nGoodbye!\n"));
175
+ process.exit(0);
176
+ break;
177
+ default:
178
+ console.log(chalk.yellow(`Unknown command: ${command}. Type /help for help.
179
+ `));
180
+ }
181
+ }
182
+ async function initProject(options) {
183
+ console.log(chalk.bold.cyan("\n\u{1F680} Cilow Project Setup\n"));
184
+ const rl = readline.createInterface({
185
+ input: process.stdin,
186
+ output: process.stdout
187
+ });
188
+ const question = (q) => new Promise((resolve) => rl.question(q, resolve));
189
+ try {
190
+ let apiKey = process.env.CILOW_API_KEY;
191
+ if (!apiKey) {
192
+ console.log(chalk.yellow("No CILOW_API_KEY found in environment.\n"));
193
+ apiKey = await question("Enter your Cilow API key (or press Enter to skip): ");
194
+ }
195
+ const fs = await import("fs");
196
+ const path = await import("path");
197
+ const cwd = process.cwd();
198
+ let framework = options.framework;
199
+ if (!framework) {
200
+ if (fs.existsSync(path.join(cwd, "package.json"))) {
201
+ const pkg = JSON.parse(fs.readFileSync(path.join(cwd, "package.json"), "utf-8"));
202
+ if (pkg.dependencies?.next || pkg.devDependencies?.next) {
203
+ framework = "nextjs";
204
+ } else if (pkg.dependencies?.react || pkg.devDependencies?.react) {
205
+ framework = "react";
206
+ } else if (pkg.dependencies?.express || pkg.devDependencies?.express) {
207
+ framework = "express";
208
+ } else {
209
+ framework = "node";
210
+ }
211
+ } else if (fs.existsSync(path.join(cwd, "requirements.txt")) || fs.existsSync(path.join(cwd, "pyproject.toml"))) {
212
+ framework = "python";
213
+ } else {
214
+ framework = "generic";
215
+ }
216
+ console.log(chalk.dim(`Detected framework: ${framework}`));
217
+ }
218
+ const envPath = path.join(cwd, ".env");
219
+ const envLocalPath = path.join(cwd, ".env.local");
220
+ if (apiKey && !fs.existsSync(envPath) && !fs.existsSync(envLocalPath)) {
221
+ const envContent = `# Cilow AI Memory
222
+ CILOW_API_KEY=${apiKey}
223
+ CILOW_API_URL=https://cilow-gateway-c8ozzar9.uc.gateway.dev
224
+ `;
225
+ fs.writeFileSync(envLocalPath, envContent);
226
+ console.log(chalk.green("\u2713 Created .env.local with Cilow configuration"));
227
+ }
228
+ console.log(chalk.dim("\nInstalling @cilow/sdk..."));
229
+ const { execSync } = await import("child_process");
230
+ if (framework === "python") {
231
+ console.log(chalk.dim(" pip install cilow"));
232
+ } else {
233
+ try {
234
+ execSync("npm install @cilow/sdk", { stdio: "inherit" });
235
+ console.log(chalk.green("\u2713 Installed @cilow/sdk"));
236
+ } catch {
237
+ console.log(chalk.yellow("\u26A0 Could not install SDK automatically. Run: npm install @cilow/sdk"));
238
+ }
239
+ }
240
+ console.log(chalk.bold("\n\u{1F4DD} Example Usage:\n"));
241
+ if (framework === "nextjs" || framework === "react") {
242
+ console.log(chalk.dim(`// app/api/chat/route.ts (or pages/api/chat.ts)
243
+ import { CilowClient } from '@cilow/sdk';
244
+
245
+ const cilow = new CilowClient({
246
+ apiKey: process.env.CILOW_API_KEY,
247
+ });
248
+
249
+ export async function POST(req: Request) {
250
+ const { message, userId } = await req.json();
251
+
252
+ // Add to memory
253
+ await cilow.add(message, { userId });
254
+
255
+ // Get context-aware response
256
+ const response = await cilow.chat({
257
+ messages: [{ role: 'user', content: message }],
258
+ userId,
259
+ });
260
+
261
+ return Response.json(response);
262
+ }`));
263
+ } else if (framework === "express" || framework === "node") {
264
+ console.log(chalk.dim(`// server.js
265
+ import { CilowClient } from '@cilow/sdk';
266
+
267
+ const cilow = new CilowClient({
268
+ apiKey: process.env.CILOW_API_KEY,
269
+ });
270
+
271
+ app.post('/chat', async (req, res) => {
272
+ const { message, userId } = req.body;
273
+
274
+ // Store in memory
275
+ await cilow.add(message, { userId });
276
+
277
+ // Get AI response with memory context
278
+ const response = await cilow.chat({
279
+ messages: [{ role: 'user', content: message }],
280
+ userId,
281
+ });
282
+
283
+ res.json(response);
284
+ });`));
285
+ } else {
286
+ console.log(chalk.dim(`import { CilowClient } from '@cilow/sdk';
287
+
288
+ const cilow = new CilowClient({
289
+ apiKey: process.env.CILOW_API_KEY,
290
+ });
291
+
292
+ // Add memory
293
+ await cilow.add("User prefers dark mode");
294
+
295
+ // Search with context
296
+ const results = await cilow.search("user preferences");
297
+
298
+ // Chat with memory-enhanced AI
299
+ const response = await cilow.chat({
300
+ messages: [{ role: 'user', content: 'What are my preferences?' }],
301
+ });`));
302
+ }
303
+ console.log(chalk.bold.green("\n\u2705 Cilow is ready!\n"));
304
+ console.log(chalk.dim("Next steps:"));
305
+ console.log(' 1. Add memories: cilow add "Important information"');
306
+ console.log(" 2. Chat with AI: cilow chat");
307
+ console.log(" 3. View docs: https://docs.cilow.ai");
308
+ console.log();
309
+ rl.close();
310
+ } catch (err) {
311
+ rl.close();
312
+ console.error(chalk.red("Setup failed:"), err);
313
+ process.exit(1);
314
+ }
315
+ }
316
+ async function startMcpServer() {
317
+ console.log(chalk.bold.cyan("\u{1F50C} Cilow MCP Server\n"));
318
+ console.log(chalk.dim("Model Context Protocol server for Claude Code integration\n"));
319
+ const config = getConfig();
320
+ const rl = readline.createInterface({
321
+ input: process.stdin,
322
+ output: process.stdout,
323
+ terminal: false
324
+ });
325
+ const sendResponse = (id, result) => {
326
+ const response = JSON.stringify({ jsonrpc: "2.0", id, result });
327
+ process.stdout.write(response + "\n");
328
+ };
329
+ const sendError = (id, code, message) => {
330
+ const response = JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } });
331
+ process.stdout.write(response + "\n");
332
+ };
333
+ const tools = [
334
+ {
335
+ name: "cilow_add_memory",
336
+ description: "Add information to Cilow memory for later recall",
337
+ inputSchema: {
338
+ type: "object",
339
+ properties: {
340
+ content: { type: "string", description: "The content to remember" },
341
+ tags: { type: "array", items: { type: "string" }, description: "Optional tags" }
342
+ },
343
+ required: ["content"]
344
+ }
345
+ },
346
+ {
347
+ name: "cilow_search",
348
+ description: "Search Cilow memory for relevant information",
349
+ inputSchema: {
350
+ type: "object",
351
+ properties: {
352
+ query: { type: "string", description: "Search query" },
353
+ limit: { type: "number", description: "Max results (default 5)" }
354
+ },
355
+ required: ["query"]
356
+ }
357
+ },
358
+ {
359
+ name: "cilow_decide",
360
+ description: "Store a decision with reasoning for future reference",
361
+ inputSchema: {
362
+ type: "object",
363
+ properties: {
364
+ decision: { type: "string", description: "The decision made" },
365
+ reasoning: { type: "string", description: "Why this decision was made" },
366
+ alternatives: { type: "array", items: { type: "string" }, description: "Alternatives considered" }
367
+ },
368
+ required: ["decision", "reasoning"]
369
+ }
370
+ },
371
+ {
372
+ name: "cilow_recall",
373
+ description: "Ask a question and get an answer based on stored memories",
374
+ inputSchema: {
375
+ type: "object",
376
+ properties: {
377
+ question: { type: "string", description: "Question to ask" }
378
+ },
379
+ required: ["question"]
380
+ }
381
+ },
382
+ {
383
+ name: "cilow_context",
384
+ description: "Get relevant context for an LLM prompt from Cilow memory",
385
+ inputSchema: {
386
+ type: "object",
387
+ properties: {
388
+ query: { type: "string", description: "Query to find relevant context for" },
389
+ max_tokens: { type: "number", description: "Maximum context tokens" }
390
+ },
391
+ required: ["query"]
392
+ }
393
+ }
394
+ ];
395
+ rl.on("line", async (line) => {
396
+ try {
397
+ const request = JSON.parse(line);
398
+ const { id, method, params } = request;
399
+ switch (method) {
400
+ case "initialize":
401
+ sendResponse(id, {
402
+ protocolVersion: "2024-11-05",
403
+ capabilities: { tools: {} },
404
+ serverInfo: { name: "cilow-mcp", version: VERSION }
405
+ });
406
+ break;
407
+ case "tools/list":
408
+ sendResponse(id, { tools });
409
+ break;
410
+ case "tools/call":
411
+ const { name, arguments: args } = params;
412
+ try {
413
+ let result;
414
+ switch (name) {
415
+ case "cilow_add_memory":
416
+ result = await makeRequest(config, "POST", "/api/v1/memory/add", {
417
+ content: args.content,
418
+ tags: args.tags
419
+ });
420
+ break;
421
+ case "cilow_search":
422
+ result = await makeRequest(config, "POST", "/api/v1/memory/search", {
423
+ query: args.query,
424
+ limit: args.limit || 5
425
+ });
426
+ break;
427
+ case "cilow_decide":
428
+ result = await makeRequest(config, "POST", "/api/v2/memory", {
429
+ action: "add",
430
+ content: args.decision,
431
+ type: "decision",
432
+ reasoning: args.reasoning,
433
+ alternatives: args.alternatives,
434
+ tags: ["type:decision"]
435
+ });
436
+ break;
437
+ case "cilow_recall":
438
+ const searchResult = await makeRequest(config, "POST", "/api/v1/memory/search", {
439
+ query: args.question,
440
+ limit: 5
441
+ });
442
+ result = {
443
+ answer: searchResult.results?.map((r) => r.content).join("\n\n") || "No relevant memories found."
444
+ };
445
+ break;
446
+ case "cilow_context":
447
+ const contextResult = await makeRequest(config, "POST", "/api/v1/memory/search", {
448
+ query: args.query,
449
+ limit: 10
450
+ });
451
+ result = {
452
+ context: contextResult.results?.map((r) => r.content).join("\n\n") || ""
453
+ };
454
+ break;
455
+ default:
456
+ throw new Error(`Unknown tool: ${name}`);
457
+ }
458
+ sendResponse(id, { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] });
459
+ } catch (err) {
460
+ sendError(id, -32603, String(err));
461
+ }
462
+ break;
463
+ default:
464
+ sendError(id, -32601, `Method not found: ${method}`);
465
+ }
466
+ } catch (err) {
467
+ console.error("MCP Error:", err);
468
+ }
469
+ });
470
+ console.log(chalk.green("MCP server running on stdio"));
471
+ console.log(chalk.dim("Add to Claude Code settings:"));
472
+ console.log(chalk.dim(` "cilow": { "command": "npx", "args": ["@cilow/cli", "mcp"] }`));
473
+ }
50
474
  var program = new Command();
51
- program.name("cilow").description("Cilow - AI Memory Infrastructure CLI").version("0.1.0");
52
- program.command("add <content>").description("Add a memory").option("-t, --tags <tags>", "Comma-separated tags").option("-u, --user <userId>", "User ID").option("--type <type>", "Memory type: hot, warm, cold").action(async (content, options) => {
475
+ program.name("cilow").description("Cilow - AI Memory Infrastructure with persistent context").version(VERSION);
476
+ program.command("chat").description("Start interactive AI chat with memory").option("-p, --provider <provider>", "LLM provider (anthropic, openai)", "anthropic").option("-m, --model <model>", "Model to use").action(async (options) => {
477
+ const config = getConfig();
478
+ await startChat(config, options);
479
+ });
480
+ program.command("init").description("Initialize Cilow in your project").option("-f, --framework <framework>", "Framework (nextjs, react, express, node, python)").action(initProject);
481
+ program.command("mcp").description("Start MCP server for Claude Code integration").action(startMcpServer);
482
+ program.command("add <content>").description("Add a memory").option("-t, --tags <tags>", "Comma-separated tags").action(async (content, options) => {
53
483
  const config = getConfig();
54
484
  const spinner = ora("Storing memory...").start();
55
485
  try {
56
486
  const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : [];
57
487
  const result = await makeRequest(config, "POST", "/api/v1/memory/add", {
58
488
  content,
59
- tags,
60
- user_id: options.user,
61
- type: options.type
489
+ tags
62
490
  });
63
491
  spinner.succeed(chalk.green("Memory stored!"));
64
492
  console.log(chalk.dim(`ID: ${result.memory_id}`));
@@ -68,16 +496,13 @@ program.command("add <content>").description("Add a memory").option("-t, --tags
68
496
  process.exit(1);
69
497
  }
70
498
  });
71
- program.command("search <query>").description("Search memories").option("-l, --limit <limit>", "Number of results", "5").option("-u, --user <userId>", "User ID").option("-t, --tags <tags>", "Filter by tags").action(async (query, options) => {
499
+ program.command("search <query>").description("Search memories").option("-l, --limit <limit>", "Number of results", "5").action(async (query, options) => {
72
500
  const config = getConfig();
73
501
  const spinner = ora("Searching...").start();
74
502
  try {
75
- const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : void 0;
76
503
  const result = await makeRequest(config, "POST", "/api/v1/memory/search", {
77
504
  query,
78
- limit: parseInt(options.limit),
79
- user_id: options.user,
80
- tags
505
+ limit: parseInt(options.limit)
81
506
  });
82
507
  spinner.stop();
83
508
  if (!result.results || result.results.length === 0) {
@@ -102,58 +527,24 @@ Found ${result.results.length} memories:
102
527
  process.exit(1);
103
528
  }
104
529
  });
105
- program.command("list").description("List recent memories").option("-l, --limit <limit>", "Number of results", "20").option("-u, --user <userId>", "User ID").action(async (options) => {
106
- const config = getConfig();
107
- const spinner = ora("Fetching memories...").start();
108
- try {
109
- const result = await makeRequest(config, "POST", "/api/v1/memory/search", {
110
- query: "user information personal data preferences",
111
- limit: parseInt(options.limit),
112
- user_id: options.user
113
- });
114
- spinner.stop();
115
- if (!result.results || result.results.length === 0) {
116
- console.log(chalk.yellow("No memories found"));
117
- return;
118
- }
119
- console.log(chalk.bold(`
120
- ${result.results.length} memories:
121
- `));
122
- for (const m of result.results) {
123
- const date = m.created_at ? new Date(m.created_at).toLocaleDateString() : "";
124
- console.log(chalk.cyan(`[${date}]`) + ` ${m.content.substring(0, 80)}${m.content.length > 80 ? "..." : ""}`);
125
- if (m.tags && m.tags.length > 0) {
126
- console.log(chalk.dim(` Tags: ${m.tags.slice(0, 5).join(", ")}${m.tags.length > 5 ? "..." : ""}`));
127
- }
128
- }
129
- } catch (err) {
130
- spinner.fail(chalk.red("Failed to list memories"));
131
- console.error(chalk.dim(String(err)));
132
- process.exit(1);
133
- }
134
- });
135
- program.command("recall <question>").alias("ask").description("Ask about your memories in natural language").option("-u, --user <userId>", "User ID").action(async (question, options) => {
530
+ program.command("decide <decision>").description("Store a decision with reasoning").requiredOption("-r, --reason <reason>", "Why you made this decision").option("-a, --alternatives <alternatives>", "Comma-separated alternatives").action(async (decision, options) => {
136
531
  const config = getConfig();
137
- const spinner = ora("Thinking...").start();
532
+ const spinner = ora("Storing decision...").start();
138
533
  try {
139
- const result = await makeRequest(config, "POST", "/api/v1/memory/search", {
140
- query: question,
141
- limit: 5,
142
- user_id: options.user
534
+ const alternatives = options.alternatives ? options.alternatives.split(",").map((a) => a.trim()) : void 0;
535
+ await makeRequest(config, "POST", "/api/v2/memory", {
536
+ action: "add",
537
+ content: decision,
538
+ type: "decision",
539
+ reasoning: options.reason,
540
+ alternatives,
541
+ tags: ["type:decision"]
143
542
  });
144
- spinner.stop();
145
- if (!result.results || result.results.length === 0) {
146
- console.log(chalk.yellow("I don't have any relevant memories for that."));
147
- return;
148
- }
149
- console.log(chalk.bold("\nHere's what I remember:\n"));
150
- for (const r of result.results) {
151
- if (r.similarity > 0.3) {
152
- console.log(chalk.green("\u2022 ") + r.content);
153
- }
154
- }
543
+ spinner.succeed(chalk.green("Decision stored!"));
544
+ console.log(chalk.dim(`Decision: ${decision}`));
545
+ console.log(chalk.dim(`Reason: ${options.reason}`));
155
546
  } catch (err) {
156
- spinner.fail(chalk.red("Recall failed"));
547
+ spinner.fail(chalk.red("Failed to store decision"));
157
548
  console.error(chalk.dim(String(err)));
158
549
  process.exit(1);
159
550
  }
@@ -176,241 +567,21 @@ program.command("config").description("Show current configuration").action(() =>
176
567
  console.log(chalk.bold("\nCilow Configuration:\n"));
177
568
  console.log(`API URL: ${chalk.cyan(apiUrl)}`);
178
569
  console.log(`API Key: ${apiKey ? chalk.green(apiKey.substring(0, 20) + "...") : chalk.red("Not set")}`);
179
- if (!apiKey) {
180
- console.log(chalk.yellow("\nSet your API key:"));
181
- console.log(chalk.dim(" export CILOW_API_KEY=your_api_key"));
182
- }
183
- });
184
- program.command("chat").description("Interactive chat with memory (coming soon)").action(() => {
185
- console.log(chalk.yellow("Interactive chat mode coming soon!"));
186
- console.log(chalk.dim('For now, use "cilow recall" for memory queries.'));
187
- });
188
- program.command("decide <decision>").description("Store a decision with reasoning (Cilow's unique feature)").requiredOption("-r, --reason <reason>", "Why you made this decision").option("-a, --alternatives <alternatives>", "Comma-separated alternatives considered").option("-u, --user <userId>", "User ID").option("-s, --source <source>", "Source of decision (e.g., slack, meeting)").action(async (decision, options) => {
189
- const config = getConfig();
190
- const spinner = ora("Storing decision trace...").start();
191
- try {
192
- const alternatives = options.alternatives ? options.alternatives.split(",").map((a) => a.trim()) : void 0;
193
- const result = await makeRequest(config, "POST", "/api/v2/memory", {
194
- action: "add",
195
- content: decision,
196
- type: "decision",
197
- reasoning: options.reason,
198
- alternatives,
199
- user_id: options.user,
200
- source: options.source,
201
- tags: ["type:decision"]
202
- });
203
- if (result.success) {
204
- spinner.succeed(chalk.green("Decision traced!"));
205
- console.log(chalk.dim(`ID: ${result.memory_id}`));
206
- console.log(chalk.cyan(`Decision: ${decision}`));
207
- console.log(chalk.dim(`Reasoning: ${options.reason}`));
208
- if (alternatives) {
209
- console.log(chalk.dim(`Alternatives: ${alternatives.join(", ")}`));
210
- }
211
- } else {
212
- spinner.fail(chalk.red("Failed to store decision"));
213
- }
214
- } catch (err) {
215
- spinner.fail(chalk.red("Failed to store decision"));
216
- console.error(chalk.dim(String(err)));
217
- process.exit(1);
218
- }
219
- });
220
- program.command("simulate <question>").description('Ask "what if" questions based on past decisions').option("-u, --user <userId>", "User ID").action(async (question, options) => {
221
- const config = getConfig();
222
- const spinner = ora("Analyzing past decisions...").start();
223
- try {
224
- const result = await makeRequest(config, "POST", "/api/v2/memory", {
225
- action: "search",
226
- query: question,
227
- tags: ["type:decision"],
228
- limit: 5,
229
- user_id: options.user
230
- });
231
- spinner.stop();
232
- if (!result.success || !result.memories || result.memories.length === 0) {
233
- console.log(chalk.yellow("No relevant decisions found for this question."));
234
- console.log(chalk.dim('Try storing more decisions with: cilow decide "..." --reason "..."'));
235
- return;
236
- }
237
- console.log(chalk.bold("\n\u{1F52E} World Model Simulation\n"));
238
- console.log(chalk.dim(`Question: ${question}
239
- `));
240
- console.log(chalk.cyan("Based on past decisions, here's what I found:\n"));
241
- for (const m of result.memories) {
242
- const lines = m.content.split("\n");
243
- const decisionLine = lines.find((l) => l.startsWith("DECISION:")) || m.content;
244
- const reasoningLine = lines.find((l) => l.startsWith("REASONING:"));
245
- const alternativesLine = lines.find((l) => l.startsWith("ALTERNATIVES CONSIDERED:"));
246
- console.log(chalk.green("\u2022 ") + chalk.bold(decisionLine.replace("DECISION: ", "")));
247
- if (reasoningLine) {
248
- console.log(chalk.dim(` Why: ${reasoningLine.replace("REASONING: ", "")}`));
249
- }
250
- if (alternativesLine) {
251
- console.log(chalk.dim(` Alternatives: ${alternativesLine.replace("ALTERNATIVES CONSIDERED: ", "")}`));
252
- }
253
- console.log();
254
- }
255
- console.log(chalk.dim("\n\u{1F4A1} These past decisions may influence the answer to your question."));
256
- console.log(chalk.dim('Use "cilow recall" for more detailed memory search.'));
257
- } catch (err) {
258
- spinner.fail(chalk.red("Simulation failed"));
259
- console.error(chalk.dim(String(err)));
260
- process.exit(1);
261
- }
262
- });
263
- program.command("ingest <url>").description("Ingest content from a URL into memory").option("-u, --user <userId>", "User ID").option("-t, --tags <tags>", "Comma-separated tags").action(async (url, options) => {
264
- const config = getConfig();
265
- const spinner = ora("Ingesting URL content...").start();
266
- try {
267
- const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : void 0;
268
- const result = await makeRequest(config, "POST", "/api/v2/memory", {
269
- action: "add",
270
- content: url,
271
- // URL auto-detected and ingested
272
- user_id: options.user,
273
- tags
274
- });
275
- if (result.success) {
276
- spinner.succeed(chalk.green("URL content ingested!"));
277
- if (result.chunks_created) {
278
- console.log(chalk.dim(`Created ${result.chunks_created} memory chunks from URL`));
279
- }
280
- if (result.source_url) {
281
- console.log(chalk.dim(`Source: ${result.source_url}`));
282
- }
283
- } else {
284
- spinner.fail(chalk.red("Failed to ingest URL"));
285
- }
286
- } catch (err) {
287
- spinner.fail(chalk.red("Failed to ingest URL"));
288
- console.error(chalk.dim(String(err)));
289
- process.exit(1);
290
- }
291
- });
292
- program.command("ask-llm <question>").description("Ask a question using LLM with memory context (Memory Router)").option("-p, --provider <provider>", "LLM provider: openai, anthropic, gemini, ollama", "openai").option("-m, --model <model>", "Model to use").option("-k, --api-key <key>", "API key for the LLM provider").option("-u, --user <userId>", "User ID for memory context").option("--store", "Store the conversation in memory").action(async (question, options) => {
293
- const config = getConfig();
294
- const spinner = ora(`Asking ${options.provider}...`).start();
295
- try {
296
- const result = await makeRequest(config, "POST", "/api/v2/router", {
297
- provider: options.provider,
298
- model: options.model,
299
- api_key: options.apiKey,
300
- messages: [{ role: "user", content: question }],
301
- user_id: options.user,
302
- store_response: options.store
303
- });
304
- spinner.stop();
305
- if (result.success && result.response) {
306
- console.log(chalk.bold("\n\u{1F4DD} Response:\n"));
307
- console.log(result.response);
308
- console.log();
309
- console.log(chalk.dim(`Model: ${result.model} | Memories used: ${result.memories_used || 0} | Latency: ${result.latency_ms}ms`));
310
- } else {
311
- console.log(chalk.red("Error: " + (result.error || "Unknown error")));
312
- }
313
- } catch (err) {
314
- spinner.fail(chalk.red("Request failed"));
315
- console.error(chalk.dim(String(err)));
316
- process.exit(1);
317
- }
318
- });
319
- program.command("connectors").description("List available external connectors and their status").action(async () => {
320
- const config = getConfig();
321
- const spinner = ora("Fetching connectors...").start();
322
- try {
323
- const result = await makeRequest(config, "GET", "/api/v1/connectors");
324
- spinner.stop();
325
- if (!result.connectors || result.connectors.length === 0) {
326
- console.log(chalk.yellow("No connectors available"));
327
- return;
328
- }
329
- console.log(chalk.bold("\nAvailable Connectors:\n"));
330
- for (const c of result.connectors) {
331
- const statusIcon = c.connected ? chalk.green("\u25CF") : chalk.dim("\u25CB");
332
- const statusText = c.connected ? chalk.green("Connected") : chalk.dim("Not connected");
333
- console.log(`${statusIcon} ${chalk.bold(c.name)} (${c.connector_type})`);
334
- console.log(` Status: ${statusText}`);
335
- if (c.connected) {
336
- console.log(chalk.dim(` Last sync: ${c.last_sync || "Never"}`));
337
- console.log(chalk.dim(` Items synced: ${c.items_synced}`));
338
- }
339
- console.log();
340
- }
341
- console.log(chalk.dim('Use "cilow connect <type>" to connect a service.'));
342
- } catch (err) {
343
- spinner.fail(chalk.red("Failed to list connectors"));
344
- console.error(chalk.dim(String(err)));
345
- process.exit(1);
346
- }
347
- });
348
- program.command("connect <connector>").description("Connect to an external service (notion, google_drive, gmail, slack, discord, linear, github, jira, onedrive)").action(async (connector) => {
349
- const config = getConfig();
350
- const spinner = ora(`Getting authorization URL for ${connector}...`).start();
351
- try {
352
- const result = await makeRequest(config, "GET", `/api/v1/connectors/${connector}/auth`);
353
- spinner.stop();
354
- console.log(chalk.bold("\nAuthorize Cilow to access your data:\n"));
355
- console.log(chalk.cyan(result.auth_url));
356
- console.log();
357
- console.log(chalk.dim("Open this URL in your browser to authorize."));
358
- console.log(chalk.dim("After authorization, run: cilow sync " + connector));
359
- } catch (err) {
360
- spinner.fail(chalk.red(`Failed to get auth URL for ${connector}`));
361
- console.error(chalk.dim(String(err)));
362
- process.exit(1);
363
- }
364
- });
365
- program.command("sync <connector>").description("Sync data from a connected service into memory").option("--full", "Perform a full sync (not incremental)").option("-l, --limit <limit>", "Maximum items to sync", "100").action(async (connector, options) => {
366
- const config = getConfig();
367
- const spinner = ora(`Syncing data from ${connector}...`).start();
368
- try {
369
- const result = await makeRequest(config, "POST", `/api/v1/connectors/${connector}/sync`, {
370
- full_sync: options.full || false,
371
- limit: parseInt(options.limit)
372
- });
373
- if (result.status === "success" || result.status === "partialsuccess") {
374
- spinner.succeed(chalk.green(`Synced ${result.items_synced} items from ${connector}`));
375
- if (result.items_skipped > 0) {
376
- console.log(chalk.dim(`Skipped: ${result.items_skipped} items`));
377
- }
378
- if (result.has_more) {
379
- console.log(chalk.yellow("More items available. Run sync again to continue."));
380
- }
381
- if (result.errors.length > 0) {
382
- console.log(chalk.yellow("\nWarnings:"));
383
- for (const err of result.errors.slice(0, 5)) {
384
- console.log(chalk.dim(` - ${err}`));
385
- }
386
- if (result.errors.length > 5) {
387
- console.log(chalk.dim(` ... and ${result.errors.length - 5} more`));
388
- }
389
- }
390
- } else {
391
- spinner.fail(chalk.red(`Sync failed: ${result.status}`));
392
- if (result.errors.length > 0) {
393
- for (const err of result.errors) {
394
- console.log(chalk.dim(` - ${err}`));
395
- }
396
- }
570
+ console.log(`Version: ${chalk.dim(VERSION)}`);
571
+ console.log(chalk.bold("\nMCP Integration:"));
572
+ console.log(chalk.dim("Add to Claude Code ~/.claude/settings.json:"));
573
+ console.log(chalk.cyan(`{
574
+ "mcpServers": {
575
+ "cilow": {
576
+ "command": "npx",
577
+ "args": ["@cilow/cli", "mcp"],
578
+ "env": { "CILOW_API_KEY": "${apiKey || "your-api-key"}" }
397
579
  }
398
- } catch (err) {
399
- spinner.fail(chalk.red(`Failed to sync ${connector}`));
400
- console.error(chalk.dim(String(err)));
401
- process.exit(1);
402
580
  }
581
+ }`));
403
582
  });
404
- program.command("disconnect <connector>").description("Disconnect an external service and revoke access").action(async (connector) => {
583
+ program.action(async () => {
405
584
  const config = getConfig();
406
- const spinner = ora(`Disconnecting ${connector}...`).start();
407
- try {
408
- await makeRequest(config, "DELETE", `/api/v1/connectors/${connector}`);
409
- spinner.succeed(chalk.green(`Disconnected from ${connector}`));
410
- } catch (err) {
411
- spinner.fail(chalk.red(`Failed to disconnect ${connector}`));
412
- console.error(chalk.dim(String(err)));
413
- process.exit(1);
414
- }
585
+ await startChat(config, {});
415
586
  });
416
587
  program.parse();
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@cilow/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "CLI for Cilow - AI Memory Infrastructure",
5
5
  "bin": {
6
- "cilow": "./dist/cli.mjs"
6
+ "cilow": "./dist/cli.js"
7
7
  },
8
- "main": "dist/cli.mjs",
9
- "module": "dist/cli.mjs",
10
- "types": "dist/cli.d.mts",
8
+ "main": "dist/cli.js",
9
+ "module": "dist/cli.js",
10
+ "types": "dist/cli.d.ts",
11
11
  "type": "module",
12
12
  "files": [
13
13
  "dist"