jinzd-ai-cli 0.4.219 → 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 (33) hide show
  1. package/README.md +101 -99
  2. package/README.zh-CN.md +40 -38
  3. package/dist/{batch-CHAPYRVM.js → batch-LSWWSINO.js} +3 -3
  4. package/dist/{chunk-GEN4O5QH.js → chunk-4AX2MEJA.js} +4 -4
  5. package/dist/{chunk-64NUW3WL.js → chunk-4MOBTDS6.js} +1 -1
  6. package/dist/{chunk-4ZN6L6K5.js → chunk-5BY4XICU.js} +6 -5
  7. package/dist/{chunk-MC34ISJU.js → chunk-5GTWZ4QF.js} +30 -6
  8. package/dist/{chunk-TZQHYZKT.js → chunk-5ULLIOVC.js} +1 -0
  9. package/dist/{chunk-3I4RZYYO.js → chunk-6SKMGONY.js} +6 -5
  10. package/dist/{chunk-QUFYBR6Q.js → chunk-BYQEWWA4.js} +2 -2
  11. package/dist/{chunk-VADHBW7T.js → chunk-OULVIHCT.js} +1 -1
  12. package/dist/{chunk-KIEZBTVD.js → chunk-PMTEZTXP.js} +2 -2
  13. package/dist/{chunk-QMXC327F.js → chunk-PUBCJF7E.js} +213 -175
  14. package/dist/{chunk-U7KVU25H.js → chunk-QDN5G7JN.js} +1 -1
  15. package/dist/{chunk-GGKQHPB3.js → chunk-QKJ3MAP7.js} +1 -1
  16. package/dist/{chunk-A4GUGWEY.js → chunk-QT5FA2ZY.js} +1 -1
  17. package/dist/{chunk-EGMORH5W.js → chunk-S5EEKS4P.js} +1 -1
  18. package/dist/{ci-QPP66T53.js → ci-OIFTAJKR.js} +6 -6
  19. package/dist/{ci-format-5S3EEYRK.js → ci-format-H6BLMWQ5.js} +2 -2
  20. package/dist/{constants-FJOLBABC.js → constants-EJRLT7DM.js} +1 -1
  21. package/dist/{doctor-cli-KB2LUDF3.js → doctor-cli-EXC7YU5D.js} +6 -6
  22. package/dist/electron-server.js +558 -466
  23. package/dist/{hub-DIM7SKKY.js → hub-HQDRQBNM.js} +2 -2
  24. package/dist/index.js +376 -45
  25. package/dist/{persist-L54DPLI7.js → persist-GTBEKVFL.js} +2 -2
  26. package/dist/{pr-YQGH72N6.js → pr-VSZNVKBS.js} +8 -7
  27. package/dist/{run-tests-O76JIBCW.js → run-tests-55JGEZYL.js} +1 -1
  28. package/dist/{run-tests-GEZSSNJM.js → run-tests-OYXK27XH.js} +2 -2
  29. package/dist/{server-5E2AIXVX.js → server-JKNIZ5CB.js} +54 -22
  30. package/dist/{server-Z6O3G2LY.js → server-XX6UM4ZQ.js} +5 -5
  31. package/dist/{task-orchestrator-5HBW4O64.js → task-orchestrator-F3DUIACA.js} +5 -5
  32. package/dist/{usage-BMK6M5U3.js → usage-4KFX3HTK.js} +3 -3
  33. package/package.json +1 -1
@@ -139,7 +139,7 @@ ${content}`);
139
139
  const state = await runDiscussion(config, providers, options.topic);
140
140
  if (options.save !== false && state.messages.length > 0) {
141
141
  try {
142
- const { persistDiscussion } = await import("./persist-L54DPLI7.js");
142
+ const { persistDiscussion } = await import("./persist-GTBEKVFL.js");
143
143
  const { path } = await persistDiscussion(state, configManager, defaultProvider, defaultModel);
144
144
  console.log(chalk.dim(`
145
145
  \u{1F4BE} Saved to history \u2014 open it in the Web UI and hit \u{1F3AC} to replay.
