jinzd-ai-cli 0.4.220 → 0.4.221

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 (29) hide show
  1. package/dist/{batch-QBSKTLTO.js → batch-LSWWSINO.js} +2 -2
  2. package/dist/{chunk-IZW5LMI6.js → chunk-4AX2MEJA.js} +3 -3
  3. package/dist/{chunk-Y25MSSUP.js → chunk-4MOBTDS6.js} +1 -1
  4. package/dist/{chunk-P4PTXCHY.js → chunk-5BY4XICU.js} +3 -3
  5. package/dist/{chunk-MC34ISJU.js → chunk-5GTWZ4QF.js} +30 -6
  6. package/dist/{chunk-P3LKFA54.js → chunk-6SKMGONY.js} +3 -3
  7. package/dist/{chunk-VHX7KZDY.js → chunk-BYQEWWA4.js} +2 -2
  8. package/dist/{chunk-P4DYAGUI.js → chunk-OULVIHCT.js} +1 -1
  9. package/dist/{chunk-5WBNXYDJ.js → chunk-PMTEZTXP.js} +1 -1
  10. package/dist/{chunk-QZYNRCPX.js → chunk-QDN5G7JN.js} +1 -1
  11. package/dist/{chunk-ALQN7VUJ.js → chunk-QKJ3MAP7.js} +1 -1
  12. package/dist/{chunk-A4GUGWEY.js → chunk-QT5FA2ZY.js} +1 -1
  13. package/dist/{chunk-CAUHLHDR.js → chunk-S5EEKS4P.js} +1 -1
  14. package/dist/{ci-5SNXGM3M.js → ci-OIFTAJKR.js} +4 -4
  15. package/dist/{ci-format-57QQTHQE.js → ci-format-H6BLMWQ5.js} +2 -2
  16. package/dist/{constants-ML5KFEOH.js → constants-EJRLT7DM.js} +1 -1
  17. package/dist/{doctor-cli-ZRH3XZ3R.js → doctor-cli-EXC7YU5D.js} +4 -4
  18. package/dist/electron-server.js +73 -44
  19. package/dist/{hub-IPLMJ6TJ.js → hub-HQDRQBNM.js} +2 -2
  20. package/dist/index.js +322 -32
  21. package/dist/{persist-L54DPLI7.js → persist-GTBEKVFL.js} +2 -2
  22. package/dist/{pr-GJYDAPK4.js → pr-VSZNVKBS.js} +4 -4
  23. package/dist/{run-tests-GK5AIAV2.js → run-tests-55JGEZYL.js} +1 -1
  24. package/dist/{run-tests-KLQAI5CX.js → run-tests-OYXK27XH.js} +2 -2
  25. package/dist/{server-RU36PRSH.js → server-JKNIZ5CB.js} +19 -14
  26. package/dist/{server-5FZDALHH.js → server-XX6UM4ZQ.js} +4 -4
  27. package/dist/{task-orchestrator-JMHPVNUO.js → task-orchestrator-F3DUIACA.js} +4 -4
  28. package/dist/{usage-IRMRCCKN.js → usage-4KFX3HTK.js} +2 -2
  29. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  scanProject,
15
15
  sessionHasMeaningfulContent,
16
16
  setupProxy
17
- } from "./chunk-VHX7KZDY.js";
17
+ } from "./chunk-BYQEWWA4.js";
18
18
  import {
19
19
  buildReviewPrompt,
20
20
  buildSecurityReviewPrompt
@@ -51,15 +51,15 @@ import {
51
51
  theme,
52
52
  undoStack,
53
53
  updateMemoryApproval
54
- } from "./chunk-IZW5LMI6.js";
54
+ } from "./chunk-4AX2MEJA.js";
55
55
  import "./chunk-K3CF65QH.js";
56
56
  import "./chunk-T2NL5ZIA.js";
57
57
  import "./chunk-BXP6YZ2P.js";
58
- import "./chunk-Y25MSSUP.js";
58
+ import "./chunk-4MOBTDS6.js";
59
59
  import {
60
60
  SessionManager,
61
61
  getContentText
62
- } from "./chunk-MC34ISJU.js";
62
+ } from "./chunk-5GTWZ4QF.js";
63
63
  import {
64
64
  CostTracker
65
65
  } from "./chunk-CITDVXXM.js";
@@ -87,7 +87,7 @@ import {
87
87
  formatDoctorReport,
88
88
  loadContextFiles,
89
89
  writeCrashLog
90
- } from "./chunk-P3LKFA54.js";
90
+ } from "./chunk-6SKMGONY.js";
91
91
  import {
92
92
  ProviderRegistry
93
93
  } from "./chunk-PUBCJF7E.js";
