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.
- package/README.md +309 -19
- package/bin/chainlesschain.js +4 -0
- package/package.json +1 -1
- package/src/commands/a2a.js +374 -0
- package/src/commands/audit.js +286 -0
- package/src/commands/auth.js +387 -0
- package/src/commands/bi.js +240 -0
- package/src/commands/browse.js +184 -0
- package/src/commands/cowork.js +317 -0
- package/src/commands/did.js +376 -0
- package/src/commands/economy.js +375 -0
- package/src/commands/encrypt.js +233 -0
- package/src/commands/evolution.js +398 -0
- package/src/commands/export.js +125 -0
- package/src/commands/git.js +215 -0
- package/src/commands/hmemory.js +273 -0
- package/src/commands/hook.js +260 -0
- package/src/commands/import.js +259 -0
- package/src/commands/init.js +184 -0
- package/src/commands/instinct.js +202 -0
- package/src/commands/llm.js +155 -4
- package/src/commands/lowcode.js +320 -0
- package/src/commands/mcp.js +302 -0
- package/src/commands/memory.js +282 -0
- package/src/commands/note.js +187 -0
- package/src/commands/org.js +505 -0
- package/src/commands/p2p.js +274 -0
- package/src/commands/plugin.js +451 -0
- package/src/commands/sandbox.js +366 -0
- package/src/commands/search.js +237 -0
- package/src/commands/session.js +238 -0
- package/src/commands/skill.js +254 -201
- package/src/commands/sync.js +249 -0
- package/src/commands/tokens.js +214 -0
- package/src/commands/wallet.js +416 -0
- package/src/commands/workflow.js +359 -0
- package/src/commands/zkp.js +277 -0
- package/src/index.js +93 -1
- package/src/lib/a2a-protocol.js +371 -0
- package/src/lib/agent-coordinator.js +273 -0
- package/src/lib/agent-economy.js +369 -0
- package/src/lib/app-builder.js +377 -0
- package/src/lib/audit-logger.js +364 -0
- package/src/lib/bi-engine.js +299 -0
- package/src/lib/bm25-search.js +322 -0
- package/src/lib/browser-automation.js +216 -0
- package/src/lib/cowork/ab-comparator-cli.js +180 -0
- package/src/lib/cowork/code-knowledge-graph-cli.js +232 -0
- package/src/lib/cowork/debate-review-cli.js +144 -0
- package/src/lib/cowork/decision-kb-cli.js +153 -0
- package/src/lib/cowork/project-style-analyzer-cli.js +168 -0
- package/src/lib/cowork-adapter.js +106 -0
- package/src/lib/crypto-manager.js +246 -0
- package/src/lib/did-manager.js +270 -0
- package/src/lib/ensure-utf8.js +59 -0
- package/src/lib/evolution-system.js +508 -0
- package/src/lib/git-integration.js +220 -0
- package/src/lib/hierarchical-memory.js +471 -0
- package/src/lib/hook-manager.js +387 -0
- package/src/lib/instinct-manager.js +190 -0
- package/src/lib/knowledge-exporter.js +302 -0
- package/src/lib/knowledge-importer.js +293 -0
- package/src/lib/llm-providers.js +325 -0
- package/src/lib/mcp-client.js +413 -0
- package/src/lib/memory-manager.js +211 -0
- package/src/lib/note-versioning.js +244 -0
- package/src/lib/org-manager.js +424 -0
- package/src/lib/p2p-manager.js +317 -0
- package/src/lib/pdf-parser.js +96 -0
- package/src/lib/permission-engine.js +374 -0
- package/src/lib/plan-mode.js +333 -0
- package/src/lib/plugin-manager.js +430 -0
- package/src/lib/project-detector.js +53 -0
- package/src/lib/response-cache.js +156 -0
- package/src/lib/sandbox-v2.js +503 -0
- package/src/lib/service-container.js +183 -0
- package/src/lib/session-manager.js +189 -0
- package/src/lib/skill-loader.js +274 -0
- package/src/lib/sync-manager.js +347 -0
- package/src/lib/token-tracker.js +200 -0
- package/src/lib/wallet-manager.js +348 -0
- package/src/lib/workflow-engine.js +503 -0
- package/src/lib/zkp-engine.js +241 -0
- 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
|
+
}
|