@tuttiai/cli 0.14.0 → 0.15.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/index.js +293 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { config } from "dotenv";
|
|
5
|
-
import { createLogger as
|
|
5
|
+
import { createLogger as createLogger16 } from "@tuttiai/core";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
|
|
8
8
|
// src/commands/init.ts
|
|
@@ -2603,8 +2603,8 @@ async function replayCommand(sessionId, opts = {}) {
|
|
|
2603
2603
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
2604
2604
|
try {
|
|
2605
2605
|
while (true) {
|
|
2606
|
-
const
|
|
2607
|
-
const raw = await rl.question(
|
|
2606
|
+
const prompt4 = chalk17.cyan("replay [" + cursor + "/" + (messages.length - 1) + "]> ");
|
|
2607
|
+
const raw = await rl.question(prompt4);
|
|
2608
2608
|
const input = raw.trim();
|
|
2609
2609
|
if (!input) continue;
|
|
2610
2610
|
const [cmd, ...args] = input.split(/\s+/);
|
|
@@ -3182,15 +3182,276 @@ function reviveSpanDates(span) {
|
|
|
3182
3182
|
};
|
|
3183
3183
|
}
|
|
3184
3184
|
|
|
3185
|
+
// src/commands/memory.ts
|
|
3186
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
3187
|
+
import chalk22 from "chalk";
|
|
3188
|
+
import Enquirer3 from "enquirer";
|
|
3189
|
+
import {
|
|
3190
|
+
MemoryUserMemoryStore,
|
|
3191
|
+
PostgresUserMemoryStore,
|
|
3192
|
+
SecretsManager as SecretsManager10,
|
|
3193
|
+
createLogger as createLogger15
|
|
3194
|
+
} from "@tuttiai/core";
|
|
3195
|
+
|
|
3196
|
+
// src/commands/memory-render.ts
|
|
3197
|
+
import chalk21 from "chalk";
|
|
3198
|
+
function visibleLen2(s) {
|
|
3199
|
+
return s.replace(/\u001b\[[0-9;]*m/g, "").length;
|
|
3200
|
+
}
|
|
3201
|
+
function pad5(s, len) {
|
|
3202
|
+
const v = visibleLen2(s);
|
|
3203
|
+
return v >= len ? s : s + " ".repeat(len - v);
|
|
3204
|
+
}
|
|
3205
|
+
function truncate(text, max) {
|
|
3206
|
+
const oneLine = text.replace(/\s+/g, " ").trim();
|
|
3207
|
+
return oneLine.length > max ? oneLine.slice(0, max - 1) + "\u2026" : oneLine;
|
|
3208
|
+
}
|
|
3209
|
+
function importanceStars(importance) {
|
|
3210
|
+
if (importance === 3) return "\u2605\u2605\u2605";
|
|
3211
|
+
if (importance === 2) return "\u2605\u2605\u2606";
|
|
3212
|
+
return "\u2605\u2606\u2606";
|
|
3213
|
+
}
|
|
3214
|
+
function formatDate(d) {
|
|
3215
|
+
const iso = d.toISOString();
|
|
3216
|
+
return iso.slice(0, 10) + " " + iso.slice(11, 16);
|
|
3217
|
+
}
|
|
3218
|
+
function sortMemoriesForList(memories) {
|
|
3219
|
+
return [...memories].sort((a, b) => {
|
|
3220
|
+
if (a.importance !== b.importance) return b.importance - a.importance;
|
|
3221
|
+
return b.created_at.getTime() - a.created_at.getTime();
|
|
3222
|
+
});
|
|
3223
|
+
}
|
|
3224
|
+
function renderTable(memories, emptyMessage) {
|
|
3225
|
+
if (memories.length === 0) return chalk21.dim(emptyMessage);
|
|
3226
|
+
const lines = [];
|
|
3227
|
+
lines.push("");
|
|
3228
|
+
lines.push(
|
|
3229
|
+
chalk21.dim(
|
|
3230
|
+
" " + pad5("ID", 10) + pad5("CONTENT", 62) + pad5("SOURCE", 12) + pad5("IMPORTANCE", 14) + "CREATED"
|
|
3231
|
+
)
|
|
3232
|
+
);
|
|
3233
|
+
lines.push(chalk21.dim(" " + "\u2500".repeat(110)));
|
|
3234
|
+
for (const m of memories) {
|
|
3235
|
+
const idShort = m.id.slice(0, 8);
|
|
3236
|
+
const content = truncate(m.content, 60);
|
|
3237
|
+
const sourceColored = m.source === "explicit" ? chalk21.green("explicit") : chalk21.yellow("inferred");
|
|
3238
|
+
const importance = importanceStars(m.importance);
|
|
3239
|
+
const created = formatDate(m.created_at);
|
|
3240
|
+
lines.push(
|
|
3241
|
+
" " + chalk21.bold(pad5(idShort, 10)) + pad5(content, 62) + pad5(sourceColored, 12) + pad5(importance, 14) + chalk21.dim(created)
|
|
3242
|
+
);
|
|
3243
|
+
}
|
|
3244
|
+
lines.push("");
|
|
3245
|
+
return lines.join("\n");
|
|
3246
|
+
}
|
|
3247
|
+
function renderMemoryList(memories, userId) {
|
|
3248
|
+
return renderTable(
|
|
3249
|
+
sortMemoriesForList(memories),
|
|
3250
|
+
'No memories stored for user "' + userId + '".'
|
|
3251
|
+
);
|
|
3252
|
+
}
|
|
3253
|
+
function renderMemorySearch(memories, userId, query) {
|
|
3254
|
+
const header = chalk21.dim(
|
|
3255
|
+
"Search for " + chalk21.bold('"' + query + '"') + ' in user "' + userId + '" \u2014 ' + memories.length + " result" + (memories.length === 1 ? "" : "s")
|
|
3256
|
+
);
|
|
3257
|
+
const body = renderTable(
|
|
3258
|
+
memories,
|
|
3259
|
+
'No memories matching "' + query + '" for user "' + userId + '".'
|
|
3260
|
+
);
|
|
3261
|
+
return header + body;
|
|
3262
|
+
}
|
|
3263
|
+
function renderMemoryAdded(memory) {
|
|
3264
|
+
return chalk21.green("\u2713") + " Stored memory " + chalk21.bold(memory.id.slice(0, 8)) + chalk21.dim(" (" + memory.source + ", " + importanceStars(memory.importance) + ")");
|
|
3265
|
+
}
|
|
3266
|
+
function renderMemoryDeleted(memoryId) {
|
|
3267
|
+
return chalk21.green("\u2713") + " Deleted memory " + chalk21.bold(memoryId.slice(0, 8));
|
|
3268
|
+
}
|
|
3269
|
+
function renderMemoryCleared(userId, count) {
|
|
3270
|
+
return chalk21.green("\u2713") + " Deleted " + chalk21.bold(String(count)) + " memor" + (count === 1 ? "y" : "ies") + ' for user "' + userId + '"';
|
|
3271
|
+
}
|
|
3272
|
+
function exportMemoriesJson(memories) {
|
|
3273
|
+
return JSON.stringify(memories, null, 2) + "\n";
|
|
3274
|
+
}
|
|
3275
|
+
function exportMemoriesCsv(memories) {
|
|
3276
|
+
const headers = [
|
|
3277
|
+
"id",
|
|
3278
|
+
"user_id",
|
|
3279
|
+
"content",
|
|
3280
|
+
"source",
|
|
3281
|
+
"importance",
|
|
3282
|
+
"tags",
|
|
3283
|
+
"created_at",
|
|
3284
|
+
"last_accessed_at",
|
|
3285
|
+
"expires_at"
|
|
3286
|
+
];
|
|
3287
|
+
const rows = [headers.join(",")];
|
|
3288
|
+
for (const m of memories) {
|
|
3289
|
+
rows.push(
|
|
3290
|
+
[
|
|
3291
|
+
m.id,
|
|
3292
|
+
m.user_id,
|
|
3293
|
+
m.content,
|
|
3294
|
+
m.source,
|
|
3295
|
+
String(m.importance),
|
|
3296
|
+
m.tags?.join(";") ?? "",
|
|
3297
|
+
m.created_at.toISOString(),
|
|
3298
|
+
m.last_accessed_at?.toISOString() ?? "",
|
|
3299
|
+
m.expires_at?.toISOString() ?? ""
|
|
3300
|
+
].map(csvEscape).join(",")
|
|
3301
|
+
);
|
|
3302
|
+
}
|
|
3303
|
+
return rows.join("\n") + "\n";
|
|
3304
|
+
}
|
|
3305
|
+
function csvEscape(field) {
|
|
3306
|
+
if (/[",\n\r]/.test(field)) {
|
|
3307
|
+
return '"' + field.replace(/"/g, '""') + '"';
|
|
3308
|
+
}
|
|
3309
|
+
return field;
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
// src/commands/memory.ts
|
|
3313
|
+
var logger15 = createLogger15("tutti-cli");
|
|
3314
|
+
var { prompt: prompt3 } = Enquirer3;
|
|
3315
|
+
function resolveStore3() {
|
|
3316
|
+
const pgUrl = SecretsManager10.optional("TUTTI_PG_URL");
|
|
3317
|
+
if (pgUrl) {
|
|
3318
|
+
return new PostgresUserMemoryStore({ connection_string: pgUrl });
|
|
3319
|
+
}
|
|
3320
|
+
logger15.warn(
|
|
3321
|
+
"TUTTI_PG_URL not set \u2014 using in-memory store (memories are ephemeral; this command will appear to do nothing useful)"
|
|
3322
|
+
);
|
|
3323
|
+
return new MemoryUserMemoryStore();
|
|
3324
|
+
}
|
|
3325
|
+
async function closeStore2(store) {
|
|
3326
|
+
if (typeof store.close === "function") {
|
|
3327
|
+
await store.close();
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
function requireUser(opts) {
|
|
3331
|
+
if (!opts.user || opts.user.trim() === "") {
|
|
3332
|
+
console.error(chalk22.red("--user <user-id> is required"));
|
|
3333
|
+
process.exit(1);
|
|
3334
|
+
}
|
|
3335
|
+
return opts.user.trim();
|
|
3336
|
+
}
|
|
3337
|
+
function parseImportance(raw) {
|
|
3338
|
+
if (raw === void 0) return 2;
|
|
3339
|
+
if (raw === "1") return 1;
|
|
3340
|
+
if (raw === "2") return 2;
|
|
3341
|
+
if (raw === "3") return 3;
|
|
3342
|
+
console.error(chalk22.red("--importance must be 1, 2, or 3"));
|
|
3343
|
+
process.exit(1);
|
|
3344
|
+
}
|
|
3345
|
+
async function memoryListCommand(opts) {
|
|
3346
|
+
const userId = requireUser(opts);
|
|
3347
|
+
const store = resolveStore3();
|
|
3348
|
+
try {
|
|
3349
|
+
const memories = await store.list(userId);
|
|
3350
|
+
console.log(renderMemoryList(memories, userId));
|
|
3351
|
+
} finally {
|
|
3352
|
+
await closeStore2(store);
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
async function memorySearchCommand(query, opts) {
|
|
3356
|
+
const userId = requireUser(opts);
|
|
3357
|
+
if (query.trim() === "") {
|
|
3358
|
+
console.error(chalk22.red("Search query is required"));
|
|
3359
|
+
process.exit(1);
|
|
3360
|
+
}
|
|
3361
|
+
const store = resolveStore3();
|
|
3362
|
+
try {
|
|
3363
|
+
const memories = await store.search(userId, query);
|
|
3364
|
+
console.log(renderMemorySearch(memories, userId, query));
|
|
3365
|
+
} finally {
|
|
3366
|
+
await closeStore2(store);
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
async function memoryAddCommand(content, opts) {
|
|
3370
|
+
const userId = requireUser(opts);
|
|
3371
|
+
if (content.trim() === "") {
|
|
3372
|
+
console.error(chalk22.red("Memory content is required"));
|
|
3373
|
+
process.exit(1);
|
|
3374
|
+
}
|
|
3375
|
+
const importance = parseImportance(opts.importance);
|
|
3376
|
+
const store = resolveStore3();
|
|
3377
|
+
try {
|
|
3378
|
+
const stored = await store.store(userId, content.trim(), {
|
|
3379
|
+
source: "explicit",
|
|
3380
|
+
importance
|
|
3381
|
+
});
|
|
3382
|
+
console.log(renderMemoryAdded(stored));
|
|
3383
|
+
} finally {
|
|
3384
|
+
await closeStore2(store);
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
async function memoryDeleteCommand(memoryId, opts) {
|
|
3388
|
+
requireUser(opts);
|
|
3389
|
+
const store = resolveStore3();
|
|
3390
|
+
try {
|
|
3391
|
+
await store.delete(memoryId);
|
|
3392
|
+
console.log(renderMemoryDeleted(memoryId));
|
|
3393
|
+
} finally {
|
|
3394
|
+
await closeStore2(store);
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
async function memoryClearCommand(opts) {
|
|
3398
|
+
const userId = requireUser(opts);
|
|
3399
|
+
const store = resolveStore3();
|
|
3400
|
+
try {
|
|
3401
|
+
const memories = await store.list(userId);
|
|
3402
|
+
if (memories.length === 0) {
|
|
3403
|
+
console.log(chalk22.dim('No memories stored for user "' + userId + '".'));
|
|
3404
|
+
return;
|
|
3405
|
+
}
|
|
3406
|
+
const { confirm } = await prompt3({
|
|
3407
|
+
type: "confirm",
|
|
3408
|
+
name: "confirm",
|
|
3409
|
+
message: "Delete all " + memories.length + ' memories for user "' + userId + '"?',
|
|
3410
|
+
initial: false
|
|
3411
|
+
});
|
|
3412
|
+
if (!confirm) {
|
|
3413
|
+
console.log(chalk22.dim(" Cancelled."));
|
|
3414
|
+
return;
|
|
3415
|
+
}
|
|
3416
|
+
await store.deleteAll(userId);
|
|
3417
|
+
console.log(renderMemoryCleared(userId, memories.length));
|
|
3418
|
+
} finally {
|
|
3419
|
+
await closeStore2(store);
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
async function memoryExportCommand(opts) {
|
|
3423
|
+
const userId = requireUser(opts);
|
|
3424
|
+
const format = (opts.format ?? "json").toLowerCase();
|
|
3425
|
+
if (format !== "json" && format !== "csv") {
|
|
3426
|
+
console.error(chalk22.red("--format must be 'json' or 'csv'"));
|
|
3427
|
+
process.exit(1);
|
|
3428
|
+
}
|
|
3429
|
+
const store = resolveStore3();
|
|
3430
|
+
try {
|
|
3431
|
+
const memories = await store.list(userId);
|
|
3432
|
+
const body = format === "json" ? exportMemoriesJson(memories) : exportMemoriesCsv(memories);
|
|
3433
|
+
if (opts.out) {
|
|
3434
|
+
writeFileSync2(opts.out, body, "utf8");
|
|
3435
|
+
console.log(
|
|
3436
|
+
chalk22.green("\u2713") + " Wrote " + chalk22.bold(String(memories.length)) + " memor" + (memories.length === 1 ? "y" : "ies") + " to " + chalk22.bold(opts.out)
|
|
3437
|
+
);
|
|
3438
|
+
} else {
|
|
3439
|
+
process.stdout.write(body);
|
|
3440
|
+
}
|
|
3441
|
+
} finally {
|
|
3442
|
+
await closeStore2(store);
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3185
3446
|
// src/index.ts
|
|
3186
3447
|
config();
|
|
3187
|
-
var
|
|
3448
|
+
var logger16 = createLogger16("tutti-cli");
|
|
3188
3449
|
process.on("unhandledRejection", (reason) => {
|
|
3189
|
-
|
|
3450
|
+
logger16.error({ error: reason instanceof Error ? reason.message : String(reason) }, "Unhandled rejection");
|
|
3190
3451
|
process.exit(1);
|
|
3191
3452
|
});
|
|
3192
3453
|
process.on("uncaughtException", (err) => {
|
|
3193
|
-
|
|
3454
|
+
logger16.error({ error: err.message }, "Fatal error");
|
|
3194
3455
|
process.exit(1);
|
|
3195
3456
|
});
|
|
3196
3457
|
var program = new Command();
|
|
@@ -3306,5 +3567,31 @@ tracesCmd.command("tail").description("Live-tail spans as they are emitted (Ctrl
|
|
|
3306
3567
|
};
|
|
3307
3568
|
await tracesTailCommand(resolved);
|
|
3308
3569
|
});
|
|
3570
|
+
var memoryCmd = program.command("memory").description("Manage per-user memories (uses TUTTI_PG_URL like the runtime)");
|
|
3571
|
+
memoryCmd.command("list").description("Show every memory for a user, sorted by importance + recency").requiredOption("--user <user-id>", "End-user identifier").action(async (opts) => {
|
|
3572
|
+
await memoryListCommand({ user: opts.user });
|
|
3573
|
+
});
|
|
3574
|
+
memoryCmd.command("search <query>").description("Search a user's memories by relevance").requiredOption("--user <user-id>", "End-user identifier").action(async (query, opts) => {
|
|
3575
|
+
await memorySearchCommand(query, { user: opts.user });
|
|
3576
|
+
});
|
|
3577
|
+
memoryCmd.command("add <content>").description("Manually store a memory for a user").requiredOption("--user <user-id>", "End-user identifier").option("--importance <n>", "Importance: 1 (low), 2 (normal), 3 (high)", "2").action(async (content, opts) => {
|
|
3578
|
+
await memoryAddCommand(content, {
|
|
3579
|
+
user: opts.user,
|
|
3580
|
+
...opts.importance !== void 0 ? { importance: opts.importance } : {}
|
|
3581
|
+
});
|
|
3582
|
+
});
|
|
3583
|
+
memoryCmd.command("delete <memory-id>").description("Delete a specific memory by id").requiredOption("--user <user-id>", "End-user identifier").action(async (memoryId, opts) => {
|
|
3584
|
+
await memoryDeleteCommand(memoryId, { user: opts.user });
|
|
3585
|
+
});
|
|
3586
|
+
memoryCmd.command("clear").description("Delete every memory for a user (prompts for confirmation)").requiredOption("--user <user-id>", "End-user identifier").action(async (opts) => {
|
|
3587
|
+
await memoryClearCommand({ user: opts.user });
|
|
3588
|
+
});
|
|
3589
|
+
memoryCmd.command("export").description("Export every memory for a user as JSON or CSV").requiredOption("--user <user-id>", "End-user identifier").option("--format <fmt>", "Output format: json | csv", "json").option("-o, --out <path>", "Write to file instead of stdout").action(async (opts) => {
|
|
3590
|
+
await memoryExportCommand({
|
|
3591
|
+
user: opts.user,
|
|
3592
|
+
...opts.format !== void 0 ? { format: opts.format } : {},
|
|
3593
|
+
...opts.out !== void 0 ? { out: opts.out } : {}
|
|
3594
|
+
});
|
|
3595
|
+
});
|
|
3309
3596
|
program.parse();
|
|
3310
3597
|
//# sourceMappingURL=index.js.map
|