claudeup 1.8.0 → 3.3.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 (301) hide show
  1. package/bin/claudeup.js +20 -2
  2. package/package.json +10 -19
  3. package/src/data/cli-tools.js +123 -0
  4. package/src/data/cli-tools.ts +140 -0
  5. package/{dist → src}/data/marketplaces.js +23 -24
  6. package/src/data/marketplaces.ts +95 -0
  7. package/src/data/mcp-servers.js +509 -0
  8. package/src/data/mcp-servers.ts +526 -0
  9. package/src/data/statuslines.js +159 -0
  10. package/src/data/statuslines.ts +188 -0
  11. package/src/index.js +4 -0
  12. package/src/index.ts +5 -0
  13. package/{dist → src}/main.js +46 -47
  14. package/src/main.tsx +145 -0
  15. package/src/opentui.d.ts +191 -0
  16. package/{dist → src}/prerunner/index.js +31 -41
  17. package/src/prerunner/index.ts +124 -0
  18. package/{dist → src}/services/claude-runner.js +9 -10
  19. package/src/services/claude-runner.ts +31 -0
  20. package/{dist → src}/services/claude-settings.js +72 -33
  21. package/src/services/claude-settings.ts +934 -0
  22. package/src/services/local-marketplace.js +339 -0
  23. package/src/services/local-marketplace.ts +489 -0
  24. package/{dist → src}/services/mcp-registry.js +13 -14
  25. package/src/services/mcp-registry.ts +105 -0
  26. package/{dist → src}/services/plugin-manager.js +61 -13
  27. package/src/services/plugin-manager.ts +693 -0
  28. package/{dist → src}/services/plugin-mcp-config.js +19 -18
  29. package/src/services/plugin-mcp-config.ts +242 -0
  30. package/{dist → src}/services/update-cache.js +0 -1
  31. package/src/services/update-cache.ts +78 -0
  32. package/{dist → src}/services/version-check.js +15 -14
  33. package/src/services/version-check.ts +122 -0
  34. package/src/types/index.js +1 -0
  35. package/src/types/index.ts +141 -0
  36. package/src/ui/App.js +213 -0
  37. package/src/ui/App.tsx +359 -0
  38. package/src/ui/components/CategoryHeader.js +9 -0
  39. package/src/ui/components/CategoryHeader.tsx +41 -0
  40. package/{dist → src}/ui/components/ScrollableList.js +19 -6
  41. package/src/ui/components/ScrollableList.tsx +98 -0
  42. package/src/ui/components/SearchInput.js +19 -0
  43. package/src/ui/components/SearchInput.tsx +56 -0
  44. package/src/ui/components/StyledText.js +39 -0
  45. package/src/ui/components/StyledText.tsx +70 -0
  46. package/src/ui/components/TabBar.js +38 -0
  47. package/src/ui/components/TabBar.tsx +88 -0
  48. package/src/ui/components/layout/Panel.js +6 -0
  49. package/src/ui/components/layout/Panel.tsx +62 -0
  50. package/src/ui/components/layout/ProgressBar.js +14 -0
  51. package/src/ui/components/layout/ProgressBar.tsx +47 -0
  52. package/src/ui/components/layout/ScopeTabs.js +6 -0
  53. package/src/ui/components/layout/ScopeTabs.tsx +53 -0
  54. package/src/ui/components/layout/ScreenLayout.js +21 -0
  55. package/src/ui/components/layout/ScreenLayout.tsx +147 -0
  56. package/src/ui/components/layout/index.js +4 -0
  57. package/src/ui/components/layout/index.ts +4 -0
  58. package/src/ui/components/modals/ConfirmModal.js +14 -0
  59. package/src/ui/components/modals/ConfirmModal.tsx +59 -0
  60. package/src/ui/components/modals/InputModal.js +16 -0
  61. package/src/ui/components/modals/InputModal.tsx +68 -0
  62. package/src/ui/components/modals/LoadingModal.js +14 -0
  63. package/src/ui/components/modals/LoadingModal.tsx +40 -0
  64. package/src/ui/components/modals/MessageModal.js +16 -0
  65. package/src/ui/components/modals/MessageModal.tsx +64 -0
  66. package/src/ui/components/modals/ModalContainer.js +56 -0
  67. package/src/ui/components/modals/ModalContainer.tsx +104 -0
  68. package/src/ui/components/modals/SelectModal.js +26 -0
  69. package/src/ui/components/modals/SelectModal.tsx +82 -0
  70. package/src/ui/components/modals/index.js +6 -0
  71. package/src/ui/components/modals/index.ts +6 -0
  72. package/src/ui/hooks/index.js +3 -0
  73. package/src/ui/hooks/index.ts +3 -0
  74. package/{dist → src}/ui/hooks/useAsyncData.js +21 -22
  75. package/src/ui/hooks/useAsyncData.ts +127 -0
  76. package/src/ui/hooks/useKeyboard.js +13 -0
  77. package/src/ui/hooks/useKeyboard.ts +26 -0
  78. package/src/ui/hooks/useKeyboardHandler.js +39 -0
  79. package/src/ui/hooks/useKeyboardHandler.ts +63 -0
  80. package/{dist → src}/ui/screens/CliToolsScreen.js +60 -54
  81. package/src/ui/screens/CliToolsScreen.tsx +468 -0
  82. package/src/ui/screens/EnvVarsScreen.js +154 -0
  83. package/src/ui/screens/EnvVarsScreen.tsx +269 -0
  84. package/{dist → src}/ui/screens/McpRegistryScreen.js +56 -55
  85. package/src/ui/screens/McpRegistryScreen.tsx +331 -0
  86. package/{dist → src}/ui/screens/McpScreen.js +46 -47
  87. package/src/ui/screens/McpScreen.tsx +392 -0
  88. package/src/ui/screens/ModelSelectorScreen.js +292 -0
  89. package/src/ui/screens/ModelSelectorScreen.tsx +441 -0
  90. package/{dist → src}/ui/screens/PluginsScreen.js +305 -293
  91. package/src/ui/screens/PluginsScreen.tsx +1231 -0
  92. package/src/ui/screens/StatusLineScreen.js +200 -0
  93. package/src/ui/screens/StatusLineScreen.tsx +411 -0
  94. package/src/ui/screens/index.js +7 -0
  95. package/src/ui/screens/index.ts +7 -0
  96. package/src/ui/state/AnimationContext.js +34 -0
  97. package/src/ui/state/AnimationContext.tsx +76 -0
  98. package/{dist → src}/ui/state/AppContext.js +31 -32
  99. package/src/ui/state/AppContext.tsx +235 -0
  100. package/{dist → src}/ui/state/DimensionsContext.js +16 -17
  101. package/src/ui/state/DimensionsContext.tsx +144 -0
  102. package/{dist → src}/ui/state/reducer.js +89 -90
  103. package/src/ui/state/reducer.ts +467 -0
  104. package/src/ui/state/types.js +1 -0
  105. package/src/ui/state/types.ts +273 -0
  106. package/{dist → src}/utils/command-utils.js +3 -4
  107. package/src/utils/command-utils.ts +20 -0
  108. package/{dist → src}/utils/fuzzy-search.js +2 -3
  109. package/src/utils/fuzzy-search.ts +138 -0
  110. package/{dist → src}/utils/string-utils.js +6 -6
  111. package/src/utils/string-utils.ts +88 -0
  112. package/dist/data/cli-tools.d.ts +0 -13
  113. package/dist/data/cli-tools.d.ts.map +0 -1
  114. package/dist/data/cli-tools.js +0 -124
  115. package/dist/data/cli-tools.js.map +0 -1
  116. package/dist/data/marketplaces.d.ts +0 -6
  117. package/dist/data/marketplaces.d.ts.map +0 -1
  118. package/dist/data/marketplaces.js.map +0 -1
  119. package/dist/data/mcp-servers.d.ts +0 -8
  120. package/dist/data/mcp-servers.d.ts.map +0 -1
  121. package/dist/data/mcp-servers.js +0 -503
  122. package/dist/data/mcp-servers.js.map +0 -1
  123. package/dist/data/statuslines.d.ts +0 -10
  124. package/dist/data/statuslines.d.ts.map +0 -1
  125. package/dist/data/statuslines.js +0 -160
  126. package/dist/data/statuslines.js.map +0 -1
  127. package/dist/index.d.ts +0 -3
  128. package/dist/index.d.ts.map +0 -1
  129. package/dist/index.js +0 -90
  130. package/dist/index.js.map +0 -1
  131. package/dist/main.d.ts +0 -3
  132. package/dist/main.d.ts.map +0 -1
  133. package/dist/main.js.map +0 -1
  134. package/dist/prerunner/index.d.ts +0 -7
  135. package/dist/prerunner/index.d.ts.map +0 -1
  136. package/dist/prerunner/index.js.map +0 -1
  137. package/dist/services/claude-runner.d.ts +0 -7
  138. package/dist/services/claude-runner.d.ts.map +0 -1
  139. package/dist/services/claude-runner.js.map +0 -1
  140. package/dist/services/claude-settings.d.ts +0 -73
  141. package/dist/services/claude-settings.d.ts.map +0 -1
  142. package/dist/services/claude-settings.js.map +0 -1
  143. package/dist/services/local-marketplace.d.ts +0 -111
  144. package/dist/services/local-marketplace.d.ts.map +0 -1
  145. package/dist/services/local-marketplace.js +0 -599
  146. package/dist/services/local-marketplace.js.map +0 -1
  147. package/dist/services/mcp-registry.d.ts +0 -10
  148. package/dist/services/mcp-registry.d.ts.map +0 -1
  149. package/dist/services/mcp-registry.js.map +0 -1
  150. package/dist/services/plugin-manager.d.ts +0 -65
  151. package/dist/services/plugin-manager.d.ts.map +0 -1
  152. package/dist/services/plugin-manager.js.map +0 -1
  153. package/dist/services/plugin-mcp-config.d.ts +0 -52
  154. package/dist/services/plugin-mcp-config.d.ts.map +0 -1
  155. package/dist/services/plugin-mcp-config.js.map +0 -1
  156. package/dist/services/update-cache.d.ts +0 -21
  157. package/dist/services/update-cache.d.ts.map +0 -1
  158. package/dist/services/update-cache.js.map +0 -1
  159. package/dist/services/version-check.d.ts +0 -20
  160. package/dist/services/version-check.d.ts.map +0 -1
  161. package/dist/services/version-check.js.map +0 -1
  162. package/dist/types/index.d.ts +0 -105
  163. package/dist/types/index.d.ts.map +0 -1
  164. package/dist/types/index.js +0 -2
  165. package/dist/types/index.js.map +0 -1
  166. package/dist/ui/InkApp.d.ts +0 -5
  167. package/dist/ui/InkApp.d.ts.map +0 -1
  168. package/dist/ui/InkApp.js +0 -188
  169. package/dist/ui/InkApp.js.map +0 -1
  170. package/dist/ui/components/CategoryHeader.d.ts +0 -16
  171. package/dist/ui/components/CategoryHeader.d.ts.map +0 -1
  172. package/dist/ui/components/CategoryHeader.js +0 -11
  173. package/dist/ui/components/CategoryHeader.js.map +0 -1
  174. package/dist/ui/components/ScrollableList.d.ts +0 -16
  175. package/dist/ui/components/ScrollableList.d.ts.map +0 -1
  176. package/dist/ui/components/ScrollableList.js.map +0 -1
  177. package/dist/ui/components/SearchInput.d.ts +0 -18
  178. package/dist/ui/components/SearchInput.d.ts.map +0 -1
  179. package/dist/ui/components/SearchInput.js +0 -30
  180. package/dist/ui/components/SearchInput.js.map +0 -1
  181. package/dist/ui/components/TabBar.d.ts +0 -8
  182. package/dist/ui/components/TabBar.d.ts.map +0 -1
  183. package/dist/ui/components/TabBar.js +0 -18
  184. package/dist/ui/components/TabBar.js.map +0 -1
  185. package/dist/ui/components/layout/Footer.d.ts +0 -14
  186. package/dist/ui/components/layout/Footer.d.ts.map +0 -1
  187. package/dist/ui/components/layout/Footer.js +0 -23
  188. package/dist/ui/components/layout/Footer.js.map +0 -1
  189. package/dist/ui/components/layout/Header.d.ts +0 -4
  190. package/dist/ui/components/layout/Header.d.ts.map +0 -1
  191. package/dist/ui/components/layout/Header.js +0 -25
  192. package/dist/ui/components/layout/Header.js.map +0 -1
  193. package/dist/ui/components/layout/Panel.d.ts +0 -22
  194. package/dist/ui/components/layout/Panel.d.ts.map +0 -1
  195. package/dist/ui/components/layout/Panel.js +0 -8
  196. package/dist/ui/components/layout/Panel.js.map +0 -1
  197. package/dist/ui/components/layout/ProgressBar.d.ts +0 -12
  198. package/dist/ui/components/layout/ProgressBar.d.ts.map +0 -1
  199. package/dist/ui/components/layout/ProgressBar.js +0 -16
  200. package/dist/ui/components/layout/ProgressBar.js.map +0 -1
  201. package/dist/ui/components/layout/ScopeTabs.d.ts +0 -12
  202. package/dist/ui/components/layout/ScopeTabs.d.ts.map +0 -1
  203. package/dist/ui/components/layout/ScopeTabs.js +0 -8
  204. package/dist/ui/components/layout/ScopeTabs.js.map +0 -1
  205. package/dist/ui/components/layout/ScreenLayout.d.ts +0 -30
  206. package/dist/ui/components/layout/ScreenLayout.d.ts.map +0 -1
  207. package/dist/ui/components/layout/ScreenLayout.js +0 -23
  208. package/dist/ui/components/layout/ScreenLayout.js.map +0 -1
  209. package/dist/ui/components/layout/index.d.ts +0 -7
  210. package/dist/ui/components/layout/index.d.ts.map +0 -1
  211. package/dist/ui/components/layout/index.js +0 -7
  212. package/dist/ui/components/layout/index.js.map +0 -1
  213. package/dist/ui/components/modals/ConfirmModal.d.ts +0 -14
  214. package/dist/ui/components/modals/ConfirmModal.d.ts.map +0 -1
  215. package/dist/ui/components/modals/ConfirmModal.js +0 -15
  216. package/dist/ui/components/modals/ConfirmModal.js.map +0 -1
  217. package/dist/ui/components/modals/InputModal.d.ts +0 -16
  218. package/dist/ui/components/modals/InputModal.d.ts.map +0 -1
  219. package/dist/ui/components/modals/InputModal.js +0 -23
  220. package/dist/ui/components/modals/InputModal.js.map +0 -1
  221. package/dist/ui/components/modals/LoadingModal.d.ts +0 -8
  222. package/dist/ui/components/modals/LoadingModal.d.ts.map +0 -1
  223. package/dist/ui/components/modals/LoadingModal.js +0 -8
  224. package/dist/ui/components/modals/LoadingModal.js.map +0 -1
  225. package/dist/ui/components/modals/MessageModal.d.ts +0 -14
  226. package/dist/ui/components/modals/MessageModal.d.ts.map +0 -1
  227. package/dist/ui/components/modals/MessageModal.js +0 -17
  228. package/dist/ui/components/modals/MessageModal.js.map +0 -1
  229. package/dist/ui/components/modals/ModalContainer.d.ts +0 -7
  230. package/dist/ui/components/modals/ModalContainer.d.ts.map +0 -1
  231. package/dist/ui/components/modals/ModalContainer.js +0 -38
  232. package/dist/ui/components/modals/ModalContainer.js.map +0 -1
  233. package/dist/ui/components/modals/SelectModal.d.ts +0 -17
  234. package/dist/ui/components/modals/SelectModal.d.ts.map +0 -1
  235. package/dist/ui/components/modals/SelectModal.js +0 -33
  236. package/dist/ui/components/modals/SelectModal.js.map +0 -1
  237. package/dist/ui/components/modals/index.d.ts +0 -7
  238. package/dist/ui/components/modals/index.d.ts.map +0 -1
  239. package/dist/ui/components/modals/index.js +0 -7
  240. package/dist/ui/components/modals/index.js.map +0 -1
  241. package/dist/ui/hooks/index.d.ts +0 -3
  242. package/dist/ui/hooks/index.d.ts.map +0 -1
  243. package/dist/ui/hooks/index.js +0 -3
  244. package/dist/ui/hooks/index.js.map +0 -1
  245. package/dist/ui/hooks/useAsyncData.d.ts +0 -40
  246. package/dist/ui/hooks/useAsyncData.d.ts.map +0 -1
  247. package/dist/ui/hooks/useAsyncData.js.map +0 -1
  248. package/dist/ui/hooks/useKeyboardNavigation.d.ts +0 -27
  249. package/dist/ui/hooks/useKeyboardNavigation.d.ts.map +0 -1
  250. package/dist/ui/hooks/useKeyboardNavigation.js +0 -82
  251. package/dist/ui/hooks/useKeyboardNavigation.js.map +0 -1
  252. package/dist/ui/screens/CliToolsScreen.d.ts +0 -4
  253. package/dist/ui/screens/CliToolsScreen.d.ts.map +0 -1
  254. package/dist/ui/screens/CliToolsScreen.js.map +0 -1
  255. package/dist/ui/screens/EnvVarsScreen.d.ts +0 -4
  256. package/dist/ui/screens/EnvVarsScreen.d.ts.map +0 -1
  257. package/dist/ui/screens/EnvVarsScreen.js +0 -145
  258. package/dist/ui/screens/EnvVarsScreen.js.map +0 -1
  259. package/dist/ui/screens/McpRegistryScreen.d.ts +0 -4
  260. package/dist/ui/screens/McpRegistryScreen.d.ts.map +0 -1
  261. package/dist/ui/screens/McpRegistryScreen.js.map +0 -1
  262. package/dist/ui/screens/McpScreen.d.ts +0 -4
  263. package/dist/ui/screens/McpScreen.d.ts.map +0 -1
  264. package/dist/ui/screens/McpScreen.js.map +0 -1
  265. package/dist/ui/screens/ModelSelectorScreen.d.ts +0 -4
  266. package/dist/ui/screens/ModelSelectorScreen.d.ts.map +0 -1
  267. package/dist/ui/screens/ModelSelectorScreen.js +0 -143
  268. package/dist/ui/screens/ModelSelectorScreen.js.map +0 -1
  269. package/dist/ui/screens/PluginsScreen.d.ts +0 -4
  270. package/dist/ui/screens/PluginsScreen.d.ts.map +0 -1
  271. package/dist/ui/screens/PluginsScreen.js.map +0 -1
  272. package/dist/ui/screens/StatusLineScreen.d.ts +0 -4
  273. package/dist/ui/screens/StatusLineScreen.d.ts.map +0 -1
  274. package/dist/ui/screens/StatusLineScreen.js +0 -197
  275. package/dist/ui/screens/StatusLineScreen.js.map +0 -1
  276. package/dist/ui/screens/index.d.ts +0 -8
  277. package/dist/ui/screens/index.d.ts.map +0 -1
  278. package/dist/ui/screens/index.js +0 -8
  279. package/dist/ui/screens/index.js.map +0 -1
  280. package/dist/ui/state/AppContext.d.ts +0 -40
  281. package/dist/ui/state/AppContext.d.ts.map +0 -1
  282. package/dist/ui/state/AppContext.js.map +0 -1
  283. package/dist/ui/state/DimensionsContext.d.ts +0 -27
  284. package/dist/ui/state/DimensionsContext.d.ts.map +0 -1
  285. package/dist/ui/state/DimensionsContext.js.map +0 -1
  286. package/dist/ui/state/reducer.d.ts +0 -4
  287. package/dist/ui/state/reducer.d.ts.map +0 -1
  288. package/dist/ui/state/reducer.js.map +0 -1
  289. package/dist/ui/state/types.d.ts +0 -266
  290. package/dist/ui/state/types.d.ts.map +0 -1
  291. package/dist/ui/state/types.js +0 -2
  292. package/dist/ui/state/types.js.map +0 -1
  293. package/dist/utils/command-utils.d.ts +0 -8
  294. package/dist/utils/command-utils.d.ts.map +0 -1
  295. package/dist/utils/command-utils.js.map +0 -1
  296. package/dist/utils/fuzzy-search.d.ts +0 -33
  297. package/dist/utils/fuzzy-search.d.ts.map +0 -1
  298. package/dist/utils/fuzzy-search.js.map +0 -1
  299. package/dist/utils/string-utils.d.ts +0 -24
  300. package/dist/utils/string-utils.d.ts.map +0 -1
  301. package/dist/utils/string-utils.js.map +0 -1
