agenttop 0.9.1 → 0.9.3

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/dist/index.js CHANGED
@@ -608,7 +608,7 @@ var SessionList = React2.memo(
608
608
  }
609
609
  const indicator = isSelected ? "\u25B8" : " ";
610
610
  const nameColor = isSelected ? colors.bright : isActive ? colors.secondary : colors.text;
611
- const getDisplayName2 = (s) => {
611
+ const getDisplayName = (s) => {
612
612
  if (s.nickname) return s.nickname;
613
613
  if (s.cwd) {
614
614
  const parts = s.cwd.replace(/\/+$/, "").split("/");
@@ -620,7 +620,7 @@ var SessionList = React2.memo(
620
620
  }
621
621
  return s.slug;
622
622
  };
623
- const displayName = truncate(getDisplayName2(session), INNER_WIDTH - 4);
623
+ const displayName = truncate(getDisplayName(session), INNER_WIDTH - 4);
624
624
  return /* @__PURE__ */ jsxs2(
625
625
  Box2,
626
626
  {
@@ -660,16 +660,7 @@ var SessionList = React2.memo(
660
660
  import React3 from "react";
661
661
  import { Box as Box3, Text as Text3 } from "ink";
662
662
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
663
- var TAG_COLORS = [
664
- "#61AFEF",
665
- "#98C379",
666
- "#C678DD",
667
- "#E5C07B",
668
- "#E06C75",
669
- "#56B6C2",
670
- "#D19A66",
671
- "#BE5046"
672
- ];
663
+ var TAG_COLORS = ["#61AFEF", "#98C379", "#C678DD", "#E5C07B", "#E06C75", "#56B6C2", "#D19A66", "#BE5046"];
673
664
  var formatTime = (ts) => {
674
665
  const d = new Date(ts);
675
666
  return d.toLocaleTimeString("en-GB", { hour12: false });
@@ -1938,11 +1929,10 @@ var SplitPanel = React14.memo(
1938
1929
  );
1939
1930
 
1940
1931
  // src/ui/hooks/useSessions.ts
1941
- import { useState as useState8, useEffect as useEffect5, useCallback as useCallback2, useRef as useRef3 } from "react";
1932
+ import { useState as useState8, useEffect as useEffect5, useCallback as useCallback2, useRef as useRef3, useMemo as useMemo2 } from "react";
1942
1933
  var ACTIVE_POLL_MS = 1e4;
1943
1934
  var IDLE_POLL_MS = 3e4;
1944
- var getDisplayName = (session) => {
1945
- if (session.nickname) return session.nickname;
1935
+ var getGroupKey = (session) => {
1946
1936
  if (session.cwd) {
1947
1937
  const parts = session.cwd.replace(/\/+$/, "").split("/");
1948
1938
  return parts[parts.length - 1] || session.slug;
@@ -1954,20 +1944,17 @@ var getDisplayName = (session) => {
1954
1944
  return session.slug;
1955
1945
  };
1956
1946
  var buildGroups = (sessions, expandedKeys) => {
1957
- const byName = /* @__PURE__ */ new Map();
1947
+ const byKey = /* @__PURE__ */ new Map();
1958
1948
  for (const s of sessions) {
1959
- const name = getDisplayName(s);
1960
- const list = byName.get(name);
1961
- if (list) list.push(s);
1962
- else byName.set(name, [s]);
1949
+ const key = getGroupKey(s);
1950
+ const existing = byKey.get(key);
1951
+ if (existing) existing.push(s);
1952
+ else byKey.set(key, [s]);
1963
1953
  }
1964
1954
  const groups = [];
1965
- for (const [key, list] of byName) {
1955
+ for (const [key, list] of byKey) {
1966
1956
  list.sort((a, b) => b.lastActivity - a.lastActivity);
1967
- const totalIn = list.reduce(
1968
- (sum, s) => sum + s.usage.inputTokens + s.usage.cacheReadTokens,
1969
- 0
1970
- );
1957
+ const totalIn = list.reduce((sum, s) => sum + s.usage.inputTokens + s.usage.cacheReadTokens, 0);
1971
1958
  const totalOut = list.reduce((sum, s) => sum + s.usage.outputTokens, 0);
1972
1959
  groups.push({
1973
1960
  key,
@@ -2045,14 +2032,16 @@ var useSessions = (allUsers, filter, archivedIds, viewingArchive) => {
2045
2032
  const interval = setInterval(refresh, pollMs);
2046
2033
  return () => clearInterval(interval);
2047
2034
  }, [refresh, sessions.length > 0]);
2048
- const groups = buildGroups(sessions, expandedKeys);
2049
- const visibleItems = buildVisibleItems(groups);
2035
+ const groups = useMemo2(() => buildGroups(sessions, expandedKeys), [sessions, expandedKeys]);
2036
+ const visibleItems = useMemo2(() => buildVisibleItems(groups), [groups]);
2037
+ const itemCountRef = useRef3(visibleItems.length);
2038
+ itemCountRef.current = visibleItems.length;
2050
2039
  const selectedItem = visibleItems[selectedIndex] ?? null;
2051
2040
  const selectedSession = selectedItem?.type === "ungrouped" ? selectedItem.session : selectedItem?.type === "session" ? selectedItem.session : null;
2052
2041
  const selectedGroup = selectedItem?.type === "group" ? selectedItem.group : null;
2053
2042
  const selectNext = useCallback2(() => {
2054
- setSelectedIndex((i) => Math.min(i + 1, Math.max(0, visibleItems.length - 1)));
2055
- }, [visibleItems.length]);
2043
+ setSelectedIndex((i) => Math.min(i + 1, Math.max(0, itemCountRef.current - 1)));
2044
+ }, []);
2056
2045
  const selectPrev = useCallback2(() => {
2057
2046
  setSelectedIndex((i) => Math.max(i - 1, 0));
2058
2047
  }, []);
@@ -2137,7 +2126,7 @@ var useActivityStream = (session, allUsers) => {
2137
2126
  };
2138
2127
 
2139
2128
  // src/ui/hooks/useFilteredEvents.ts
2140
- import { useMemo as useMemo2 } from "react";
2129
+ import { useMemo as useMemo3 } from "react";
2141
2130
  var applyFilter = (events, filter) => {
2142
2131
  if (!filter) return events;
2143
2132
  const lower = filter.toLowerCase();
@@ -2145,7 +2134,7 @@ var applyFilter = (events, filter) => {
2145
2134
  (e) => e.toolName.toLowerCase().includes(lower) || JSON.stringify(e.toolInput).toLowerCase().includes(lower)
2146
2135
  );
2147
2136
  };
2148
- var useFilteredEvents = (rawEvents, filter) => useMemo2(() => applyFilter(rawEvents, filter), [rawEvents, filter]);
2137
+ var useFilteredEvents = (rawEvents, filter) => useMemo3(() => applyFilter(rawEvents, filter), [rawEvents, filter]);
2149
2138
 
2150
2139
  // src/ui/hooks/useAlerts.ts
2151
2140
  import { useState as useState10, useEffect as useEffect7, useRef as useRef5 } from "react";
@@ -2859,14 +2848,30 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
2859
2848
  const [updateStatus, setUpdateStatus] = useState15("");
2860
2849
  const [showDetail, setShowDetail] = useState15(false);
2861
2850
  const [viewingArchive, setViewingArchive] = useState15(false);
2862
- const [confirmAction, setConfirmAction] = useState15(null);
2851
+ const [confirmAction, setConfirmAction] = useState15(
2852
+ null
2853
+ );
2863
2854
  const [archivedIds, setArchivedIds] = useState15(() => new Set(Object.keys(getArchived())));
2864
2855
  const refreshArchived = useCallback6(() => setArchivedIds(new Set(Object.keys(getArchived()))), []);
2865
- const updateInfo = useUpdateChecker(options.noUpdates, setup.liveConfig.updates.checkOnLaunch, setup.liveConfig.updates.checkInterval);
2856
+ const updateInfo = useUpdateChecker(
2857
+ options.noUpdates,
2858
+ setup.liveConfig.updates.checkOnLaunch,
2859
+ setup.liveConfig.updates.checkInterval
2860
+ );
2866
2861
  useEffect9(() => {
2867
2862
  applyTheme(resolveTheme(setup.liveConfig.theme, setup.liveConfig.customThemes));
2868
2863
  }, [setup.liveConfig.theme, setup.liveConfig.customThemes]);
2869
- const { sessions, visibleItems, selectedSession, selectedGroup, selectedIndex, selectNext, selectPrev, toggleExpand, refresh } = useSessions(options.allUsers, filter || void 0, archivedIds, viewingArchive);
2864
+ const {
2865
+ sessions,
2866
+ visibleItems,
2867
+ selectedSession,
2868
+ selectedGroup,
2869
+ selectedIndex,
2870
+ selectNext,
2871
+ selectPrev,
2872
+ toggleExpand,
2873
+ refresh
2874
+ } = useSessions(options.allUsers, filter || void 0, archivedIds, viewingArchive);
2870
2875
  const activityTarget = split.splitMode ? null : selectedGroup ? selectedGroup.sessions : selectedSession;
2871
2876
  const rawEvents = useActivityStream(activityTarget, options.allUsers);
2872
2877
  const leftRawEvents = useActivityStream(split.splitMode ? split.leftSession : null, options.allUsers);
@@ -2911,7 +2916,10 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
2911
2916
  const alertHeight = options.noSecurity ? 0 : 6;
2912
2917
  const mainHeight = termHeight - 3 - alertHeight - 1 - (inputMode !== "normal" ? 1 : 0);
2913
2918
  const viewportRows = mainHeight - 2;
2914
- const switchPanel = useCallback6((dir) => split.switchPanel(dir, setActivePanel), [split.switchPanel]);
2919
+ const switchPanel = useCallback6(
2920
+ (dir) => split.switchPanel(dir, setActivePanel),
2921
+ [split.switchPanel]
2922
+ );
2915
2923
  const clearSplitState = useCallback6(() => {
2916
2924
  split.clearSplitState();
2917
2925
  setActivePanel("sessions");
@@ -3018,8 +3026,18 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3018
3026
  });
3019
3027
  if (setup.showSetup) {
3020
3028
  const steps = [
3021
- ...setup.liveConfig.prompts.hook === "pending" ? [{ title: "Install Claude Code hook?", description: "Adds a PostToolUse hook that blocks prompt injection attempts in real-time." }] : [],
3022
- ...setup.liveConfig.prompts.mcp === "pending" ? [{ title: "Install MCP server?", description: "Registers agenttop as an MCP server so Claude Code can query session status and alerts." }] : []
3029
+ ...setup.liveConfig.prompts.hook === "pending" ? [
3030
+ {
3031
+ title: "Install Claude Code hook?",
3032
+ description: "Adds a PostToolUse hook that blocks prompt injection attempts in real-time."
3033
+ }
3034
+ ] : [],
3035
+ ...setup.liveConfig.prompts.mcp === "pending" ? [
3036
+ {
3037
+ title: "Install MCP server?",
3038
+ description: "Registers agenttop as an MCP server so Claude Code can query session status and alerts."
3039
+ }
3040
+ ] : []
3023
3041
  ];
3024
3042
  if (steps.length === 0) {
3025
3043
  setup.setShowSetup(false);
@@ -3027,20 +3045,85 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3027
3045
  }
3028
3046
  return /* @__PURE__ */ jsx15(SetupModal, { steps, onComplete: setup.handleSetupComplete });
3029
3047
  }
3030
- if (setup.showThemePicker) return /* @__PURE__ */ jsx15(ThemePickerModal, { onSelect: setup.handleThemePickerSelect, onSkip: setup.handleThemePickerSkip, onDismiss: setup.handleThemePickerDismiss });
3048
+ if (setup.showThemePicker)
3049
+ return /* @__PURE__ */ jsx15(
3050
+ ThemePickerModal,
3051
+ {
3052
+ onSelect: setup.handleThemePickerSelect,
3053
+ onSkip: setup.handleThemePickerSkip,
3054
+ onDismiss: setup.handleThemePickerDismiss
3055
+ }
3056
+ );
3031
3057
  if (setup.showTour) return /* @__PURE__ */ jsx15(GuidedTour, { onComplete: setup.handleTourComplete, onSkip: setup.handleTourSkip });
3032
3058
  if (setup.showThemeMenu) return /* @__PURE__ */ jsx15(ThemeMenu, { config: setup.liveConfig, onClose: setup.handleThemeMenuClose });
3033
- if (setup.showSettings) return /* @__PURE__ */ jsx15(SettingsMenu, { config: setup.liveConfig, onClose: setup.handleSettingsClose, onOpenThemeMenu: setup.handleOpenThemeMenu });
3059
+ if (setup.showSettings)
3060
+ return /* @__PURE__ */ jsx15(
3061
+ SettingsMenu,
3062
+ {
3063
+ config: setup.liveConfig,
3064
+ onClose: setup.handleSettingsClose,
3065
+ onOpenThemeMenu: setup.handleOpenThemeMenu
3066
+ }
3067
+ );
3034
3068
  if (confirmAction) {
3035
- return /* @__PURE__ */ jsx15(Box15, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx15(ConfirmModal, { title: confirmAction.title, message: confirmAction.message, onConfirm: confirmAction.onConfirm, onCancel: () => setConfirmAction(null) }) });
3069
+ return /* @__PURE__ */ jsx15(Box15, { flexDirection: "column", height: termHeight, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx15(
3070
+ ConfirmModal,
3071
+ {
3072
+ title: confirmAction.title,
3073
+ message: confirmAction.message,
3074
+ onConfirm: confirmAction.onConfirm,
3075
+ onCancel: () => setConfirmAction(null)
3076
+ }
3077
+ ) });
3036
3078
  }
3037
3079
  const activitySlug = selectedGroup ? selectedGroup.key : selectedSession?.slug ?? null;
3038
3080
  const isMerged = selectedGroup !== null;
3039
- const rightPanel = split.splitMode ? /* @__PURE__ */ jsx15(SplitPanel, { activePanel, leftSession: split.leftSession, rightSession: split.rightSession, leftEvents, rightEvents, leftScroll: split.leftScroll, rightScroll: split.rightScroll, leftFilter: split.leftFilter, rightFilter: split.rightFilter, leftShowDetail: split.leftShowDetail, rightShowDetail: split.rightShowDetail, height: mainHeight }) : showDetail && selectedSession ? /* @__PURE__ */ jsx15(SessionDetail, { session: selectedSession, focused: activePanel === "activity", height: mainHeight }) : /* @__PURE__ */ jsx15(ActivityFeed, { events, sessionSlug: activitySlug, sessionId: selectedSession?.sessionId, isActive: selectedGroup ? selectedGroup.isActive : selectedSession ? selectedSession.pid !== null : void 0, focused: activePanel === "activity", height: mainHeight, scrollOffset: activityScroll, filter: activityFilter || void 0, merged: isMerged, mergedSessions: selectedGroup?.sessions });
3081
+ const rightPanel = split.splitMode ? /* @__PURE__ */ jsx15(
3082
+ SplitPanel,
3083
+ {
3084
+ activePanel,
3085
+ leftSession: split.leftSession,
3086
+ rightSession: split.rightSession,
3087
+ leftEvents,
3088
+ rightEvents,
3089
+ leftScroll: split.leftScroll,
3090
+ rightScroll: split.rightScroll,
3091
+ leftFilter: split.leftFilter,
3092
+ rightFilter: split.rightFilter,
3093
+ leftShowDetail: split.leftShowDetail,
3094
+ rightShowDetail: split.rightShowDetail,
3095
+ height: mainHeight
3096
+ }
3097
+ ) : showDetail && selectedSession ? /* @__PURE__ */ jsx15(SessionDetail, { session: selectedSession, focused: activePanel === "activity", height: mainHeight }) : /* @__PURE__ */ jsx15(
3098
+ ActivityFeed,
3099
+ {
3100
+ events,
3101
+ sessionSlug: activitySlug,
3102
+ sessionId: selectedSession?.sessionId,
3103
+ isActive: selectedGroup ? selectedGroup.isActive : selectedSession ? selectedSession.pid !== null : void 0,
3104
+ focused: activePanel === "activity",
3105
+ height: mainHeight,
3106
+ scrollOffset: activityScroll,
3107
+ filter: activityFilter || void 0,
3108
+ merged: isMerged,
3109
+ mergedSessions: selectedGroup?.sessions
3110
+ }
3111
+ );
3040
3112
  return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", height: termHeight, children: [
3041
3113
  /* @__PURE__ */ jsx15(StatusBar, { sessionCount: sessions.length, alertCount: alerts.length, version, updateInfo }),
3042
3114
  /* @__PURE__ */ jsxs15(Box15, { flexGrow: 1, height: mainHeight, children: [
3043
- /* @__PURE__ */ jsx15(SessionList, { visibleItems, selectedIndex, focused: activePanel === "sessions", height: mainHeight, filter: filter || void 0, viewingArchive, totalSessions: sessions.length }),
3115
+ /* @__PURE__ */ jsx15(
3116
+ SessionList,
3117
+ {
3118
+ visibleItems,
3119
+ selectedIndex,
3120
+ focused: activePanel === "sessions",
3121
+ height: mainHeight,
3122
+ filter: filter || void 0,
3123
+ viewingArchive,
3124
+ totalSessions: sessions.length
3125
+ }
3126
+ ),
3044
3127
  rightPanel
3045
3128
  ] }),
3046
3129
  !options.noSecurity && /* @__PURE__ */ jsx15(AlertBar, { alerts }),
@@ -3055,7 +3138,15 @@ var App = ({ options, config: initialConfig, version, firstRun }) => {
3055
3138
  /* @__PURE__ */ jsx15(Text14, { color: colors.bright, children: filterInput.value }),
3056
3139
  /* @__PURE__ */ jsx15(Text14, { color: colors.muted, children: "_" })
3057
3140
  ] }),
3058
- inputMode === "normal" && /* @__PURE__ */ jsx15(FooterBar, { keybindings: kb, updateStatus, viewingArchive, splitMode: split.splitMode })
3141
+ inputMode === "normal" && /* @__PURE__ */ jsx15(
3142
+ FooterBar,
3143
+ {
3144
+ keybindings: kb,
3145
+ updateStatus,
3146
+ viewingArchive,
3147
+ splitMode: split.splitMode
3148
+ }
3149
+ )
3059
3150
  ] });
3060
3151
  };
3061
3152