jinzd-ai-cli 0.1.92 → 0.1.93

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.
@@ -8,7 +8,7 @@ import { platform } from "os";
8
8
  import chalk from "chalk";
9
9
 
10
10
  // src/core/constants.ts
11
- var VERSION = "0.1.92";
11
+ var VERSION = "0.1.93";
12
12
  var APP_NAME = "ai-cli";
13
13
  var CONFIG_DIR_NAME = ".aicli";
14
14
  var CONFIG_FILE_NAME = "config.json";
@@ -16,7 +16,7 @@ import {
16
16
  SUBAGENT_MAX_ROUNDS_LIMIT,
17
17
  VERSION,
18
18
  runTestsTool
19
- } from "./chunk-W7ZOYZFR.js";
19
+ } from "./chunk-BZVXXYAB.js";
20
20
 
21
21
  // src/config/config-manager.ts
22
22
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  theme,
36
36
  truncateOutput,
37
37
  undoStack
38
- } from "./chunk-OZ2VVEBE.js";
38
+ } from "./chunk-HLMYGNCU.js";
39
39
  import {
40
40
  AGENTIC_BEHAVIOR_GUIDELINE,
41
41
  AUTHOR,
@@ -55,7 +55,7 @@ import {
55
55
  REPO_URL,
56
56
  SKILLS_DIR_NAME,
57
57
  VERSION
58
- } from "./chunk-W7ZOYZFR.js";
58
+ } from "./chunk-BZVXXYAB.js";
59
59
 
60
60
  // src/index.ts
61
61
  import { program } from "commander";
@@ -1904,7 +1904,7 @@ ${hint}` : "")
1904
1904
  description: "Run project tests and show structured report",
1905
1905
  usage: "/test [command|filter]",
1906
1906
  async execute(args, _ctx) {
1907
- const { executeTests } = await import("./run-tests-XWNY6FYC.js");
1907
+ const { executeTests } = await import("./run-tests-X5ZA6FTL.js");
1908
1908
  const argStr = args.join(" ").trim();
1909
1909
  let testArgs = {};
1910
1910
  if (argStr) {
@@ -5292,7 +5292,7 @@ program.command("web").description("Start Web UI server with browser-based chat
5292
5292
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
5293
5293
  process.exit(1);
5294
5294
  }
5295
- const { startWebServer } = await import("./server-Y67I66FG.js");
5295
+ const { startWebServer } = await import("./server-G4DGAXKU.js");
5296
5296
  await startWebServer({ port, host: options.host });
5297
5297
  });
5298
5298
  program.command("sessions").description("List recent conversation sessions").action(async () => {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-W7ZOYZFR.js";
5
+ } from "./chunk-BZVXXYAB.js";
6
6
  export {
7
7
  executeTests,
8
8
  runTestsTool
@@ -23,7 +23,7 @@ import {
23
23
  setupProxy,
24
24
  spawnAgentContext,
25
25
  truncateOutput
26
- } from "./chunk-OZ2VVEBE.js";
26
+ } from "./chunk-HLMYGNCU.js";
27
27
  import {
28
28
  AGENTIC_BEHAVIOR_GUIDELINE,
29
29
  CONTEXT_FILE_CANDIDATES,
@@ -35,7 +35,7 @@ import {
35
35
  PLAN_MODE_SYSTEM_ADDON,
36
36
  SKILLS_DIR_NAME,
37
37
  VERSION
38
- } from "./chunk-W7ZOYZFR.js";
38
+ } from "./chunk-BZVXXYAB.js";
39
39
 
40
40
  // src/web/server.ts
41
41
  import express from "express";
@@ -448,7 +448,6 @@ var SessionHandler = class {
448
448
  this.currentModel = "";
449
449
  }
450
450
  }
451
- this.sessions.createSession(this.currentProvider, this.currentModel);
452
451
  this.activeSystemPrompt = this.loadContextFiles();
453
452
  const hooks = this.config.get("hooks");
454
453
  const permissionRules = this.config.get("permissionRules");
@@ -525,7 +524,15 @@ var SessionHandler = class {
525
524
  if (this.abortController) this.abortController.abort();
526
525
  for (const resolve3 of this.pendingAskUser.values()) resolve3(null);
527
526
  this.pendingAskUser.clear();
528
- this.sessions.save();
527
+ if (this.sessions.current && this.sessions.current.messages.length > 0) {
528
+ this.sessions.save();
529
+ }
530
+ }
531
+ /** Lazily create a session if none exists yet (deferred from constructor). */
532
+ ensureSession() {
533
+ if (!this.sessions.current) {
534
+ this.sessions.createSession(this.currentProvider, this.currentModel);
535
+ }
529
536
  }
530
537
  // ── Chat handling ────────────────────────────────────────────────
531
538
  async handleChat(content, images) {
@@ -535,6 +542,7 @@ var SessionHandler = class {
535
542
  }
536
543
  this.processing = true;
537
544
  try {
545
+ this.ensureSession();
538
546
  const session = this.sessions.current;
539
547
  let msgContent;
540
548
  if (images && images.length > 0) {
@@ -947,6 +955,23 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
947
955
  } else {
948
956
  this.send({ type: "error", message: `Session not found: ${targetId}` });
949
957
  }
958
+ } else if (sub === "batch-delete") {
959
+ const ids = args.slice(1).filter(Boolean);
960
+ if (ids.length === 0) {
961
+ this.send({ type: "error", message: "No session IDs provided." });
962
+ break;
963
+ }
964
+ const list = this.sessions.listSessions();
965
+ let deleted = 0;
966
+ for (const targetId of ids) {
967
+ const found = list.find((s) => s.id.startsWith(targetId));
968
+ if (found) {
969
+ this.sessions.deleteSession(found.id);
970
+ deleted++;
971
+ }
972
+ }
973
+ this.send({ type: "info", message: `Deleted ${deleted} session(s).` });
974
+ this.sendSessionList();
950
975
  } else {
951
976
  this.send({ type: "info", message: "Usage: /session new | list | load <id> | delete <id>" });
952
977
  }
@@ -741,6 +741,9 @@ function renderSessionList(sessions) {
741
741
  renderFilteredSessions(sessionSearchInput?.value || '');
742
742
  }
743
743
 
744
+ let batchSelectMode = false;
745
+ const batchSelectedIds = new Set();
746
+
744
747
  function renderFilteredSessions(filter) {
745
748
  const sessions = filter
746
749
  ? cachedSessions.filter(s => (s.title || '').toLowerCase().includes(filter.toLowerCase()))
@@ -753,37 +756,91 @@ function renderFilteredSessions(filter) {
753
756
  const title = s.title || 'Untitled';
754
757
  const date = new Date(s.updated);
755
758
  const timeStr = date.toLocaleDateString() + ' ' + date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
759
+ const checked = batchSelectedIds.has(s.id) ? 'checked' : '';
760
+ const checkbox = batchSelectMode
761
+ ? `<input type="checkbox" class="checkbox checkbox-xs checkbox-error session-batch-cb" data-sid="${s.id}" ${checked}>`
762
+ : '';
756
763
  return `<div class="session-item ${s.isCurrent ? 'active' : ''}" data-session-id="${s.id}" title="${escapeHtml(title)}">
