chainlesschain 0.37.9 → 0.37.11

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 (84) 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/a2a.js +374 -0
  5. package/src/commands/audit.js +286 -0
  6. package/src/commands/auth.js +387 -0
  7. package/src/commands/bi.js +240 -0
  8. package/src/commands/browse.js +184 -0
  9. package/src/commands/cowork.js +317 -0
  10. package/src/commands/did.js +376 -0
  11. package/src/commands/economy.js +375 -0
  12. package/src/commands/encrypt.js +233 -0
  13. package/src/commands/evolution.js +398 -0
  14. package/src/commands/export.js +125 -0
  15. package/src/commands/git.js +215 -0
  16. package/src/commands/hmemory.js +273 -0
  17. package/src/commands/hook.js +260 -0
  18. package/src/commands/import.js +259 -0
  19. package/src/commands/init.js +184 -0
  20. package/src/commands/instinct.js +202 -0
  21. package/src/commands/llm.js +155 -4
  22. package/src/commands/lowcode.js +320 -0
  23. package/src/commands/mcp.js +302 -0
  24. package/src/commands/memory.js +282 -0
  25. package/src/commands/note.js +187 -0
  26. package/src/commands/org.js +505 -0
  27. package/src/commands/p2p.js +274 -0
  28. package/src/commands/plugin.js +451 -0
  29. package/src/commands/sandbox.js +366 -0
  30. package/src/commands/search.js +237 -0
  31. package/src/commands/session.js +238 -0
  32. package/src/commands/skill.js +254 -201
  33. package/src/commands/sync.js +249 -0
  34. package/src/commands/tokens.js +214 -0
  35. package/src/commands/wallet.js +416 -0
  36. package/src/commands/workflow.js +359 -0
  37. package/src/commands/zkp.js +277 -0
  38. package/src/index.js +93 -1
  39. package/src/lib/a2a-protocol.js +371 -0
  40. package/src/lib/agent-coordinator.js +273 -0
  41. package/src/lib/agent-economy.js +369 -0
  42. package/src/lib/app-builder.js +377 -0
  43. package/src/lib/audit-logger.js +364 -0
  44. package/src/lib/bi-engine.js +299 -0
  45. package/src/lib/bm25-search.js +322 -0
  46. package/src/lib/browser-automation.js +216 -0
  47. package/src/lib/cowork/ab-comparator-cli.js +180 -0
  48. package/src/lib/cowork/code-knowledge-graph-cli.js +232 -0
  49. package/src/lib/cowork/debate-review-cli.js +144 -0
  50. package/src/lib/cowork/decision-kb-cli.js +153 -0
  51. package/src/lib/cowork/project-style-analyzer-cli.js +168 -0
  52. package/src/lib/cowork-adapter.js +106 -0
  53. package/src/lib/crypto-manager.js +246 -0
  54. package/src/lib/did-manager.js +270 -0
  55. package/src/lib/ensure-utf8.js +59 -0
  56. package/src/lib/evolution-system.js +508 -0
  57. package/src/lib/git-integration.js +220 -0
  58. package/src/lib/hierarchical-memory.js +471 -0
  59. package/src/lib/hook-manager.js +387 -0
  60. package/src/lib/instinct-manager.js +190 -0
  61. package/src/lib/knowledge-exporter.js +302 -0
  62. package/src/lib/knowledge-importer.js +293 -0
  63. package/src/lib/llm-providers.js +325 -0
  64. package/src/lib/mcp-client.js +413 -0
  65. package/src/lib/memory-manager.js +211 -0
  66. package/src/lib/note-versioning.js +244 -0
  67. package/src/lib/org-manager.js +424 -0
  68. package/src/lib/p2p-manager.js +317 -0
  69. package/src/lib/pdf-parser.js +96 -0
  70. package/src/lib/permission-engine.js +374 -0
  71. package/src/lib/plan-mode.js +333 -0
  72. package/src/lib/plugin-manager.js +430 -0
  73. package/src/lib/project-detector.js +53 -0
  74. package/src/lib/response-cache.js +156 -0
  75. package/src/lib/sandbox-v2.js +503 -0
  76. package/src/lib/service-container.js +183 -0
  77. package/src/lib/session-manager.js +189 -0
  78. package/src/lib/skill-loader.js +274 -0
  79. package/src/lib/sync-manager.js +347 -0
  80. package/src/lib/token-tracker.js +200 -0
  81. package/src/lib/wallet-manager.js +348 -0
  82. package/src/lib/workflow-engine.js +503 -0
  83. package/src/lib/zkp-engine.js +241 -0
  84. package/src/repl/agent-repl.js +259 -124
