claudeup 0.6.2 → 1.0.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.
Files changed (209) hide show
  1. package/bin/claudeup.js +1 -1
  2. package/dist/data/marketplaces.d.ts +2 -0
  3. package/dist/data/marketplaces.d.ts.map +1 -1
  4. package/dist/data/marketplaces.js +51 -8
  5. package/dist/data/marketplaces.js.map +1 -1
  6. package/dist/data/mcp-servers.d.ts.map +1 -1
  7. package/dist/data/mcp-servers.js +82 -0
  8. package/dist/data/mcp-servers.js.map +1 -1
  9. package/dist/index.js +8 -5
  10. package/dist/index.js.map +1 -1
  11. package/dist/main.d.ts +3 -0
  12. package/dist/main.d.ts.map +1 -0
  13. package/dist/main.js +79 -0
  14. package/dist/main.js.map +1 -0
  15. package/dist/services/claude-settings.d.ts +8 -1
  16. package/dist/services/claude-settings.d.ts.map +1 -1
  17. package/dist/services/claude-settings.js +79 -0
  18. package/dist/services/claude-settings.js.map +1 -1
  19. package/dist/services/local-marketplace.d.ts +76 -0
  20. package/dist/services/local-marketplace.d.ts.map +1 -0
  21. package/dist/services/local-marketplace.js +340 -0
  22. package/dist/services/local-marketplace.js.map +1 -0
  23. package/dist/services/plugin-manager.d.ts +39 -2
  24. package/dist/services/plugin-manager.d.ts.map +1 -1
  25. package/dist/services/plugin-manager.js +259 -9
  26. package/dist/services/plugin-manager.js.map +1 -1
  27. package/dist/types/index.d.ts +6 -1
  28. package/dist/types/index.d.ts.map +1 -1
  29. package/dist/ui/InkApp.d.ts +5 -0
  30. package/dist/ui/InkApp.d.ts.map +1 -0
  31. package/dist/ui/InkApp.js +175 -0
  32. package/dist/ui/InkApp.js.map +1 -0
  33. package/dist/ui/components/CategoryHeader.d.ts +16 -0
  34. package/dist/ui/components/CategoryHeader.d.ts.map +1 -0
  35. package/dist/ui/components/CategoryHeader.js +11 -0
  36. package/dist/ui/components/CategoryHeader.js.map +1 -0
  37. package/dist/ui/components/ScrollableList.d.ts +16 -0
  38. package/dist/ui/components/ScrollableList.d.ts.map +1 -0
  39. package/dist/ui/components/ScrollableList.js +35 -0
  40. package/dist/ui/components/ScrollableList.js.map +1 -0
  41. package/dist/ui/components/SearchInput.d.ts +18 -0
  42. package/dist/ui/components/SearchInput.d.ts.map +1 -0
  43. package/dist/ui/components/SearchInput.js +30 -0
  44. package/dist/ui/components/SearchInput.js.map +1 -0
  45. package/dist/ui/components/TabBar.d.ts +8 -0
  46. package/dist/ui/components/TabBar.d.ts.map +1 -0
  47. package/dist/ui/components/TabBar.js +18 -0
  48. package/dist/ui/components/TabBar.js.map +1 -0
  49. package/dist/ui/components/layout/Footer.d.ts +14 -0
  50. package/dist/ui/components/layout/Footer.d.ts.map +1 -0
  51. package/dist/ui/components/layout/Footer.js +23 -0
  52. package/dist/ui/components/layout/Footer.js.map +1 -0
  53. package/dist/ui/components/layout/Header.d.ts +4 -0
  54. package/dist/ui/components/layout/Header.d.ts.map +1 -0
  55. package/dist/ui/components/layout/Header.js +25 -0
  56. package/dist/ui/components/layout/Header.js.map +1 -0
  57. package/dist/ui/components/layout/Panel.d.ts +22 -0
  58. package/dist/ui/components/layout/Panel.d.ts.map +1 -0
  59. package/dist/ui/components/layout/Panel.js +8 -0
  60. package/dist/ui/components/layout/Panel.js.map +1 -0
  61. package/dist/ui/components/layout/ProgressBar.d.ts +12 -0
  62. package/dist/ui/components/layout/ProgressBar.d.ts.map +1 -0
  63. package/dist/ui/components/layout/ProgressBar.js +16 -0
  64. package/dist/ui/components/layout/ProgressBar.js.map +1 -0
  65. package/dist/ui/components/layout/ScopeTabs.d.ts +12 -0
  66. package/dist/ui/components/layout/ScopeTabs.d.ts.map +1 -0
  67. package/dist/ui/components/layout/ScopeTabs.js +8 -0
  68. package/dist/ui/components/layout/ScopeTabs.js.map +1 -0
  69. package/dist/ui/components/layout/ScreenLayout.d.ts +30 -0
  70. package/dist/ui/components/layout/ScreenLayout.d.ts.map +1 -0
  71. package/dist/ui/components/layout/ScreenLayout.js +23 -0
  72. package/dist/ui/components/layout/ScreenLayout.js.map +1 -0
  73. package/dist/ui/components/layout/index.d.ts +7 -0
  74. package/dist/ui/components/layout/index.d.ts.map +1 -0
  75. package/dist/ui/components/layout/index.js +7 -0
  76. package/dist/ui/components/layout/index.js.map +1 -0
  77. package/dist/ui/components/modals/ConfirmModal.d.ts +14 -0
  78. package/dist/ui/components/modals/ConfirmModal.d.ts.map +1 -0
  79. package/dist/ui/components/modals/ConfirmModal.js +15 -0
  80. package/dist/ui/components/modals/ConfirmModal.js.map +1 -0
  81. package/dist/ui/components/modals/InputModal.d.ts +16 -0
  82. package/dist/ui/components/modals/InputModal.d.ts.map +1 -0
  83. package/dist/ui/components/modals/InputModal.js +23 -0
  84. package/dist/ui/components/modals/InputModal.js.map +1 -0
  85. package/dist/ui/components/modals/LoadingModal.d.ts +8 -0
  86. package/dist/ui/components/modals/LoadingModal.d.ts.map +1 -0
  87. package/dist/ui/components/modals/LoadingModal.js +8 -0
  88. package/dist/ui/components/modals/LoadingModal.js.map +1 -0
  89. package/dist/ui/components/modals/MessageModal.d.ts +14 -0
  90. package/dist/ui/components/modals/MessageModal.d.ts.map +1 -0
  91. package/dist/ui/components/modals/MessageModal.js +17 -0
  92. package/dist/ui/components/modals/MessageModal.js.map +1 -0
  93. package/dist/ui/components/modals/ModalContainer.d.ts +7 -0
  94. package/dist/ui/components/modals/ModalContainer.d.ts.map +1 -0
  95. package/dist/ui/components/modals/ModalContainer.js +38 -0
  96. package/dist/ui/components/modals/ModalContainer.js.map +1 -0
  97. package/dist/ui/components/modals/SelectModal.d.ts +17 -0
  98. package/dist/ui/components/modals/SelectModal.d.ts.map +1 -0
  99. package/dist/ui/components/modals/SelectModal.js +33 -0
  100. package/dist/ui/components/modals/SelectModal.js.map +1 -0
  101. package/dist/ui/components/modals/index.d.ts +7 -0
  102. package/dist/ui/components/modals/index.d.ts.map +1 -0
  103. package/dist/ui/components/modals/index.js +7 -0
  104. package/dist/ui/components/modals/index.js.map +1 -0
  105. package/dist/ui/hooks/index.d.ts +3 -0
  106. package/dist/ui/hooks/index.d.ts.map +1 -0
  107. package/dist/ui/hooks/index.js +3 -0
  108. package/dist/ui/hooks/index.js.map +1 -0
  109. package/dist/ui/hooks/useAsyncData.d.ts +40 -0
  110. package/dist/ui/hooks/useAsyncData.d.ts.map +1 -0
  111. package/dist/ui/hooks/useAsyncData.js +78 -0
  112. package/dist/ui/hooks/useAsyncData.js.map +1 -0
  113. package/dist/ui/hooks/useKeyboardNavigation.d.ts +27 -0
  114. package/dist/ui/hooks/useKeyboardNavigation.d.ts.map +1 -0
  115. package/dist/ui/hooks/useKeyboardNavigation.js +82 -0
  116. package/dist/ui/hooks/useKeyboardNavigation.js.map +1 -0
  117. package/dist/ui/screens/CliToolsScreen.d.ts +4 -0
  118. package/dist/ui/screens/CliToolsScreen.d.ts.map +1 -0
  119. package/dist/ui/screens/CliToolsScreen.js +268 -0
  120. package/dist/ui/screens/CliToolsScreen.js.map +1 -0
  121. package/dist/ui/screens/EnvVarsScreen.d.ts +4 -0
  122. package/dist/ui/screens/EnvVarsScreen.d.ts.map +1 -0
  123. package/dist/ui/screens/EnvVarsScreen.js +145 -0
  124. package/dist/ui/screens/EnvVarsScreen.js.map +1 -0
  125. package/dist/ui/screens/McpRegistryScreen.d.ts +4 -0
  126. package/dist/ui/screens/McpRegistryScreen.d.ts.map +1 -0
  127. package/dist/ui/screens/McpRegistryScreen.js +226 -0
  128. package/dist/ui/screens/McpRegistryScreen.js.map +1 -0
  129. package/dist/ui/screens/McpScreen.d.ts +4 -0
  130. package/dist/ui/screens/McpScreen.d.ts.map +1 -0
  131. package/dist/ui/screens/McpScreen.js +222 -0
  132. package/dist/ui/screens/McpScreen.js.map +1 -0
  133. package/dist/ui/screens/ModelSelectorScreen.d.ts +4 -0
  134. package/dist/ui/screens/ModelSelectorScreen.d.ts.map +1 -0
  135. package/dist/ui/screens/ModelSelectorScreen.js +143 -0
  136. package/dist/ui/screens/ModelSelectorScreen.js.map +1 -0
  137. package/dist/ui/screens/PluginsScreen.d.ts +4 -0
  138. package/dist/ui/screens/PluginsScreen.d.ts.map +1 -0
  139. package/dist/ui/screens/PluginsScreen.js +747 -0
  140. package/dist/ui/screens/PluginsScreen.js.map +1 -0
  141. package/dist/ui/screens/StatusLineScreen.d.ts +4 -0
  142. package/dist/ui/screens/StatusLineScreen.d.ts.map +1 -0
  143. package/dist/ui/screens/StatusLineScreen.js +197 -0
  144. package/dist/ui/screens/StatusLineScreen.js.map +1 -0
  145. package/dist/ui/screens/index.d.ts +8 -0
  146. package/dist/ui/screens/index.d.ts.map +1 -0
  147. package/dist/ui/screens/index.js +8 -0
  148. package/dist/ui/screens/index.js.map +1 -0
  149. package/dist/ui/state/AppContext.d.ts +40 -0
  150. package/dist/ui/state/AppContext.d.ts.map +1 -0
  151. package/dist/ui/state/AppContext.js +162 -0
  152. package/dist/ui/state/AppContext.js.map +1 -0
  153. package/dist/ui/state/DimensionsContext.d.ts +25 -0
  154. package/dist/ui/state/DimensionsContext.d.ts.map +1 -0
  155. package/dist/ui/state/DimensionsContext.js +68 -0
  156. package/dist/ui/state/DimensionsContext.js.map +1 -0
  157. package/dist/ui/state/reducer.d.ts +4 -0
  158. package/dist/ui/state/reducer.d.ts.map +1 -0
  159. package/dist/ui/state/reducer.js +412 -0
  160. package/dist/ui/state/reducer.js.map +1 -0
  161. package/dist/ui/state/types.d.ts +266 -0
  162. package/dist/ui/state/types.d.ts.map +1 -0
  163. package/dist/ui/state/types.js +2 -0
  164. package/dist/ui/state/types.js.map +1 -0
  165. package/dist/utils/fuzzy-search.d.ts +33 -0
  166. package/dist/utils/fuzzy-search.d.ts.map +1 -0
  167. package/dist/utils/fuzzy-search.js +102 -0
  168. package/dist/utils/fuzzy-search.js.map +1 -0
  169. package/dist/utils/string-utils.d.ts +24 -0
  170. package/dist/utils/string-utils.d.ts.map +1 -0
  171. package/dist/utils/string-utils.js +62 -0
  172. package/dist/utils/string-utils.js.map +1 -0
  173. package/package.json +24 -8
  174. package/dist/ui/app.d.ts +0 -38
  175. package/dist/ui/app.d.ts.map +0 -1
  176. package/dist/ui/app.js +0 -590
  177. package/dist/ui/app.js.map +0 -1
  178. package/dist/ui/screens/cli-tools.d.ts +0 -4
  179. package/dist/ui/screens/cli-tools.d.ts.map +0 -1
  180. package/dist/ui/screens/cli-tools.js +0 -369
  181. package/dist/ui/screens/cli-tools.js.map +0 -1
  182. package/dist/ui/screens/env-vars.d.ts +0 -3
  183. package/dist/ui/screens/env-vars.d.ts.map +0 -1
  184. package/dist/ui/screens/env-vars.js +0 -119
  185. package/dist/ui/screens/env-vars.js.map +0 -1
  186. package/dist/ui/screens/main-menu.d.ts +0 -3
  187. package/dist/ui/screens/main-menu.d.ts.map +0 -1
  188. package/dist/ui/screens/main-menu.js +0 -110
  189. package/dist/ui/screens/main-menu.js.map +0 -1
  190. package/dist/ui/screens/marketplace.d.ts +0 -3
  191. package/dist/ui/screens/marketplace.d.ts.map +0 -1
  192. package/dist/ui/screens/marketplace.js +0 -132
  193. package/dist/ui/screens/marketplace.js.map +0 -1
  194. package/dist/ui/screens/mcp-registry.d.ts +0 -10
  195. package/dist/ui/screens/mcp-registry.d.ts.map +0 -1
  196. package/dist/ui/screens/mcp-registry.js +0 -310
  197. package/dist/ui/screens/mcp-registry.js.map +0 -1
  198. package/dist/ui/screens/mcp-setup.d.ts +0 -4
  199. package/dist/ui/screens/mcp-setup.d.ts.map +0 -1
  200. package/dist/ui/screens/mcp-setup.js +0 -492
  201. package/dist/ui/screens/mcp-setup.js.map +0 -1
  202. package/dist/ui/screens/plugins.d.ts +0 -3
  203. package/dist/ui/screens/plugins.d.ts.map +0 -1
  204. package/dist/ui/screens/plugins.js +0 -443
  205. package/dist/ui/screens/plugins.js.map +0 -1
  206. package/dist/ui/screens/statusline.d.ts +0 -5
  207. package/dist/ui/screens/statusline.d.ts.map +0 -1
  208. package/dist/ui/screens/statusline.js +0 -235
  209. package/dist/ui/screens/statusline.js.map +0 -1