@@ -107,7 +107,7 @@ import {
107
107
  trustHook,
108
108
  trustPlugin,
109
109
  untrustHook
110
- } from "./chunk-ALQN7VUJ.js";
110
+ } from "./chunk-QKJ3MAP7.js";
111
111
  import {
112
112
  CONTENT_ONLY_STREAM_REMINDER,
113
113
  TEE_FINAL_USER_NUDGE,
@@ -129,7 +129,7 @@ import {
129
129
  } from "./chunk-XPBEJB27.js";
130
130
  import {
131
131
  ConfigManager
132
- } from "./chunk-5WBNXYDJ.js";
132
+ } from "./chunk-PMTEZTXP.js";
133
133
  import {
134
134
  AuthError,
135
135
  ProviderError,
@@ -153,7 +153,7 @@ import {
153
153
  SKILLS_DIR_NAME,
154
154
  VERSION,
155
155
  buildUserIdentityPrompt
156
- } from "./chunk-QZYNRCPX.js";
156
+ } from "./chunk-QDN5G7JN.js";
157
157
  import {
158
158
  formatGitContextForPrompt,
159
159
  getGitContext,
@@ -780,6 +780,117 @@ function oneLinePreview(s, max) {
780
780
  const flat = s.replace(/\s+/g, " ").trim();
781
781
  return flat.length > max ? flat.slice(0, max - 1) + "\u2026" : flat;
782
782
  }
783
+ var DEFAULT_SESSION_LIST_LIMIT = 50;
784
+ var MAX_TITLE_WIDTH = 40;
785
+ function formatFileSize(bytes) {
786
+ if (bytes < 1024) return `${bytes} B`;
787
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
788
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
789
+ }
790
+ function visualWidth(s) {
791
+ const clean = s.replace(/\x1b\[[0-9;]*m/g, "");
792
+ let w = 0;
793
+ for (const ch of clean) {
794
+ const cp = ch.codePointAt(0);
795
+ if (cp >= 4352 && cp <= 4447 || // Hangul Jamo
796
+ cp >= 11904 && cp <= 42191 || // CJK Radicals … Yi
797
+ cp >= 44032 && cp <= 55203 || // Hangul Syllables
798
+ cp >= 63744 && cp <= 64255 || // CJK Compatibility Ideographs
799
+ cp >= 65072 && cp <= 65135 || // CJK Compatibility Forms
800
+ cp >= 65281 && cp <= 65376 || // Fullwidth Forms
801
+ cp >= 65504 && cp <= 65510 || // Fullwidth Signs
802
+ cp >= 126976 && cp <= 129791 || // Mahjong / Domino / …
803
+ cp >= 131072 && cp <= 196605 || // CJK Extension B+
804
+ cp >= 196608 && cp <= 262141) {
805
+ w += 2;
806
+ } else {
807
+ w += 1;
808
+ }
809
+ }
810
+ return w;
811
+ }
812
+ function truncateTitle(title, maxWidth) {
813
+ if (visualWidth(title) <= maxWidth) return title;
814
+ let w = 0;
815
+ let end = 0;
816
+ for (const ch of title) {
817
+ const chW = visualWidth(ch);
818
+ if (w + chW > maxWidth - 1) break;
819
+ w += chW;
820
+ end++;
821
+ }
822
+ return title.slice(0, end) + "\u2026";
823
+ }
824
+ function formatSessionDate(date) {
825
+ const pad = (n) => n.toString().padStart(2, "0");
826
+ const ymd = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
827
+ const hm = `${pad(date.getHours())}:${pad(date.getMinutes())}`;
828
+ const abs = `${ymd} ${hm}`;
829
+ const now = /* @__PURE__ */ new Date();
830
+ const diffMs = now.getTime() - date.getTime();
831
+ const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
832
+ if (diffDays === 0) return `${abs} (today)`;
833
+ if (diffDays === 1) return `${abs} (yesterday)`;
834
+ if (diffDays < 7) return `${abs} (${diffDays} days ago)`;
835
+ return abs;
836
+ }
837
+ function parseSessionListArgs(args) {
838
+ let limit = DEFAULT_SESSION_LIST_LIMIT;
839
+ let all = false;
840
+ const filter = {};
841
+ let search = null;
842
+ let days = null;
843
+ let since = null;
844
+ let sort = { field: "updated", order: "desc" };
845
+ let json = false;
846
+ let stale = null;
847
+ let verbose = false;
848
+ for (let i = 1; i < args.length; i++) {
849
+ const a = args[i];
850
+ if (a === "--all" || a === "-a") {
851
+ all = true;
852
+ } else if ((a === "--limit" || a === "-n") && i + 1 < args.length) {
853
+ const n = Number(args[i + 1]);
854
+ if (Number.isFinite(n) && n > 0) {
855
+ limit = n;
856
+ i++;
857
+ }
858
+ } else if (a === "--filter" && i + 1 < args.length) {
859
+ const val = args[++i];
860
+ const eq = val.indexOf("=");
861
+ if (eq > 0) {
862
+ const key = val.slice(0, eq);
863
+ const vals = val.slice(eq + 1).split(",").map((s) => s.trim().toLowerCase());
864
+ if (key === "provider") filter.provider = vals;
865
+ else if (key === "model") filter.model = vals;
866
+ }
867
+ } else if ((a === "--search" || a === "-s") && i + 1 < args.length) {
868
+ search = args[++i];
869
+ } else if (a === "--days" && i + 1 < args.length) {
870
+ const n = Number(args[++i]);
871
+ if (Number.isFinite(n) && n > 0) days = n;
872
+ } else if (a === "--since" && i + 1 < args.length) {
873
+ since = args[++i];
874
+ } else if (a === "--sort" && i + 1 < args.length) {
875
+ const sv = args[++i];
876
+ const colon = sv.indexOf(":");
877
+ const field = colon > 0 ? sv.slice(0, colon) : sv;
878
+ const order = colon > 0 ? sv.slice(colon + 1) : "desc";
879
+ if (field === "updated" || field === "created" || field === "messages" || field === "size") {
880
+ sort = { field, order: order === "asc" ? "asc" : "desc" };
881
+ }
882
+ } else if (a === "--json") {
883
+ json = true;
884
+ } else if (a === "--verbose" || a === "-v") {
885
+ verbose = true;
886
+ } else if (a === "--stale" && i + 1 < args.length) {
887
+ const n = Number(args[++i]);
888
+ if (Number.isFinite(n) && n > 0) stale = n;
889
+ }
890
+ }
891
+ if (all) limit = -1;
892
+ return { limit, all, filter, search, days, since, sort, json, stale, verbose };
893
+ }
783
894
  function renderTranscriptMessage(msg, renderer) {
784
895
  if (msg.role === "user") {
785
896
  const text = getContentText(msg.content);
@@ -1222,7 +1333,7 @@ No commands match "${filter}".
1222
1333
  {
1223
1334
  name: "session",
1224
1335
  description: "Session management",
1225
- usage: "/session new|list|load <id>|show <id> [page]|page <n>|remove <id>",
1336
+ usage: "/session new|list [--limit N|--all] [--filter k=v] [--search kw] [--days N|--stale N] [--sort f[:o]] [--json] [--verbose]|load <id>|show <id> [page]|page <n>|remove <id>|clean --stale N [--dry-run]",
1226
1337
  async execute(args, ctx) {
1227
1338
  const sub = args[0];
1228
1339
  if (sub === "new") {
@@ -1233,23 +1344,137 @@ No commands match "${filter}".
1233
1344
  ctx.clearDevState();
1234
1345
  ctx.renderer.printSuccess("New session started.");
1235
1346
  } else if (sub === "list") {
1236
- const sessions = ctx.sessions.listSessions();
1237
- if (sessions.length === 0) {
1347
+ const opts = parseSessionListArgs(args);
1348
+ const needFileSize = opts.verbose || opts.sort.field === "size";
1349
+ let allSessions = ctx.sessions.listSessions({ includeFileSize: needFileSize });
1350
+ if (allSessions.length === 0) {
1238
1351
  ctx.renderer.printInfo("No saved sessions.");
1239
1352
  return;
1240
1353
  }
1354
+ const hasFilters = opts.filter.provider || opts.filter.model;
1355
+ const hasSearch = !!opts.search;
1356
+ const hasTimeFilter = opts.days !== null || opts.since !== null;
1357
+ const hasStale = opts.stale !== null;
1358
+ if (hasFilters) {
1359
+ const p = opts.filter.provider;
1360
+ const m = opts.filter.model;
1361
+ allSessions = allSessions.filter((s) => {
1362
+ if (p && !p.includes(s.provider.toLowerCase())) return false;
1363
+ if (m && !m.some((kw) => s.model.toLowerCase().includes(kw))) return false;
1364
+ return true;
1365
+ });
1366
+ }
1367
+ if (hasSearch) {
1368
+ const q = opts.search.toLowerCase();
1369
+ allSessions = allSessions.filter(
1370
+ (s) => (s.title ?? "").toLowerCase().includes(q) || s.id.toLowerCase().includes(q)
1371
+ );
1372
+ }
1373
+ if (hasTimeFilter) {
1374
+ const now = Date.now();
1375
+ let cutoff = 0;
1376
+ if (opts.since) {
1377
+ cutoff = new Date(opts.since).getTime();
1378
+ } else if (opts.days !== null) {
1379
+ cutoff = now - opts.days * 24 * 60 * 60 * 1e3;
1380
+ }
1381
+ if (!isNaN(cutoff)) {
1382
+ allSessions = allSessions.filter((s) => s.updated.getTime() >= cutoff);
1383
+ }
1384
+ }
1385
+ const staleCutoff = hasStale ? Date.now() - opts.stale * 24 * 60 * 60 * 1e3 : 0;
1386
+ if (hasStale) {
1387
+ allSessions = allSessions.filter((s) => s.updated.getTime() < staleCutoff);
1388
+ }
1389
+ const defaultSort = opts.sort.field === "updated" && opts.sort.order === "desc";
1390
+ if (!defaultSort) {
1391
+ const mul = opts.sort.order === "asc" ? 1 : -1;
1392
+ allSessions = [...allSessions].sort((a, b) => {
1393
+ switch (opts.sort.field) {
1394
+ case "created":
1395
+ return (a.created.getTime() - b.created.getTime()) * mul;
1396
+ case "messages":
1397
+ return (a.messageCount - b.messageCount) * mul;
1398
+ case "size":
1399
+ return ((a.fileSize ?? 0) - (b.fileSize ?? 0)) * mul;
1400
+ default:
1401
+ return (a.updated.getTime() - b.updated.getTime()) * mul;
1402
+ }
1403
+ });
1404
+ }
1405
+ const totalCount = allSessions.length;
1406
+ if (totalCount === 0) {
1407
+ ctx.renderer.printInfo("No sessions match the filter criteria.");
1408
+ return;
1409
+ }
1410
+ if (opts.json) {
1411
+ const sliced2 = opts.limit > 0 ? allSessions.slice(0, opts.limit) : allSessions;
1412
+ const payload = sliced2.map((s) => ({
1413
+ id: s.id,
1414
+ title: s.title ?? null,
1415
+ provider: s.provider,
1416
+ model: s.model,
1417
+ messageCount: s.messageCount,
1418
+ created: s.created.toISOString(),
1419
+ updated: s.updated.toISOString(),
1420
+ ...needFileSize ? { fileSize: s.fileSize } : {},
1421
+ ...s.branchCount !== void 0 ? { branchCount: s.branchCount, activeBranch: s.activeBranch } : {}
1422
+ }));
1423
+ console.log(JSON.stringify(payload, null, 2));
1424
+ if (opts.limit > 0 && totalCount > opts.limit) {
1425
+ console.error(
1426
+ `
1427
+ (truncated: ${opts.limit} of ${totalCount} sessions shown; use --all for full list)`
1428
+ );
1429
+ }
1430
+ return;
1431
+ }
1432
+ const sliced = opts.limit > 0 ? allSessions.slice(0, opts.limit) : allSessions;
1433
+ const headers = ["ID (short)", "Provider", "Model", "Messages"];
1434
+ if (opts.verbose) {
1435
+ headers.push("Size");
1436
+ headers.push("Br");
1437
+ }
1438
+ headers.push("Date", "Title");
1439
+ const rows = sliced.map((s) => {
1440
+ const isStale = hasStale ? s.updated.getTime() < staleCutoff : false;
1441
+ const dim = isStale ? theme.dim : (x) => String(x);
1442
+ const row = [
1443
+ dim(s.id.slice(0, 8)),
1444
+ dim(s.provider),
1445
+ dim(s.model),
1446
+ s.messageCount
1447
+ ];
1448
+ if (opts.verbose) {
1449
+ row.push(
1450
+ s.fileSize !== void 0 ? formatFileSize(s.fileSize) : "\u2014",
1451
+ s.branchCount ?? 1
1452
+ );
1453
+ }
1454
+ row.push(
1455
+ formatSessionDate(s.updated),
1456
+ dim(truncateTitle(s.title ?? "(untitled)", MAX_TITLE_WIDTH))
1457
+ );
1458
+ return row;
1459
+ });
1241
1460
  console.log();
1242
- ctx.renderer.printTable(
1243
- ["ID (short)", "Provider", "Model", "Messages", "Date", "Title"],
1244
- sessions.map((s) => [
1245
- s.id.slice(0, 8),
1246
- s.provider,
1247
- s.model,
1248
- s.messageCount,
1249
- s.updated.toLocaleDateString(),
1250
- s.title ?? "(untitled)"
1251
- ])
1461
+ ctx.renderer.printTable(headers, rows);
1462
+ const totalMessages = allSessions.reduce((sum, s) => sum + s.messageCount, 0);
1463
+ const shown = sliced.length;
1464
+ const filterHints = [];
1465
+ if (hasFilters) filterHints.push("filtered");
1466
+ if (hasSearch) filterHints.push("searched");
1467
+ if (hasTimeFilter) filterHints.push("time-limited");
1468
+ if (hasStale) filterHints.push(`stale >${opts.stale}d`);
1469
+ const filterTag = filterHints.length > 0 ? ` (${filterHints.join(", ")})` : "";
1470
+ console.log(
1471
+ theme.dim(
1472
+ ` \u2500\u2500 ${shown} of ${totalCount} sessions${filterTag} \xB7 ${totalMessages} messages total`
1473
+ )
1252
1474
  );
1475
+ if (shown < totalCount) {
1476
+ console.log(theme.dim(` Use --all to show all, or --limit N to change count.`));
1477
+ }
1253
1478
  } else if (sub === "load" || sub === "show") {
1254
1479
  const id = args[1];
1255
1480
  if (!id) {
@@ -1337,6 +1562,71 @@ No commands match "${filter}".
1337
1562
  ctx.clearDevState();
1338
1563
  ctx.renderer.printInfo("Current session was deleted \u2014 started a fresh session.");
1339
1564
  }
1565
+ } else if (sub === "clean") {
1566
+ let staleDays = 90;
1567
+ let dryRun = false;
1568
+ for (let i = 1; i < args.length; i++) {
1569
+ if (args[i] === "--stale" && i + 1 < args.length) {
1570
+ const n = Number(args[++i]);
1571
+ if (Number.isFinite(n) && n > 0) staleDays = n;
1572
+ } else if (args[i] === "--dry-run") {
1573
+ dryRun = true;
1574
+ }
1575
+ }
1576
+ const cutoff = Date.now() - staleDays * 24 * 60 * 60 * 1e3;
1577
+ const allSessions = ctx.sessions.listSessions();
1578
+ const stale = allSessions.filter((s) => s.updated.getTime() < cutoff);
1579
+ if (stale.length === 0) {
1580
+ ctx.renderer.printInfo(`No sessions older than ${staleDays} days found.`);
1581
+ return;
1582
+ }
1583
+ console.log();
1584
+ console.log(
1585
+ theme.warning(
1586
+ ` Found ${stale.length} session(s) not updated in ${staleDays}+ days:`
1587
+ )
1588
+ );
1589
+ for (const s of stale.slice(0, 20)) {
1590
+ console.log(
1591
+ theme.dim(
1592
+ ` ${s.id.slice(0, 12)} ${String(s.messageCount).padStart(4)} msgs ${s.updated.toISOString().slice(0, 10)} ${truncateTitle(s.title ?? "(untitled)", 60)}`
1593
+ )
1594
+ );
1595
+ }
1596
+ if (stale.length > 20) {
1597
+ console.log(theme.dim(` ... and ${stale.length - 20} more`));
1598
+ }
1599
+ if (dryRun) {
1600
+ console.log(
1601
+ theme.info(`
1602
+ --dry-run: no sessions were deleted. Remove --dry-run to actually delete.`)
1603
+ );
1604
+ return;
1605
+ }
1606
+ console.log();
1607
+ const choice = await ctx.select(
1608
+ `Delete all ${stale.length} stale sessions?`,
1609
+ [
1610
+ { label: "No \u2014 keep them", value: "no" },
1611
+ { label: "Yes \u2014 delete permanently", value: "yes", hint: "unlinks all stale history files" }
1612
+ ]
1613
+ );
1614
+ if (choice !== "yes") {
1615
+ ctx.renderer.printInfo("Cancelled \u2014 no sessions deleted.");
1616
+ return;
1617
+ }
1618
+ let deleted = 0;
1619
+ let failed = 0;
1620
+ for (const s of stale) {
1621
+ if (ctx.sessions.deleteSession(s.id)) {
1622
+ deleted++;
1623
+ } else {
1624
+ failed++;
1625
+ }
1626
+ }
1627
+ ctx.renderer.printSuccess(
1628
+ `Cleaned up: ${deleted} deleted${failed > 0 ? `, ${failed} failed` : ""}.`
1629
+ );
1340
1630
  } else {
1341
1631
  ctx.renderer.renderError("Usage: /session new|list|load <id>|show <id> [page]|page <n>|remove <id>");
1342
1632
  }
@@ -1796,7 +2086,7 @@ No tools match "${filter}".
1796
2086
  const { join: join5 } = await import("path");
1797
2087
  const { existsSync: existsSync5 } = await import("fs");
1798
2088
  const { getGitRoot: getGitRoot2 } = await import("./git-context-EXOEHQSF.js");
1799
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-ML5KFEOH.js");
2089
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-EJRLT7DM.js");
1800
2090
  const { approveProject, hashMcpFile } = await import("./project-trust-NKYHL3VZ.js");
1801
2091
  const cwd = process.cwd();
1802
2092
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2855,7 +3145,7 @@ ${hint}` : "")
2855
3145
  usage: "/test [command|filter]",
2856
3146
  async execute(args, ctx) {
2857
3147
  try {
2858
- const { executeTests } = await import("./run-tests-KLQAI5CX.js");
3148
+ const { executeTests } = await import("./run-tests-OYXK27XH.js");
2859
3149
  const argStr = args.join(" ").trim();
2860
3150
  let testArgs = {};
2861
3151
  if (argStr) {
@@ -7292,7 +7582,7 @@ program.command("web").description("Start Web UI server with browser-based chat
7292
7582
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
7293
7583
  process.exit(1);
7294
7584
  }
7295
- const { startWebServer } = await import("./server-RU36PRSH.js");
7585
+ const { startWebServer } = await import("./server-JKNIZ5CB.js");
7296
7586
  await startWebServer({ port, host: options.host });
7297
7587
  });
7298
7588
  program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | logout-all <name> | migrate <name>)").action(async (action, username) => {
@@ -7459,11 +7749,11 @@ program.command("sessions").description("List recent conversation sessions").opt
7459
7749
  console.log(footer + "\n");
7460
7750
  });
7461
7751
  program.command("usage").description("Show token + cost usage grouped by provider/model (cross-session)").option("--days <n>", "Only the last N days (inclusive of today)").option("--month <ym>", "Only a specific month, format YYYY-MM (e.g. 2026-06)").option("--json", "Output as JSON (for scripting)").action(async (options) => {
7462
- const { runUsageCli } = await import("./usage-IRMRCCKN.js");
7752
+ const { runUsageCli } = await import("./usage-4KFX3HTK.js");
7463
7753
  await runUsageCli(options);
7464
7754
  });
7465
7755
  program.command("doctor").description("Health check: API keys, config, MCP, recent crashes, tool usage, disk usage").option("--json", "Output as JSON (for scripting)").option("--reset-stats", "Reset accumulated tool usage statistics").action(async (options) => {
7466
- const { runDoctorCli } = await import("./doctor-cli-ZRH3XZ3R.js");
7756
+ const { runDoctorCli } = await import("./doctor-cli-EXC7YU5D.js");
7467
7757
  const argv = process.argv.slice(2);
7468
7758
  await runDoctorCli({
7469
7759
  json: !!options.json || argv.includes("--json"),
@@ -7472,7 +7762,7 @@ program.command("doctor").description("Health check: API keys, config, MCP, rece
7472
7762
  });
7473
7763
  program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
7474
7764
  try {
7475
- const batch = await import("./batch-QBSKTLTO.js");
7765
+ const batch = await import("./batch-LSWWSINO.js");
7476
7766
  switch (action) {
7477
7767
  case "submit":
7478
7768
  if (!arg) {
@@ -7515,7 +7805,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7515
7805
  }
7516
7806
  });
7517
7807
  program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
7518
- const { startMcpServer } = await import("./server-5FZDALHH.js");
7808
+ const { startMcpServer } = await import("./server-XX6UM4ZQ.js");
7519
7809
  await startMcpServer({
7520
7810
  allowDestructive: !!options.allowDestructive,
7521
7811
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7528,7 +7818,7 @@ program.command("pr <action> [target]").description("PR review helpers: review |
7528
7818
  console.error(`Unknown pr action: ${action}. Use review | security-review | summarize.`);
7529
7819
  process.exit(1);
7530
7820
  }
7531
- const { runPrCli } = await import("./pr-GJYDAPK4.js");
7821
+ const { runPrCli } = await import("./pr-VSZNVKBS.js");
7532
7822
  const result = await runPrCli({
7533
7823
  action,
7534
7824
  target,
@@ -7548,8 +7838,8 @@ program.command("pr <action> [target]").description("PR review helpers: review |
7548
7838
  process.exit(result.exitCode);
7549
7839
  });
7550
7840
  program.command("ci").description("Headless PR review (code + security) \u2014 reads git/gh diff, optionally posts to PR. Designed for GitHub Actions.").option("--pr <num>", "PR number or URL; diff fetched via gh pr diff <value>", (v) => /^#?\d+$/.test(v) ? parseInt(v.replace(/^#/, ""), 10) : v).option("--base <ref>", "Base ref for git diff <ref>...HEAD (ignored when --pr set)").option("--post", "Post review as a PR comment (requires gh CLI + GH_TOKEN, needs --pr)").option("--no-update", "Always create a new comment instead of updating the previous aicli review").option("--skip-code", "Skip the code review section").option("--skip-security", "Skip the security review section").option("--detailed", "Use the detailed code-review prompt").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").option("--json", "Output a structured JSON report").option("--markdown", "Output Markdown report (default)").option("--sarif", "Output SARIF 2.1.0 for code scanning artifacts").option("--fail-on-security-high", "Fail when the security review contains HIGH/CRITICAL findings (default)").option("--no-fail-on-security-high", "Do not fail on security HIGH/CRITICAL findings").option("--fail-on-test-failure", "Fail when the report mentions test failures (default)").option("--no-fail-on-test-failure", "Do not fail on test failure findings").option("--fail-on-lint-failure", "Fail when the report mentions lint/typecheck failures (default)").option("--no-fail-on-lint-failure", "Do not fail on lint/typecheck failure findings").action(async (options) => {
7551
- const { runCi } = await import("./ci-5SNXGM3M.js");
7552
- const { formatCiResult } = await import("./ci-format-57QQTHQE.js");
7841
+ const { runCi } = await import("./ci-OIFTAJKR.js");
7842
+ const { formatCiResult } = await import("./ci-format-H6BLMWQ5.js");
7553
7843
  const formats = [options.json, options.markdown, options.sarif].filter(Boolean).length;
7554
7844
  if (formats > 1) {
7555
7845
  console.error("Choose only one output format: --json, --markdown, or --sarif.");
@@ -7707,7 +7997,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7707
7997
  config.get("customProviders"),
7708
7998
  config.getConfigDir()
7709
7999
  );
7710
- const { startHub } = await import("./hub-IPLMJ6TJ.js");
8000
+ const { startHub } = await import("./hub-HQDRQBNM.js");
7711
8001
  await startHub(
7712
8002
  {
7713
8003
  topic: topic ?? "",
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  discussionToMessages,
4
4
  persistDiscussion
5
- } from "./chunk-A4GUGWEY.js";
6
- import "./chunk-MC34ISJU.js";
5
+ } from "./chunk-QT5FA2ZY.js";
6
+ import "./chunk-5GTWZ4QF.js";
7
7
  import "./chunk-SLSWPBK3.js";
8
8
  import "./chunk-IW3Q7AE5.js";
9
9
  export {
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  countSeverity,
4
4
  runCi
5
- } from "./chunk-P4PTXCHY.js";
6
- import "./chunk-P4DYAGUI.js";
5
+ } from "./chunk-5BY4XICU.js";
6
+ import "./chunk-OULVIHCT.js";
7
7
  import {
8
8
  buildReviewPrompt,
9
9
  buildSecurityReviewPrompt,
@@ -15,11 +15,11 @@ import {
15
15
  import "./chunk-XPBEJB27.js";
16
16
  import {
17
17
  ConfigManager
18
- } from "./chunk-5WBNXYDJ.js";
18
+ } from "./chunk-PMTEZTXP.js";
19
19
  import "./chunk-5ULLIOVC.js";
20
20
  import {
21
21
  VERSION
22
- } from "./chunk-QZYNRCPX.js";
22
+ } from "./chunk-QDN5G7JN.js";
23
23
  import "./chunk-IW3Q7AE5.js";
24
24
 
25
25
  // src/cli/pr.ts
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-CAUHLHDR.js";
4
+ } from "./chunk-S5EEKS4P.js";
5
5
  export {
6
6
  executeTests,
7
7
  runTestsTool
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-Y25MSSUP.js";
6
- import "./chunk-QZYNRCPX.js";
5
+ } from "./chunk-4MOBTDS6.js";
6
+ import "./chunk-QDN5G7JN.js";
7
7
  export {
8
8
  executeTests,
9
9
  runTestsTool
@@ -7,7 +7,7 @@ import {
7
7
  import "./chunk-NZ4X6GUC.js";
8
8
  import {
9
9
  persistDiscussion
10
- } from "./chunk-A4GUGWEY.js";
10
+ } from "./chunk-QT5FA2ZY.js";
11
11
  import {
12
12
  AuthManager,
13
13
  TOKEN_EXPIRY_MS
@@ -23,7 +23,7 @@ import {
23
23
  scanDirTree,
24
24
  scanProject,
25
25
  setupProxy
26
- } from "./chunk-VHX7KZDY.js";
26
+ } from "./chunk-BYQEWWA4.js";
27
27
  import {
28
28
  buildReviewPrompt,
29
29
  buildSecurityReviewPrompt
@@ -66,15 +66,15 @@ import {
66
66
  truncateOutput,
67
67
  undoStack,
68
68
  updateMemoryApproval
69
- } from "./chunk-IZW5LMI6.js";
69
+ } from "./chunk-4AX2MEJA.js";
70
70
  import "./chunk-K3CF65QH.js";
71
71
  import "./chunk-T2NL5ZIA.js";
72
72
  import "./chunk-BXP6YZ2P.js";
73
- import "./chunk-Y25MSSUP.js";
73
+ import "./chunk-4MOBTDS6.js";
74
74
  import {
75
75
  SessionManager,
76
76
  getContentText
77
- } from "./chunk-MC34ISJU.js";
77
+ } from "./chunk-5GTWZ4QF.js";
78
78
  import {
79
79
  computeCost,
80
80
  formatCost
@@ -89,7 +89,7 @@ import {
89
89
  buildDoctorReport,
90
90
  formatDoctorReport,
91
91
  loadContextFiles
92
- } from "./chunk-P3LKFA54.js";
92
+ } from "./chunk-6SKMGONY.js";
93
93
  import {
94
94
  ProviderRegistry
95
95
  } from "./chunk-PUBCJF7E.js";
@@ -107,7 +107,7 @@ import {
107
107
  runTool,
108
108
  setPluginEnabled,
109
109
  trustPlugin
110
- } from "./chunk-ALQN7VUJ.js";
110
+ } from "./chunk-QKJ3MAP7.js";
111
111
  import {
112
112
  CONTENT_ONLY_STREAM_REMINDER,
113
113
  TEE_FINAL_USER_NUDGE,
@@ -126,7 +126,7 @@ import {
126
126
  } from "./chunk-XPBEJB27.js";
127
127
  import {
128
128
  ConfigManager
129
- } from "./chunk-5WBNXYDJ.js";
129
+ } from "./chunk-PMTEZTXP.js";
130
130
  import "./chunk-5ULLIOVC.js";
131
131
  import {
132
132
  AGENTIC_BEHAVIOR_GUIDELINE,
@@ -144,7 +144,7 @@ import {
144
144
  SKILLS_DIR_NAME,
145
145
  VERSION,
146
146
  buildUserIdentityPrompt
147
- } from "./chunk-QZYNRCPX.js";
147
+ } from "./chunk-QDN5G7JN.js";
148
148
  import {
149
149
  formatGitContextForPrompt,
150
150
  getGitContext,
@@ -2791,7 +2791,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2791
2791
  case "test": {
2792
2792
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2793
2793
  try {
2794
- const { executeTests } = await import("./run-tests-KLQAI5CX.js");
2794
+ const { executeTests } = await import("./run-tests-OYXK27XH.js");
2795
2795
  const argStr = args.join(" ").trim();
2796
2796
  let testArgs = {};
2797
2797
  if (argStr) {
@@ -3214,7 +3214,8 @@ Add .md files to create commands.` });
3214
3214
  messageCount: s.messageCount,
3215
3215
  updated: s.updated instanceof Date ? s.updated.toISOString() : String(s.updated),
3216
3216
  isCurrent: s.id === this.sessions.current?.id
3217
- }))
3217
+ })),
3218
+ totalCount: list.length
3218
3219
  });