@@ -0,0 +1,302 @@
1
+ /**
2
+ * MCP (Model Context Protocol) commands
3
+ * chainlesschain mcp servers|connect|disconnect|tools|call
4
+ */
5
+
6
+ import chalk from "chalk";
7
+ import ora from "ora";
8
+ import { logger } from "../lib/logger.js";
9
+ import { bootstrap, shutdown } from "../runtime/bootstrap.js";
10
+ import { MCPClient, MCPServerConfig } from "../lib/mcp-client.js";
11
+
12
+ // Singleton MCP client for session reuse
13
+ let mcpClient = null;
14
+
15
+ function getClient() {
16
+ if (!mcpClient) mcpClient = new MCPClient();
17
+ return mcpClient;
18
+ }
19
+
20
+ export function registerMcpCommand(program) {
21
+ const mcp = program
22
+ .command("mcp")
23
+ .description("MCP server management and tool execution");
24
+
25
+ // mcp servers — list configured servers
26
+ mcp
27
+ .command("servers")
28
+ .description("List configured MCP servers")
29
+ .option("--json", "Output as JSON")
30
+ .action(async (options) => {
31
+ try {
32
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
33
+ if (!ctx.db) {
34
+ logger.error("Database not available");
35
+ process.exit(1);
36
+ }
37
+
38
+ const db = ctx.db.getDatabase();
39
+ const config = new MCPServerConfig(db);
40
+ const servers = config.list();
41
+
42
+ if (options.json) {
43
+ console.log(JSON.stringify(servers, null, 2));
44
+ } else if (servers.length === 0) {
45
+ logger.info("No MCP servers configured. Use 'mcp add' to add one.");
46
+ } else {
47
+ logger.log(chalk.bold(`MCP Servers (${servers.length}):\n`));
48
+ for (const s of servers) {
49
+ const auto = s.autoConnect ? chalk.green(" [auto]") : "";
50
+ logger.log(` ${chalk.cyan(s.name)}${auto}`);
51
+ logger.log(
52
+ ` ${chalk.gray("Command:")} ${s.command} ${s.args.join(" ")}`,
53
+ );
54
+ }
55
+ }
56
+
57
+ await shutdown();
58
+ } catch (err) {
59
+ logger.error(`Failed: ${err.message}`);
60
+ process.exit(1);
61
+ }
62
+ });
63
+
64
+ // mcp add — add/update a server config
65
+ mcp
66
+ .command("add")
67
+ .description("Add or update an MCP server configuration")
68
+ .argument("<name>", "Server name")
69
+ .requiredOption("-c, --command <cmd>", "Server command to run")
70
+ .option("-a, --args <args>", "Command arguments (comma-separated)")
71
+ .option("--auto-connect", "Auto-connect on startup")
72
+ .option("--json", "Output as JSON")
73
+ .action(async (name, options) => {
74
+ try {
75
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
76
+ if (!ctx.db) {
77
+ logger.error("Database not available");
78
+ process.exit(1);
79
+ }
80
+
81
+ const db = ctx.db.getDatabase();
82
+ const config = new MCPServerConfig(db);
83
+ const args = options.args
84
+ ? options.args.split(",").map((a) => a.trim())
85
+ : [];
86
+
87
+ config.add(name, {
88
+ command: options.command,
89
+ args,
90
+ autoConnect: !!options.autoConnect,
91
+ });
92
+
93
+ if (options.json) {
94
+ console.log(
95
+ JSON.stringify({
96
+ name,
97
+ command: options.command,
98
+ args,
99
+ autoConnect: !!options.autoConnect,
100
+ }),
101
+ );
102
+ } else {
103
+ logger.success(`MCP server "${chalk.cyan(name)}" configured`);
104
+ logger.log(
105
+ ` ${chalk.gray("Command:")} ${options.command} ${args.join(" ")}`,
106
+ );
107
+ }
108
+
109
+ await shutdown();
110
+ } catch (err) {
111
+ logger.error(`Failed: ${err.message}`);
112
+ process.exit(1);
113
+ }
114
+ });
115
+
116
+ // mcp remove — remove a server config
117
+ mcp
118
+ .command("remove")
119
+ .description("Remove an MCP server configuration")
120
+ .argument("<name>", "Server name")
121
+ .action(async (name) => {
122
+ try {
123
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
124
+ if (!ctx.db) {
125
+ logger.error("Database not available");
126
+ process.exit(1);
127
+ }
128
+
129
+ const db = ctx.db.getDatabase();
130
+ const config = new MCPServerConfig(db);
131
+ const removed = config.remove(name);
132
+
133
+ if (removed) {
134
+ logger.success(`MCP server "${name}" removed`);
135
+ } else {
136
+ logger.error(`Server "${name}" not found`);
137
+ }
138
+
139
+ await shutdown();
140
+ } catch (err) {
141
+ logger.error(`Failed: ${err.message}`);
142
+ process.exit(1);
143
+ }
144
+ });
145
+
146
+ // mcp connect — connect to a server
147
+ mcp
148
+ .command("connect")
149
+ .description("Connect to an MCP server")
150
+ .argument("<name>", "Server name (configured or command)")
151
+ .option("--json", "Output as JSON")
152
+ .action(async (name, options) => {
153
+ try {
154
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
155
+ if (!ctx.db) {
156
+ logger.error("Database not available");
157
+ process.exit(1);
158
+ }
159
+
160
+ const db = ctx.db.getDatabase();
161
+ const config = new MCPServerConfig(db);
162
+ const serverConfig = config.get(name);
163
+
164
+ if (!serverConfig) {
165
+ logger.error(`Server "${name}" not configured. Use 'mcp add' first.`);
166
+ process.exit(1);
167
+ }
168
+
169
+ const spinner = ora(`Connecting to ${name}...`).start();
170
+ const client = getClient();
171
+
172
+ const result = await client.connect(name, serverConfig);
173
+ spinner.stop();
174
+
175
+ if (options.json) {
176
+ console.log(JSON.stringify(result, null, 2));
177
+ } else {
178
+ logger.success(`Connected to ${chalk.cyan(name)}`);
179
+ logger.log(` ${chalk.gray("Tools:")} ${result.tools.length}`);
180
+ logger.log(
181
+ ` ${chalk.gray("Resources:")} ${result.resources.length}`,
182
+ );
183
+ if (result.serverInfo?.name) {
184
+ logger.log(` ${chalk.gray("Server:")} ${result.serverInfo.name}`);
185
+ }
186
+ }
187
+
188
+ // Don't shutdown — keep connection alive for subsequent calls
189
+ } catch (err) {
190
+ logger.error(`Connection failed: ${err.message}`);
191
+ process.exit(1);
192
+ }
193
+ });
194
+
195
+ // mcp disconnect — disconnect from a server
196
+ mcp
197
+ .command("disconnect")
198
+ .description("Disconnect from an MCP server")
199
+ .argument("<name>", "Server name")
200
+ .action(async (name) => {
201
+ try {
202
+ const client = getClient();
203
+ const ok = await client.disconnect(name);
204
+ if (ok) {
205
+ logger.success(`Disconnected from ${name}`);
206
+ } else {
207
+ logger.error(`Server "${name}" not connected`);
208
+ }
209
+ } catch (err) {
210
+ logger.error(`Disconnect failed: ${err.message}`);
211
+ process.exit(1);
212
+ }
213
+ });
214
+
215
+ // mcp tools — list available tools
216
+ mcp
217
+ .command("tools")
218
+ .description("List available MCP tools")
219
+ .option("-s, --server <name>", "Filter by server name")
220
+ .option("--json", "Output as JSON")
221
+ .action(async (options) => {
222
+ try {
223
+ const client = getClient();
224
+ const tools = client.listTools(options.server);
225
+
226
+ if (options.json) {
227
+ console.log(JSON.stringify(tools, null, 2));
228
+ } else if (tools.length === 0) {
229
+ logger.info("No tools available. Connect to a server first.");
230
+ } else {
231
+ logger.log(chalk.bold(`MCP Tools (${tools.length}):\n`));
232
+ for (const t of tools) {
233
+ logger.log(
234
+ ` ${chalk.cyan(t.name)} ${chalk.gray(`[${t.server}]`)}`,
235
+ );
236
+ if (t.description) {
237
+ logger.log(` ${chalk.gray(t.description)}`);
238
+ }
239
+ }
240
+ }
241
+ } catch (err) {
242
+ logger.error(`Failed: ${err.message}`);
243
+ process.exit(1);
244
+ }
245
+ });
246
+
247
+ // mcp call — call a tool
248
+ mcp
249
+ .command("call")
250
+ .description("Call an MCP tool")
251
+ .argument("<tool>", "Tool name")
252
+ .option("-s, --server <name>", "Server name")
253
+ .option("-a, --args <json>", "Tool arguments as JSON")
254
+ .option("--json", "Output as JSON")
255
+ .action(async (tool, options) => {
256
+ try {
257
+ const client = getClient();
258
+
259
+ // Find which server has this tool
260
+ let serverName = options.server;
261
+ if (!serverName) {
262
+ const allTools = client.listTools();
263
+ const match = allTools.find((t) => t.name === tool);
264
+ if (!match) {
265
+ logger.error(
266
+ `Tool "${tool}" not found. Run 'mcp tools' to see available tools.`,
267
+ );
268
+ process.exit(1);
269
+ }
270
+ serverName = match.server;
271
+ }
272
+
273
+ const args = options.args ? JSON.parse(options.args) : {};
274
+ const spinner = ora(`Calling ${tool}...`).start();
275
+
276
+ const result = await client.callTool(serverName, tool, args);
277
+ spinner.stop();
278
+
279
+ if (options.json) {
280
+ console.log(JSON.stringify(result, null, 2));
281
+ } else {
282
+ // Display content blocks
283
+ if (result?.content) {
284
+ for (const block of result.content) {
285
+ if (block.type === "text") {
286
+ logger.log(block.text);
287
+ } else if (block.type === "image") {
288
+ logger.log(chalk.gray(`[Image: ${block.mimeType || "image"}]`));
289
+ } else {
290
+ logger.log(JSON.stringify(block, null, 2));
291
+ }
292
+ }
293
+ } else {
294
+ logger.log(JSON.stringify(result, null, 2));
295
+ }
296
+ }
297
+ } catch (err) {
298
+ logger.error(`Tool call failed: ${err.message}`);
299
+ process.exit(1);
300
+ }
301
+ });
302
+ }
@@ -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
+ }