chainlesschain 0.37.10 → 0.37.12
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 +166 -10
- package/package.json +1 -1
- package/src/commands/a2a.js +374 -0
- package/src/commands/bi.js +240 -0
- package/src/commands/cowork.js +317 -0
- package/src/commands/economy.js +375 -0
- package/src/commands/evolution.js +398 -0
- package/src/commands/hmemory.js +273 -0
- package/src/commands/hook.js +260 -0
- package/src/commands/init.js +184 -0
- package/src/commands/lowcode.js +320 -0
- package/src/commands/plugin.js +55 -2
- package/src/commands/sandbox.js +366 -0
- package/src/commands/skill.js +254 -201
- package/src/commands/workflow.js +359 -0
- package/src/commands/zkp.js +277 -0
- package/src/index.js +44 -0
- 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/bi-engine.js +299 -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/evolution-system.js +508 -0
- package/src/lib/hierarchical-memory.js +471 -0
- package/src/lib/hook-manager.js +387 -0
- package/src/lib/plugin-manager.js +118 -0
- package/src/lib/project-detector.js +53 -0
- package/src/lib/sandbox-v2.js +503 -0
- package/src/lib/service-container.js +183 -0
- package/src/lib/skill-loader.js +274 -0
- package/src/lib/workflow-engine.js +503 -0
- package/src/lib/zkp-engine.js +241 -0
- package/src/repl/agent-repl.js +117 -112
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-Evolving System commands
|
|
3
|
+
* chainlesschain evolution assess|learn|diagnose|repair|predict|growth|stats|export
|
|
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 {
|
|
11
|
+
assessCapability,
|
|
12
|
+
trainIncremental,
|
|
13
|
+
selfDiagnose,
|
|
14
|
+
selfRepair,
|
|
15
|
+
predictBehavior,
|
|
16
|
+
getGrowthLog,
|
|
17
|
+
getCapabilities,
|
|
18
|
+
getModels,
|
|
19
|
+
exportModel,
|
|
20
|
+
} from "../lib/evolution-system.js";
|
|
21
|
+
|
|
22
|
+
export function registerEvolutionCommand(program) {
|
|
23
|
+
const evolution = program
|
|
24
|
+
.command("evolution")
|
|
25
|
+
.description("Self-evolving AI system — capabilities, learning, diagnosis");
|
|
26
|
+
|
|
27
|
+
// evolution assess <name> <score>
|
|
28
|
+
evolution
|
|
29
|
+
.command("assess")
|
|
30
|
+
.description("Assess and track a capability")
|
|
31
|
+
.argument("<name>", "Capability name")
|
|
32
|
+
.argument("<score>", "Score (0-1)")
|
|
33
|
+
.option("--category <cat>", "Capability category", "general")
|
|
34
|
+
.option("--json", "Output as JSON")
|
|
35
|
+
.action(async (name, score, options) => {
|
|
36
|
+
try {
|
|
37
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
38
|
+
if (!ctx.db) {
|
|
39
|
+
logger.error("Database not available");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const db = ctx.db.getDatabase();
|
|
43
|
+
const spinner = ora(`Assessing capability: ${name}...`).start();
|
|
44
|
+
|
|
45
|
+
const result = assessCapability(
|
|
46
|
+
db,
|
|
47
|
+
name,
|
|
48
|
+
parseFloat(score),
|
|
49
|
+
options.category,
|
|
50
|
+
);
|
|
51
|
+
spinner.succeed("Capability assessed");
|
|
52
|
+
|
|
53
|
+
if (options.json) {
|
|
54
|
+
console.log(JSON.stringify(result, null, 2));
|
|
55
|
+
} else {
|
|
56
|
+
const trendIcon =
|
|
57
|
+
result.trend === "improving"
|
|
58
|
+
? chalk.green("improving")
|
|
59
|
+
: result.trend === "declining"
|
|
60
|
+
? chalk.red("declining")
|
|
61
|
+
: chalk.gray("stable");
|
|
62
|
+
logger.log(chalk.bold(`Capability: ${result.name}`));
|
|
63
|
+
logger.log(` Score: ${chalk.cyan(result.score)}`);
|
|
64
|
+
logger.log(` Trend: ${trendIcon}`);
|
|
65
|
+
logger.log(` History: ${result.history.length} assessments`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
await shutdown();
|
|
69
|
+
} catch (err) {
|
|
70
|
+
logger.error(`Failed: ${err.message}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// evolution learn <model-id>
|
|
76
|
+
evolution
|
|
77
|
+
.command("learn")
|
|
78
|
+
.description("Train a model with incremental data")
|
|
79
|
+
.argument("<model-id>", "Model ID to train")
|
|
80
|
+
.option("--data <json>", "Training data as JSON array")
|
|
81
|
+
.option(
|
|
82
|
+
"--type <type>",
|
|
83
|
+
"Model type (classification|regression)",
|
|
84
|
+
"classification",
|
|
85
|
+
)
|
|
86
|
+
.option("--json", "Output as JSON")
|
|
87
|
+
.action(async (modelId, options) => {
|
|
88
|
+
try {
|
|
89
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
90
|
+
if (!ctx.db) {
|
|
91
|
+
logger.error("Database not available");
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
const db = ctx.db.getDatabase();
|
|
95
|
+
const spinner = ora(`Training model: ${modelId}...`).start();
|
|
96
|
+
|
|
97
|
+
let data = [{ sample: true }];
|
|
98
|
+
if (options.data) {
|
|
99
|
+
try {
|
|
100
|
+
data = JSON.parse(options.data);
|
|
101
|
+
} catch (_err) {
|
|
102
|
+
// Use data string as single data point
|
|
103
|
+
data = [options.data];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const result = trainIncremental(db, modelId, data, {
|
|
108
|
+
type: options.type,
|
|
109
|
+
});
|
|
110
|
+
spinner.succeed("Model trained");
|
|
111
|
+
|
|
112
|
+
if (options.json) {
|
|
113
|
+
console.log(JSON.stringify(result, null, 2));
|
|
114
|
+
} else {
|
|
115
|
+
logger.log(chalk.bold(`Model: ${result.name}`));
|
|
116
|
+
logger.log(` Type: ${result.type}`);
|
|
117
|
+
logger.log(` Accuracy: ${chalk.cyan(result.accuracy.toFixed(4))}`);
|
|
118
|
+
logger.log(` Data Points: ${result.dataPoints}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
await shutdown();
|
|
122
|
+
} catch (err) {
|
|
123
|
+
logger.error(`Failed: ${err.message}`);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// evolution diagnose
|
|
129
|
+
evolution
|
|
130
|
+
.command("diagnose")
|
|
131
|
+
.description("Run self-diagnosis on the system")
|
|
132
|
+
.option("--json", "Output as JSON")
|
|
133
|
+
.action(async (options) => {
|
|
134
|
+
try {
|
|
135
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
136
|
+
if (!ctx.db) {
|
|
137
|
+
logger.error("Database not available");
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
const db = ctx.db.getDatabase();
|
|
141
|
+
const spinner = ora("Running self-diagnosis...").start();
|
|
142
|
+
|
|
143
|
+
const result = selfDiagnose(db);
|
|
144
|
+
spinner.succeed("Diagnosis complete");
|
|
145
|
+
|
|
146
|
+
if (options.json) {
|
|
147
|
+
console.log(JSON.stringify(result, null, 2));
|
|
148
|
+
} else {
|
|
149
|
+
const statusColor =
|
|
150
|
+
result.overallStatus === "healthy" ? chalk.green : chalk.yellow;
|
|
151
|
+
logger.log(
|
|
152
|
+
chalk.bold(`Overall Status: ${statusColor(result.overallStatus)}`),
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
logger.log(chalk.bold("\nComponents:"));
|
|
156
|
+
for (const c of result.components) {
|
|
157
|
+
const icon =
|
|
158
|
+
c.status === "healthy" ? chalk.green("OK") : chalk.yellow("WARN");
|
|
159
|
+
logger.log(` ${icon} ${c.name}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (result.issues.length > 0) {
|
|
163
|
+
logger.log(chalk.bold("\nIssues:"));
|
|
164
|
+
for (const issue of result.issues) {
|
|
165
|
+
logger.log(` - ${chalk.yellow(issue.type)}: ${issue.details}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (result.recommendations.length > 0) {
|
|
170
|
+
logger.log(chalk.bold("\nRecommendations:"));
|
|
171
|
+
for (const rec of result.recommendations) {
|
|
172
|
+
logger.log(` - ${rec}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
await shutdown();
|
|
178
|
+
} catch (err) {
|
|
179
|
+
logger.error(`Failed: ${err.message}`);
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// evolution repair <issue>
|
|
185
|
+
evolution
|
|
186
|
+
.command("repair")
|
|
187
|
+
.description("Self-repair a diagnosed issue")
|
|
188
|
+
.argument("<issue>", "Issue type (high-memory|stale-cache|degraded-model)")
|
|
189
|
+
.option("--json", "Output as JSON")
|
|
190
|
+
.action(async (issue, options) => {
|
|
191
|
+
try {
|
|
192
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
193
|
+
if (!ctx.db) {
|
|
194
|
+
logger.error("Database not available");
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
const db = ctx.db.getDatabase();
|
|
198
|
+
const spinner = ora(`Repairing: ${issue}...`).start();
|
|
199
|
+
|
|
200
|
+
const result = selfRepair(db, issue);
|
|
201
|
+
spinner.succeed("Repair complete");
|
|
202
|
+
|
|
203
|
+
if (options.json) {
|
|
204
|
+
console.log(JSON.stringify(result, null, 2));
|
|
205
|
+
} else {
|
|
206
|
+
logger.log(chalk.bold(`Repaired: ${result.issue}`));
|
|
207
|
+
logger.log(chalk.bold("Actions taken:"));
|
|
208
|
+
for (const action of result.actions) {
|
|
209
|
+
logger.log(` - ${chalk.green(action)}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
await shutdown();
|
|
214
|
+
} catch (err) {
|
|
215
|
+
logger.error(`Failed: ${err.message}`);
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// evolution predict [user-id]
|
|
221
|
+
evolution
|
|
222
|
+
.command("predict")
|
|
223
|
+
.description("Predict user behavior")
|
|
224
|
+
.argument("[user-id]", "User ID (optional)")
|
|
225
|
+
.option("--json", "Output as JSON")
|
|
226
|
+
.action(async (userId, options) => {
|
|
227
|
+
try {
|
|
228
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
229
|
+
if (!ctx.db) {
|
|
230
|
+
logger.error("Database not available");
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
const db = ctx.db.getDatabase();
|
|
234
|
+
const spinner = ora("Predicting behavior...").start();
|
|
235
|
+
|
|
236
|
+
const result = predictBehavior(db, userId);
|
|
237
|
+
spinner.succeed("Prediction complete");
|
|
238
|
+
|
|
239
|
+
if (options.json) {
|
|
240
|
+
console.log(JSON.stringify(result, null, 2));
|
|
241
|
+
} else {
|
|
242
|
+
logger.log(
|
|
243
|
+
chalk.bold(`Behavior Prediction (user: ${result.userId})`),
|
|
244
|
+
);
|
|
245
|
+
logger.log(` Confidence: ${chalk.cyan(result.confidence)}`);
|
|
246
|
+
logger.log(` Based on: ${result.basedOnEvents} events\n`);
|
|
247
|
+
|
|
248
|
+
if (result.predictions.length > 0) {
|
|
249
|
+
logger.log(chalk.bold(" Predicted Actions:"));
|
|
250
|
+
for (const p of result.predictions) {
|
|
251
|
+
const bar = "=".repeat(Math.round(p.probability * 20));
|
|
252
|
+
logger.log(
|
|
253
|
+
` ${p.action.padEnd(25)} ${chalk.cyan(bar)} ${(p.probability * 100).toFixed(1)}%`,
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
logger.log(chalk.gray(" No predictions available yet."));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
await shutdown();
|
|
262
|
+
} catch (err) {
|
|
263
|
+
logger.error(`Failed: ${err.message}`);
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// evolution growth
|
|
269
|
+
evolution
|
|
270
|
+
.command("growth")
|
|
271
|
+
.description("Show growth log")
|
|
272
|
+
.option("--type <filter>", "Filter by event type")
|
|
273
|
+
.option("--limit <n>", "Limit entries", parseInt)
|
|
274
|
+
.option("--json", "Output as JSON")
|
|
275
|
+
.action(async (options) => {
|
|
276
|
+
try {
|
|
277
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
278
|
+
if (!ctx.db) {
|
|
279
|
+
logger.error("Database not available");
|
|
280
|
+
process.exit(1);
|
|
281
|
+
}
|
|
282
|
+
const db = ctx.db.getDatabase();
|
|
283
|
+
const entries = getGrowthLog(db, {
|
|
284
|
+
type: options.type,
|
|
285
|
+
limit: options.limit,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
if (options.json) {
|
|
289
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
290
|
+
} else if (entries.length === 0) {
|
|
291
|
+
logger.info("No growth events recorded yet.");
|
|
292
|
+
} else {
|
|
293
|
+
logger.log(chalk.bold(`Growth Log (${entries.length} entries):\n`));
|
|
294
|
+
for (const e of entries) {
|
|
295
|
+
const ts = chalk.gray(e.timestamp);
|
|
296
|
+
const type = chalk.yellow(e.eventType);
|
|
297
|
+
logger.log(` ${ts} ${type}`);
|
|
298
|
+
logger.log(` ${e.description}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
await shutdown();
|
|
303
|
+
} catch (err) {
|
|
304
|
+
logger.error(`Failed: ${err.message}`);
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// evolution stats
|
|
310
|
+
evolution
|
|
311
|
+
.command("stats")
|
|
312
|
+
.description("Show capabilities and models overview")
|
|
313
|
+
.option("--json", "Output as JSON")
|
|
314
|
+
.action(async (options) => {
|
|
315
|
+
try {
|
|
316
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
317
|
+
if (!ctx.db) {
|
|
318
|
+
logger.error("Database not available");
|
|
319
|
+
process.exit(1);
|
|
320
|
+
}
|
|
321
|
+
const db = ctx.db.getDatabase();
|
|
322
|
+
const caps = getCapabilities(db);
|
|
323
|
+
const mdls = getModels(db);
|
|
324
|
+
|
|
325
|
+
if (options.json) {
|
|
326
|
+
console.log(
|
|
327
|
+
JSON.stringify({ capabilities: caps, models: mdls }, null, 2),
|
|
328
|
+
);
|
|
329
|
+
} else {
|
|
330
|
+
logger.log(chalk.bold(`Capabilities (${caps.length}):\n`));
|
|
331
|
+
if (caps.length === 0) {
|
|
332
|
+
logger.log(chalk.gray(" No capabilities tracked yet."));
|
|
333
|
+
}
|
|
334
|
+
for (const c of caps) {
|
|
335
|
+
const trendColor =
|
|
336
|
+
c.trend === "improving"
|
|
337
|
+
? chalk.green
|
|
338
|
+
: c.trend === "declining"
|
|
339
|
+
? chalk.red
|
|
340
|
+
: chalk.gray;
|
|
341
|
+
logger.log(
|
|
342
|
+
` ${chalk.cyan(c.name.padEnd(20))} score=${c.score.toFixed(2)} trend=${trendColor(c.trend)} [${c.category}]`,
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
logger.log(chalk.bold(`\nModels (${mdls.length}):\n`));
|
|
347
|
+
if (mdls.length === 0) {
|
|
348
|
+
logger.log(chalk.gray(" No models registered yet."));
|
|
349
|
+
}
|
|
350
|
+
for (const m of mdls) {
|
|
351
|
+
logger.log(
|
|
352
|
+
` ${chalk.cyan(m.id.padEnd(20))} type=${m.type} accuracy=${m.accuracy.toFixed(4)} data=${m.dataPoints}`,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
await shutdown();
|
|
358
|
+
} catch (err) {
|
|
359
|
+
logger.error(`Failed: ${err.message}`);
|
|
360
|
+
process.exit(1);
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// evolution export <model-id>
|
|
365
|
+
evolution
|
|
366
|
+
.command("export")
|
|
367
|
+
.description("Export a model for portability")
|
|
368
|
+
.argument("<model-id>", "Model ID to export")
|
|
369
|
+
.option("--json", "Output as JSON")
|
|
370
|
+
.action(async (modelId, options) => {
|
|
371
|
+
try {
|
|
372
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
373
|
+
if (!ctx.db) {
|
|
374
|
+
logger.error("Database not available");
|
|
375
|
+
process.exit(1);
|
|
376
|
+
}
|
|
377
|
+
const db = ctx.db.getDatabase();
|
|
378
|
+
|
|
379
|
+
const result = exportModel(db, modelId);
|
|
380
|
+
|
|
381
|
+
if (options.json) {
|
|
382
|
+
console.log(JSON.stringify(result, null, 2));
|
|
383
|
+
} else {
|
|
384
|
+
logger.log(chalk.bold(`Exported Model: ${result.name}`));
|
|
385
|
+
logger.log(` ID: ${result.id}`);
|
|
386
|
+
logger.log(` Type: ${result.type}`);
|
|
387
|
+
logger.log(` Accuracy: ${result.accuracy.toFixed(4)}`);
|
|
388
|
+
logger.log(` Data Points: ${result.dataPoints}`);
|
|
389
|
+
logger.log(` Exported: ${result.exportedAt}`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
await shutdown();
|
|
393
|
+
} catch (err) {
|
|
394
|
+
logger.error(`Failed: ${err.message}`);
|
|
395
|
+
process.exit(1);
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hierarchical Memory 2.0 commands
|
|
3
|
+
* chainlesschain hmemory store|recall|consolidate|search|stats|share|prune
|
|
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 {
|
|
11
|
+
storeMemory,
|
|
12
|
+
recallMemory,
|
|
13
|
+
consolidateMemory,
|
|
14
|
+
searchEpisodic,
|
|
15
|
+
searchSemantic,
|
|
16
|
+
getMemoryStats,
|
|
17
|
+
shareMemory,
|
|
18
|
+
pruneMemory,
|
|
19
|
+
} from "../lib/hierarchical-memory.js";
|
|
20
|
+
|
|
21
|
+
export function registerHmemoryCommand(program) {
|
|
22
|
+
const hmemory = program
|
|
23
|
+
.command("hmemory")
|
|
24
|
+
.description("Hierarchical Memory 2.0 — four-layer memory system");
|
|
25
|
+
|
|
26
|
+
// hmemory store <content>
|
|
27
|
+
hmemory
|
|
28
|
+
.command("store")
|
|
29
|
+
.description("Store a memory at the appropriate layer")
|
|
30
|
+
.argument("<content>", "Memory content")
|
|
31
|
+
.option("--importance <n>", "Importance 0.0-1.0", "0.5")
|
|
32
|
+
.option("--type <type>", "Memory type (episodic|semantic)", "episodic")
|
|
33
|
+
.option("--core", "Force store as core memory (importance=1.0)")
|
|
34
|
+
.option("--json", "Output as JSON")
|
|
35
|
+
.action(async (content, options) => {
|
|
36
|
+
try {
|
|
37
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
38
|
+
if (!ctx.db) {
|
|
39
|
+
logger.error("Database not available");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const db = ctx.db.getDatabase();
|
|
43
|
+
const importance = options.core
|
|
44
|
+
? 1.0
|
|
45
|
+
: parseFloat(options.importance) || 0.5;
|
|
46
|
+
const entry = storeMemory(db, content, {
|
|
47
|
+
importance,
|
|
48
|
+
type: options.type,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (options.json) {
|
|
52
|
+
console.log(JSON.stringify(entry, null, 2));
|
|
53
|
+
} else {
|
|
54
|
+
logger.success(
|
|
55
|
+
`Memory stored: ${chalk.gray(entry.id.slice(0, 16))} → ${chalk.cyan(entry.layer)}`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await shutdown();
|
|
60
|
+
} catch (err) {
|
|
61
|
+
logger.error(`Failed: ${err.message}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// hmemory recall <query>
|
|
67
|
+
hmemory
|
|
68
|
+
.command("recall")
|
|
69
|
+
.description("Recall memories with forgetting curve")
|
|
70
|
+
.argument("<query>", "Search query")
|
|
71
|
+
.option("-n, --limit <n>", "Max results", "20")
|
|
72
|
+
.option("--json", "Output as JSON")
|
|
73
|
+
.action(async (query, 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
|
+
const db = ctx.db.getDatabase();
|
|
81
|
+
const results = recallMemory(db, query, {
|
|
82
|
+
limit: parseInt(options.limit) || 20,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (options.json) {
|
|
86
|
+
console.log(JSON.stringify(results, null, 2));
|
|
87
|
+
} else if (results.length === 0) {
|
|
88
|
+
logger.info(`No memories matching "${query}" above recall threshold`);
|
|
89
|
+
} else {
|
|
90
|
+
logger.log(chalk.bold(`Recalled ${results.length} memories:\n`));
|
|
91
|
+
for (const r of results) {
|
|
92
|
+
const retention = (r.retention * 100).toFixed(0);
|
|
93
|
+
logger.log(
|
|
94
|
+
` ${chalk.gray(r.id.slice(0, 16))} [${chalk.cyan(r.layer)}] retention=${chalk.yellow(retention + "%")}`,
|
|
95
|
+
);
|
|
96
|
+
logger.log(
|
|
97
|
+
` ${chalk.white(r.content.substring(0, 120).replace(/\n/g, " "))}`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
await shutdown();
|
|
103
|
+
} catch (err) {
|
|
104
|
+
logger.error(`Failed: ${err.message}`);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// hmemory consolidate
|
|
110
|
+
hmemory
|
|
111
|
+
.command("consolidate")
|
|
112
|
+
.description("Promote and forget memories across layers")
|
|
113
|
+
.action(async () => {
|
|
114
|
+
try {
|
|
115
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
116
|
+
if (!ctx.db) {
|
|
117
|
+
logger.error("Database not available");
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
const db = ctx.db.getDatabase();
|
|
121
|
+
const spinner = ora("Consolidating memories...").start();
|
|
122
|
+
const result = consolidateMemory(db);
|
|
123
|
+
spinner.succeed(
|
|
124
|
+
`Consolidation complete: ${chalk.green(result.promoted)} promoted, ${chalk.red(result.forgotten)} forgotten`,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
await shutdown();
|
|
128
|
+
} catch (err) {
|
|
129
|
+
logger.error(`Failed: ${err.message}`);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// hmemory search <query>
|
|
135
|
+
hmemory
|
|
136
|
+
.command("search")
|
|
137
|
+
.description("Search memories by type")
|
|
138
|
+
.argument("<query>", "Search query")
|
|
139
|
+
.option("--type <type>", "Memory type (episodic|semantic)", "episodic")
|
|
140
|
+
.option("-n, --limit <n>", "Max results", "20")
|
|
141
|
+
.option("--json", "Output as JSON")
|
|
142
|
+
.action(async (query, options) => {
|
|
143
|
+
try {
|
|
144
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
145
|
+
if (!ctx.db) {
|
|
146
|
+
logger.error("Database not available");
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
const db = ctx.db.getDatabase();
|
|
150
|
+
const searchFn =
|
|
151
|
+
options.type === "semantic" ? searchSemantic : searchEpisodic;
|
|
152
|
+
const results = searchFn(db, query, {
|
|
153
|
+
limit: parseInt(options.limit) || 20,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
if (options.json) {
|
|
157
|
+
console.log(JSON.stringify(results, null, 2));
|
|
158
|
+
} else if (results.length === 0) {
|
|
159
|
+
logger.info(`No ${options.type} memories matching "${query}"`);
|
|
160
|
+
} else {
|
|
161
|
+
logger.log(
|
|
162
|
+
chalk.bold(`${options.type} search (${results.length} results):\n`),
|
|
163
|
+
);
|
|
164
|
+
for (const r of results) {
|
|
165
|
+
logger.log(
|
|
166
|
+
` ${chalk.gray(r.id.slice(0, 16))} [${chalk.cyan(r.layer)}] importance=${chalk.yellow(r.importance)}`,
|
|
167
|
+
);
|
|
168
|
+
logger.log(
|
|
169
|
+
` ${chalk.white(r.content.substring(0, 120).replace(/\n/g, " "))}`,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await shutdown();
|
|
175
|
+
} catch (err) {
|
|
176
|
+
logger.error(`Failed: ${err.message}`);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// hmemory stats
|
|
182
|
+
hmemory
|
|
183
|
+
.command("stats")
|
|
184
|
+
.description("Show memory statistics")
|
|
185
|
+
.option("--json", "Output as JSON")
|
|
186
|
+
.action(async (options) => {
|
|
187
|
+
try {
|
|
188
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
189
|
+
if (!ctx.db) {
|
|
190
|
+
logger.error("Database not available");
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
const db = ctx.db.getDatabase();
|
|
194
|
+
const stats = getMemoryStats(db);
|
|
195
|
+
|
|
196
|
+
if (options.json) {
|
|
197
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
198
|
+
} else {
|
|
199
|
+
logger.log(chalk.bold("Hierarchical Memory Stats:\n"));
|
|
200
|
+
logger.log(` Working: ${chalk.yellow(stats.working)}`);
|
|
201
|
+
logger.log(` Short-term: ${chalk.yellow(stats.shortTerm)}`);
|
|
202
|
+
logger.log(` Long-term: ${chalk.yellow(stats.longTerm)}`);
|
|
203
|
+
logger.log(` Core: ${chalk.yellow(stats.core)}`);
|
|
204
|
+
logger.log(` Shared: ${chalk.yellow(stats.shared)}`);
|
|
205
|
+
logger.log(
|
|
206
|
+
` ${chalk.bold("Total:")} ${chalk.green(stats.total)}`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
await shutdown();
|
|
211
|
+
} catch (err) {
|
|
212
|
+
logger.error(`Failed: ${err.message}`);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// hmemory share <id> <agent-id>
|
|
218
|
+
hmemory
|
|
219
|
+
.command("share")
|
|
220
|
+
.description("Share a memory with another agent")
|
|
221
|
+
.argument("<id>", "Memory ID")
|
|
222
|
+
.argument("<agent-id>", "Target agent ID")
|
|
223
|
+
.option("--privacy <level>", "Privacy level (full|filtered)", "filtered")
|
|
224
|
+
.option("--json", "Output as JSON")
|
|
225
|
+
.action(async (id, agentId, options) => {
|
|
226
|
+
try {
|
|
227
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
228
|
+
if (!ctx.db) {
|
|
229
|
+
logger.error("Database not available");
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
const db = ctx.db.getDatabase();
|
|
233
|
+
const result = shareMemory(db, id, agentId, options.privacy);
|
|
234
|
+
|
|
235
|
+
if (options.json) {
|
|
236
|
+
console.log(JSON.stringify(result, null, 2));
|
|
237
|
+
} else {
|
|
238
|
+
logger.success(
|
|
239
|
+
`Memory ${chalk.gray(id.slice(0, 16))} shared with ${chalk.cyan(agentId)} [${options.privacy}]`,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
await shutdown();
|
|
244
|
+
} catch (err) {
|
|
245
|
+
logger.error(`Failed: ${err.message}`);
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// hmemory prune
|
|
251
|
+
hmemory
|
|
252
|
+
.command("prune")
|
|
253
|
+
.description("Remove weak old memories")
|
|
254
|
+
.option("--max-age <hours>", "Maximum age in hours", "720")
|
|
255
|
+
.action(async (options) => {
|
|
256
|
+
try {
|
|
257
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
258
|
+
if (!ctx.db) {
|
|
259
|
+
logger.error("Database not available");
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
const db = ctx.db.getDatabase();
|
|
263
|
+
const spinner = ora("Pruning stale memories...").start();
|
|
264
|
+
const result = pruneMemory(db, { maxAge: options.maxAge });
|
|
265
|
+
spinner.succeed(`Pruned ${chalk.red(result.pruned)} stale memories`);
|
|
266
|
+
|
|
267
|
+
await shutdown();
|
|
268
|
+
} catch (err) {
|
|
269
|
+
logger.error(`Failed: ${err.message}`);
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|