@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.
- package/dist/cli.js +473 -302
- 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
|
|
19
|
-
|
|
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
|
|
52
|
-
program.command("
|
|
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").
|
|
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("
|
|
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("
|
|
532
|
+
const spinner = ora("Storing decision...").start();
|
|
138
533
|
try {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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.
|
|
145
|
-
|
|
146
|
-
|
|
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("
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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.
|
|
583
|
+
program.action(async () => {
|
|
405
584
|
const config = getConfig();
|
|
406
|
-
|
|
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.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "CLI for Cilow - AI Memory Infrastructure",
|
|
5
5
|
"bin": {
|
|
6
|
-
"cilow": "./dist/cli.
|
|
6
|
+
"cilow": "./dist/cli.js"
|
|
7
7
|
},
|
|
8
|
-
"main": "dist/cli.
|
|
9
|
-
"module": "dist/cli.
|
|
10
|
-
"types": "dist/cli.d.
|
|
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"
|