@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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { config } from "dotenv";
5
- import { createLogger as createLogger15 } from "@tuttiai/core";
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 prompt3 = chalk17.cyan("replay [" + cursor + "/" + (messages.length - 1) + "]> ");
2607
- const raw = await rl.question(prompt3);
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 logger15 = createLogger15("tutti-cli");
3448
+ var logger16 = createLogger16("tutti-cli");
3188
3449
  process.on("unhandledRejection", (reason) => {
3189
- logger15.error({ error: reason instanceof Error ? reason.message : String(reason) }, "Unhandled rejection");
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
- logger15.error({ error: err.message }, "Fatal error");
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