@@ -0,0 +1,269 @@
1
+ import React, { useEffect, useCallback, useState } from "react";
2
+ import { useApp, useModal } from "../state/AppContext.js";
3
+ import { useDimensions } from "../state/DimensionsContext.js";
4
+ import { useKeyboard } from "../hooks/useKeyboard.js";
5
+ import { ScreenLayout } from "../components/layout/index.js";
6
+ import { ScrollableList } from "../components/ScrollableList.js";
7
+ import {
8
+ getMcpEnvVars,
9
+ setMcpEnvVar,
10
+ removeMcpEnvVar,
11
+ } from "../../services/claude-settings.js";
12
+
13
+ interface EnvVar {
14
+ name: string;
15
+ value: string;
16
+ }
17
+
18
+ export function EnvVarsScreen() {
19
+ const { state, dispatch } = useApp();
20
+ const { envVars } = state;
21
+ const modal = useModal();
22
+ const dimensions = useDimensions();
23
+
24
+ const [envVarList, setEnvVarList] = useState<EnvVar[]>([]);
25
+ const [isLoading, setIsLoading] = useState(true);
26
+
27
+ // Fetch data
28
+ const fetchData = useCallback(async () => {
29
+ setIsLoading(true);
30
+ try {
31
+ const vars = await getMcpEnvVars(state.projectPath);
32
+ const list = Object.entries(vars).map(([name, value]) => ({
33
+ name,
34
+ value,
35
+ }));
36
+ setEnvVarList(list);
37
+ } catch (error) {
38
+ setEnvVarList([]);
39
+ }
40
+ setIsLoading(false);
41
+ }, [state.projectPath]);
42
+
43
+ useEffect(() => {
44
+ fetchData();
45
+ }, [fetchData]);
46
+
47
+ // Keyboard handling
48
+ useKeyboard((event) => {
49
+ if (state.isSearching || state.modal) return;
50
+
51
+ if (event.name === "up" || event.name === "k") {
52
+ const newIndex = Math.max(0, envVars.selectedIndex - 1);
53
+ dispatch({ type: "ENVVARS_SELECT", index: newIndex });
54
+ } else if (event.name === "down" || event.name === "j") {
55
+ const newIndex = Math.min(
56
+ Math.max(0, envVarList.length - 1),
57
+ envVars.selectedIndex + 1,
58
+ );
59
+ dispatch({ type: "ENVVARS_SELECT", index: newIndex });
60
+ } else if (event.name === "a") {
61
+ handleAdd();
62
+ } else if (event.name === "e" || event.name === "enter") {
63
+ handleEdit();
64
+ } else if (event.name === "d") {
65
+ handleDelete();
66
+ }
67
+ });
68
+
69
+ const handleAdd = async () => {
70
+ const varName = await modal.input("Add Variable", "Variable name:");
71
+ if (varName === null || !varName.trim()) return;
72
+
73
+ const cleanName = varName
74
+ .trim()
75
+ .toUpperCase()
76
+ .replace(/[^A-Z0-9_]/g, "_");
77
+
78
+ // Check if already exists
79
+ const existing = envVarList.find((v) => v.name === cleanName);
80
+ if (existing) {
81
+ const overwrite = await modal.confirm(
82
+ `${cleanName} exists`,
83
+ "Overwrite existing value?",
84
+ );
85
+ if (!overwrite) return;
86
+ }
87
+
88
+ const value = await modal.input(`Set ${cleanName}`, "Value:");
89
+ if (value === null) return;
90
+
91
+ modal.loading(`Adding ${cleanName}...`);
92
+ try {
93
+ await setMcpEnvVar(cleanName, value, state.projectPath);
94
+ modal.hideModal();
95
+ await modal.message(
96
+ "Added",
97
+ `${cleanName} added.\nRestart Claude Code to apply.`,
98
+ "success",
99
+ );
100
+ fetchData();
101
+ } catch (error) {
102
+ modal.hideModal();
103
+ await modal.message("Error", `Failed to add: ${error}`, "error");
104
+ }
105
+ };
106
+
107
+ const handleEdit = async () => {
108
+ if (envVarList.length === 0) return;
109
+ const envVar = envVarList[envVars.selectedIndex];
110
+ if (!envVar) return;
111
+
112
+ const newValue = await modal.input(
113
+ `Edit ${envVar.name}`,
114
+ "New value:",
115
+ envVar.value,
116
+ );
117
+ if (newValue === null) return;
118
+
119
+ modal.loading(`Updating ${envVar.name}...`);
120
+ try {
121
+ await setMcpEnvVar(envVar.name, newValue, state.projectPath);
122
+ modal.hideModal();
123
+ await modal.message(
124
+ "Updated",
125
+ `${envVar.name} updated.\nRestart Claude Code to apply.`,
126
+ "success",
127
+ );
128
+ fetchData();
129
+ } catch (error) {
130
+ modal.hideModal();
131
+ await modal.message("Error", `Failed to update: ${error}`, "error");
132
+ }
133
+ };
134
+
135
+ const handleDelete = async () => {
136
+ if (envVarList.length === 0) return;
137
+ const envVar = envVarList[envVars.selectedIndex];
138
+ if (!envVar) return;
139
+
140
+ const confirmed = await modal.confirm(
141
+ `Delete ${envVar.name}?`,
142
+ "This will remove the variable from configuration.",
143
+ );
144
+
145
+ if (confirmed) {
146
+ modal.loading(`Deleting ${envVar.name}...`);
147
+ try {
148
+ await removeMcpEnvVar(envVar.name, state.projectPath);
149
+ modal.hideModal();
150
+ await modal.message("Deleted", `${envVar.name} removed.`, "success");
151
+ fetchData();
152
+ } catch (error) {
153
+ modal.hideModal();
154
+ await modal.message("Error", `Failed to delete: ${error}`, "error");
155
+ }
156
+ }
157
+ };
158
+
159
+ // Get selected item
160
+ const selectedVar = envVarList[envVars.selectedIndex];
161
+
162
+ const renderDetail = () => {
163
+ if (isLoading) {
164
+ return <text fg="gray">Loading environment variables...</text>;
165
+ }
166
+
167
+ if (envVarList.length === 0) {
168
+ return (
169
+ <box flexDirection="column">
170
+ <text fg="gray">No environment variables configured.</text>
171
+ <box marginTop={1}>
172
+ <text fg="green">Press 'a' to add a new variable</text>
173
+ </box>
174
+ </box>
175
+ );
176
+ }
177
+
178
+ if (!selectedVar) {
179
+ return <text fg="gray">Select a variable to see details</text>;
180
+ }
181
+
182
+ return (
183
+ <box flexDirection="column">
184
+ <text fg="cyan">
185
+ <strong>{selectedVar.name}</strong>
186
+ </text>
187
+ <box marginTop={1}>
188
+ <text fg="gray">Value: </text>
189
+ <text>
190
+ {selectedVar.value.length > 50
191
+ ? selectedVar.value.slice(0, 50) + "..."
192
+ : selectedVar.value}
193
+ </text>
194
+ </box>
195
+ <box marginTop={2} flexDirection="column">
196
+ <box>
197
+ <text bg="magenta" fg="white">
198
+ {" "}
199
+ Enter{" "}
200
+ </text>
201
+ <text fg="gray"> Edit value</text>
202
+ </box>
203
+ <box marginTop={1}>
204
+ <text bg="red" fg="white">
205
+ {" "}
206
+ d{" "}
207
+ </text>
208
+ <text fg="gray"> Delete variable</text>
209
+ </box>
210
+ </box>
211
+ </box>
212
+ );
213
+ };
214
+
215
+ const renderListItem = (
216
+ envVar: EnvVar,
217
+ _idx: number,
218
+ isSelected: boolean,
219
+ ) => {
220
+ const masked =
221
+ envVar.value.length > 20
222
+ ? envVar.value.slice(0, 20) + "..."
223
+ : envVar.value;
224
+ return isSelected ? (
225
+ <text bg="magenta" fg="white">
226
+ {" "}
227
+ {envVar.name} = "{masked}"{" "}
228
+ </text>
229
+ ) : (
230
+ <text>
231
+ <span fg="cyan">{envVar.name}</span>
232
+ <span fg="gray"> = "{masked}"</span>
233
+ </text>
234
+ );
235
+ };
236
+
237
+ const statusContent = (
238
+ <>
239
+ <text fg="gray">Variables: </text>
240
+ <text fg="cyan">{envVarList.length}</text>
241
+ <text fg="gray"> │ Location: </text>
242
+ <text fg="green">.claude/settings.local.json</text>
243
+ </>
244
+ );
245
+
246
+ return (
247
+ <ScreenLayout
248
+ title="claudeup Environment Variables"
249
+ currentScreen="env-vars"
250
+ statusLine={statusContent}
251
+ footerHints="↑↓:nav │ Enter/e:edit │ a:add │ d:delete"
252
+ listPanel={
253
+ envVarList.length === 0 ? (
254
+ <text fg="gray">No environment variables configured</text>
255
+ ) : (
256
+ <ScrollableList
257
+ items={envVarList}
258
+ selectedIndex={envVars.selectedIndex}
259
+ renderItem={renderListItem}
260
+ maxHeight={dimensions.listPanelHeight}
261
+ />
262
+ )
263
+ }
264
+ detailPanel={renderDetail()}
265
+ />
266
+ );
267
+ }
268
+
269
+ export default EnvVarsScreen;
@@ -1,12 +1,12 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useCallback, useState, useRef } from 'react';
3
- import { Box, Text, useInput } from 'ink';
4
- import { useApp, useModal, useNavigation } from '../state/AppContext.js';
5
- import { useDimensions } from '../state/DimensionsContext.js';
6
- import { ScreenLayout } from '../components/layout/index.js';
7
- import { ScrollableList } from '../components/ScrollableList.js';
8
- import { searchMcpServers, formatDate } from '../../services/mcp-registry.js';
9
- import { addMcpServer, setAllowMcp } from '../../services/claude-settings.js';
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ import { useEffect, useCallback, useState, useRef } from "react";
3
+ import { useApp, useModal, useNavigation } from "../state/AppContext.js";
4
+ import { useDimensions } from "../state/DimensionsContext.js";
5
+ import { useKeyboard } from "../hooks/useKeyboard.js";
6
+ import { ScreenLayout } from "../components/layout/index.js";
7
+ import { ScrollableList } from "../components/ScrollableList.js";
8
+ import { searchMcpServers, formatDate } from "../../services/mcp-registry.js";
9
+ import { addMcpServer, setAllowMcp } from "../../services/claude-settings.js";
10
10
  /**
11
11
  * Deduplicate servers by name, keeping only the latest version.
12
12
  * Uses version string comparison, falling back to published_at date.
@@ -36,8 +36,8 @@ function deduplicateServers(servers) {
36
36
  function compareVersions(a, b) {
37
37
  // Try semver comparison first
38
38
  if (a.version && b.version) {
39
- const aParts = a.version.split('.').map(n => parseInt(n, 10) || 0);
40
- const bParts = b.version.split('.').map(n => parseInt(n, 10) || 0);
39
+ const aParts = a.version.split(".").map((n) => parseInt(n, 10) || 0);
40
+ const bParts = b.version.split(".").map((n) => parseInt(n, 10) || 0);
41
41
  for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
42
42
  const aVal = aParts[i] || 0;
43
43
  const bVal = bParts[i] || 0;
@@ -48,7 +48,7 @@ function compareVersions(a, b) {
48
48
  }
49
49
  // Fall back to published_at date
50
50
  if (a.published_at && b.published_at) {
51
- return new Date(a.published_at).getTime() - new Date(b.published_at).getTime();
51
+ return (new Date(a.published_at).getTime() - new Date(b.published_at).getTime());
52
52
  }
53
53
  // If one has a date and other doesn't, prefer the one with date
54
54
  if (a.published_at && !b.published_at)
@@ -66,8 +66,10 @@ export function McpRegistryScreen() {
66
66
  const [servers, setServers] = useState([]);
67
67
  const [isLoading, setIsLoading] = useState(true);
68
68
  const [error, setError] = useState(null);
69
- const [searchQuery, setSearchQuery] = useState(mcpRegistry.searchQuery || '');
70
- const isSearchActive = state.isSearching && state.currentRoute.screen === 'mcp-registry' && !state.modal;
69
+ const [searchQuery, setSearchQuery] = useState(mcpRegistry.searchQuery || "");
70
+ const isSearchActive = state.isSearching &&
71
+ state.currentRoute.screen === "mcp-registry" &&
72
+ !state.modal;
71
73
  const searchTimeoutRef = useRef(null);
72
74
  // Load servers
73
75
  const loadServers = useCallback(async (query) => {
@@ -76,14 +78,14 @@ export function McpRegistryScreen() {
76
78
  try {
77
79
  const response = await searchMcpServers({ query, limit: 50 });
78
80
  if (!response || !Array.isArray(response.servers)) {
79
- throw new Error('Invalid response from MCP Registry API');
81
+ throw new Error("Invalid response from MCP Registry API");
80
82
  }
81
83
  // Deduplicate to show only latest version of each server
82
84
  const deduplicated = deduplicateServers(response.servers);
83
85
  setServers(deduplicated);
84
86
  }
85
87
  catch (err) {
86
- setError(err instanceof Error ? err.message : 'Failed to load servers');
88
+ setError(err instanceof Error ? err.message : "Failed to load servers");
87
89
  setServers([]);
88
90
  }
89
91
  setIsLoading(false);
@@ -106,36 +108,36 @@ export function McpRegistryScreen() {
106
108
  };
107
109
  }, []);
108
110
  // Keyboard handling
109
- useInput((input, key) => {
111
+ useKeyboard((event) => {
110
112
  // Handle search mode
111
113
  if (isSearchActive) {
112
- if (key.escape) {
113
- dispatch({ type: 'SET_SEARCHING', isSearching: false });
114
+ if (event.name === "escape") {
115
+ dispatch({ type: "SET_SEARCHING", isSearching: false });
114
116
  }
115
- else if (key.return) {
117
+ else if (event.name === "enter") {
116
118
  // Exit search mode and stay on list for navigation
117
- dispatch({ type: 'SET_SEARCHING', isSearching: false });
119
+ dispatch({ type: "SET_SEARCHING", isSearching: false });
118
120
  }
119
- else if (key.upArrow) {
121
+ else if (event.name === "up") {
120
122
  // Allow navigation while searching
121
123
  const newIndex = Math.max(0, mcpRegistry.selectedIndex - 1);
122
- dispatch({ type: 'MCPREGISTRY_SELECT', index: newIndex });
124
+ dispatch({ type: "MCPREGISTRY_SELECT", index: newIndex });
123
125
  }
124
- else if (key.downArrow) {
126
+ else if (event.name === "down") {
125
127
  // Allow navigation while searching
126
128
  const newIndex = Math.min(Math.max(0, servers.length - 1), mcpRegistry.selectedIndex + 1);
127
- dispatch({ type: 'MCPREGISTRY_SELECT', index: newIndex });
129
+ dispatch({ type: "MCPREGISTRY_SELECT", index: newIndex });
128
130
  }
129
- else if (key.backspace || key.delete) {
131
+ else if (event.name === "backspace" || event.name === "delete") {
130
132
  const newQuery = searchQuery.slice(0, -1);
131
133
  setSearchQuery(newQuery);
132
- dispatch({ type: 'MCPREGISTRY_SEARCH', query: newQuery });
134
+ dispatch({ type: "MCPREGISTRY_SEARCH", query: newQuery });
133
135
  debouncedSearch(newQuery);
134
136
  }
135
- else if (input && !key.ctrl && !key.meta) {
136
- const newQuery = searchQuery + input;
137
+ else if (event.name.length === 1 && !event.ctrl && !event.meta) {
138
+ const newQuery = searchQuery + event.name;
137
139
  setSearchQuery(newQuery);
138
- dispatch({ type: 'MCPREGISTRY_SEARCH', query: newQuery });
140
+ dispatch({ type: "MCPREGISTRY_SEARCH", query: newQuery });
139
141
  debouncedSearch(newQuery);
140
142
  }
141
143
  return;
@@ -143,26 +145,26 @@ export function McpRegistryScreen() {
143
145
  if (state.modal)
144
146
  return;
145
147
  // Start search with /
146
- if (input === '/') {
147
- dispatch({ type: 'SET_SEARCHING', isSearching: true });
148
+ if (event.name === "/") {
149
+ dispatch({ type: "SET_SEARCHING", isSearching: true });
148
150
  return;
149
151
  }
150
152
  // Navigation
151
- if (key.upArrow || input === 'k') {
153
+ if (event.name === "up" || event.name === "k") {
152
154
  const newIndex = Math.max(0, mcpRegistry.selectedIndex - 1);
153
- dispatch({ type: 'MCPREGISTRY_SELECT', index: newIndex });
155
+ dispatch({ type: "MCPREGISTRY_SELECT", index: newIndex });
154
156
  }
155
- else if (key.downArrow || input === 'j') {
157
+ else if (event.name === "down" || event.name === "j") {
156
158
  const newIndex = Math.min(Math.max(0, servers.length - 1), mcpRegistry.selectedIndex + 1);
157
- dispatch({ type: 'MCPREGISTRY_SELECT', index: newIndex });
159
+ dispatch({ type: "MCPREGISTRY_SELECT", index: newIndex });
158
160
  }
159
- else if (input === 'l') {
160
- navigateToScreen('mcp');
161
+ else if (event.name === "l") {
162
+ navigateToScreen("mcp");
161
163
  }
162
- else if (input === 'R') {
164
+ else if (event.name === "R") {
163
165
  loadServers(searchQuery);
164
166
  }
165
- else if (key.return) {
167
+ else if (event.name === "enter") {
166
168
  handleInstall();
167
169
  }
168
170
  });
@@ -171,7 +173,7 @@ export function McpRegistryScreen() {
171
173
  if (!server)
172
174
  return;
173
175
  const config = {
174
- type: 'http',
176
+ type: "http",
175
177
  url: server.url,
176
178
  };
177
179
  modal.loading(`Installing ${server.name}...`);
@@ -179,48 +181,47 @@ export function McpRegistryScreen() {
179
181
  await setAllowMcp(true, state.projectPath);
180
182
  await addMcpServer(server.name, config, state.projectPath);
181
183
  modal.hideModal();
182
- await modal.message('Installed', `${server.name} has been configured.\n\nRestart Claude Code to activate.`, 'success');
184
+ await modal.message("Installed", `${server.name} has been configured.\n\nRestart Claude Code to activate.`, "success");
183
185
  }
184
186
  catch (error) {
185
187
  modal.hideModal();
186
- await modal.message('Error', `Failed to install: ${error}`, 'error');
188
+ await modal.message("Error", `Failed to install: ${error}`, "error");
187
189
  }
188
190
  };
189
191
  // Get selected server
190
192
  const selectedServer = servers[mcpRegistry.selectedIndex];
191
193
  const renderDetail = () => {
192
194
  if (isLoading) {
193
- return _jsx(Text, { color: "gray", children: "Loading..." });
195
+ return _jsx("text", { fg: "gray", children: "Loading..." });
194
196
  }
195
197
  if (error) {
196
- return _jsxs(Text, { color: "red", children: ["Error: ", error] });
198
+ return _jsxs("text", { fg: "red", children: ["Error: ", error] });
197
199
  }
198
200
  if (!selectedServer) {
199
- return _jsx(Text, { color: "gray", children: "Select a server to see details" });
201
+ return _jsx("text", { fg: "gray", children: "Select a server to see details" });
200
202
  }
201
203
  const dateDisplay = selectedServer.published_at
202
204
  ? formatDate(selectedServer.published_at)
203
- : 'unknown';
205
+ : "unknown";
204
206
  const versionDisplay = selectedServer.version
205
207
  ? `v${selectedServer.version}`
206
- : 'unknown';
207
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: "magenta", children: selectedServer.name }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: selectedServer.short_description }) }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { bold: true, children: "Version: " }), _jsx(Text, { color: "green", children: versionDisplay })] }), _jsxs(Box, { children: [_jsx(Text, { bold: true, children: "Published: " }), _jsx(Text, { color: "cyan", children: dateDisplay })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "URL:" }), _jsx(Text, { color: "cyan", children: selectedServer.url })] }), selectedServer.source_code_url && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Source:" }), _jsx(Text, { color: "gray", children: selectedServer.source_code_url })] })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "green", children: "Press Enter to install" }) })] }));
208
+ : "unknown";
209
+ return (_jsxs("box", { flexDirection: "column", children: [_jsx("text", { fg: "magenta", children: _jsx("strong", { children: selectedServer.name }) }), _jsx("box", { marginTop: 1, children: _jsx("text", { children: selectedServer.short_description }) }), _jsxs("box", { marginTop: 1, children: [_jsx("text", { children: _jsx("strong", { children: "Version: " }) }), _jsx("text", { fg: "green", children: versionDisplay })] }), _jsxs("box", { children: [_jsx("text", { children: _jsx("strong", { children: "Published: " }) }), _jsx("text", { fg: "cyan", children: dateDisplay })] }), _jsxs("box", { marginTop: 1, flexDirection: "column", children: [_jsx("text", { children: _jsx("strong", { children: "URL:" }) }), _jsx("text", { fg: "cyan", children: selectedServer.url })] }), selectedServer.source_code_url && (_jsxs("box", { marginTop: 1, flexDirection: "column", children: [_jsx("text", { children: _jsx("strong", { children: "Source:" }) }), _jsx("text", { fg: "gray", children: selectedServer.source_code_url })] })), _jsx("box", { marginTop: 1, children: _jsx("text", { fg: "green", children: "Press Enter to install" }) })] }));
208
210
  };
209
211
  const renderListItem = (server, _idx, isSelected) => {
210
- const version = server.version ? ` v${server.version}` : '';
211
- return isSelected ? (_jsxs(Text, { backgroundColor: "magenta", color: "white", wrap: "truncate", children: [' ', server.name, version, ' '] })) : (_jsxs(Text, { wrap: "truncate", children: [_jsx(Text, { bold: true, children: server.name }), _jsx(Text, { color: "green", children: version })] }));
212
+ const version = server.version ? ` v${server.version}` : "";
213
+ return isSelected ? (_jsxs("text", { bg: "magenta", fg: "white", children: [" ", server.name, version, " "] })) : (_jsxs("text", { children: [_jsx("span", { children: _jsx("strong", { children: server.name }) }), _jsx("span", { fg: "green", children: version })] }));
212
214
  };
213
215
  // Footer hints
214
216
  const footerHints = isSearchActive
215
- ? 'Type to search │ ↑↓:nav │ Enter:done │ Esc:cancel'
216
- : '↑↓:nav │ Enter:install │ /:search │ R:refresh │ l:local';
217
+ ? "Type to search │ ↑↓:nav │ Enter:done │ Esc:cancel"
218
+ : "↑↓:nav │ Enter:install │ /:search │ R:refresh │ l:local";
217
219
  // Status for search placeholder
218
220
  const searchPlaceholder = `${servers.length} servers │ / to search`;
219
221
  return (_jsx(ScreenLayout, { title: "claudeup MCP Registry", subtitle: "Powered by MCP Registry", currentScreen: "mcp-registry", search: {
220
222
  isActive: isSearchActive,
221
223
  query: searchQuery,
222
224
  placeholder: searchPlaceholder,
223
- }, footerHints: footerHints, listPanel: isLoading ? (_jsx(Text, { color: "gray", children: "Loading..." })) : error ? (_jsxs(Text, { color: "red", children: ["Error: ", error] })) : servers.length === 0 ? (_jsx(Text, { color: "gray", children: "No servers found" })) : (_jsx(ScrollableList, { items: servers, selectedIndex: mcpRegistry.selectedIndex, renderItem: renderListItem, maxHeight: dimensions.listPanelHeight })), detailPanel: renderDetail() }));
225
+ }, footerHints: footerHints, listPanel: isLoading ? (_jsx("text", { fg: "gray", children: "Loading..." })) : error ? (_jsxs("text", { fg: "red", children: ["Error: ", error] })) : servers.length === 0 ? (_jsx("text", { fg: "gray", children: "No servers found" })) : (_jsx(ScrollableList, { items: servers, selectedIndex: mcpRegistry.selectedIndex, renderItem: renderListItem, maxHeight: dimensions.listPanelHeight })), detailPanel: renderDetail() }));
224
226
  }
225
227
  export default McpRegistryScreen;
226
- //# sourceMappingURL=McpRegistryScreen.js.map