chainlesschain 0.37.9 → 0.37.10

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 (51) hide show
  1. package/README.md +309 -19
  2. package/bin/chainlesschain.js +4 -0
  3. package/package.json +1 -1
  4. package/src/commands/audit.js +286 -0
  5. package/src/commands/auth.js +387 -0
  6. package/src/commands/browse.js +184 -0
  7. package/src/commands/did.js +376 -0
  8. package/src/commands/encrypt.js +233 -0
  9. package/src/commands/export.js +125 -0
  10. package/src/commands/git.js +215 -0
  11. package/src/commands/import.js +259 -0
  12. package/src/commands/instinct.js +202 -0
  13. package/src/commands/llm.js +155 -4
  14. package/src/commands/mcp.js +302 -0
  15. package/src/commands/memory.js +282 -0
  16. package/src/commands/note.js +187 -0
  17. package/src/commands/org.js +505 -0
  18. package/src/commands/p2p.js +274 -0
  19. package/src/commands/plugin.js +398 -0
  20. package/src/commands/search.js +237 -0
  21. package/src/commands/session.js +238 -0
  22. package/src/commands/sync.js +249 -0
  23. package/src/commands/tokens.js +214 -0
  24. package/src/commands/wallet.js +416 -0
  25. package/src/index.js +49 -1
  26. package/src/lib/audit-logger.js +364 -0
  27. package/src/lib/bm25-search.js +322 -0
  28. package/src/lib/browser-automation.js +216 -0
  29. package/src/lib/crypto-manager.js +246 -0
  30. package/src/lib/did-manager.js +270 -0
  31. package/src/lib/ensure-utf8.js +59 -0
  32. package/src/lib/git-integration.js +220 -0
  33. package/src/lib/instinct-manager.js +190 -0
  34. package/src/lib/knowledge-exporter.js +302 -0
  35. package/src/lib/knowledge-importer.js +293 -0
  36. package/src/lib/llm-providers.js +325 -0
  37. package/src/lib/mcp-client.js +413 -0
  38. package/src/lib/memory-manager.js +211 -0
  39. package/src/lib/note-versioning.js +244 -0
  40. package/src/lib/org-manager.js +424 -0
  41. package/src/lib/p2p-manager.js +317 -0
  42. package/src/lib/pdf-parser.js +96 -0
  43. package/src/lib/permission-engine.js +374 -0
  44. package/src/lib/plan-mode.js +333 -0
  45. package/src/lib/plugin-manager.js +312 -0
  46. package/src/lib/response-cache.js +156 -0
  47. package/src/lib/session-manager.js +189 -0
  48. package/src/lib/sync-manager.js +347 -0
  49. package/src/lib/token-tracker.js +200 -0
  50. package/src/lib/wallet-manager.js +348 -0
  51. package/src/repl/agent-repl.js +142 -12
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Persistent memory management commands
3
+ * chainlesschain memory show|add|search|daily|file
4
+ */
5
+
6
+ import chalk from "chalk";
7
+ import { logger } from "../lib/logger.js";
8
+ import { bootstrap, shutdown } from "../runtime/bootstrap.js";
9
+ import {
10
+ getMemoryDir,
11
+ addMemory,
12
+ searchMemory,
13
+ listMemory,
14
+ deleteMemory,
15
+ appendDailyNote,
16
+ getDailyNote,
17
+ listDailyNotes,
18
+ getMemoryFile,
19
+ updateMemoryFile,
20
+ } from "../lib/memory-manager.js";
21
+
22
+ export function registerMemoryCommand(program) {
23
+ const memory = program
24
+ .command("memory")
25
+ .description("Persistent memory and daily notes");
26
+
27
+ // memory show
28
+ memory
29
+ .command("show", { isDefault: true })
30
+ .description("Show memory entries")
31
+ .option("-n, --limit <n>", "Max entries", "20")
32
+ .option("--category <cat>", "Filter by category")
33
+ .option("--json", "Output as JSON")
34
+ .action(async (options) => {
35
+ try {
36
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
37
+ if (!ctx.db) {
38
+ logger.error("Database not available");
39
+ process.exit(1);
40
+ }
41
+ const db = ctx.db.getDatabase();
42
+ const entries = listMemory(db, {
43
+ limit: Math.max(1, parseInt(options.limit) || 20),
44
+ category: options.category,
45
+ });
46
+
47
+ if (options.json) {
48
+ console.log(JSON.stringify(entries, null, 2));
49
+ } else if (entries.length === 0) {
50
+ logger.info("No memory entries. Use 'memory add' to create one.");
51
+ } else {
52
+ logger.log(chalk.bold(`Memory (${entries.length} entries):\n`));
53
+ for (const e of entries) {
54
+ const stars =
55
+ "★".repeat(e.importance) + "☆".repeat(5 - e.importance);
56
+ logger.log(
57
+ ` ${chalk.gray(e.id.slice(0, 12))} ${chalk.yellow(stars)} ${chalk.cyan(e.category)}`,
58
+ );
59
+ logger.log(
60
+ ` ${chalk.white(e.content.substring(0, 120).replace(/\n/g, " "))}`,
61
+ );
62
+ logger.log(` ${chalk.gray(e.created_at)}`);
63
+ }
64
+ }
65
+
66
+ await shutdown();
67
+ } catch (err) {
68
+ logger.error(`Failed: ${err.message}`);
69
+ process.exit(1);
70
+ }
71
+ });
72
+
73
+ // memory add
74
+ memory
75
+ .command("add")
76
+ .description("Add a memory entry")
77
+ .argument("<text>", "Memory content")
78
+ .option("--category <cat>", "Category", "general")
79
+ .option("--importance <n>", "Importance 1-5", "3")
80
+ .option("--json", "Output as JSON")
81
+ .action(async (text, options) => {
82
+ try {
83
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
84
+ if (!ctx.db) {
85
+ logger.error("Database not available");
86
+ process.exit(1);
87
+ }
88
+ const db = ctx.db.getDatabase();
89
+ const entry = addMemory(db, text, {
90
+ category: options.category,
91
+ importance: Math.max(
92
+ 1,
93
+ Math.min(5, parseInt(options.importance) || 3),
94
+ ),
95
+ });
96
+
97
+ if (options.json) {
98
+ console.log(JSON.stringify(entry, null, 2));
99
+ } else {
100
+ logger.success(
101
+ `Memory added: ${chalk.gray(entry.id.slice(0, 12))} [${chalk.cyan(entry.category)}]`,
102
+ );
103
+ }
104
+
105
+ await shutdown();
106
+ } catch (err) {
107
+ logger.error(`Failed: ${err.message}`);
108
+ process.exit(1);
109
+ }
110
+ });
111
+
112
+ // memory search
113
+ memory
114
+ .command("search")
115
+ .description("Search memory entries")
116
+ .argument("<query>", "Search query")
117
+ .option("-n, --limit <n>", "Max results", "20")
118
+ .option("--json", "Output as JSON")
119
+ .action(async (query, options) => {
120
+ try {
121
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
122
+ if (!ctx.db) {
123
+ logger.error("Database not available");
124
+ process.exit(1);
125
+ }
126
+ const db = ctx.db.getDatabase();
127
+ const results = searchMemory(db, query, {
128
+ limit: Math.max(1, parseInt(options.limit) || 20),
129
+ });
130
+
131
+ if (options.json) {
132
+ console.log(JSON.stringify(results, null, 2));
133
+ } else if (results.length === 0) {
134
+ logger.info(`No memory entries matching "${query}"`);
135
+ } else {
136
+ logger.log(
137
+ chalk.bold(
138
+ `Memory search "${query}" (${results.length} results):\n`,
139
+ ),
140
+ );
141
+ for (const e of results) {
142
+ logger.log(
143
+ ` ${chalk.gray(e.id.slice(0, 12))} ${chalk.cyan(e.category)} ${chalk.gray(e.created_at)}`,
144
+ );
145
+ logger.log(
146
+ ` ${chalk.white(e.content.substring(0, 120).replace(/\n/g, " "))}`,
147
+ );
148
+ }
149
+ }
150
+
151
+ await shutdown();
152
+ } catch (err) {
153
+ logger.error(`Failed: ${err.message}`);
154
+ process.exit(1);
155
+ }
156
+ });
157
+
158
+ // memory delete
159
+ memory
160
+ .command("delete")
161
+ .description("Delete a memory entry")
162
+ .argument("<id>", "Entry ID (or prefix)")
163
+ .action(async (id) => {
164
+ try {
165
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
166
+ if (!ctx.db) {
167
+ logger.error("Database not available");
168
+ process.exit(1);
169
+ }
170
+ const db = ctx.db.getDatabase();
171
+ const ok = deleteMemory(db, id);
172
+ if (ok) {
173
+ logger.success("Memory entry deleted");
174
+ } else {
175
+ logger.error(`Memory entry not found: ${id}`);
176
+ }
177
+
178
+ await shutdown();
179
+ } catch (err) {
180
+ logger.error(`Failed: ${err.message}`);
181
+ process.exit(1);
182
+ }
183
+ });
184
+
185
+ // memory daily
186
+ memory
187
+ .command("daily")
188
+ .description("View or append to daily notes")
189
+ .argument("[date]", "Date (YYYY-MM-DD, default: today)")
190
+ .option("-a, --append <text>", "Append text to daily note")
191
+ .option("--list", "List available daily notes")
192
+ .option("--json", "Output as JSON")
193
+ .action(async (date, options) => {
194
+ try {
195
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
196
+ const memoryDir = getMemoryDir(ctx.env.dataDir);
197
+
198
+ if (options.list) {
199
+ const notes = listDailyNotes(memoryDir);
200
+ if (options.json) {
201
+ console.log(JSON.stringify(notes, null, 2));
202
+ } else if (notes.length === 0) {
203
+ logger.info("No daily notes yet");
204
+ } else {
205
+ logger.log(chalk.bold("Daily Notes:\n"));
206
+ for (const n of notes) {
207
+ logger.log(
208
+ ` ${chalk.cyan(n.date)} ${chalk.gray(n.size + " bytes")}`,
209
+ );
210
+ }
211
+ }
212
+ await shutdown();
213
+ return;
214
+ }
215
+
216
+ if (options.append) {
217
+ const result = appendDailyNote(memoryDir, options.append);
218
+ logger.success(`Added to daily note: ${chalk.cyan(result.date)}`);
219
+ await shutdown();
220
+ return;
221
+ }
222
+
223
+ // Show daily note
224
+ const targetDate = date || new Date().toISOString().slice(0, 10);
225
+ const content = getDailyNote(memoryDir, targetDate);
226
+
227
+ if (!content) {
228
+ logger.info(`No daily note for ${targetDate}`);
229
+ } else if (options.json) {
230
+ console.log(JSON.stringify({ date: targetDate, content }, null, 2));
231
+ } else {
232
+ logger.log(content);
233
+ }
234
+
235
+ await shutdown();
236
+ } catch (err) {
237
+ logger.error(`Failed: ${err.message}`);
238
+ process.exit(1);
239
+ }
240
+ });
241
+
242
+ // memory file
243
+ memory
244
+ .command("file")
245
+ .description("View or edit the long-term MEMORY.md file")
246
+ .option("--edit", "Open in $EDITOR")
247
+ .option("--json", "Output as JSON")
248
+ .action(async (options) => {
249
+ try {
250
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
251
+ const memoryDir = getMemoryDir(ctx.env.dataDir);
252
+ const content = getMemoryFile(memoryDir);
253
+
254
+ if (options.edit) {
255
+ const { execSync } = await import("child_process");
256
+ const editor = process.env.EDITOR || process.env.VISUAL || "nano";
257
+ const filePath = `${memoryDir}/MEMORY.md`;
258
+ try {
259
+ execSync(`${editor} "${filePath}"`, { stdio: "inherit" });
260
+ logger.success("Memory file updated");
261
+ } catch {
262
+ logger.error(
263
+ `Failed to open editor. Set $EDITOR environment variable.`,
264
+ );
265
+ }
266
+ } else if (options.json) {
267
+ console.log(JSON.stringify({ content }, null, 2));
268
+ } else if (!content) {
269
+ logger.info(
270
+ "MEMORY.md is empty. Use 'memory file --edit' to add content.",
271
+ );
272
+ } else {
273
+ logger.log(content);
274
+ }
275
+
276
+ await shutdown();
277
+ } catch (err) {
278
+ logger.error(`Failed: ${err.message}`);
279
+ process.exit(1);
280
+ }
281
+ });
282
+ }
@@ -6,6 +6,15 @@
6
6
  import chalk from "chalk";
