claude-discover 1.9.5 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/out/main/main.js CHANGED
@@ -6,7 +6,6 @@ import fs from "node:fs";
6
6
  import { sync } from "write-file-atomic";
7
7
  import fsp from "node:fs/promises";
8
8
  import crypto from "node:crypto";
9
- import chokidar from "chokidar";
10
9
  import which from "which";
11
10
  import { execFile } from "node:child_process";
12
11
  import { promisify } from "node:util";
@@ -564,10 +563,10 @@ class RequestParser {
564
563
  for (const t of fresh) this.seenTools.add(t.name);
565
564
  const schema = (t) => t.input_schema ? "```json\n" + JSON.stringify(t.input_schema, null, 2) + "\n```" : "";
566
565
  const strip = (file_path, pick) => {
567
- const list = fresh.filter((t) => pick(t.name));
566
+ const list = fresh.filter((t) => pick(t.name)).map((t) => ({ name: t.name, content: [t.description || "", schema(t)].filter(Boolean).join("\n\n") }));
568
567
  if (!list.length) return null;
569
- const content = list.map((t) => [`## ${t.name}`, t.description || "", schema(t)].filter(Boolean).join("\n\n")).join("\n\n");
570
- return toStrip(rec, "tools", file_path, `${list.length} tool${list.length === 1 ? "" : "s"}`, content);
568
+ const content = list.map((t) => [`## ${t.name}`, t.content].filter(Boolean).join("\n\n")).join("\n\n");
569
+ return { ...toStrip(rec, "tools", file_path, `${list.length} tool${list.length === 1 ? "" : "s"}`, content), tools: list };
571
570
  };
572
571
  return [strip("System Tools", (n) => !isMcp(n)), strip("MCP Tools", isMcp)].filter(Boolean);
573
572
  }