@@ -154,7 +154,7 @@ ${content}`);
154
154
  }
155
155
  }
156
156
  async function runTaskMode(config, providers, configManager, topic) {
157
- const { TaskOrchestrator } = await import("./task-orchestrator-5HBW4O64.js");
157
+ const { TaskOrchestrator } = await import("./task-orchestrator-F3DUIACA.js");
158
158
  const orchestrator = new TaskOrchestrator(config, providers, configManager);
159
159
  let interrupted = false;
160
160
  const onSigint = () => {
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  scanProject,
15
15
  sessionHasMeaningfulContent,
16
16
  setupProxy
17
- } from "./chunk-QUFYBR6Q.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-GEN4O5QH.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-64NUW3WL.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,10 +87,10 @@ import {
87
87
  formatDoctorReport,
88
88
  loadContextFiles,
89
89
  writeCrashLog
90
- } from "./chunk-3I4RZYYO.js";
90
+ } from "./chunk-6SKMGONY.js";
91
91
  import {
92
92
  ProviderRegistry
93
- } from "./chunk-QMXC327F.js";
93
+ } from "./chunk-PUBCJF7E.js";
94
94
  import {
95
95
  describePlugin,
96
96
  getActivePluginAssets,
@@ -107,7 +107,7 @@ import {
107
107
  trustHook,
108
108
  trustPlugin,
109
109
  untrustHook
110
- } from "./chunk-GGKQHPB3.js";
110
+ } from "./chunk-QKJ3MAP7.js";
111
111
  import {
112
112
  CONTENT_ONLY_STREAM_REMINDER,
113
113
  TEE_FINAL_USER_NUDGE,
@@ -129,12 +129,12 @@ import {
129
129
  } from "./chunk-XPBEJB27.js";
130
130
  import {
131
131
  ConfigManager
132
- } from "./chunk-KIEZBTVD.js";
132
+ } from "./chunk-PMTEZTXP.js";
133
133
  import {
134
134
  AuthError,
135
135
  ProviderError,
136
136
  RateLimitError
137
- } from "./chunk-TZQHYZKT.js";
137
+ } from "./chunk-5ULLIOVC.js";
138
138
  import {
139
139
  AGENTIC_BEHAVIOR_GUIDELINE,
140
140
  AUTHOR,
@@ -153,7 +153,7 @@ import {
153
153
  SKILLS_DIR_NAME,
154
154
  VERSION,
155
155
  buildUserIdentityPrompt
156
- } from "./chunk-U7KVU25H.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);
@@ -891,7 +1002,7 @@ function createDefaultCommands() {
891
1002
  title: "Chat & model",
892
1003
  rows: [
893
1004
  ["/provider <name>", "Switch AI provider"],
894
- ["/model <name>", "Switch model"],
1005
+ ["/model [name|refresh|cache]", "Switch model or refresh live model cache"],
895
1006
  ["/route [on|off|show|test <msg>]", "Smart model routing rules"],
896
1007
  ["/think [on|off|status]", "Toggle Extended Thinking (deep reasoning)"],
897
1008
  ["/plan [execute|exit|status]", "Enter/manage Plan Mode (read-only)"],
@@ -1059,13 +1170,46 @@ No commands match "${filter}".
1059
1170
  },
1060
1171
  {
1061
1172
  name: "model",
1062
- description: "Switch model (interactive selector when no arg)",
1063
- usage: "/model [name]",
1173
+ description: "Switch model, refresh live model list, or inspect model cache",
1174
+ usage: "/model [name|refresh [provider]|cache [clear [provider]]]",
1064
1175
  async execute(args, ctx) {
1176
+ const sub = (args[0] ?? "").toLowerCase();
1177
+ if (sub === "refresh") {
1178
+ const providerId = args[1] ?? ctx.getCurrentProvider();
1179
+ if (!ctx.providers.has(providerId)) {
1180
+ ctx.renderer.printInfo(`Provider is not configured: ${providerId}`);
1181
+ return;
1182
+ }
1183
+ ctx.renderer.printInfo(`Refreshing models for ${providerId}...`);
1184
+ try {
1185
+ const models = await ctx.providers.refreshModels(providerId);
1186
+ ctx.renderer.printSuccess(`Refreshed ${models.length} model(s) for ${providerId}.`);
1187
+ } catch (err) {
1188
+ ctx.renderer.printInfo(`Model refresh failed: ${err instanceof Error ? err.message : String(err)}`);
1189
+ }
1190
+ return;
1191
+ }
1192
+ if (sub === "cache") {
1193
+ if ((args[1] ?? "").toLowerCase() === "clear") {
1194
+ const providerId = args[2];
1195
+ ctx.providers.clearModelCache(providerId);
1196
+ ctx.renderer.printSuccess(providerId ? `Cleared model cache for ${providerId}.` : "Cleared model cache.");
1197
+ return;
1198
+ }
1199
+ const rows = ctx.providers.getModelCacheStatus();
1200
+ if (rows.length === 0) {
1201
+ ctx.renderer.printInfo("Model cache is empty. Use /model refresh to populate it.");
1202
+ return;
1203
+ }
1204
+ ctx.renderer.printTable(
1205
+ ["Provider", "Models", "Fetched"],
1206
+ rows.map((row) => [row.providerId, row.count, row.fetchedAt])
1207
+ );
1208
+ return;
1209
+ }
1065
1210
  let targetModel;
1066
1211
  if (!args[0]) {
1067
- const provider = ctx.providers.get(ctx.getCurrentProvider());
1068
- const models = await provider.listModels();
1212
+ const models = await ctx.providers.listModels(ctx.getCurrentProvider());
1069
1213
  const currentModel = ctx.getCurrentModel();
1070
1214
  const items = models.map((m) => ({
1071
1215
  value: m.id,
@@ -1073,7 +1217,9 @@ No commands match "${filter}".
1073
1217
  hint: [
1074
1218
  m.displayName,
1075
1219
  `ctx:${fmtCtx(m.contextWindow)}`,
1076
- m.supportsThinking ? "\u{1F9E0}" : "",
1220
+ m.supportsThinking ? "thinking" : "",
1221
+ m.metadataUnknown ? "metadata?" : "",
1222
+ m.source && m.source !== "curated" ? m.source : "",
1077
1223
  m.id === currentModel ? "(current)" : ""
1078
1224
  ].filter(Boolean).join(" ")
1079
1225
  }));
@@ -1187,7 +1333,7 @@ No commands match "${filter}".
1187
1333
  {
1188
1334
  name: "session",
1189
1335
  description: "Session management",
1190
- 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]",
1191
1337
  async execute(args, ctx) {
1192
1338
  const sub = args[0];
1193
1339
  if (sub === "new") {
@@ -1198,23 +1344,137 @@ No commands match "${filter}".
1198
1344
  ctx.clearDevState();
1199
1345
  ctx.renderer.printSuccess("New session started.");
1200
1346
  } else if (sub === "list") {
1201
- const sessions = ctx.sessions.listSessions();
1202
- 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) {
1203
1351
  ctx.renderer.printInfo("No saved sessions.");
1204
1352
  return;
1205
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
+ });
1206
1460
  console.log();
1207
- ctx.renderer.printTable(
1208
- ["ID (short)", "Provider", "Model", "Messages", "Date", "Title"],
1209
- sessions.map((s) => [
1210
- s.id.slice(0, 8),
1211
- s.provider,
1212
- s.model,
1213
- s.messageCount,
1214
- s.updated.toLocaleDateString(),
1215
- s.title ?? "(untitled)"
1216
- ])
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
+ )
1217
1474
  );
1475
+ if (shown < totalCount) {
1476
+ console.log(theme.dim(` Use --all to show all, or --limit N to change count.`));
1477
+ }
1218
1478
  } else if (sub === "load" || sub === "show") {
1219
1479
  const id = args[1];
1220
1480
  if (!id) {
@@ -1302,6 +1562,71 @@ No commands match "${filter}".
1302
1562
  ctx.clearDevState();
1303
1563
  ctx.renderer.printInfo("Current session was deleted \u2014 started a fresh session.");
1304
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
+ );
1305
1630
  } else {
1306
1631
  ctx.renderer.renderError("Usage: /session new|list|load <id>|show <id> [page]|page <n>|remove <id>");
1307
1632
  }
@@ -1761,7 +2086,7 @@ No tools match "${filter}".
1761
2086
  const { join: join5 } = await import("path");
1762
2087
  const { existsSync: existsSync5 } = await import("fs");
1763
2088
  const { getGitRoot: getGitRoot2 } = await import("./git-context-EXOEHQSF.js");
1764
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-FJOLBABC.js");
2089
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-EJRLT7DM.js");
1765
2090
  const { approveProject, hashMcpFile } = await import("./project-trust-NKYHL3VZ.js");
1766
2091
  const cwd = process.cwd();
1767
2092
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2820,7 +3145,7 @@ ${hint}` : "")
2820
3145
  usage: "/test [command|filter]",