@@ -0,0 +1,747 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useCallback, useMemo } from 'react';
3
+ import { Box, Text, useInput } from 'ink';
4
+ import { useApp, useModal, useProgress } from '../state/AppContext.js';
5
+ import { useDimensions } from '../state/DimensionsContext.js';
6
+ import { ScreenLayout } from '../components/layout/index.js';
7
+ import { CategoryHeader } from '../components/CategoryHeader.js';
8
+ import { ScrollableList } from '../components/ScrollableList.js';
9
+ import { fuzzyFilter, highlightMatches } from '../../utils/fuzzy-search.js';
10
+ import { getAllMarketplaces } from '../../data/marketplaces.js';
11
+ import { getAvailablePlugins, refreshAllMarketplaces, clearMarketplaceCache, saveInstalledPluginVersion, removeInstalledPluginVersion, getLocalMarketplacesInfo, } from '../../services/plugin-manager.js';
12
+ import { addMarketplace, removeMarketplace, addGlobalMarketplace, removeGlobalMarketplace, enablePlugin, enableGlobalPlugin, enableLocalPlugin, saveGlobalInstalledPluginVersion, removeGlobalInstalledPluginVersion, saveLocalInstalledPluginVersion, removeLocalInstalledPluginVersion, } from '../../services/claude-settings.js';
13
+ import { cloneMarketplace, deleteMarketplace, addToKnownMarketplaces, removeFromKnownMarketplaces, } from '../../services/local-marketplace.js';
14
+ export function PluginsScreen() {
15
+ const { state, dispatch } = useApp();
16
+ const { plugins: pluginsState } = state;
17
+ const modal = useModal();
18
+ const progress = useProgress();
19
+ const dimensions = useDimensions();
20
+ const isSearchActive = state.isSearching && state.currentRoute.screen === 'plugins' && !state.modal;
21
+ // Fetch data (always fetches all scopes)
22
+ const fetchData = useCallback(async () => {
23
+ dispatch({ type: 'PLUGINS_DATA_LOADING' });
24
+ try {
25
+ const localMarketplaces = await getLocalMarketplacesInfo();
26
+ const allMarketplaces = getAllMarketplaces(localMarketplaces);
27
+ // Always use getAvailablePlugins which fetches all scope data
28
+ const pluginData = await getAvailablePlugins(state.projectPath);
29
+ dispatch({
30
+ type: 'PLUGINS_DATA_SUCCESS',
31
+ marketplaces: allMarketplaces,
32
+ plugins: pluginData,
33
+ });
34
+ }
35
+ catch (error) {
36
+ dispatch({
37
+ type: 'PLUGINS_DATA_ERROR',
38
+ error: error instanceof Error ? error : new Error(String(error)),
39
+ });
40
+ }
41
+ }, [dispatch, state.projectPath]);
42
+ // Load data on mount or when dataRefreshVersion changes
43
+ useEffect(() => {
44
+ fetchData();
45
+ }, [fetchData, state.dataRefreshVersion]);
46
+ // Build list items (categories + plugins)
47
+ const allItems = useMemo(() => {
48
+ if (pluginsState.marketplaces.status !== 'success' || pluginsState.plugins.status !== 'success') {
49
+ return [];
50
+ }
51
+ const marketplaces = pluginsState.marketplaces.data;
52
+ const plugins = pluginsState.plugins.data;
53
+ const collapsed = pluginsState.collapsedMarketplaces;
54
+ const pluginsByMarketplace = new Map();
55
+ for (const plugin of plugins) {
56
+ const existing = pluginsByMarketplace.get(plugin.marketplace) || [];
57
+ existing.push(plugin);
58
+ pluginsByMarketplace.set(plugin.marketplace, existing);
59
+ }
60
+ // Sort marketplaces: deprecated ones go to the bottom
61
+ const sortedMarketplaces = [...marketplaces].sort((a, b) => {
62
+ const aDeprecated = a.name === 'claude-code-plugins' ? 1 : 0;
63
+ const bDeprecated = b.name === 'claude-code-plugins' ? 1 : 0;
64
+ return aDeprecated - bDeprecated;
65
+ });
66
+ const items = [];
67
+ for (const marketplace of sortedMarketplaces) {
68
+ const marketplacePlugins = pluginsByMarketplace.get(marketplace.name) || [];
69
+ const isCollapsed = collapsed.has(marketplace.name);
70
+ const isEnabled = marketplacePlugins.length > 0 || marketplace.official;
71
+ const hasPlugins = marketplacePlugins.length > 0;
72
+ // Category header (marketplace)
73
+ items.push({
74
+ id: `mp:${marketplace.name}`,
75
+ type: 'category',
76
+ label: marketplace.displayName,
77
+ marketplace,
78
+ marketplaceEnabled: isEnabled,
79
+ pluginCount: marketplacePlugins.length,
80
+ isExpanded: !isCollapsed && hasPlugins,
81
+ });
82
+ // Plugins under this marketplace (if expanded)
83
+ if (isEnabled && hasPlugins && !isCollapsed) {
84
+ for (const plugin of marketplacePlugins) {
85
+ items.push({
86
+ id: `pl:${plugin.id}`,
87
+ type: 'plugin',
88
+ label: plugin.name,
89
+ plugin,
90
+ });
91
+ }
92
+ }
93
+ }
94
+ return items;
95
+ }, [pluginsState.marketplaces, pluginsState.plugins, pluginsState.collapsedMarketplaces]);
96
+ // Filter items by search query
97
+ const filteredItems = useMemo(() => {
98
+ const query = pluginsState.searchQuery.trim();
99
+ if (!query)
100
+ return allItems;
101
+ // Only search plugins, not categories
102
+ const pluginItems = allItems.filter(item => item.type === 'plugin');
103
+ const fuzzyResults = fuzzyFilter(pluginItems, query, item => item.label);
104
+ // Include parent categories for matched plugins
105
+ const matchedMarketplaces = new Set();
106
+ for (const result of fuzzyResults) {
107
+ if (result.item.plugin) {
108
+ matchedMarketplaces.add(result.item.plugin.marketplace);
109
+ }
110
+ }
111
+ const result = [];
112
+ let currentMarketplace = null;
113
+ for (const item of allItems) {
114
+ if (item.type === 'category' && item.marketplace) {
115
+ if (matchedMarketplaces.has(item.marketplace.name)) {
116
+ result.push(item);
117
+ currentMarketplace = item.marketplace.name;
118
+ }
119
+ else {
120
+ currentMarketplace = null;
121
+ }
122
+ }
123
+ else if (item.type === 'plugin' && item.plugin) {
124
+ if (currentMarketplace === item.plugin.marketplace) {
125
+ // Check if this plugin matched
126
+ const matched = fuzzyResults.find(r => r.item.id === item.id);
127
+ if (matched) {
128
+ result.push({ ...item, _matches: matched.matches });
129
+ }
130
+ }
131
+ }
132
+ }
133
+ return result;
134
+ }, [allItems, pluginsState.searchQuery]);
135
+ // Only selectable items (plugins, not categories)
136
+ const selectableItems = useMemo(() => {
137
+ return filteredItems.filter(item => item.type === 'plugin' || item.type === 'category');
138
+ }, [filteredItems]);
139
+ // Keyboard handling
140
+ useInput((input, key) => {
141
+ // Handle search mode
142
+ if (isSearchActive) {
143
+ if (key.escape) {
144
+ dispatch({ type: 'SET_SEARCHING', isSearching: false });
145
+ dispatch({ type: 'PLUGINS_SET_SEARCH', query: '' });
146
+ }
147
+ else if (key.return) {
148
+ dispatch({ type: 'SET_SEARCHING', isSearching: false });
149
+ // Keep the search query, just exit search mode
150
+ }
151
+ else if (key.backspace || key.delete) {
152
+ dispatch({ type: 'PLUGINS_SET_SEARCH', query: pluginsState.searchQuery.slice(0, -1) });
153
+ }
154
+ else if (input && !key.ctrl && !key.meta) {
155
+ dispatch({ type: 'PLUGINS_SET_SEARCH', query: pluginsState.searchQuery + input });
156
+ }
157
+ return;
158
+ }
159
+ if (state.modal)
160
+ return;
161
+ // Start search with /
162
+ if (input === '/') {
163
+ dispatch({ type: 'SET_SEARCHING', isSearching: true });
164
+ return;
165
+ }
166
+ // Navigation
167
+ if (key.upArrow || input === 'k') {
168
+ const newIndex = Math.max(0, pluginsState.selectedIndex - 1);
169
+ dispatch({ type: 'PLUGINS_SELECT', index: newIndex });
170
+ }
171
+ else if (key.downArrow || input === 'j') {
172
+ const newIndex = Math.min(selectableItems.length - 1, pluginsState.selectedIndex + 1);
173
+ dispatch({ type: 'PLUGINS_SELECT', index: newIndex });
174
+ }
175
+ // Collapse/expand marketplace
176
+ else if ((key.leftArrow || key.rightArrow || input === '<' || input === '>') && selectableItems[pluginsState.selectedIndex]?.marketplace) {
177
+ const item = selectableItems[pluginsState.selectedIndex];
178
+ if (item?.marketplace) {
179
+ dispatch({ type: 'PLUGINS_TOGGLE_MARKETPLACE', name: item.marketplace.name });
180
+ }
181
+ }
182
+ // Refresh
183
+ else if (input === 'r') {
184
+ handleRefresh();
185
+ }
186
+ // New marketplace
187
+ else if (input === 'n') {
188
+ handleAddMarketplace();
189
+ }
190
+ // Scope-specific toggle shortcuts (u/p/l)
191
+ else if (input === 'u') {
192
+ handleScopeToggle('user');
193
+ }
194
+ else if (input === 'p') {
195
+ handleScopeToggle('project');
196
+ }
197
+ else if (input === 'l') {
198
+ handleScopeToggle('local');
199
+ }
200
+ // Update plugin (Shift+U)
201
+ else if (input === 'U') {
202
+ handleUpdate();
203
+ }
204
+ // Update all
205
+ else if (input === 'a') {
206
+ handleUpdateAll();
207
+ }
208
+ // Delete/uninstall
209
+ else if (input === 'd') {
210
+ handleUninstall();
211
+ }
212
+ // Enter for selection
213
+ else if (key.return) {
214
+ handleSelect();
215
+ }
216
+ });
217
+ // Handle actions
218
+ const handleRefresh = async () => {
219
+ progress.show('Refreshing marketplaces...');
220
+ try {
221
+ await refreshAllMarketplaces((p) => {
222
+ progress.show(`Refreshing ${p.name}...`, p.current, p.total);
223
+ });
224
+ clearMarketplaceCache();
225
+ progress.hide();
226
+ await modal.message('Refreshed', 'Marketplaces refreshed successfully.', 'success');
227
+ fetchData();
228
+ }
229
+ catch (error) {
230
+ progress.hide();
231
+ await modal.message('Error', `Refresh failed: ${error}`, 'error');
232
+ }
233
+ };
234
+ const handleAddMarketplace = async () => {
235
+ const repo = await modal.input('Add Marketplace', 'GitHub repo (owner/repo):');
236
+ if (!repo || !repo.trim())
237
+ return;
238
+ progress.show('Cloning marketplace...');
239
+ try {
240
+ const result = await cloneMarketplace(repo.trim());
241
+ if (result.success) {
242
+ const normalizedRepo = repo.trim().replace(/^https:\/\/github\.com\//, '').replace(/\.git$/, '');
243
+ const marketplace = {
244
+ name: result.name,
245
+ displayName: result.name,
246
+ source: { source: 'github', repo: normalizedRepo },
247
+ description: '',
248
+ official: false,
249
+ };
250
+ if (pluginsState.scope === 'global') {
251
+ await addGlobalMarketplace(marketplace);
252
+ }
253
+ else {
254
+ await addMarketplace(marketplace, state.projectPath);
255
+ }
256
+ await addToKnownMarketplaces(result.name, normalizedRepo);
257
+ clearMarketplaceCache();
258
+ progress.hide();
259
+ await modal.message('Added', `${result.name} marketplace added.`, 'success');
260
+ fetchData();
261
+ }
262
+ else {
263
+ progress.hide();
264
+ await modal.message('Failed', result.error || 'Clone failed', 'error');
265
+ }
266
+ }
267
+ catch (error) {
268
+ progress.hide();
269
+ await modal.message('Error', `Failed to add marketplace: ${error}`, 'error');
270
+ }
271
+ };
272
+ const handleSelect = async () => {
273
+ const item = selectableItems[pluginsState.selectedIndex];
274
+ if (!item)
275
+ return;
276
+ if (item.type === 'category' && item.marketplace) {
277
+ const mp = item.marketplace;
278
+ const isGlobal = pluginsState.scope === 'global';
279
+ if (item.marketplaceEnabled) {
280
+ const isCollapsed = pluginsState.collapsedMarketplaces.has(mp.name);
281
+ // If collapsed, expand first (even if no plugins - they might load)
282
+ if (isCollapsed) {
283
+ dispatch({ type: 'PLUGINS_TOGGLE_MARKETPLACE', name: mp.name });
284
+ }
285
+ else if (item.pluginCount && item.pluginCount > 0) {
286
+ // If expanded with plugins, collapse
287
+ dispatch({ type: 'PLUGINS_TOGGLE_MARKETPLACE', name: mp.name });
288
+ }
289
+ else {
290
+ // If expanded with no plugins, offer to remove
291
+ const confirmed = await modal.confirm(`Remove ${mp.displayName}?`, 'Plugins from this marketplace will no longer be available.');
292
+ if (confirmed) {
293
+ modal.loading(`Removing ${mp.displayName}...`);
294
+ try {
295
+ if (isGlobal) {
296
+ await removeGlobalMarketplace(mp.name);
297
+ }
298
+ else {
299
+ await removeMarketplace(mp.name, state.projectPath);
300
+ }
301
+ await deleteMarketplace(mp.name);
302
+ await removeFromKnownMarketplaces(mp.name);
303
+ clearMarketplaceCache();
304
+ modal.hideModal();
305
+ await modal.message('Removed', `${mp.displayName} removed.`, 'success');
306
+ fetchData();
307
+ }
308
+ catch (error) {
309
+ modal.hideModal();
310
+ await modal.message('Error', `Failed to remove: ${error}`, 'error');
311
+ }
312
+ }
313
+ }
314
+ }
315
+ else {
316
+ // Add marketplace
317
+ modal.loading(`Adding ${mp.displayName}...`);
318
+ try {
319
+ if (isGlobal) {
320
+ await addGlobalMarketplace(mp);
321
+ }
322
+ else {
323
+ await addMarketplace(mp, state.projectPath);
324
+ }
325
+ modal.hideModal();
326
+ fetchData();
327
+ }
328
+ catch (error) {
329
+ modal.hideModal();
330
+ await modal.message('Error', `Failed to add: ${error}`, 'error');
331
+ }
332
+ }
333
+ }
334
+ else if (item.type === 'plugin' && item.plugin) {
335
+ const plugin = item.plugin;
336
+ const latestVersion = plugin.version || '0.0.0';
337
+ // Build scope options with status info
338
+ const buildScopeLabel = (name, scope, desc) => {
339
+ const installed = scope?.enabled;
340
+ const ver = scope?.version;
341
+ const hasUpdate = ver && latestVersion && ver !== latestVersion && latestVersion !== '0.0.0';
342
+ let label = installed ? `● ${name}` : `○ ${name}`;
343
+ label += ` (${desc})`;
344
+ if (ver)
345
+ label += ` v${ver}`;
346
+ if (hasUpdate)
347
+ label += ` → v${latestVersion}`;
348
+ return label;
349
+ };
350
+ const scopeOptions = [
351
+ { label: buildScopeLabel('User', plugin.userScope, 'global'), value: 'user' },
352
+ { label: buildScopeLabel('Project', plugin.projectScope, 'team'), value: 'project' },
353
+ { label: buildScopeLabel('Local', plugin.localScope, 'private'), value: 'local' },
354
+ ];
355
+ const scopeValue = await modal.select(plugin.name, `Select scope to toggle:`, scopeOptions);
356
+ if (scopeValue === null)
357
+ return; // Cancelled
358
+ // Determine action based on selected scope's current state
359
+ const selectedScope = scopeValue === 'user' ? plugin.userScope :
360
+ scopeValue === 'project' ? plugin.projectScope :
361
+ plugin.localScope;
362
+ const isInstalledInScope = selectedScope?.enabled;
363
+ const installedVersion = selectedScope?.version;
364
+ const scopeLabel = scopeValue === 'user' ? 'User' : scopeValue === 'project' ? 'Project' : 'Local';
365
+ // Check if this scope has an update available
366
+ const hasUpdateInScope = isInstalledInScope && installedVersion &&
367
+ latestVersion !== '0.0.0' &&
368
+ installedVersion !== latestVersion;
369
+ // Determine action: update if available, otherwise toggle
370
+ let action;
371
+ if (isInstalledInScope && hasUpdateInScope) {
372
+ action = 'update';
373
+ }
374
+ else if (isInstalledInScope) {
375
+ action = 'uninstall';
376
+ }
377
+ else {
378
+ action = 'install';
379
+ }
380
+ const actionLabel = action === 'update' ? `Updating ${scopeLabel}` :
381
+ action === 'install' ? `Installing to ${scopeLabel}` :
382
+ `Uninstalling from ${scopeLabel}`;
383
+ modal.loading(`${actionLabel}...`);
384
+ try {
385
+ if (action === 'uninstall') {
386
+ // Uninstall from this scope
387
+ if (scopeValue === 'user') {
388
+ await enableGlobalPlugin(plugin.id, false);
389
+ await removeGlobalInstalledPluginVersion(plugin.id);
390
+ }
391
+ else if (scopeValue === 'project') {
392
+ await enablePlugin(plugin.id, false, state.projectPath);
393
+ await removeInstalledPluginVersion(plugin.id, state.projectPath);
394
+ }
395
+ else {
396
+ await enableLocalPlugin(plugin.id, false, state.projectPath);
397
+ await removeLocalInstalledPluginVersion(plugin.id, state.projectPath);
398
+ }
399
+ }
400
+ else {
401
+ // Install or update (both save the latest version)
402
+ if (scopeValue === 'user') {
403
+ await enableGlobalPlugin(plugin.id, true);
404
+ await saveGlobalInstalledPluginVersion(plugin.id, latestVersion);
405
+ }
406
+ else if (scopeValue === 'project') {
407
+ await enablePlugin(plugin.id, true, state.projectPath);
408
+ await saveInstalledPluginVersion(plugin.id, latestVersion, state.projectPath);
409
+ }
410
+ else {
411
+ await enableLocalPlugin(plugin.id, true, state.projectPath);
412
+ await saveLocalInstalledPluginVersion(plugin.id, latestVersion, state.projectPath);
413
+ }
414
+ }
415
+ modal.hideModal();
416
+ fetchData();
417
+ }
418
+ catch (error) {
419
+ modal.hideModal();
420
+ await modal.message('Error', `Failed: ${error}`, 'error');
421
+ }
422
+ }
423
+ };
424
+ const handleUpdate = async () => {
425
+ const item = selectableItems[pluginsState.selectedIndex];
426
+ if (!item || item.type !== 'plugin' || !item.plugin?.hasUpdate)
427
+ return;
428
+ const plugin = item.plugin;
429
+ const isGlobal = pluginsState.scope === 'global';
430
+ modal.loading(`Updating ${plugin.name}...`);
431
+ try {
432
+ const versionToSave = plugin.version || '0.0.0';
433
+ if (isGlobal) {
434
+ await saveGlobalInstalledPluginVersion(plugin.id, versionToSave);
435
+ }
436
+ else {
437
+ await saveInstalledPluginVersion(plugin.id, versionToSave, state.projectPath);
438
+ }
439
+ modal.hideModal();
440
+ fetchData();
441
+ }
442
+ catch (error) {
443
+ modal.hideModal();
444
+ await modal.message('Error', `Failed to update: ${error}`, 'error');
445
+ }
446
+ };
447
+ const handleUpdateAll = async () => {
448
+ if (pluginsState.plugins.status !== 'success')
449
+ return;
450
+ const updatable = pluginsState.plugins.data.filter((p) => p.hasUpdate);
451
+ if (updatable.length === 0)
452
+ return;
453
+ const isGlobal = pluginsState.scope === 'global';
454
+ modal.loading(`Updating ${updatable.length} plugin(s)...`);
455
+ try {
456
+ for (const plugin of updatable) {
457
+ const versionToSave = plugin.version || '0.0.0';
458
+ if (isGlobal) {
459
+ await saveGlobalInstalledPluginVersion(plugin.id, versionToSave);
460
+ }
461
+ else {
462
+ await saveInstalledPluginVersion(plugin.id, versionToSave, state.projectPath);
463
+ }
464
+ }
465
+ modal.hideModal();
466
+ fetchData();
467
+ }
468
+ catch (error) {
469
+ modal.hideModal();
470
+ await modal.message('Error', `Failed to update: ${error}`, 'error');
471
+ }
472
+ };
473
+ // Scope-specific toggle (install if not installed, uninstall if installed)
474
+ const handleScopeToggle = async (scope) => {
475
+ const item = selectableItems[pluginsState.selectedIndex];
476
+ if (!item || item.type !== 'plugin' || !item.plugin)
477
+ return;
478
+ const plugin = item.plugin;
479
+ const latestVersion = plugin.version || '0.0.0';
480
+ const scopeLabel = scope === 'user' ? 'User' : scope === 'project' ? 'Project' : 'Local';
481
+ // Check if installed in this scope
482
+ const scopeData = scope === 'user' ? plugin.userScope :
483
+ scope === 'project' ? plugin.projectScope :
484
+ plugin.localScope;
485
+ const isInstalledInScope = scopeData?.enabled;
486
+ const installedVersion = scopeData?.version;
487
+ // Check if this scope has an update available
488
+ const hasUpdateInScope = isInstalledInScope && installedVersion &&
489
+ latestVersion !== '0.0.0' &&
490
+ installedVersion !== latestVersion;
491
+ // Determine action: update if available, otherwise toggle install/uninstall
492
+ let action;
493
+ if (isInstalledInScope && hasUpdateInScope) {
494
+ action = 'update';
495
+ }
496
+ else if (isInstalledInScope) {
497
+ action = 'uninstall';
498
+ }
499
+ else {
500
+ action = 'install';
501
+ }
502
+ const actionLabel = action === 'update' ? `Updating ${scopeLabel}` :
503
+ action === 'install' ? `Installing to ${scopeLabel}` :
504
+ `Uninstalling from ${scopeLabel}`;
505
+ modal.loading(`${actionLabel}...`);
506
+ try {
507
+ if (action === 'uninstall') {
508
+ // Uninstall from this scope
509
+ if (scope === 'user') {
510
+ await enableGlobalPlugin(plugin.id, false);
511
+ await removeGlobalInstalledPluginVersion(plugin.id);
512
+ }
513
+ else if (scope === 'project') {
514
+ await enablePlugin(plugin.id, false, state.projectPath);
515
+ await removeInstalledPluginVersion(plugin.id, state.projectPath);
516
+ }
517
+ else {
518
+ await enableLocalPlugin(plugin.id, false, state.projectPath);
519
+ await removeLocalInstalledPluginVersion(plugin.id, state.projectPath);
520
+ }
521
+ }
522
+ else {
523
+ // Install or update to this scope (both save the latest version)
524
+ if (scope === 'user') {
525
+ await enableGlobalPlugin(plugin.id, true);
526
+ await saveGlobalInstalledPluginVersion(plugin.id, latestVersion);
527
+ }
528
+ else if (scope === 'project') {
529
+ await enablePlugin(plugin.id, true, state.projectPath);
530
+ await saveInstalledPluginVersion(plugin.id, latestVersion, state.projectPath);
531
+ }
532
+ else {
533
+ await enableLocalPlugin(plugin.id, true, state.projectPath);
534
+ await saveLocalInstalledPluginVersion(plugin.id, latestVersion, state.projectPath);
535
+ }
536
+ }
537
+ modal.hideModal();
538
+ fetchData();
539
+ }
540
+ catch (error) {
541
+ modal.hideModal();
542
+ await modal.message('Error', `Failed: ${error}`, 'error');
543
+ }
544
+ };
545
+ const handleUninstall = async () => {
546
+ const item = selectableItems[pluginsState.selectedIndex];
547
+ if (!item || item.type !== 'plugin' || !item.plugin)
548
+ return;
549
+ const plugin = item.plugin;
550
+ // Build list of scopes where plugin is installed
551
+ const installedScopes = [];
552
+ if (plugin.userScope?.enabled) {
553
+ const ver = plugin.userScope.version ? ` v${plugin.userScope.version}` : '';
554
+ installedScopes.push({ label: `User (global)${ver}`, value: 'user' });
555
+ }
556
+ if (plugin.projectScope?.enabled) {
557
+ const ver = plugin.projectScope.version ? ` v${plugin.projectScope.version}` : '';
558
+ installedScopes.push({ label: `Project${ver}`, value: 'project' });
559
+ }
560
+ if (plugin.localScope?.enabled) {
561
+ const ver = plugin.localScope.version ? ` v${plugin.localScope.version}` : '';
562
+ installedScopes.push({ label: `Local${ver}`, value: 'local' });
563
+ }
564
+ if (installedScopes.length === 0) {
565
+ await modal.message('Not Installed', `${plugin.name} is not installed in any scope.`, 'info');
566
+ return;
567
+ }
568
+ const scopeValue = await modal.select(`Uninstall ${plugin.name}`, `Installed in ${installedScopes.length} scope(s):`, installedScopes);
569
+ if (scopeValue === null)
570
+ return; // Cancelled
571
+ modal.loading(`Uninstalling ${plugin.name}...`);
572
+ try {
573
+ if (scopeValue === 'user') {
574
+ await enableGlobalPlugin(plugin.id, false);
575
+ await removeGlobalInstalledPluginVersion(plugin.id);
576
+ }
577
+ else if (scopeValue === 'project') {
578
+ await enablePlugin(plugin.id, false, state.projectPath);
579
+ await removeInstalledPluginVersion(plugin.id, state.projectPath);
580
+ }
581
+ else {
582
+ // local scope
583
+ await enableLocalPlugin(plugin.id, false, state.projectPath);
584
+ await removeLocalInstalledPluginVersion(plugin.id, state.projectPath);
585
+ }
586
+ modal.hideModal();
587
+ fetchData();
588
+ }
589
+ catch (error) {
590
+ modal.hideModal();
591
+ await modal.message('Error', `Failed to uninstall: ${error}`, 'error');
592
+ }
593
+ };
594
+ // Render loading state
595
+ if (pluginsState.marketplaces.status === 'loading' || pluginsState.plugins.status === 'loading') {
596
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { color: "#7e57c2", bold: true, children: "claudeup Plugins" }), _jsx(Text, { color: "gray", children: "Loading..." })] }));
597
+ }
598
+ // Render error state
599
+ if (pluginsState.marketplaces.status === 'error' || pluginsState.plugins.status === 'error') {
600
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { color: "#7e57c2", bold: true, children: "claudeup Plugins" }), _jsx(Text, { color: "red", children: "Error loading data" })] }));
601
+ }
602
+ // Get selected item for detail panel
603
+ const selectedItem = selectableItems[pluginsState.selectedIndex];
604
+ // Render item with fuzzy highlight support
605
+ const renderListItem = (item, _idx, isSelected) => {
606
+ if (item.type === 'category' && item.marketplace) {
607
+ const mp = item.marketplace;
608
+ // Differentiate marketplace types with appropriate badges
609
+ let statusText = '';
610
+ let statusColor = 'green';
611
+ if (item.marketplaceEnabled) {
612
+ if (mp.name === 'claude-plugins-official') {
613
+ statusText = '★ Official';
614
+ statusColor = 'yellow';
615
+ }
616
+ else if (mp.name === 'claude-code-plugins') {
617
+ statusText = '⚠ Deprecated';
618
+ statusColor = 'gray';
619
+ }
620
+ else if (mp.official) {
621
+ statusText = '★ Official';
622
+ statusColor = 'yellow';
623
+ }
624
+ else {
625
+ statusText = '✓ Added';
626
+ statusColor = 'green';
627
+ }
628
+ }
629
+ if (isSelected) {
630
+ const arrow = item.isExpanded ? '▼' : '▶';
631
+ const count = item.pluginCount !== undefined && item.pluginCount > 0 ? ` (${item.pluginCount})` : '';
632
+ return (_jsx(Text, { backgroundColor: "magenta", color: "white", bold: true, children: ` ${arrow} ${mp.displayName}${count} ` }));
633
+ }
634
+ return (_jsx(CategoryHeader, { title: mp.displayName, expanded: item.isExpanded, count: item.pluginCount, status: statusText, statusColor: statusColor }));
635
+ }
636
+ if (item.type === 'plugin' && item.plugin) {
637
+ const plugin = item.plugin;
638
+ let statusIcon = '○';
639
+ let statusColor = 'gray';
640
+ if (plugin.enabled) {
641
+ statusIcon = '●';
642
+ statusColor = 'green';
643
+ }
644
+ else if (plugin.installedVersion) {
645
+ statusIcon = '●';
646
+ statusColor = 'yellow';
647
+ }
648
+ // Build version string
649
+ let versionStr = '';
650
+ if (plugin.installedVersion && plugin.installedVersion !== '0.0.0') {
651
+ versionStr = ` v${plugin.installedVersion}`;
652
+ if (plugin.hasUpdate && plugin.version) {
653
+ versionStr += ` → v${plugin.version}`;
654
+ }
655
+ }
656
+ // Get fuzzy match highlights if available
657
+ const matches = item._matches;
658
+ const segments = matches ? highlightMatches(plugin.name, matches) : null;
659
+ if (isSelected) {
660
+ const displayText = ` ${statusIcon} ${plugin.name}${versionStr} `;
661
+ return (_jsx(Text, { backgroundColor: "magenta", color: "white", wrap: "truncate", children: displayText }));
662
+ }
663
+ // For non-selected, render with colors
664
+ const displayName = segments
665
+ ? segments.map(seg => seg.text).join('')
666
+ : plugin.name;
667
+ return (_jsxs(Text, { wrap: "truncate", children: [_jsxs(Text, { color: statusColor, children: [' ', statusIcon, " "] }), _jsx(Text, { children: displayName }), _jsx(Text, { color: plugin.hasUpdate ? 'yellow' : 'gray', children: versionStr })] }));
668
+ }
669
+ return _jsx(Text, { color: "gray", children: item.label });
670
+ };
671
+ // Render detail content - compact to fit in available space
672
+ const renderDetail = () => {
673
+ if (!selectedItem) {
674
+ return _jsx(Text, { color: "gray", children: "Select an item" });
675
+ }
676
+ if (selectedItem.type === 'category' && selectedItem.marketplace) {
677
+ const mp = selectedItem.marketplace;
678
+ const isEnabled = selectedItem.marketplaceEnabled;
679
+ // Get appropriate badge for marketplace type
680
+ const getBadge = () => {
681
+ if (mp.name === 'claude-plugins-official')
682
+ return ' ★';
683
+ if (mp.name === 'claude-code-plugins')
684
+ return ' ⚠';
685
+ if (mp.official)
686
+ return ' ★';
687
+ return '';
688
+ };
689
+ // Determine action hint based on state
690
+ const isCollapsed = pluginsState.collapsedMarketplaces.has(mp.name);
691
+ const hasPlugins = (selectedItem.pluginCount || 0) > 0;
692
+ let actionHint = 'Add';
693
+ if (isEnabled) {
694
+ if (isCollapsed) {
695
+ actionHint = 'Expand';
696
+ }
697
+ else if (hasPlugins) {
698
+ actionHint = 'Collapse';
699
+ }
700
+ else {
701
+ actionHint = 'Remove';
702
+ }
703
+ }
704
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, color: "cyan", children: [mp.displayName, getBadge()] }), _jsx(Text, { color: "gray", wrap: "wrap", children: mp.description || 'No description' }), _jsx(Text, { color: isEnabled ? 'green' : 'gray', children: isEnabled ? '● Added' : '○ Not added' }), _jsxs(Text, { color: "blue", wrap: "wrap", children: ["github.com/", mp.source.repo] }), _jsxs(Text, { children: ["Plugins: ", selectedItem.pluginCount || 0] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { backgroundColor: isEnabled ? 'cyan' : 'green', color: "black", children: " Enter " }), _jsxs(Text, { color: "gray", children: [" ", actionHint] })] }), isEnabled && (_jsx(Box, { children: _jsx(Text, { color: "gray", children: "\u2190 \u2192 to expand/collapse" }) }))] }));
705
+ }
706
+ if (selectedItem.type === 'plugin' && selectedItem.plugin) {
707
+ const plugin = selectedItem.plugin;
708
+ const isInstalled = plugin.enabled || plugin.installedVersion;
709
+ // Build component counts
710
+ const components = [];
711
+ if (plugin.agents?.length)
712
+ components.push(`${plugin.agents.length} agents`);
713
+ if (plugin.commands?.length)
714
+ components.push(`${plugin.commands.length} commands`);
715
+ if (plugin.skills?.length)
716
+ components.push(`${plugin.skills.length} skills`);
717
+ if (plugin.mcpServers?.length)
718
+ components.push(`${plugin.mcpServers.length} MCP`);
719
+ if (plugin.lspServers && Object.keys(plugin.lspServers).length) {
720
+ components.push(`${Object.keys(plugin.lspServers).length} LSP`);
721
+ }
722
+ // Show version only if valid (not null, not 0.0.0)
723
+ const showVersion = plugin.version && plugin.version !== '0.0.0';
724
+ const showInstalledVersion = plugin.installedVersion && plugin.installedVersion !== '0.0.0';
725
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { justifyContent: "center", children: _jsx(Text, { backgroundColor: "magenta", color: "white", bold: true, children: ` ${plugin.name}${plugin.hasUpdate ? ' ⬆' : ''} ` }) }), _jsx(Box, { marginTop: 1, children: isInstalled ? (_jsx(Text, { color: plugin.enabled ? 'green' : 'yellow', children: plugin.enabled ? '● Enabled' : '● Disabled' })) : (_jsx(Text, { color: "gray", children: "\u25CB Not installed" })) }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { color: "white", wrap: "wrap", children: plugin.description }) }), showVersion && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "Version " }), _jsxs(Text, { color: "blue", children: ["v", plugin.version] }), showInstalledVersion && plugin.installedVersion !== plugin.version && _jsxs(Text, { dimColor: true, children: [" (v", plugin.installedVersion, " installed)"] })] })), plugin.category && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "Category " }), _jsx(Text, { color: "magenta", children: plugin.category })] })), plugin.author && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "Author " }), _jsx(Text, { children: plugin.author.name })] })), components.length > 0 && (_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "Contains " }), _jsx(Text, { color: "yellow", children: components.join(' · ') })] })), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), _jsx(Text, { bold: true, children: "Scopes:" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { backgroundColor: "cyan", color: "black", children: " u " }), _jsx(Text, { color: plugin.userScope?.enabled ? 'cyan' : 'gray', children: plugin.userScope?.enabled ? ' ● ' : ' ○ ' }), _jsx(Text, { color: "cyan", children: "User" }), _jsx(Text, { dimColor: true, children: " global" }), plugin.userScope?.version && _jsxs(Text, { color: "cyan", children: [" v", plugin.userScope.version] })] }), _jsxs(Box, { children: [_jsx(Text, { backgroundColor: "green", color: "black", children: " p " }), _jsx(Text, { color: plugin.projectScope?.enabled ? 'green' : 'gray', children: plugin.projectScope?.enabled ? ' ● ' : ' ○ ' }), _jsx(Text, { color: "green", children: "Project" }), _jsx(Text, { dimColor: true, children: " team" }), plugin.projectScope?.version && _jsxs(Text, { color: "green", children: [" v", plugin.projectScope.version] })] }), _jsxs(Box, { children: [_jsx(Text, { backgroundColor: "yellow", color: "black", children: " l " }), _jsx(Text, { color: plugin.localScope?.enabled ? 'yellow' : 'gray', children: plugin.localScope?.enabled ? ' ● ' : ' ○ ' }), _jsx(Text, { color: "yellow", children: "Local" }), _jsx(Text, { dimColor: true, children: " private" }), plugin.localScope?.version && _jsxs(Text, { color: "yellow", children: [" v", plugin.localScope.version] })] })] })] }), isInstalled && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [plugin.hasUpdate && (_jsxs(Box, { children: [_jsx(Text, { backgroundColor: "magenta", color: "white", children: " U " }), _jsxs(Text, { children: [" Update to v", plugin.version] })] })), _jsxs(Box, { children: [_jsx(Text, { backgroundColor: "red", color: "white", children: " d " }), _jsx(Text, { children: " Uninstall" })] })] }))] }));
726
+ }
727
+ return null;
728
+ };
729
+ const footerHints = isSearchActive
730
+ ? 'Type to search │ Enter Confirm │ Esc Cancel'
731
+ : 'u/p/l:scope │ U:update │ a:all │ d:remove │ /:search';
732
+ // Calculate status for subtitle
733
+ const scopeLabel = pluginsState.scope === 'global' ? 'Global' : 'Project';
734
+ const plugins = pluginsState.plugins.status === 'success' ? pluginsState.plugins.data : [];
735
+ const installedCount = plugins.filter(p => p.enabled).length;
736
+ const updateCount = plugins.filter(p => p.hasUpdate).length;
737
+ const subtitle = `${scopeLabel} │ ${installedCount} installed${updateCount > 0 ? ` │ ${updateCount} updates` : ''}`;
738
+ // Search placeholder shows status when not searching
739
+ const searchPlaceholder = `${scopeLabel} │ ${installedCount} installed${updateCount > 0 ? ` │ ${updateCount} ⬆` : ''} │ / to search`;
740
+ return (_jsx(ScreenLayout, { title: "claudeup Plugins", subtitle: subtitle, currentScreen: "plugins", search: {
741
+ isActive: isSearchActive,
742
+ query: pluginsState.searchQuery,
743
+ placeholder: searchPlaceholder,
744
+ }, footerHints: footerHints, listPanel: _jsx(ScrollableList, { items: selectableItems, selectedIndex: pluginsState.selectedIndex, renderItem: renderListItem, maxHeight: dimensions.listPanelHeight }), detailPanel: renderDetail() }));
745
+ }
746
+ export default PluginsScreen;
747
+ //# sourceMappingURL=PluginsScreen.js.map