7
7
  import { logger } from "../lib/logger.js";
8
8
  import { bootstrap, shutdown } from "../runtime/bootstrap.js";
9
+ import {
10
+ ensureVersionsTable,
11
+ saveVersion,
12
+ getHistory,
13
+ getVersion,
14
+ simpleDiff,
15
+ formatDiff,
16
+ revertToVersion,
17
+ } from "../lib/note-versioning.js";
9
18
 
10
19
  /**
11
20
  * Ensure the notes table exists in the database
@@ -299,4 +308,182 @@ export function registerNoteCommand(program) {
299
308
  process.exit(1);
300
309
  }
301
310
  });
311
+
312
+ // note history
313
+ note
314
+ .command("history")
315
+ .description("Show version history for a note")
316
+ .argument("<id>", "Note ID (or prefix)")
317
+ .option("--json", "Output as JSON")
318
+ .action(async (id, options) => {
319
+ try {
320
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
321
+ if (!ctx.db) {
322
+ logger.error("Database not available");
323
+ process.exit(1);
324
+ }
325
+
326
+ const rawDb = ctx.db.getDatabase();
327
+ ensureNotesTable(rawDb);
328
+ ensureVersionsTable(rawDb);
329
+
330
+ // Find the note
331
+ const noteRow = rawDb
332
+ .prepare(
333
+ "SELECT id FROM notes WHERE id LIKE ? AND deleted_at IS NULL",
334
+ )
335
+ .get(`${id}%`);
336
+
337
+ if (!noteRow) {
338
+ logger.error(`Note not found: ${id}`);
339
+ process.exit(1);
340
+ }
341
+
342
+ const history = getHistory(rawDb, noteRow.id);
343
+
344
+ if (options.json) {
345
+ console.log(JSON.stringify(history, null, 2));
346
+ } else if (history.length === 0) {
347
+ logger.info("No version history found");
348
+ } else {
349
+ logger.log(
350
+ chalk.bold(`Version history (${history.length} versions):\n`),
351
+ );
352
+ for (const v of history) {
353
+ logger.log(
354
+ ` ${chalk.cyan(`v${v.version}`)} ${chalk.white(v.title)} ${chalk.gray(v.change_type)} ${chalk.gray(v.created_at || "")}`,
355
+ );
356
+ }
357
+ }
358
+
359
+ await shutdown();
360
+ } catch (err) {
361
+ logger.error(`Failed to get history: ${err.message}`);
362
+ process.exit(1);
363
+ }
364
+ });
365
+
366
+ // note diff
367
+ note
368
+ .command("diff")
369
+ .description("Show diff between two versions of a note")
370
+ .argument("<id>", "Note ID (or prefix)")
371
+ .argument("<v1>", "First version number")
372
+ .argument("<v2>", "Second version number")
373
+ .option("--json", "Output as JSON")
374
+ .action(async (id, v1, v2, options) => {
375
+ try {
376
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
377
+ if (!ctx.db) {
378
+ logger.error("Database not available");
379
+ process.exit(1);
380
+ }
381
+
382
+ const rawDb = ctx.db.getDatabase();
383
+ ensureNotesTable(rawDb);
384
+ ensureVersionsTable(rawDb);
385
+
386
+ const noteRow = rawDb
387
+ .prepare(
388
+ "SELECT id FROM notes WHERE id LIKE ? AND deleted_at IS NULL",
389
+ )
390
+ .get(`${id}%`);
391
+
392
+ if (!noteRow) {
393
+ logger.error(`Note not found: ${id}`);
394
+ process.exit(1);
395
+ }
396
+
397
+ const ver1 = getVersion(rawDb, noteRow.id, parseInt(v1));
398
+ const ver2 = getVersion(rawDb, noteRow.id, parseInt(v2));
399
+
400
+ if (!ver1) {
401
+ logger.error(`Version ${v1} not found`);
402
+ process.exit(1);
403
+ }
404
+ if (!ver2) {
405
+ logger.error(`Version ${v2} not found`);
406
+ process.exit(1);
407
+ }
408
+
409
+ const diff = simpleDiff(ver1.content, ver2.content);
410
+
411
+ if (options.json) {
412
+ console.log(
413
+ JSON.stringify(
414
+ { v1: parseInt(v1), v2: parseInt(v2), diff },
415
+ null,
416
+ 2,
417
+ ),
418
+ );
419
+ } else {
420
+ logger.log(chalk.bold(`Diff: v${v1} → v${v2}\n`));
421
+ logger.log(formatDiff(diff));
422
+ }
423
+
424
+ await shutdown();
425
+ } catch (err) {
426
+ logger.error(`Failed to compute diff: ${err.message}`);
427
+ process.exit(1);
428
+ }
429
+ });
430
+
431
+ // note revert
432
+ note
433
+ .command("revert")
434
+ .description("Revert a note to a specific version")
435
+ .argument("<id>", "Note ID (or prefix)")
436
+ .argument("<version>", "Version number to revert to")
437
+ .option("--force", "Skip confirmation")
438
+ .action(async (id, version, options) => {
439
+ try {
440
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
441
+ if (!ctx.db) {
442
+ logger.error("Database not available");
443
+ process.exit(1);
444
+ }
445
+
446
+ const rawDb = ctx.db.getDatabase();
447
+ ensureNotesTable(rawDb);
448
+ ensureVersionsTable(rawDb);
449
+
450
+ const noteRow = rawDb
451
+ .prepare(
452
+ "SELECT id, title FROM notes WHERE id LIKE ? AND deleted_at IS NULL",
453
+ )
454
+ .get(`${id}%`);
455
+
456
+ if (!noteRow) {
457
+ logger.error(`Note not found: ${id}`);
458
+ process.exit(1);
459
+ }
460
+
461
+ if (!options.force) {
462
+ const { confirm } = await import("@inquirer/prompts");
463
+ const ok = await confirm({
464
+ message: `Revert note "${noteRow.title}" to version ${version}?`,
465
+ });
466
+ if (!ok) {
467
+ logger.info("Cancelled");
468
+ return;
469
+ }
470
+ }
471
+
472
+ const result = revertToVersion(rawDb, noteRow.id, parseInt(version));
473
+
474
+ if (!result) {
475
+ logger.error(`Version ${version} not found or note unavailable`);
476
+ process.exit(1);
477
+ }
478
+
479
+ logger.success(
480
+ `Note reverted to v${result.reverted_to} → new version v${result.new_version}`,
481
+ );
482
+
483
+ await shutdown();
484
+ } catch (err) {
485
+ logger.error(`Failed to revert: ${err.message}`);
486
+ process.exit(1);
487
+ }
488
+ });
302
489
  }