2821
3146
  async execute(args, ctx) {
2822
3147
  try {
2823
- const { executeTests } = await import("./run-tests-GEZSSNJM.js");
3148
+ const { executeTests } = await import("./run-tests-OYXK27XH.js");
2824
3149
  const argStr = args.join(" ").trim();
2825
3150
  let testArgs = {};
2826
3151
  if (argStr) {
@@ -3786,6 +4111,7 @@ var PROVIDERS = [
3786
4111
  { value: "zhipu", name: "Zhipu (GLM)" },
3787
4112
  { value: "kimi", name: "Kimi (Moonshot AI)" },
3788
4113
  { value: "minimax", name: "MiniMax (\u6D77\u87BA)" },
4114
+ { value: "qwen", name: "Qwen (Alibaba Cloud)" },
3789
4115
  { value: "openai", name: "OpenAI" },
3790
4116
  { value: "openrouter", name: "OpenRouter" },
3791
4117
  { value: "ollama", name: "Ollama (Local)" }
@@ -7231,7 +7557,8 @@ program.command("providers").description("List all AI providers and their config
7231
7557
  baseUrl: config.get("customBaseUrls")[id],
7232
7558
  timeout: config.get("timeouts")[id]
7233
7559
  }),
7234
- config.get("customProviders")
7560
+ config.get("customProviders"),
7561
+ config.getConfigDir()
7235
7562
  );
7236
7563
  const all = registry.listAll();
7237
7564
  console.log("\nAvailable providers:\n");
@@ -7255,7 +7582,7 @@ program.command("web").description("Start Web UI server with browser-based chat
7255
7582
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
7256
7583
  process.exit(1);
7257
7584
  }
7258
- const { startWebServer } = await import("./server-5E2AIXVX.js");
7585
+ const { startWebServer } = await import("./server-JKNIZ5CB.js");
7259
7586
  await startWebServer({ port, host: options.host });
7260
7587
  });
