@cilow/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,416 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { Command } from "commander";
5
+ import chalk from "chalk";
6
+ import ora from "ora";
7
+ var DEFAULT_API_URL = "https://cilow-gateway-c8ozzar9.uc.gateway.dev";
8
+ function getConfig() {
9
+ const apiKey = process.env.CILOW_API_KEY;
10
+ const apiUrl = process.env.CILOW_API_URL || DEFAULT_API_URL;
11
+ if (!apiKey) {
12
+ console.error(chalk.red("Error: CILOW_API_KEY environment variable not set"));
13
+ console.error(chalk.dim("Get your API key at https://cilow.ai/dashboard"));
14
+ process.exit(1);
15
+ }
16
+ return { apiUrl, apiKey };
17
+ }
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
+ }
30
+ const headers = {
31
+ "Content-Type": "application/json"
32
+ };
33
+ if (config.apiKey.startsWith("cilow_")) {
34
+ headers["X-API-Key"] = config.apiKey;
35
+ } else {
36
+ headers["Authorization"] = `Bearer ${config.apiKey}`;
37
+ }
38
+ const response = await fetch(url, {
39
+ method,
40
+ headers,
41
+ body: body ? JSON.stringify(body) : void 0
42
+ });
43
+ if (!response.ok) {
44
+ const errText = await response.text();
45
+ throw new Error(`API Error (${response.status}): ${errText}`);
46
+ }
47
+ const text = await response.text();
48
+ return text ? JSON.parse(text) : null;
49
+ }
50
+ 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) => {
53
+ const config = getConfig();
54
+ const spinner = ora("Storing memory...").start();
55
+ try {
56
+ const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : [];
57
+ const result = await makeRequest(config, "POST", "/api/v1/memory/add", {
58
+ content,
59
+ tags,
60
+ user_id: options.user,
61
+ type: options.type
62
+ });
63
+ spinner.succeed(chalk.green("Memory stored!"));
64
+ console.log(chalk.dim(`ID: ${result.memory_id}`));
65
+ } catch (err) {
66
+ spinner.fail(chalk.red("Failed to store memory"));
67
+ console.error(chalk.dim(String(err)));
68
+ process.exit(1);
69
+ }
70
+ });
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) => {
72
+ const config = getConfig();
73
+ const spinner = ora("Searching...").start();
74
+ try {
75
+ const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : void 0;
76
+ const result = await makeRequest(config, "POST", "/api/v1/memory/search", {
77
+ query,
78
+ limit: parseInt(options.limit),
79
+ user_id: options.user,
80
+ tags
81
+ });
82
+ spinner.stop();
83
+ if (!result.results || result.results.length === 0) {
84
+ console.log(chalk.yellow("No memories found"));
85
+ return;
86
+ }
87
+ console.log(chalk.bold(`
88
+ Found ${result.results.length} memories:
89
+ `));
90
+ for (let i = 0; i < result.results.length; i++) {
91
+ const r = result.results[i];
92
+ const score = (r.similarity * 100).toFixed(1);
93
+ console.log(chalk.cyan(`${i + 1}. [${score}%]`) + ` ${r.content}`);
94
+ if (r.tags && r.tags.length > 0) {
95
+ console.log(chalk.dim(` Tags: ${r.tags.join(", ")}`));
96
+ }
97
+ console.log();
98
+ }
99
+ } catch (err) {
100
+ spinner.fail(chalk.red("Search failed"));
101
+ console.error(chalk.dim(String(err)));
102
+ process.exit(1);
103
+ }
104
+ });
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) => {
136
+ const config = getConfig();
137
+ const spinner = ora("Thinking...").start();
138
+ try {
139
+ const result = await makeRequest(config, "POST", "/api/v1/memory/search", {
140
+ query: question,
141
+ limit: 5,
142
+ user_id: options.user
143
+ });
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
+ }
155
+ } catch (err) {
156
+ spinner.fail(chalk.red("Recall failed"));
157
+ console.error(chalk.dim(String(err)));
158
+ process.exit(1);
159
+ }
160
+ });
161
+ program.command("health").description("Check API health").action(async () => {
162
+ const config = getConfig();
163
+ const spinner = ora("Checking...").start();
164
+ try {
165
+ await makeRequest(config, "GET", "/health");
166
+ spinner.succeed(chalk.green("Cilow API is healthy!"));
167
+ } catch (err) {
168
+ spinner.fail(chalk.red("API is not responding"));
169
+ console.error(chalk.dim(String(err)));
170
+ process.exit(1);
171
+ }
172
+ });
173
+ program.command("config").description("Show current configuration").action(() => {
174
+ const apiUrl = process.env.CILOW_API_URL || DEFAULT_API_URL;
175
+ const apiKey = process.env.CILOW_API_KEY || "";
176
+ console.log(chalk.bold("\nCilow Configuration:\n"));
177
+ console.log(`API URL: ${chalk.cyan(apiUrl)}`);
178
+ 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
+ }
397
+ }
398
+ } catch (err) {
399
+ spinner.fail(chalk.red(`Failed to sync ${connector}`));
400
+ console.error(chalk.dim(String(err)));
401
+ process.exit(1);
402
+ }
403
+ });
404
+ program.command("disconnect <connector>").description("Disconnect an external service and revoke access").action(async (connector) => {
405
+ 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
+ }
415
+ });
416
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@cilow/cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI for Cilow - AI Memory Infrastructure",
5
+ "bin": {
6
+ "cilow": "./dist/cli.mjs"
7
+ },
8
+ "main": "dist/cli.mjs",
9
+ "module": "dist/cli.mjs",
10
+ "types": "dist/cli.d.mts",
11
+ "type": "module",
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsup src/cli.ts --format esm --dts --clean",
17
+ "dev": "tsup src/cli.ts --format esm --watch",
18
+ "start": "node dist/cli.js"
19
+ },
20
+ "keywords": [
21
+ "cilow",
22
+ "ai",
23
+ "memory",
24
+ "llm",
25
+ "cli"
26
+ ],
27
+ "author": "Cilow Team",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/cilow-ai/cilow"
32
+ },
33
+ "homepage": "https://cilow.ai",
34
+ "dependencies": {
35
+ "commander": "^12.0.0",
36
+ "chalk": "^5.3.0",
37
+ "ora": "^8.0.0",
38
+ "inquirer": "^9.2.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^20.0.0",
42
+ "typescript": "^5.0.0",
43
+ "tsup": "^8.0.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ }
48
+ }