@learnrudi/cli 1.0.0 → 1.2.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.
Files changed (2) hide show
  1. package/dist/index.cjs +625 -39
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -223,7 +223,7 @@ async function fetchIndex(options = {}) {
223
223
  const response = await fetch(url, {
224
224
  headers: {
225
225
  "Accept": "application/json",
226
- "User-Agent": "pstack-cli/2.0"
226
+ "User-Agent": "rudi-cli/2.0"
227
227
  }
228
228
  });
229
229
  if (!response.ok) {
@@ -401,7 +401,7 @@ async function downloadRuntime(runtime, version, destPath, options = {}) {
401
401
  try {
402
402
  const response = await fetch(url, {
403
403
  headers: {
404
- "User-Agent": "pstack-cli/2.0",
404
+ "User-Agent": "rudi-cli/2.0",
405
405
  "Accept": "application/octet-stream"
406
406
  }
407
407
  });
@@ -470,7 +470,7 @@ async function downloadTool(toolName, destPath, options = {}) {
470
470
  try {
471
471
  const response = await fetch(url, {
472
472
  headers: {
473
- "User-Agent": "pstack-cli/2.0",
473
+ "User-Agent": "rudi-cli/2.0",
474
474
  "Accept": "application/octet-stream"
475
475
  }
476
476
  });
@@ -522,7 +522,7 @@ async function downloadTool(toolName, destPath, options = {}) {
522
522
  try {
523
523
  const response = await fetch(upstreamUrl, {
524
524
  headers: {
525
- "User-Agent": "pstack-cli/2.0",
525
+ "User-Agent": "rudi-cli/2.0",
526
526
  "Accept": "application/octet-stream"
527
527
  }
528
528
  });
@@ -620,10 +620,10 @@ async function loadToolManifest(toolName) {
620
620
  }
621
621
  }
622
622
  try {
623
- const url = `https://raw.githubusercontent.com/prompt/registry/main/catalog/binaries/${toolName}.json`;
623
+ const url = `https://raw.githubusercontent.com/learn-rudi/registry/main/catalog/binaries/${toolName}.json`;
624
624
  const response = await fetch(url, {
625
625
  headers: {
626
- "User-Agent": "pstack-cli/2.0",
626
+ "User-Agent": "rudi-cli/2.0",
627
627
  "Accept": "application/json"
628
628
  }
629
629
  });
@@ -683,13 +683,13 @@ var init_src2 = __esm({
683
683
  import_path2 = __toESM(require("path"), 1);
684
684
  import_crypto = __toESM(require("crypto"), 1);
685
685
  init_src();
686
- DEFAULT_REGISTRY_URL = "https://raw.githubusercontent.com/prompt/registry/main/index.json";
687
- RUNTIMES_DOWNLOAD_BASE = "https://github.com/prompt/registry/releases/download";
686
+ DEFAULT_REGISTRY_URL = "https://raw.githubusercontent.com/learn-rudi/registry/main/index.json";
687
+ RUNTIMES_DOWNLOAD_BASE = "https://github.com/learn-rudi/registry/releases/download";
688
688
  CACHE_TTL = 24 * 60 * 60 * 1e3;
689
689
  LOCAL_REGISTRY_PATHS = process.env.USE_LOCAL_REGISTRY === "true" ? [
690
690
  import_path2.default.join(process.cwd(), "registry", "index.json"),
691
691
  import_path2.default.join(process.cwd(), "..", "registry", "index.json"),
692
- "/Users/hoff/dev/prompt/registry/index.json"
692
+ "/Users/hoff/dev/RUDI/registry/index.json"
693
693
  ] : [];
694
694
  PACKAGE_KINDS2 = ["stack", "prompt", "runtime", "binary", "agent"];
695
695
  KIND_PLURALS = {
@@ -14830,6 +14830,9 @@ COMMANDS
14830
14830
  db stats Show database statistics
14831
14831
  db search <query> Search conversation history
14832
14832
 
14833
+ import sessions Import sessions from AI providers (claude, codex, gemini)
14834
+ import status Show import status for all providers
14835
+
14833
14836
  logs [options] Query agent visibility logs
14834
14837
 
14835
14838
  doctor Check system health and configuration
@@ -14958,10 +14961,47 @@ COMMANDS
14958
14961
  stats Show usage statistics
14959
14962
  search <query> Search conversation history
14960
14963
  init Initialize or migrate database
14964
+ path Show database file path
14965
+ reset Delete all data (requires --force)
14966
+ vacuum Compact database and reclaim space
14967
+ backup [file] Create database backup
14968
+ prune [days] Delete sessions older than N days (default: 90)
14969
+ tables Show table row counts
14970
+
14971
+ OPTIONS
14972
+ --force Required for destructive operations
14973
+ --dry-run Preview without making changes
14974
+ --json Output as JSON
14961
14975
 
14962
14976
  EXAMPLES
14963
14977
  rudi db stats
14964
14978
  rudi db search "authentication bug"
14979
+ rudi db reset --force
14980
+ rudi db vacuum
14981
+ rudi db backup ~/backups/rudi.db
14982
+ rudi db prune 30 --dry-run
14983
+ rudi db tables
14984
+ `,
14985
+ import: `
14986
+ rudi import - Import sessions from AI providers
14987
+
14988
+ USAGE
14989
+ rudi import <command> [options]
14990
+
14991
+ COMMANDS
14992
+ sessions [provider] Import sessions from provider (claude, codex, gemini, or all)
14993
+ status Show import status for all providers
14994
+
14995
+ OPTIONS
14996
+ --dry-run Show what would be imported without making changes
14997
+ --max-age=DAYS Only import sessions newer than N days
14998
+ --verbose Show detailed progress
14999
+
15000
+ EXAMPLES
15001
+ rudi import sessions Import from all providers
15002
+ rudi import sessions claude Import only Claude sessions
15003
+ rudi import sessions --dry-run Preview without importing
15004
+ rudi import status Check what's available to import
14965
15005
  `,
14966
15006
  doctor: `
14967
15007
  rudi doctor - System health check
@@ -17214,6 +17254,10 @@ function promptSecret(prompt) {
17214
17254
  });
17215
17255
  }
17216
17256
 
17257
+ // src/commands/db.js
17258
+ var import_fs10 = require("fs");
17259
+ var import_path10 = require("path");
17260
+
17217
17261
  // ../packages/db/src/index.js
17218
17262
  var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
17219
17263
  var import_path9 = __toESM(require("path"), 1);
@@ -17950,7 +17994,6 @@ function getToolsUsage(db3) {
17950
17994
  }
17951
17995
 
17952
17996
  // ../packages/db/src/index.js
17953
- var PROMPT_STACK_HOME2 = PATHS.home;
17954
17997
  var DB_PATH = PATHS.dbFile;
17955
17998
  var db = null;
17956
17999
  function getDb(options = {}) {
@@ -18013,6 +18056,21 @@ async function cmdDb(args, flags) {
18013
18056
  case "path":
18014
18057
  console.log(getDbPath());
18015
18058
  break;
18059
+ case "reset":
18060
+ await dbReset(flags);
18061
+ break;
18062
+ case "vacuum":
18063
+ dbVacuum(flags);
18064
+ break;
18065
+ case "backup":
18066
+ dbBackup(args.slice(1), flags);
18067
+ break;
18068
+ case "prune":
18069
+ dbPrune(args.slice(1), flags);
18070
+ break;
18071
+ case "tables":
18072
+ dbTables(flags);
18073
+ break;
18016
18074
  default:
18017
18075
  console.log(`
18018
18076
  rudi db - Database operations
@@ -18022,11 +18080,24 @@ COMMANDS
18022
18080
  search <query> Search conversation history
18023
18081
  init Initialize or migrate database
18024
18082
  path Show database file path
18083
+ reset Delete all data (requires --force)
18084
+ vacuum Compact database and reclaim space
18085
+ backup [file] Create database backup
18086
+ prune [days] Delete sessions older than N days (default: 90)
18087
+ tables Show table row counts
18088
+
18089
+ OPTIONS
18090
+ --force Required for destructive operations
18091
+ --dry-run Preview without making changes
18025
18092
 
18026
18093
  EXAMPLES
18027
18094
  rudi db stats
18028
18095
  rudi db search "authentication bug"
18029
18096
  rudi db init
18097
+ rudi db reset --force
18098
+ rudi db vacuum
18099
+ rudi db backup ~/backups/rudi-backup.db
18100
+ rudi db prune 30 --dry-run
18030
18101
  `);
18031
18102
  }
18032
18103
  }
@@ -18153,9 +18224,521 @@ function truncate(str, len) {
18153
18224
  function stripHighlight(str) {
18154
18225
  return str.replace(/>>>/g, "").replace(/<<</g, "");
18155
18226
  }
18227
+ async function dbReset(flags) {
18228
+ if (!isDatabaseInitialized()) {
18229
+ console.log("Database not initialized.");
18230
+ return;
18231
+ }
18232
+ if (!flags.force) {
18233
+ console.error("This will delete ALL data from the database.");
18234
+ console.error("Use --force to confirm.");
18235
+ process.exit(1);
18236
+ }
18237
+ const db3 = getDb();
18238
+ const dbPath = getDbPath();
18239
+ const tables = ["sessions", "turns", "tool_calls", "projects"];
18240
+ const counts = {};
18241
+ for (const table of tables) {
18242
+ try {
18243
+ const row = db3.prepare(`SELECT COUNT(*) as count FROM ${table}`).get();
18244
+ counts[table] = row.count;
18245
+ } catch (e) {
18246
+ counts[table] = 0;
18247
+ }
18248
+ }
18249
+ console.log("Deleting all data...");
18250
+ console.log("\u2500".repeat(40));
18251
+ const deleteOrder = ["tool_calls", "turns", "sessions", "projects"];
18252
+ for (const table of deleteOrder) {
18253
+ try {
18254
+ db3.prepare(`DELETE FROM ${table}`).run();
18255
+ console.log(` ${table}: ${counts[table]} rows deleted`);
18256
+ } catch (e) {
18257
+ }
18258
+ }
18259
+ try {
18260
+ db3.prepare("DELETE FROM turns_fts").run();
18261
+ console.log(" turns_fts: cleared");
18262
+ } catch (e) {
18263
+ }
18264
+ console.log("\u2500".repeat(40));
18265
+ console.log("Database reset complete.");
18266
+ console.log(`Path: ${dbPath}`);
18267
+ }
18268
+ function dbVacuum(flags) {
18269
+ if (!isDatabaseInitialized()) {
18270
+ console.log("Database not initialized.");
18271
+ return;
18272
+ }
18273
+ const dbPath = getDbPath();
18274
+ const sizeBefore = getDbSize();
18275
+ console.log("Compacting database...");
18276
+ console.log(` Before: ${formatBytes(sizeBefore)}`);
18277
+ const db3 = getDb();
18278
+ db3.exec("VACUUM");
18279
+ const sizeAfter = getDbSize();
18280
+ const saved = sizeBefore - sizeAfter;
18281
+ console.log(` After: ${formatBytes(sizeAfter)}`);
18282
+ if (saved > 0) {
18283
+ console.log(` Saved: ${formatBytes(saved)} (${(saved / sizeBefore * 100).toFixed(1)}%)`);
18284
+ } else {
18285
+ console.log(" No space reclaimed.");
18286
+ }
18287
+ }
18288
+ function dbBackup(args, flags) {
18289
+ if (!isDatabaseInitialized()) {
18290
+ console.log("Database not initialized.");
18291
+ return;
18292
+ }
18293
+ const dbPath = getDbPath();
18294
+ let backupPath = args[0];
18295
+ if (!backupPath) {
18296
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
18297
+ backupPath = (0, import_path10.join)((0, import_path10.dirname)(dbPath), `rudi-backup-${timestamp}.db`);
18298
+ }
18299
+ if (backupPath.startsWith("~")) {
18300
+ backupPath = (0, import_path10.join)(process.env.HOME || "", backupPath.slice(1));
18301
+ }
18302
+ if ((0, import_fs10.existsSync)(backupPath) && !flags.force) {
18303
+ console.error(`Backup file already exists: ${backupPath}`);
18304
+ console.error("Use --force to overwrite.");
18305
+ process.exit(1);
18306
+ }
18307
+ console.log("Creating backup...");
18308
+ console.log(` Source: ${dbPath}`);
18309
+ console.log(` Dest: ${backupPath}`);
18310
+ try {
18311
+ const db3 = getDb();
18312
+ db3.exec("VACUUM INTO ?", [backupPath]);
18313
+ } catch (e) {
18314
+ (0, import_fs10.copyFileSync)(dbPath, backupPath);
18315
+ }
18316
+ const size = getDbSize();
18317
+ console.log(` Size: ${formatBytes(size)}`);
18318
+ console.log("Backup complete.");
18319
+ }
18320
+ function dbPrune(args, flags) {
18321
+ if (!isDatabaseInitialized()) {
18322
+ console.log("Database not initialized.");
18323
+ return;
18324
+ }
18325
+ const days = parseInt(args[0]) || 90;
18326
+ const dryRun = flags["dry-run"] || flags.dryRun;
18327
+ const cutoffDate = new Date(Date.now() - days * 24 * 60 * 60 * 1e3).toISOString();
18328
+ const db3 = getDb();
18329
+ const toDelete = db3.prepare(`
18330
+ SELECT COUNT(*) as count FROM sessions
18331
+ WHERE last_active_at < ? OR (last_active_at IS NULL AND created_at < ?)
18332
+ `).get(cutoffDate, cutoffDate);
18333
+ const total = db3.prepare("SELECT COUNT(*) as count FROM sessions").get();
18334
+ console.log(`Sessions older than ${days} days: ${toDelete.count}`);
18335
+ console.log(`Total sessions: ${total.count}`);
18336
+ console.log(`Cutoff date: ${cutoffDate.slice(0, 10)}`);
18337
+ if (toDelete.count === 0) {
18338
+ console.log("\nNo sessions to prune.");
18339
+ return;
18340
+ }
18341
+ if (dryRun) {
18342
+ console.log("\n(Dry run - no changes made)");
18343
+ return;
18344
+ }
18345
+ if (!flags.force) {
18346
+ console.error(`
18347
+ This will delete ${toDelete.count} sessions and their turns.`);
18348
+ console.error("Use --force to confirm, or --dry-run to preview.");
18349
+ process.exit(1);
18350
+ }
18351
+ console.log("\nDeleting old sessions...");
18352
+ const sessionIds = db3.prepare(`
18353
+ SELECT id FROM sessions
18354
+ WHERE last_active_at < ? OR (last_active_at IS NULL AND created_at < ?)
18355
+ `).all(cutoffDate, cutoffDate).map((r) => r.id);
18356
+ let turnsDeleted = 0;
18357
+ let toolCallsDeleted = 0;
18358
+ for (const sessionId of sessionIds) {
18359
+ const turnIds = db3.prepare("SELECT id FROM turns WHERE session_id = ?").all(sessionId).map((r) => r.id);
18360
+ for (const turnId of turnIds) {
18361
+ const result = db3.prepare("DELETE FROM tool_calls WHERE turn_id = ?").run(turnId);
18362
+ toolCallsDeleted += result.changes;
18363
+ }
18364
+ const turnResult = db3.prepare("DELETE FROM turns WHERE session_id = ?").run(sessionId);
18365
+ turnsDeleted += turnResult.changes;
18366
+ }
18367
+ const sessionResult = db3.prepare(`
18368
+ DELETE FROM sessions
18369
+ WHERE last_active_at < ? OR (last_active_at IS NULL AND created_at < ?)
18370
+ `).run(cutoffDate, cutoffDate);
18371
+ console.log(` Sessions deleted: ${sessionResult.changes}`);
18372
+ console.log(` Turns deleted: ${turnsDeleted}`);
18373
+ console.log(` Tool calls deleted: ${toolCallsDeleted}`);
18374
+ console.log('\nPrune complete. Run "rudi db vacuum" to reclaim disk space.');
18375
+ }
18376
+ function dbTables(flags) {
18377
+ if (!isDatabaseInitialized()) {
18378
+ console.log("Database not initialized.");
18379
+ return;
18380
+ }
18381
+ const db3 = getDb();
18382
+ const tables = db3.prepare(`
18383
+ SELECT name FROM sqlite_master
18384
+ WHERE type = 'table' AND name NOT LIKE 'sqlite_%'
18385
+ ORDER BY name
18386
+ `).all();
18387
+ if (flags.json) {
18388
+ const result = {};
18389
+ for (const { name } of tables) {
18390
+ try {
18391
+ const row = db3.prepare(`SELECT COUNT(*) as count FROM "${name}"`).get();
18392
+ result[name] = row.count;
18393
+ } catch (e) {
18394
+ result[name] = -1;
18395
+ }
18396
+ }
18397
+ console.log(JSON.stringify(result, null, 2));
18398
+ return;
18399
+ }
18400
+ console.log("\nDatabase Tables");
18401
+ console.log("\u2550".repeat(40));
18402
+ let totalRows = 0;
18403
+ for (const { name } of tables) {
18404
+ try {
18405
+ const row = db3.prepare(`SELECT COUNT(*) as count FROM "${name}"`).get();
18406
+ console.log(` ${name.padEnd(25)} ${row.count.toLocaleString().padStart(10)}`);
18407
+ totalRows += row.count;
18408
+ } catch (e) {
18409
+ console.log(` ${name.padEnd(25)} ${"error".padStart(10)}`);
18410
+ }
18411
+ }
18412
+ console.log("\u2500".repeat(40));
18413
+ console.log(` ${"Total".padEnd(25)} ${totalRows.toLocaleString().padStart(10)}`);
18414
+ console.log(`
18415
+ Size: ${formatBytes(getDbSize())}`);
18416
+ }
18417
+
18418
+ // src/commands/import.js
18419
+ var import_fs11 = require("fs");
18420
+ var import_path11 = require("path");
18421
+ var import_os3 = require("os");
18422
+ var import_crypto2 = require("crypto");
18423
+ var PROVIDERS = {
18424
+ claude: {
18425
+ name: "Claude Code",
18426
+ baseDir: (0, import_path11.join)((0, import_os3.homedir)(), ".claude", "projects"),
18427
+ pattern: /\.jsonl$/
18428
+ },
18429
+ codex: {
18430
+ name: "Codex",
18431
+ baseDir: (0, import_path11.join)((0, import_os3.homedir)(), ".codex", "sessions"),
18432
+ pattern: /\.jsonl$/
18433
+ },
18434
+ gemini: {
18435
+ name: "Gemini",
18436
+ baseDir: (0, import_path11.join)((0, import_os3.homedir)(), ".gemini", "sessions"),
18437
+ pattern: /\.jsonl$/
18438
+ }
18439
+ };
18440
+ async function cmdImport(args, flags) {
18441
+ const subcommand = args[0];
18442
+ switch (subcommand) {
18443
+ case "sessions":
18444
+ await importSessions(args.slice(1), flags);
18445
+ break;
18446
+ case "status":
18447
+ showImportStatus(flags);
18448
+ break;
18449
+ default:
18450
+ console.log(`
18451
+ rudi import - Import data from AI agent providers
18452
+
18453
+ COMMANDS
18454
+ sessions [provider] Import sessions from provider (claude, codex, gemini, or all)
18455
+ status Show import status for all providers
18456
+
18457
+ OPTIONS
18458
+ --dry-run Show what would be imported without making changes
18459
+ --max-age=DAYS Only import sessions newer than N days
18460
+ --verbose Show detailed progress
18461
+
18462
+ EXAMPLES
18463
+ rudi import sessions # Import from all providers
18464
+ rudi import sessions claude # Import only Claude sessions
18465
+ rudi import sessions --dry-run # Preview without importing
18466
+ rudi import status # Check what's available to import
18467
+ `);
18468
+ }
18469
+ }
18470
+ async function importSessions(args, flags) {
18471
+ const providerArg = args[0] || "all";
18472
+ const dryRun = flags["dry-run"] || flags.dryRun;
18473
+ const verbose = flags.verbose;
18474
+ const maxAgeDays = flags["max-age"] ? parseInt(flags["max-age"]) : null;
18475
+ if (!isDatabaseInitialized()) {
18476
+ console.log("Initializing database...");
18477
+ initSchema();
18478
+ }
18479
+ const db3 = getDb();
18480
+ const providers = providerArg === "all" ? Object.keys(PROVIDERS) : [providerArg];
18481
+ for (const p of providers) {
18482
+ if (!PROVIDERS[p]) {
18483
+ console.error(`Unknown provider: ${p}`);
18484
+ console.error(`Available: ${Object.keys(PROVIDERS).join(", ")}`);
18485
+ process.exit(1);
18486
+ }
18487
+ }
18488
+ console.log("\u2550".repeat(60));
18489
+ console.log("RUDI Session Import");
18490
+ console.log("\u2550".repeat(60));
18491
+ console.log(`Providers: ${providers.join(", ")}`);
18492
+ console.log(`Database: ${getDbPath()}`);
18493
+ console.log(`Max age: ${maxAgeDays ? `${maxAgeDays} days` : "all"}`);
18494
+ console.log(`Dry run: ${dryRun ? "yes" : "no"}`);
18495
+ console.log("\u2550".repeat(60));
18496
+ let totalImported = 0;
18497
+ let totalSkipped = 0;
18498
+ for (const providerKey of providers) {
18499
+ const provider = PROVIDERS[providerKey];
18500
+ console.log(`
18501
+ \u25B6 ${provider.name}`);
18502
+ console.log(` Source: ${provider.baseDir}`);
18503
+ if (!(0, import_fs11.existsSync)(provider.baseDir)) {
18504
+ console.log(` \u26A0 Directory not found, skipping`);
18505
+ continue;
18506
+ }
18507
+ const existingIds = /* @__PURE__ */ new Set();
18508
+ try {
18509
+ const rows = db3.prepare(
18510
+ "SELECT provider_session_id FROM sessions WHERE provider = ? AND provider_session_id IS NOT NULL"
18511
+ ).all(providerKey);
18512
+ for (const row of rows) {
18513
+ existingIds.add(row.provider_session_id);
18514
+ }
18515
+ } catch (e) {
18516
+ }
18517
+ console.log(` Existing: ${existingIds.size} sessions`);
18518
+ const files = findSessionFiles(provider.baseDir, provider.pattern);
18519
+ console.log(` Found: ${files.length} session files`);
18520
+ const insertStmt = db3.prepare(`
18521
+ INSERT INTO sessions (
18522
+ id, provider, provider_session_id, project_id,
18523
+ origin, origin_imported_at, origin_native_file,
18524
+ title, snippet, status, model,
18525
+ inherit_project_prompt,
18526
+ cwd, dir_scope, native_storage_path,
18527
+ created_at, last_active_at,
18528
+ turn_count, total_cost, total_input_tokens, total_output_tokens, total_duration_ms,
18529
+ is_warmup, parent_session_id, agent_id, is_sidechain, session_type, version, user_type
18530
+ ) VALUES (
18531
+ ?, ?, ?, NULL,
18532
+ 'provider-import', ?, ?,
18533
+ ?, '', 'active', ?,
18534
+ 1,
18535
+ ?, 'project', ?,
18536
+ ?, ?,
18537
+ 0, 0, 0, 0, 0,
18538
+ 0, ?, ?, ?, ?, '2.0.76', 'external'
18539
+ )
18540
+ `);
18541
+ let imported = 0;
18542
+ let skipped = { existing: 0, empty: 0, old: 0, error: 0 };
18543
+ const now = Date.now();
18544
+ const maxAgeMs = maxAgeDays ? maxAgeDays * 24 * 60 * 60 * 1e3 : null;
18545
+ for (const filepath of files) {
18546
+ const sessionId = (0, import_path11.basename)(filepath, ".jsonl");
18547
+ if (existingIds.has(sessionId)) {
18548
+ skipped.existing++;
18549
+ continue;
18550
+ }
18551
+ let stat;
18552
+ try {
18553
+ stat = (0, import_fs11.statSync)(filepath);
18554
+ } catch (e) {
18555
+ skipped.error++;
18556
+ continue;
18557
+ }
18558
+ if (stat.size === 0) {
18559
+ skipped.empty++;
18560
+ continue;
18561
+ }
18562
+ if (maxAgeMs && now - stat.mtimeMs > maxAgeMs) {
18563
+ skipped.old++;
18564
+ continue;
18565
+ }
18566
+ const session = parseSessionFile(filepath, providerKey);
18567
+ if (!session) {
18568
+ skipped.error++;
18569
+ continue;
18570
+ }
18571
+ if (dryRun) {
18572
+ if (verbose || imported < 5) {
18573
+ console.log(` [would import] ${sessionId}: ${session.title.slice(0, 40)}`);
18574
+ }
18575
+ imported++;
18576
+ continue;
18577
+ }
18578
+ try {
18579
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
18580
+ insertStmt.run(
18581
+ (0, import_crypto2.randomUUID)(),
18582
+ providerKey,
18583
+ sessionId,
18584
+ nowIso,
18585
+ filepath,
18586
+ session.title,
18587
+ session.model || "unknown",
18588
+ session.cwd,
18589
+ filepath,
18590
+ session.createdAt,
18591
+ session.lastActiveAt,
18592
+ session.parentSessionId,
18593
+ session.agentId,
18594
+ session.isAgent ? 1 : 0,
18595
+ session.sessionType
18596
+ );
18597
+ imported++;
18598
+ if (verbose) {
18599
+ console.log(` \u2713 ${sessionId}: ${session.title.slice(0, 40)}`);
18600
+ } else if (imported % 100 === 0) {
18601
+ console.log(` Imported ${imported}...`);
18602
+ }
18603
+ } catch (e) {
18604
+ skipped.error++;
18605
+ if (verbose) {
18606
+ console.log(` \u2717 ${sessionId}: ${e.message}`);
18607
+ }
18608
+ }
18609
+ }
18610
+ console.log(` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
18611
+ console.log(` Imported: ${imported}`);
18612
+ console.log(` Skipped: ${skipped.existing} existing, ${skipped.empty} empty, ${skipped.old} old, ${skipped.error} errors`);
18613
+ totalImported += imported;
18614
+ totalSkipped += skipped.existing + skipped.empty + skipped.old + skipped.error;
18615
+ }
18616
+ console.log("\n" + "\u2550".repeat(60));
18617
+ console.log(`Total imported: ${totalImported}`);
18618
+ console.log(`Total skipped: ${totalSkipped}`);
18619
+ console.log("\u2550".repeat(60));
18620
+ if (dryRun) {
18621
+ console.log("\n(Dry run - no changes made)");
18622
+ }
18623
+ if (!dryRun && totalImported > 0) {
18624
+ const count = db3.prepare("SELECT COUNT(*) as count FROM sessions").get();
18625
+ console.log(`
18626
+ Total sessions in database: ${count.count}`);
18627
+ }
18628
+ }
18629
+ function showImportStatus(flags) {
18630
+ console.log("\u2550".repeat(60));
18631
+ console.log("Import Status");
18632
+ console.log("\u2550".repeat(60));
18633
+ if (!isDatabaseInitialized()) {
18634
+ console.log("\nDatabase: Not initialized");
18635
+ console.log("Run: rudi db init");
18636
+ } else {
18637
+ const db3 = getDb();
18638
+ const stats = db3.prepare(`
18639
+ SELECT provider, COUNT(*) as count
18640
+ FROM sessions
18641
+ WHERE status = 'active'
18642
+ GROUP BY provider
18643
+ `).all();
18644
+ console.log("\nDatabase sessions:");
18645
+ for (const row of stats) {
18646
+ console.log(` ${row.provider}: ${row.count}`);
18647
+ }
18648
+ }
18649
+ console.log("\nProvider directories:");
18650
+ for (const [key, provider] of Object.entries(PROVIDERS)) {
18651
+ const exists = (0, import_fs11.existsSync)(provider.baseDir);
18652
+ let count = 0;
18653
+ if (exists) {
18654
+ const files = findSessionFiles(provider.baseDir, provider.pattern);
18655
+ count = files.length;
18656
+ }
18657
+ console.log(` ${provider.name}:`);
18658
+ console.log(` Path: ${provider.baseDir}`);
18659
+ console.log(` Status: ${exists ? `${count} session files` : "not found"}`);
18660
+ }
18661
+ console.log("\n" + "\u2550".repeat(60));
18662
+ console.log("To import: rudi import sessions [provider]");
18663
+ }
18664
+ function findSessionFiles(dir, pattern, files = []) {
18665
+ if (!(0, import_fs11.existsSync)(dir)) return files;
18666
+ try {
18667
+ for (const entry of (0, import_fs11.readdirSync)(dir, { withFileTypes: true })) {
18668
+ const fullPath = (0, import_path11.join)(dir, entry.name);
18669
+ if (entry.isDirectory()) {
18670
+ findSessionFiles(fullPath, pattern, files);
18671
+ } else if (pattern.test(entry.name)) {
18672
+ files.push(fullPath);
18673
+ }
18674
+ }
18675
+ } catch (e) {
18676
+ }
18677
+ return files;
18678
+ }
18679
+ function parseSessionFile(filepath, provider) {
18680
+ try {
18681
+ const stat = (0, import_fs11.statSync)(filepath);
18682
+ const content = (0, import_fs11.readFileSync)(filepath, "utf-8");
18683
+ const lines = content.split("\n").filter((l) => l.trim());
18684
+ if (lines.length === 0) return null;
18685
+ const sessionId = (0, import_path11.basename)(filepath, ".jsonl");
18686
+ const isAgent = sessionId.startsWith("agent-");
18687
+ let title = null;
18688
+ let cwd = null;
18689
+ let createdAt = null;
18690
+ let model = null;
18691
+ let parentSessionId = null;
18692
+ let agentId = isAgent ? sessionId.replace("agent-", "") : null;
18693
+ for (const line of lines.slice(0, 50)) {
18694
+ try {
18695
+ const data = JSON.parse(line);
18696
+ if (!cwd && data.cwd) cwd = data.cwd;
18697
+ if (!createdAt && data.timestamp) createdAt = data.timestamp;
18698
+ if (!model && data.model) model = data.model;
18699
+ if (!parentSessionId && (data.parentSessionId || data.parentUuid)) {
18700
+ parentSessionId = data.parentSessionId || data.parentUuid;
18701
+ }
18702
+ if (!agentId && data.agentId) agentId = data.agentId;
18703
+ if (!title) {
18704
+ const msg = data.message?.content || data.userMessage || (data.type === "user" ? data.message?.content : null);
18705
+ if (msg && msg.length > 2) {
18706
+ title = msg.split("\n")[0].slice(0, 50).trim();
18707
+ }
18708
+ }
18709
+ } catch (e) {
18710
+ continue;
18711
+ }
18712
+ }
18713
+ if (!title || title.length < 3) {
18714
+ title = isAgent ? "Agent Session" : "Imported Session";
18715
+ }
18716
+ if (!cwd) {
18717
+ const parentDir = (0, import_path11.basename)((0, import_path11.dirname)(filepath));
18718
+ if (parentDir.startsWith("-")) {
18719
+ cwd = parentDir.replace(/-/g, "/").replace(/^\//, "/");
18720
+ } else {
18721
+ cwd = (0, import_os3.homedir)();
18722
+ }
18723
+ }
18724
+ return {
18725
+ title,
18726
+ cwd,
18727
+ createdAt: createdAt || stat.birthtime.toISOString(),
18728
+ lastActiveAt: stat.mtime.toISOString(),
18729
+ model,
18730
+ isAgent,
18731
+ agentId,
18732
+ parentSessionId,
18733
+ sessionType: isAgent ? "agent" : "main"
18734
+ };
18735
+ } catch (e) {
18736
+ return null;
18737
+ }
18738
+ }
18156
18739
 
18157
18740
  // src/commands/doctor.js
18158
- var import_fs10 = __toESM(require("fs"), 1);
18741
+ var import_fs12 = __toESM(require("fs"), 1);
18159
18742
  async function cmdDoctor(args, flags) {
18160
18743
  console.log("RUDI Health Check");
18161
18744
  console.log("\u2550".repeat(50));
@@ -18173,12 +18756,12 @@ async function cmdDoctor(args, flags) {
18173
18756
  { path: PATHS.cache, name: "Cache" }
18174
18757
  ];
18175
18758
  for (const dir of dirs) {
18176
- const exists = import_fs10.default.existsSync(dir.path);
18759
+ const exists = import_fs12.default.existsSync(dir.path);
18177
18760
  const status = exists ? "\u2713" : "\u2717";
18178
18761
  console.log(` ${status} ${dir.name}: ${dir.path}`);
18179
18762
  if (!exists) {
18180
18763
  issues.push(`Missing directory: ${dir.name}`);
18181
- fixes.push(() => import_fs10.default.mkdirSync(dir.path, { recursive: true }));
18764
+ fixes.push(() => import_fs12.default.mkdirSync(dir.path, { recursive: true }));
18182
18765
  }
18183
18766
  }
18184
18767
  console.log("\n\u{1F4BE} Database");
@@ -18248,8 +18831,8 @@ async function cmdDoctor(args, flags) {
18248
18831
  }
18249
18832
 
18250
18833
  // src/commands/update.js
18251
- var import_fs11 = __toESM(require("fs"), 1);
18252
- var import_path10 = __toESM(require("path"), 1);
18834
+ var import_fs13 = __toESM(require("fs"), 1);
18835
+ var import_path12 = __toESM(require("path"), 1);
18253
18836
  var import_child_process2 = require("child_process");
18254
18837
  init_src();
18255
18838
  init_src2();
@@ -18275,7 +18858,7 @@ async function cmdUpdate(args, flags) {
18275
18858
  async function updatePackage(pkgId, flags) {
18276
18859
  const [kind, name] = parsePackageId(pkgId);
18277
18860
  const installPath = getPackagePath(pkgId);
18278
- if (!import_fs11.default.existsSync(installPath)) {
18861
+ if (!import_fs13.default.existsSync(installPath)) {
18279
18862
  return { success: false, error: "Package not installed" };
18280
18863
  }
18281
18864
  const pkg = await getPackage(pkgId);
@@ -18298,7 +18881,7 @@ async function updatePackage(pkgId, flags) {
18298
18881
  }
18299
18882
  if (pkg.pipPackage) {
18300
18883
  try {
18301
- const venvPip = import_path10.default.join(installPath, "venv", "bin", "pip");
18884
+ const venvPip = import_path12.default.join(installPath, "venv", "bin", "pip");
18302
18885
  (0, import_child_process2.execSync)(`"${venvPip}" install --upgrade ${pkg.pipPackage}`, {
18303
18886
  stdio: flags.verbose ? "inherit" : "pipe"
18304
18887
  });
@@ -18315,7 +18898,7 @@ async function updatePackage(pkgId, flags) {
18315
18898
  if (kind === "runtime" && !pkg.npmPackage && !pkg.pipPackage) {
18316
18899
  try {
18317
18900
  const { downloadRuntime: downloadRuntime2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
18318
- import_fs11.default.rmSync(installPath, { recursive: true, force: true });
18901
+ import_fs13.default.rmSync(installPath, { recursive: true, force: true });
18319
18902
  await downloadRuntime2(name, pkg.version || "latest", installPath, {
18320
18903
  onProgress: (p) => {
18321
18904
  if (flags.verbose) console.log(` ${p.phase}...`);
@@ -18335,8 +18918,8 @@ async function updateAll(flags) {
18335
18918
  let failed = 0;
18336
18919
  for (const kind of kinds) {
18337
18920
  const dir = kind === "runtime" ? PATHS.runtimes : kind === "stack" ? PATHS.stacks : PATHS.prompts;
18338
- if (!import_fs11.default.existsSync(dir)) continue;
18339
- const entries = import_fs11.default.readdirSync(dir, { withFileTypes: true });
18921
+ if (!import_fs13.default.existsSync(dir)) continue;
18922
+ const entries = import_fs13.default.readdirSync(dir, { withFileTypes: true });
18340
18923
  for (const entry of entries) {
18341
18924
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
18342
18925
  const pkgId = `${kind}:${entry.name}`;
@@ -18355,14 +18938,14 @@ Updated ${updated} package(s)${failed > 0 ? `, ${failed} failed` : ""}`);
18355
18938
  }
18356
18939
  function getInstalledVersion(installPath, npmPackage) {
18357
18940
  try {
18358
- const pkgJsonPath = import_path10.default.join(installPath, "node_modules", npmPackage.replace("@", "").split("/")[0], "package.json");
18359
- if (import_fs11.default.existsSync(pkgJsonPath)) {
18360
- const pkgJson = JSON.parse(import_fs11.default.readFileSync(pkgJsonPath, "utf-8"));
18941
+ const pkgJsonPath = import_path12.default.join(installPath, "node_modules", npmPackage.replace("@", "").split("/")[0], "package.json");
18942
+ if (import_fs13.default.existsSync(pkgJsonPath)) {
18943
+ const pkgJson = JSON.parse(import_fs13.default.readFileSync(pkgJsonPath, "utf-8"));
18361
18944
  return pkgJson.version;
18362
18945
  }
18363
- const rootPkgPath = import_path10.default.join(installPath, "package.json");
18364
- if (import_fs11.default.existsSync(rootPkgPath)) {
18365
- const rootPkg = JSON.parse(import_fs11.default.readFileSync(rootPkgPath, "utf-8"));
18946
+ const rootPkgPath = import_path12.default.join(installPath, "package.json");
18947
+ if (import_fs13.default.existsSync(rootPkgPath)) {
18948
+ const rootPkg = JSON.parse(import_fs13.default.readFileSync(rootPkgPath, "utf-8"));
18366
18949
  const dep = rootPkg.dependencies?.[npmPackage];
18367
18950
  if (dep) return dep.replace(/[\^~]/, "");
18368
18951
  }
@@ -18371,30 +18954,30 @@ function getInstalledVersion(installPath, npmPackage) {
18371
18954
  return null;
18372
18955
  }
18373
18956
  function updateRuntimeMetadata(installPath, updates) {
18374
- const metaPath = import_path10.default.join(installPath, "runtime.json");
18957
+ const metaPath = import_path12.default.join(installPath, "runtime.json");
18375
18958
  try {
18376
18959
  let meta = {};
18377
- if (import_fs11.default.existsSync(metaPath)) {
18378
- meta = JSON.parse(import_fs11.default.readFileSync(metaPath, "utf-8"));
18960
+ if (import_fs13.default.existsSync(metaPath)) {
18961
+ meta = JSON.parse(import_fs13.default.readFileSync(metaPath, "utf-8"));
18379
18962
  }
18380
18963
  meta = { ...meta, ...updates };
18381
- import_fs11.default.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
18964
+ import_fs13.default.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
18382
18965
  } catch {
18383
18966
  }
18384
18967
  }
18385
18968
 
18386
18969
  // db/index.js
18387
18970
  var import_better_sqlite32 = __toESM(require("better-sqlite3"), 1);
18388
- var import_path11 = __toESM(require("path"), 1);
18389
- var import_os3 = __toESM(require("os"), 1);
18390
- var import_fs12 = __toESM(require("fs"), 1);
18391
- var PROMPT_STACK_HOME3 = import_path11.default.join(import_os3.default.homedir(), ".prompt-stack");
18392
- var DB_PATH2 = import_path11.default.join(PROMPT_STACK_HOME3, "prompt-stack.db");
18971
+ var import_path13 = __toESM(require("path"), 1);
18972
+ var import_os4 = __toESM(require("os"), 1);
18973
+ var import_fs14 = __toESM(require("fs"), 1);
18974
+ var PROMPT_STACK_HOME = import_path13.default.join(import_os4.default.homedir(), ".prompt-stack");
18975
+ var DB_PATH2 = import_path13.default.join(PROMPT_STACK_HOME, "prompt-stack.db");
18393
18976
  var db2 = null;
18394
18977
  function getDb2(options = {}) {
18395
18978
  if (!db2) {
18396
- if (!import_fs12.default.existsSync(PROMPT_STACK_HOME3)) {
18397
- import_fs12.default.mkdirSync(PROMPT_STACK_HOME3, { recursive: true });
18979
+ if (!import_fs14.default.existsSync(PROMPT_STACK_HOME)) {
18980
+ import_fs14.default.mkdirSync(PROMPT_STACK_HOME, { recursive: true });
18398
18981
  }
18399
18982
  db2 = new import_better_sqlite32.default(DB_PATH2, {
18400
18983
  readonly: options.readonly || false
@@ -18552,7 +19135,7 @@ function getBeforeCrashLogs() {
18552
19135
  }
18553
19136
 
18554
19137
  // src/commands/logs.js
18555
- var import_fs13 = __toESM(require("fs"), 1);
19138
+ var import_fs15 = __toESM(require("fs"), 1);
18556
19139
  function parseTimeAgo(str) {
18557
19140
  const match = str.match(/^(\d+)([smhd])$/);
18558
19141
  if (!match) return null;
@@ -18652,7 +19235,7 @@ function exportLogs(logs, filepath, format) {
18652
19235
  });
18653
19236
  content = JSON.stringify(formatted, null, 2);
18654
19237
  }
18655
- import_fs13.default.writeFileSync(filepath, content, "utf-8");
19238
+ import_fs15.default.writeFileSync(filepath, content, "utf-8");
18656
19239
  return filepath;
18657
19240
  }
18658
19241
  function printStats(stats) {
@@ -19189,6 +19772,9 @@ async function main() {
19189
19772
  case "database":
19190
19773
  await cmdDb(args, flags);
19191
19774
  break;
19775
+ case "import":
19776
+ await cmdImport(args, flags);
19777
+ break;
19192
19778
  case "doctor":
19193
19779
  case "check":
19194
19780
  await cmdDoctor(args, flags);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@learnrudi/cli",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "RUDI CLI - Install and manage MCP stacks, runtimes, and AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",