7261
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) => {
@@ -7422,11 +7749,11 @@ program.command("sessions").description("List recent conversation sessions").opt
7422
7749
  console.log(footer + "\n");
7423
7750
  });
7424
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) => {
7425
- const { runUsageCli } = await import("./usage-BMK6M5U3.js");
7752
+ const { runUsageCli } = await import("./usage-4KFX3HTK.js");
7426
7753
  await runUsageCli(options);
7427
7754
  });
7428
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) => {
7429
- const { runDoctorCli } = await import("./doctor-cli-KB2LUDF3.js");
7756
+ const { runDoctorCli } = await import("./doctor-cli-EXC7YU5D.js");
7430
7757
  const argv = process.argv.slice(2);
7431
7758
  await runDoctorCli({
7432
7759
  json: !!options.json || argv.includes("--json"),
@@ -7435,7 +7762,7 @@ program.command("doctor").description("Health check: API keys, config, MCP, rece
7435
7762
  });
7436
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) => {
7437
7764
  try {
7438
- const batch = await import("./batch-CHAPYRVM.js");
7765
+ const batch = await import("./batch-LSWWSINO.js");
7439
7766
  switch (action) {
7440
7767
  case "submit":
7441
7768
  if (!arg) {
@@ -7478,7 +7805,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7478
7805
  }
7479
7806
  });
7480
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) => {
7481
- const { startMcpServer } = await import("./server-Z6O3G2LY.js");
7808
+ const { startMcpServer } = await import("./server-XX6UM4ZQ.js");
7482
7809
  await startMcpServer({
7483
7810
  allowDestructive: !!options.allowDestructive,
7484
7811
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7491,7 +7818,7 @@ program.command("pr <action> [target]").description("PR review helpers: review |
7491
7818
  console.error(`Unknown pr action: ${action}. Use review | security-review | summarize.`);
7492
7819
  process.exit(1);
7493
7820
  }
7494
- const { runPrCli } = await import("./pr-YQGH72N6.js");
7821
+ const { runPrCli } = await import("./pr-VSZNVKBS.js");
7495
7822
  const result = await runPrCli({
7496
7823
  action,
7497
7824
  target,
@@ -7511,8 +7838,8 @@ program.command("pr <action> [target]").description("PR review helpers: review |
7511
7838
  process.exit(result.exitCode);
7512
7839
  });
7513
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) => {
7514
- const { runCi } = await import("./ci-QPP66T53.js");
7515
- const { formatCiResult } = await import("./ci-format-5S3EEYRK.js");
7841
+ const { runCi } = await import("./ci-OIFTAJKR.js");
7842
+ const { formatCiResult } = await import("./ci-format-H6BLMWQ5.js");
7516
7843
  const formats = [options.json, options.markdown, options.sarif].filter(Boolean).length;
7517
7844
  if (formats > 1) {
7518
7845
  console.error("Choose only one output format: --json, --markdown, or --sarif.");
@@ -7667,9 +7994,10 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7667
7994
  baseUrl: config.get("customBaseUrls")[id],
7668
7995
  timeout: config.get("timeouts")[id]
7669
7996
  }),
7670
- config.get("customProviders")
7997
+ config.get("customProviders"),
7998
+ config.getConfigDir()
7671
7999
  );
7672
- const { startHub } = await import("./hub-DIM7SKKY.js");
8000
+ const { startHub } = await import("./hub-HQDRQBNM.js");
7673
8001
  await startHub(
7674
8002
  {
7675
8003
  topic: topic ?? "",
@@ -7702,7 +8030,8 @@ program.command("join").description("Join a running hub as a remote AI agent").o
7702
8030
  baseUrl: config.get("customBaseUrls")[id],
7703
8031
  timeout: config.get("timeouts")[id]
7704
8032
  }),
7705
- config.get("customProviders")
8033
+ config.get("customProviders"),
8034
+ config.getConfigDir()
7706
8035
  );
7707
8036
  const { joinHub } = await import("./agent-client-6GX6QQDU.js");
7708
8037
  await joinHub(
@@ -7743,7 +8072,8 @@ async function runHeadless(options) {
7743
8072
  timeout: config.get("timeouts")[id],
7744
8073
  proxy: config.get("proxy")
7745
8074
  }),
7746
- config.get("customProviders")
8075
+ config.get("customProviders"),
8076
+ config.getConfigDir()
7747
8077
  );
7748
8078
  const providerId = options.provider ?? config.getDefaultProvider();
7749
8079
  if (!providers.has(providerId)) {
@@ -7902,7 +8232,8 @@ async function startRepl(options) {
7902
8232
  timeout: config.get("timeouts")[id],
7903
8233
  proxy: config.get("proxy")
7904
8234
  }),
7905
- config.get("customProviders")
8235
+ config.get("customProviders"),
8236
+ config.getConfigDir()
7906
8237
  );
7907
8238
  if (providers.listAvailable().length === 0) {
7908
8239
  console.error(
@@ -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 {