@@ -710,6 +709,7 @@ function freshMeta({ sessionId, parentSessionId, filePath, parentFilePath, fileS
710
709
  aiTitle: null,
711
710
  customTitle: null,
712
711
  agentName: null,
712
+ tag: null,
713
713
  firstUserPrompt: null,
714
714
  firstUserCommand: null,
715
715
  forkedFrom: null,
@@ -747,6 +747,7 @@ class SessionParser {
747
747
  if (t === "ai-title" && obj.aiTitle) meta.aiTitle = obj.aiTitle;
748
748
  if (t === "custom-title" && obj.customTitle) meta.customTitle = obj.customTitle;
749
749
  if (t === "agent-name" && obj.agentName) meta.agentName = obj.agentName;
750
+ if (t === "tag") meta.tag = obj.tag || null;
750
751
  if (t === "queue-operation" && obj.content?.includes("<scheduled-task")) meta.hasScheduledTask = true;
751
752
  if (t === "started" && obj.key && obj.agentId) meta.workflowAgents += 1;
752
753
  if (obj.cwd && !meta.project) meta.project = obj.cwd;
@@ -943,25 +944,130 @@ class StatCache {
943
944
  onProgress?.({ done: 1, total: 1, scanning: false });
944
945
  }
945
946
  }
947
+ async function listFiles(dir, filter) {
948
+ const entries = await fsp.readdir(dir, { recursive: true, withFileTypes: true });
949
+ return entries.filter((e) => e.isFile() && filter(e.name)).map((e) => path.join(e.parentPath, e.name));
950
+ }
951
+ class FileWatcher {
952
+ #root;
953
+ #opts;
954
+ #watcher = null;
955
+ #closed = false;
956
+ constructor(root, opts) {
957
+ this.#root = root;
958
+ this.#opts = opts;
959
+ try {
960
+ this.#watcher = new EventFileWatcher(root, { ...opts, onFail: (err) => this.#fallBack(err) });
961
+ } catch (err) {
962
+ this.#fallBack(err);
963
+ }
964
+ }
965
+ get mode() {
966
+ return this.#watcher instanceof IntervalFileWatcher ? "interval" : "event";
967
+ }
968
+ // Its first diff against the baseline also catches whatever changed while events were down
969
+ #fallBack(err) {
970
+ if (this.#closed) return;
971
+ console.warn("[watch] checking on an interval instead:", err.message);
972
+ this.#watcher = new IntervalFileWatcher(this.#root, this.#opts);
973
+ }
974
+ close() {
975
+ this.#closed = true;
976
+ this.#watcher?.close();
977
+ }
978
+ }
979
+ class EventFileWatcher {
980
+ #opts;
981
+ #handle = null;
982
+ #timers = /* @__PURE__ */ new Map();
983
+ // filePath -> pending settle timer
984
+ #closed = false;
985
+ // Throws if the OS refuses the watch, e.g. on \\wsl.localhost\...
986
+ constructor(root, opts) {
987
+ this.#opts = { settleMs: 100, ...opts };
988
+ this.#handle = fs.watch(root, { recursive: true }, (_type, name) => {
989
+ if (!name) return this.#opts.onDrop();
990
+ if (this.#opts.filter(name)) this.#settle(path.join(root, name));
991
+ });
992
+ this.#handle.on("error", (err) => {
993
+ this.close();
994
+ this.#opts.onFail(err);
995
+ });
996
+ }
997
+ // Events carry no stat or kind, so stat each path once its writes settle
998
+ #settle(filePath) {
999
+ const { onChange, onUnlink, settleMs } = this.#opts;
1000
+ clearTimeout(this.#timers.get(filePath));
1001
+ this.#timers.set(filePath, setTimeout(async () => {
1002
+ this.#timers.delete(filePath);
1003
+ const stat = await fsp.stat(filePath).catch(() => null);
1004
+ if (this.#closed) return;
1005
+ if (!stat) onUnlink(filePath);
1006
+ else if (stat.isFile()) onChange(filePath, stat);
1007
+ }, settleMs));
1008
+ }
1009
+ close() {
1010
+ this.#closed = true;
1011
+ for (const timer of this.#timers.values()) {
1012
+ clearTimeout(timer);
1013
+ }
1014
+ this.#timers.clear();
1015
+ this.#handle?.close();
1016
+ }
1017
+ }
1018
+ class IntervalFileWatcher {
1019
+ #root;
1020
+ #opts;
1021
+ #timer = null;
1022
+ #checks = 0;
1023
+ #closed = false;
1024
+ // Stats on a share are the bulk of a check, so each one stats only new files and those written within hotMs;
1025
+ // every sweepEvery-th check stats them all, catching an idle transcript that grows again (e.g. a resumed session)
1026
+ constructor(root, opts) {
1027
+ this.#root = root;
1028
+ this.#opts = { intervalMs: 2e3, hotMs: 10 * 6e4, sweepEvery: 30, ...opts };
1029
+ this.#schedule();
1030
+ }
1031
+ // Timed from the end of the last check, so a slow share can't pile them up
1032
+ #schedule() {
1033
+ this.#timer = setTimeout(() => this.#check().finally(() => this.#closed || this.#schedule()), this.#opts.intervalMs);
1034
+ }
1035
+ async #check() {
1036
+ const { filter, onChange, onUnlink, baseline } = this.#opts;
1037
+ const known = baseline();
1038
+ if (!known) return;
1039
+ const files = await listFiles(this.#root, filter).catch(() => null);
1040
+ if (!files) return;
1041
+ const sweep = ++this.#checks % this.#opts.sweepEvery === 0;
1042
+ const now = Date.now();
1043
+ await Promise.all(files.map(async (fp) => {
1044
+ const old = known.get(fp);
1045
+ if (!sweep && old && now - old.mtimeMs >= this.#opts.hotMs) return;
1046
+ const stat = await fsp.stat(fp).catch(() => null);
1047
+ if (stat && !this.#closed && (!old || old.size !== stat.size || old.mtimeMs !== stat.mtimeMs)) onChange(fp, stat);
1048
+ }));
1049
+ if (this.#closed) return;
1050
+ const listed = new Set(files);
1051
+ for (const fp of known.keys()) {
1052
+ if (!listed.has(fp)) onUnlink(fp);
1053
+ }
1054
+ }
1055
+ close() {
1056
+ this.#closed = true;
1057
+ clearTimeout(this.#timer);
1058
+ }
1059
+ }
946
1060
  const isJsonl = (p) => p.endsWith(".jsonl");
947
1061
  class SessionsScanner {
948
1062
  constructor({ root = CLAUDE_PROJECTS_DIR } = {}) {
949
1063
  this.root = root;
950
1064
  this.watcher = null;
951
- this.watchWanted = false;
952
- this.walkDone = Promise.withResolvers();
953
1065
  this.statCache = new StatCache();
954
1066
  }
955
1067
  async _listDirs(p) {
956
1068
  const entries = await fsp.readdir(p, { withFileTypes: true }).catch(() => []);
957
1069
  return entries.filter((e) => e.isDirectory()).map((e) => path.join(e.parentPath, e.name));
958
1070
  }
959
- // Every .jsonl under `dir`, at any depth — subagent transcripts nest arbitrarily deep. Subtrees
960
- // can't be pruned by directory mtime: on Windows, appending to a transcript doesn't bump its parent dir.
961
- async _listJsonl(dir) {
962
- const entries = await fsp.readdir(dir, { recursive: true, withFileTypes: true }).catch(() => []);
963
- return entries.filter((e) => e.isFile() && isJsonl(e.name)).map((e) => path.join(e.parentPath, e.name));
964
- }
965
1071
  // Append-only transcripts span [birthtime, mtime], so files outside the period are skipped
966
1072
  // Note birthtime is trusted only when well older than mtime, since copies/restores (git checkout, scp, zip) get a fresh birthtime over old content.
967
1073
  _inPeriod(stat, day) {
@@ -973,14 +1079,10 @@ class SessionsScanner {
973
1079
  // `signal` (a superseded scan) stops everything; once complete with the watcher live, the
974
1080
  // StatCache mirrors disk and serves later scans in memory.
975
1081
  async scan(day, opts = {}) {
976
- if (this.statCache.complete && this.watcher) {
1082
+ if (this.statCache.complete) {
977
1083
  return this.statCache.scan((stat) => this._inPeriod(stat, day), opts);
978
1084
  }
979
- try {
980
- await this._walk(day, opts);
981
- } finally {
982
- this.walkDone.resolve();
983
- }
1085
+ return this._walk(day, opts);
984
1086
  }
985
1087
  async _walk(day, { onFile, onBatchDone, onProgress, signal } = {}) {
986
1088
  const projects = await this._listDirs(this.root);
@@ -989,7 +1091,8 @@ class SessionsScanner {
989
1091
  progress();
990
1092
  await Promise.all(projects.map(async (projDir) => {
991
1093
  if (signal?.aborted) return;
992
- const results = await Promise.all((await this._listJsonl(projDir)).map(async (fp) => {
1094
+ const files = await listFiles(projDir, isJsonl).catch(() => []);
1095
+ const results = await Promise.all(files.map(async (fp) => {
993
1096
  const stat = signal?.aborted ? null : await fsp.stat(fp).catch(() => null);
994
1097
  this.statCache.record(fp, stat);
995
1098
  return stat && this._inPeriod(stat, day) && !signal?.aborted ? onFile?.(fp, stat) : null;
@@ -1004,38 +1107,28 @@ class SessionsScanner {
1004
1107
  if (onBatchDone) await onBatchDone();
1005
1108
  progress();
1006
1109
  }
1007
- // Starts chokidar once a scan has finished, and resolves when it's live. Its setup walks the whole
1008
- // projects dir to register watchers, and on a cold disk those stats are the bulk of startup, so it
1009
- // waits instead of racing our walk. The gap is self-healing: `ignoreInitial` skips files present at
1010
- // startup anyway, and a session still being written emits on its next append.
1011
- async watch({ onChange, onUnlink } = {}) {
1012
- this.watchWanted = true;
1013
- await this.walkDone.promise;
1014
- if (!this.watchWanted) return;
1015
- const isUNC = this.root.startsWith("\\\\") || this.root.startsWith("//");
1016
- this.watcher = chokidar.watch(this.root, {
1017
- ignoreInitial: true,
1018
- alwaysStat: true,
1019
- awaitWriteFinish: { stabilityThreshold: 400, pollInterval: 100 },
1020
- ignored: (p, stats) => !!stats?.isFile() && !isJsonl(p),
1021
- ...isUNC && { usePolling: true, interval: 1e3 }
1110
+ // Starts before the first walk, so that walk already completes the StatCache
1111
+ watch({ onChange, onUnlink }) {
1112
+ try {
1113
+ fs.mkdirSync(this.root, { recursive: true });
1114
+ } catch {
1115
+ }
1116
+ this.watcher = new FileWatcher(this.root, {
1117
+ filter: isJsonl,
1118
+ onChange: (p, stat) => {
1119
+ this.statCache.record(p, stat);
1120
+ onChange(p, stat);
1121
+ },
1122
+ onUnlink: (p) => {
1123
+ this.statCache.remove(p);
1124
+ onUnlink(p);
1125
+ },
1126
+ onDrop: () => this.statCache.clear(),
1127
+ baseline: () => this.statCache.complete ? this.statCache.stats : null
1128
+ // null until a full walk exists to diff against
1022
1129
  });
1023
- const changed = (p, stat) => {
1024
- if (!isJsonl(p)) return;
1025
- this.statCache.record(p, stat);
1026
- onChange?.(p, stat);
1027
- };
1028
- const removed = (p) => {
1029
- if (!isJsonl(p)) return;
1030
- this.statCache.remove(p);
1031
- onUnlink?.(p);
1032
- };
1033
- this.watcher.on("add", changed);
1034
- this.watcher.on("change", changed);
1035
- this.watcher.on("unlink", removed);
1036
1130
  }
1037
1131
  stop() {
1038
- this.watchWanted = false;
1039
1132
  if (this.watcher) {
1040
1133
  this.watcher.close();
1041
1134
  this.watcher = null;
@@ -627,6 +627,8 @@
627
627
 
628
628
  .worktree-tag { color: #34d399; }
629
629
 
630
+ .session-tag { color: #4ade80; }
631
+
630
632
  .fork-tag {
631
633
  color: var(--accent);
632
634
  font-weight: bold;
@@ -1076,6 +1078,10 @@ button.msg-summary:hover { color: var(--text); background: var(--bg-elev-2); bor
1076
1078
  .instruction-source:first-child { padding-top: 0; }
1077
1079
 
1078
1080
  /* Full-width toggle so the estimated token count can sit right-aligned, like msg-header-right. */
1081
+ /* Nested tool rows cancel the aux-body's right padding so their counts line up with the strips'. */
1082
+ .instruction-file .instruction-file { border-left-color: var(--border); margin-right: -8px; }
1083
+ /* A rule under the strip's count marks it as the sum of the tool counts below. */
1084
+ .aux-body > .instruction-file:first-child .instruction-tokens { border-top: 1px solid var(--border); padding-top: 2px; }
1079
1085
  .instruction-file .aux-toggle { display: flex; width: 100%; align-items: center; }
1080
1086
  .instruction-tokens { margin-left: auto; color: var(--text-faint); }
1081
1087
 
@@ -1,4 +1,4 @@
1
- import { r as reactExports, j as jsxRuntimeExports, f as format, s as startOfWeek, a as startOfMonth, b as startOfDay, e as endOfWeek, c as endOfMonth, d as endOfDay, g as addWeeks, h as addMonths, i as addDays, k as isSameWeek, l as isSameMonth, m as isSameDay, n as intervalToDuration, o as getDaysInMonth, p as addHours, q as encode, H as Hourglass, G as Gauge, M as MessageSquare, C as Clock, Z as Zap, D as DollarSign, R as React, t as Markdown$1, u as rehypeHighlight, v as remarkGfm, T as Terminal, J as JsonView, w as vscodeTheme, x as tippy, y as reactDomExports, z as an, A as qt, Q as Qt, B as nn, E as parse, F as delegate, I as clientExports } from "./vendors-JM3FOKMR.js";
1
+ import { r as reactExports, j as jsxRuntimeExports, f as format, s as startOfWeek, a as startOfMonth, b as startOfDay, e as endOfWeek, c as endOfMonth, d as endOfDay, g as addWeeks, h as addMonths, i as addDays, k as isSameWeek, l as isSameMonth, m as isSameDay, n as intervalToDuration, o as getDaysInMonth, p as addHours, q as encode, H as Hourglass, G as Gauge, M as MessageSquare, C as Clock, Z as Zap, D as DollarSign, R as React, t as Markdown$1, u as rehypeHighlight, v as remarkGfm, w as defaultUrlTransform, T as Terminal, J as JsonView, x as vscodeTheme, y as tippy, z as reactDomExports, A as an, B as qt, Q as Qt, E as nn, F as parse, I as delegate, K as clientExports } from "./vendors-D2DMOduT.js";
2
2
  const SOURCE_COLORS = {
3
3
  scheduled: "#14b8a6",
4
4
  cli: "#3b82f6",
@@ -34,13 +34,13 @@ function useLocalStorage(key, initial) {
34
34
  }
35
35
  function clearOutdatedLocalStorage() {
36
36
  const version = localStorage.getItem(VERSION_KEY);
37
- if (version !== "1.9.5") {
38
- const [MAJOR, MINOR, PATCH] = "1.9.5".split(".");
37
+ if (version !== "1.10.0") {
38
+ const [MAJOR, MINOR, PATCH] = "1.10.0".split(".");
39
39
  const [major, minor, patch] = (version ?? "").split(".");
40
40
  if (MAJOR !== major || MINOR !== minor) {
41
41
  localStorage.clear();
42
42
  }
43
- localStorage.setItem(VERSION_KEY, "1.9.5");
43
+ localStorage.setItem(VERSION_KEY, "1.10.0");
44
44
  }
45
45
  }
46
46
  function useMouseFontScale(storageKey) {
@@ -248,9 +248,6 @@ function WorkTimeOverlay({
248
248
  const id = setInterval(() => setNow(Date.now()), 6e4);
249
249
  return () => clearInterval(id);
250
250
  }, []);
251
- reactExports.useEffect(() => {
252
- if (workTime) window.api.setWorkHours({ work_hours: { start: fmtHM(workTime.startMin), end: fmtHM(workTime.endMin) } });
253
- }, [workTime]);
254
251
  const chartRight = chartLeft + chartWidth;
255
252
  const xForMin = (min) => chartLeft + (dayStart + min * 6e4 - viewStart) / (viewEnd - viewStart) * chartWidth;
256
253
  const xStart = workTime ? xForMin(workTime.startMin) : 0;
@@ -259,15 +256,18 @@ function WorkTimeOverlay({
259
256
  e.stopPropagation();
260
257
  e.preventDefault();
261
258
  setDragging(which);
259
+ let next = null;
262
260
  const onMove = (m) => {
263
261
  const rect = containerRef.current.getBoundingClientRect();
264
262
  const ratio = clamp((m.clientX - rect.left - chartLeft) / chartWidth, 0, 1);
265
263
  const ts = viewStart + ratio * (viewEnd - viewStart);
266
264
  const min = clamp(Math.round((ts - dayStart) / 6e4 / SNAP_MIN) * SNAP_MIN, 0, 24 * 60);
267
- setWorkTime((wt) => which === "start" ? { ...wt, startMin: Math.min(min, wt.endMin - SNAP_MIN) } : { ...wt, endMin: Math.max(min, wt.startMin + SNAP_MIN) });
265
+ next = which === "start" ? { ...workTime, startMin: Math.min(min, workTime.endMin - SNAP_MIN) } : { ...workTime, endMin: Math.max(min, workTime.startMin + SNAP_MIN) };
266
+ setWorkTime(next);
268
267
  };
269
268
  const onUp = () => {
270
269
  setDragging(null);
270
+ if (next) window.api.setWorkHours({ work_hours: { start: fmtHM(next.startMin), end: fmtHM(next.endMin) } });
271
271
  document.removeEventListener("mousemove", onMove);
272
272
  document.removeEventListener("mouseup", onUp);
273
273
  };
@@ -1189,6 +1189,7 @@ function SessionList({ sessions, selectedId, deepLink, onSelect }) {
1189
1189
  const filtered = q ? sessions.filter((s) => [
1190
1190
  s.customTitle,
1191
1191
  s.agentName,
1192
+ s.tag,
1192
1193
  s.aiTitle,
1193
1194
  s.summary,
1194
1195
  s.firstUserPrompt,
@@ -1296,6 +1297,10 @@ ${TIPS.time}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { size: 11
1296
1297
  ] }) : sessionLabel(s) })
1297
1298
  ] }),
1298
1299
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "session-time-bottom", children: [
1300
+ s.tag && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "session-tag", title: "Session tag", children: [
1301
+ "#",
1302
+ s.tag
1303
+ ] }),
1299
1304
  s.project && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "session-project", title: s.project, children: s.projectShort }),
1300
1305
  s.models?.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "session-model", children: s.models.map((m) => m.replace(/^claude-/, "")).join(", ") }),
1301
1306
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { title: format(s.lastActivityAt, "MMM d, HH:mm:ss"), children: fmtAgo(s.lastActivityAt) }),
@@ -1440,7 +1445,8 @@ function buildLinkComponents(basePath) {
1440
1445
  }
1441
1446
  };
1442
1447
  }
1443
- const Chunk = React.memo(({ text, components }) => /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown$1, { remarkPlugins: [remarkGfm], rehypePlugins: [rehypeHighlight], components, children: text }));
1448
+ const urlTransform = (url, key, node) => node.tagName === "img" && /^data:image\//i.test(url) ? url : defaultUrlTransform(url);
1449
+ const Chunk = React.memo(({ text, components }) => /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown$1, { remarkPlugins: [remarkGfm], rehypePlugins: [rehypeHighlight], components, urlTransform, children: text }));
1444
1450
  function Markdown({ text, basePath = null, autoFence = false, className = "", ...rest }) {
1445
1451
  const ref = reactExports.useRef(null);
1446
1452
  const findOpen = useFindActive();
@@ -1748,7 +1754,7 @@ function InstructionFile({ it, model, tokens: tokens2 }) {
1748
1754
  ] }),
1749
1755
  open && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "aux-body", children: [
1750
1756
  it.name && it.name !== it.file_path && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "instruction-path", children: it.file_path }),
1751
- /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { className: "block-text", text: it.content || "", basePath: it.file_path })
1757
+ it.tools ? it.tools.map((t) => /* @__PURE__ */ jsxRuntimeExports.jsx(InstructionFile, { it: { file_path: t.name, content: t.content }, model, tokens: countTokens(t.content, it.model ?? model) }, t.name)) : /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { className: "block-text", text: it.content || "", basePath: it.file_path })
1752
1758
  ] })
1753
1759
  ] });
1754
1760
  }
@@ -2087,7 +2093,8 @@ ${transcript}`;
2087
2093
  - Model: ${meta.models.join(", ") || "—"}
2088
2094
  - Service tier: ${meta.serviceTier || "—"}
2089
2095
  - Speed: ${meta.speed ? meta.speed === "fast" ? meta.fastPricingUnknown ? "fast (pricing unknown)" : "fast" : meta.speed : "—"}
2090
- - Git branch: ${meta.gitBranch || "—"}
2096
+ ${meta.tag ? `- Tag: ${meta.tag}
2097
+ ` : ""}- Git branch: ${meta.gitBranch || "—"}
2091
2098
  - Source: ${meta.source || meta.entrypoint || "—"}
2092
2099
  - Scheduled: ${meta.hasScheduledTask ? "yes" : "no"}
2093
2100
  - CLI version: ${meta.version || "—"}
@@ -2332,6 +2339,7 @@ ${body}`);
2332
2339
  /* @__PURE__ */ jsxRuntimeExports.jsx(Field, { label: "Model", value: modelLabel, mono: true }),
2333
2340
  meta.serviceTier && /* @__PURE__ */ jsxRuntimeExports.jsx(Field, { label: "Service tier", value: meta.serviceTier }),
2334
2341
  meta.speed && /* @__PURE__ */ jsxRuntimeExports.jsx(Field, { label: "Speed", value: meta.speed === "fast" ? meta.fastPricingUnknown ? "fast (pricing unknown)" : "fast" : meta.speed }),
2342
+ meta.tag && /* @__PURE__ */ jsxRuntimeExports.jsx(Field, { label: "Tag", value: meta.tag, mono: true }),
2335
2343
  /* @__PURE__ */ jsxRuntimeExports.jsx(Field, { label: "Git branch", value: meta.gitBranch || "—", mono: true }),
2336
2344
  /* @__PURE__ */ jsxRuntimeExports.jsx(Field, { label: "Source", value: meta.source || meta.entrypoint || "—" }),
2337
2345
  meta.hasScheduledTask && /* @__PURE__ */ jsxRuntimeExports.jsx(Field, { label: "Scheduled", value: "yes" }),
@@ -54069,16 +54069,17 @@ var vscodeTheme = {
54069
54069
  };
54070
54070
  var reactDomExports = requireReactDom();
54071
54071
  export {
54072
- qt as A,
54073
- nn as B,
54072
+ an as A,
54073
+ qt as B,
54074
54074
  Clock as C,
54075
54075
  DollarSign as D,
54076
- parse$1 as E,
54077
- delegate as F,
54076
+ nn as E,
54077
+ parse$1 as F,
54078
54078
  Gauge as G,
54079
54079
  Hourglass as H,
54080
- clientExports as I,
54080
+ delegate as I,
54081
54081
  JsonView as J,
54082
+ clientExports as K,
54082
54083
  MessageSquare as M,
54083
54084
  Qt as Q,
54084
54085
  React as R,
@@ -54106,8 +54107,8 @@ export {
54106
54107
  Markdown as t,
54107
54108
  rehypeHighlight as u,
54108
54109
  remarkGfm as v,
54109
- vscodeTheme as w,
54110
- tippy as x,
54111
- reactDomExports as y,
54112
- an as z
54110
+ defaultUrlTransform as w,
54111
+ vscodeTheme as x,
54112
+ tippy as y,
54113
+ reactDomExports as z
54113
54114
  };
@@ -6,10 +6,10 @@
6
6
  <meta charset="UTF-8" />
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
8
  <title>Discover</title>
9
- <script type="module" crossorigin src="./assets/index-CX9ZlZGo.js"></script>
10
- <link rel="modulepreload" crossorigin href="./assets/vendors-JM3FOKMR.js">
9
+ <script type="module" crossorigin src="./assets/index-x_-ZeUa3.js"></script>
10
+ <link rel="modulepreload" crossorigin href="./assets/vendors-D2DMOduT.js">
11
11
  <link rel="stylesheet" crossorigin href="./assets/vendors-B4XMnGdr.css">
12
- <link rel="stylesheet" crossorigin href="./assets/index-DZ1Gj6hz.css">
12
+ <link rel="stylesheet" crossorigin href="./assets/index-DnYN00T3.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-discover",
3
- "version": "1.9.5",
3
+ "version": "1.10.0",
4
4
  "type": "module",
5
5
  "description": "Claude Discover — a desktop tool to browse your local Claude Code sessions: conversations, token costs, and captured API traffic",
6
6
  "license": "MIT",
@@ -32,7 +32,6 @@
32
32
  "dependencies": {
33
33
  "@anthropic-ai/claude-agent-sdk": "^0.3.266",
34
34
  "@date-fns/tz": "^1.5.0",
35
- "chokidar": "^5.0.0",
36
35
  "date-fns": "^4.4.0",
37
36
  "electron": "43.2.0",
38
37
  "electron-context-menu": "^4.1.2",