757
764
  <div class="flex items-center gap-1">
765
+ ${checkbox}
758
766
  <div class="session-title flex-1">${escapeHtml(title)}</div>
759
- <button class="session-delete-btn opacity-0 hover:opacity-100 text-error text-xs px-1 flex-shrink-0" data-delete-id="${s.id}" title="Delete session">&times;</button>
767
+ ${batchSelectMode ? '' : `<button class="session-delete-btn opacity-0 hover:opacity-100 text-error text-xs px-1 flex-shrink-0" data-delete-id="${s.id}" title="Delete session">&times;</button>`}
760
768
  </div>
761
769
  <div class="session-meta">${s.messageCount} msgs · ${timeStr}</div>
762
770
  </div>`;
763
771
  }).join('');
764
772
 
765
- // Click to load session
773
+ // Click to load session (only in normal mode)
766
774
  sessionListEl.querySelectorAll('.session-item').forEach(el => {
767
775
  el.addEventListener('click', (e) => {
768
- // Don't load if clicking the delete button
769
- if (e.target.closest('.session-delete-btn')) return;
776
+ if (e.target.closest('.session-delete-btn') || e.target.closest('.session-batch-cb')) return;
777
+ if (batchSelectMode) {
778
+ // In batch mode, clicking the row toggles the checkbox
779
+ const cb = el.querySelector('.session-batch-cb');
780
+ if (cb) { cb.checked = !cb.checked; cb.dispatchEvent(new Event('change')); }
781
+ return;
782
+ }
770
783
  const id = el.dataset.sessionId;
771
784
  if (!id) return;
772
785
  send({ type: 'command', name: 'session', args: ['load', id] });
773
786
  });
774
787
  });
775
788
 
776
- // Click to delete session
777
- sessionListEl.querySelectorAll('.session-delete-btn').forEach(btn => {
778
- btn.addEventListener('click', (e) => {
779
- e.stopPropagation();
780
- const id = btn.dataset.deleteId;
781
- if (!id) return;
782
- if (confirm('Delete this session?')) {
783
- send({ type: 'command', name: 'session', args: ['delete', id] });
784
- }
789
+ // Checkbox change in batch mode
790
+ sessionListEl.querySelectorAll('.session-batch-cb').forEach(cb => {
791
+ cb.addEventListener('change', () => {
792
+ const sid = cb.dataset.sid;
793
+ if (cb.checked) batchSelectedIds.add(sid); else batchSelectedIds.delete(sid);
794
+ updateBatchBar();
785
795
  });
786
796
  });
797
+
798
+ // Click to delete session (normal mode only)
799
+ if (!batchSelectMode) {
800
+ sessionListEl.querySelectorAll('.session-delete-btn').forEach(btn => {
801
+ btn.addEventListener('click', (e) => {
802
+ e.stopPropagation();
803
+ const id = btn.dataset.deleteId;
804
+ if (!id) return;
805
+ if (confirm('Delete this session?')) {
806
+ send({ type: 'command', name: 'session', args: ['delete', id] });
807
+ }
808
+ });
809
+ });
810
+ }
811
+ }
812
+
813
+ function toggleBatchSelect() {
814
+ batchSelectMode = !batchSelectMode;
815
+ batchSelectedIds.clear();
816
+ updateBatchBar();
817
+ renderFilteredSessions(sessionSearchInput?.value || '');
818
+ }
819
+
820
+ function selectAllSessions() {
821
+ cachedSessions.forEach(s => batchSelectedIds.add(s.id));
822
+ renderFilteredSessions(sessionSearchInput?.value || '');
823
+ updateBatchBar();
824
+ }
825
+
826
+ function batchDeleteSelected() {
827
+ if (batchSelectedIds.size === 0) return;
828
+ if (!confirm(`Delete ${batchSelectedIds.size} session(s)?`)) return;
829
+ send({ type: 'command', name: 'session', args: ['batch-delete', ...batchSelectedIds] });
830
+ batchSelectMode = false;
831
+ batchSelectedIds.clear();
832
+ updateBatchBar();
833
+ }
834
+
835
+ function updateBatchBar() {
836
+ const bar = document.getElementById('batch-bar');
837
+ if (!bar) return;
838
+ if (batchSelectMode) {
839
+ bar.classList.remove('hidden');
840
+ bar.querySelector('.batch-count').textContent = `${batchSelectedIds.size} selected`;
841
+ } else {
842
+ bar.classList.add('hidden');
843
+ }
787
844
  }
788
845
 
789
846
  function renderSessionMessages(messages) {
@@ -58,10 +58,19 @@
58
58
  </div>
59
59
  <!-- Sessions tab -->
60
60
  <div id="tab-sessions" class="sidebar-tab-content flex flex-col flex-1 overflow-hidden">
61
- <div class="p-2 border-b border-base-content/10 flex items-center justify-between">
62
- <input id="session-search" type="text" class="input input-xs input-bordered flex-1 mr-2" placeholder="Search sessions...">
61
+ <div class="p-2 border-b border-base-content/10 flex items-center justify-between gap-1">
62
+ <input id="session-search" type="text" class="input input-xs input-bordered flex-1" placeholder="Search sessions...">
63
+ <button id="btn-batch-select" class="btn btn-xs btn-ghost flex-shrink-0" title="Select multiple" onclick="toggleBatchSelect()">☑</button>
63
64
  <button id="btn-new-session" class="btn btn-xs btn-primary btn-outline flex-shrink-0" title="New session">+ New</button>
64
65
  </div>
66
+ <!-- Batch action bar (hidden by default) -->
67
+ <div id="batch-bar" class="hidden px-2 py-1 border-b border-base-content/10 flex items-center gap-1 bg-base-300 text-xs">
68
+ <span class="batch-count opacity-60">0 selected</span>
69
+ <span class="flex-1"></span>
70
+ <button class="btn btn-xs btn-ghost" onclick="selectAllSessions()">All</button>
71
+ <button class="btn btn-xs btn-error btn-outline" onclick="batchDeleteSelected()">🗑 Delete</button>
72
+ <button class="btn btn-xs btn-ghost" onclick="toggleBatchSelect()">Cancel</button>
73
+ </div>
65
74
  <div id="session-list" class="flex-1 overflow-y-auto p-2 flex flex-col gap-1 text-sm">
66
75
  <div class="text-xs opacity-40 text-center py-4">No sessions yet</div>
67
76
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jinzd-ai-cli",
3
- "version": "0.1.92",
3
+ "version": "0.1.93",
4
4
  "description": "Cross-platform REPL-style AI CLI with multi-provider support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",