3219
3220
  }
3220
3221
  sendToolsList() {
@@ -3856,18 +3857,22 @@ async function startWebServer(options = {}) {
3856
3857
  const authUser = req._authUser;
3857
3858
  const sm = authUser ? getUserShared(authUser).sessions : sessions;
3858
3859
  const list = sm.listSessions();
3860
+ const all = req.query.all === "true";
3861
+ const limit = all ? -1 : Number(req.query.limit) || 50;
3862
+ const sliced = limit > 0 ? list.slice(0, limit) : list;
3859
3863
  res.json({
3860
- sessions: list.slice(0, 50).map((s) => ({
3864
+ sessions: sliced.map((s) => ({
3861
3865
  id: s.id,
3862
3866
  title: s.title,
3863
3867
  provider: s.provider,
3864
3868
  model: s.model,
3865
3869
  messageCount: s.messageCount,
3866
3870
  updated: s.updated
3867
- }))
3871
+ })),
3872
+ totalCount: list.length
3868
3873
  });
3869
3874
  } catch {
3870
- res.json({ sessions: [] });
3875
+ res.json({ sessions: [], totalCount: 0 });
3871
3876
  }
3872
3877
  });
3873
3878
  app.get("/api/sessions/:id/replay", requireAuth, (req, res) => {