claudeup 1.7.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 (302) 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/src/prerunner/index.js +87 -0
  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 +7 -8
  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 +0 -64
  137. package/dist/prerunner/index.js.map +0 -1
  138. package/dist/services/claude-runner.d.ts +0 -7
  139. package/dist/services/claude-runner.d.ts.map +0 -1
  140. package/dist/services/claude-runner.js.map +0 -1
  141. package/dist/services/claude-settings.d.ts +0 -73
  142. package/dist/services/claude-settings.d.ts.map +0 -1
  143. package/dist/services/claude-settings.js.map +0 -1
  144. package/dist/services/local-marketplace.d.ts +0 -111
  145. package/dist/services/local-marketplace.d.ts.map +0 -1
  146. package/dist/services/local-marketplace.js +0 -599
  147. package/dist/services/local-marketplace.js.map +0 -1
  148. package/dist/services/mcp-registry.d.ts +0 -10
  149. package/dist/services/mcp-registry.d.ts.map +0 -1
  150. package/dist/services/mcp-registry.js.map +0 -1
  151. package/dist/services/plugin-manager.d.ts +0 -65
  152. package/dist/services/plugin-manager.d.ts.map +0 -1
  153. package/dist/services/plugin-manager.js.map +0 -1
  154. package/dist/services/plugin-mcp-config.d.ts +0 -52
  155. package/dist/services/plugin-mcp-config.d.ts.map +0 -1
  156. package/dist/services/plugin-mcp-config.js.map +0 -1
  157. package/dist/services/update-cache.d.ts +0 -16
  158. package/dist/services/update-cache.d.ts.map +0 -1
  159. package/dist/services/update-cache.js.map +0 -1
  160. package/dist/services/version-check.d.ts +0 -20
  161. package/dist/services/version-check.d.ts.map +0 -1
  162. package/dist/services/version-check.js.map +0 -1
  163. package/dist/types/index.d.ts +0 -105
  164. package/dist/types/index.d.ts.map +0 -1
  165. package/dist/types/index.js +0 -2
  166. package/dist/types/index.js.map +0 -1
  167. package/dist/ui/InkApp.d.ts +0 -5
  168. package/dist/ui/InkApp.d.ts.map +0 -1
  169. package/dist/ui/InkApp.js +0 -188
  170. package/dist/ui/InkApp.js.map +0 -1
  171. package/dist/ui/components/CategoryHeader.d.ts +0 -16
  172. package/dist/ui/components/CategoryHeader.d.ts.map +0 -1
  173. package/dist/ui/components/CategoryHeader.js +0 -11
  174. package/dist/ui/components/CategoryHeader.js.map +0 -1
  175. package/dist/ui/components/ScrollableList.d.ts +0 -16
  176. package/dist/ui/components/ScrollableList.d.ts.map +0 -1
  177. package/dist/ui/components/ScrollableList.js.map +0 -1
  178. package/dist/ui/components/SearchInput.d.ts +0 -18
  179. package/dist/ui/components/SearchInput.d.ts.map +0 -1
  180. package/dist/ui/components/SearchInput.js +0 -30
  181. package/dist/ui/components/SearchInput.js.map +0 -1
  182. package/dist/ui/components/TabBar.d.ts +0 -8
  183. package/dist/ui/components/TabBar.d.ts.map +0 -1
  184. package/dist/ui/components/TabBar.js +0 -18
  185. package/dist/ui/components/TabBar.js.map +0 -1
  186. package/dist/ui/components/layout/Footer.d.ts +0 -14
  187. package/dist/ui/components/layout/Footer.d.ts.map +0 -1
  188. package/dist/ui/components/layout/Footer.js +0 -23
  189. package/dist/ui/components/layout/Footer.js.map +0 -1
  190. package/dist/ui/components/layout/Header.d.ts +0 -4
  191. package/dist/ui/components/layout/Header.d.ts.map +0 -1
  192. package/dist/ui/components/layout/Header.js +0 -25
  193. package/dist/ui/components/layout/Header.js.map +0 -1
  194. package/dist/ui/components/layout/Panel.d.ts +0 -22
  195. package/dist/ui/components/layout/Panel.d.ts.map +0 -1
  196. package/dist/ui/components/layout/Panel.js +0 -8
  197. package/dist/ui/components/layout/Panel.js.map +0 -1
  198. package/dist/ui/components/layout/ProgressBar.d.ts +0 -12
  199. package/dist/ui/components/layout/ProgressBar.d.ts.map +0 -1
  200. package/dist/ui/components/layout/ProgressBar.js +0 -16
  201. package/dist/ui/components/layout/ProgressBar.js.map +0 -1
  202. package/dist/ui/components/layout/ScopeTabs.d.ts +0 -12
  203. package/dist/ui/components/layout/ScopeTabs.d.ts.map +0 -1
  204. package/dist/ui/components/layout/ScopeTabs.js +0 -8
  205. package/dist/ui/components/layout/ScopeTabs.js.map +0 -1
  206. package/dist/ui/components/layout/ScreenLayout.d.ts +0 -30
  207. package/dist/ui/components/layout/ScreenLayout.d.ts.map +0 -1
  208. package/dist/ui/components/layout/ScreenLayout.js +0 -23
  209. package/dist/ui/components/layout/ScreenLayout.js.map +0 -1
  210. package/dist/ui/components/layout/index.d.ts +0 -7
  211. package/dist/ui/components/layout/index.d.ts.map +0 -1
  212. package/dist/ui/components/layout/index.js +0 -7
  213. package/dist/ui/components/layout/index.js.map +0 -1
  214. package/dist/ui/components/modals/ConfirmModal.d.ts +0 -14
  215. package/dist/ui/components/modals/ConfirmModal.d.ts.map +0 -1
  216. package/dist/ui/components/modals/ConfirmModal.js +0 -15
  217. package/dist/ui/components/modals/ConfirmModal.js.map +0 -1
  218. package/dist/ui/components/modals/InputModal.d.ts +0 -16
  219. package/dist/ui/components/modals/InputModal.d.ts.map +0 -1
  220. package/dist/ui/components/modals/InputModal.js +0 -23
  221. package/dist/ui/components/modals/InputModal.js.map +0 -1
  222. package/dist/ui/components/modals/LoadingModal.d.ts +0 -8
  223. package/dist/ui/components/modals/LoadingModal.d.ts.map +0 -1
  224. package/dist/ui/components/modals/LoadingModal.js +0 -8
  225. package/dist/ui/components/modals/LoadingModal.js.map +0 -1
  226. package/dist/ui/components/modals/MessageModal.d.ts +0 -14
  227. package/dist/ui/components/modals/MessageModal.d.ts.map +0 -1
  228. package/dist/ui/components/modals/MessageModal.js +0 -17
  229. package/dist/ui/components/modals/MessageModal.js.map +0 -1
  230. package/dist/ui/components/modals/ModalContainer.d.ts +0 -7
  231. package/dist/ui/components/modals/ModalContainer.d.ts.map +0 -1
  232. package/dist/ui/components/modals/ModalContainer.js +0 -38
  233. package/dist/ui/components/modals/ModalContainer.js.map +0 -1
  234. package/dist/ui/components/modals/SelectModal.d.ts +0 -17
  235. package/dist/ui/components/modals/SelectModal.d.ts.map +0 -1
  236. package/dist/ui/components/modals/SelectModal.js +0 -33
  237. package/dist/ui/components/modals/SelectModal.js.map +0 -1
  238. package/dist/ui/components/modals/index.d.ts +0 -7
  239. package/dist/ui/components/modals/index.d.ts.map +0 -1
  240. package/dist/ui/components/modals/index.js +0 -7
  241. package/dist/ui/components/modals/index.js.map +0 -1
  242. package/dist/ui/hooks/index.d.ts +0 -3
  243. package/dist/ui/hooks/index.d.ts.map +0 -1
  244. package/dist/ui/hooks/index.js +0 -3
  245. package/dist/ui/hooks/index.js.map +0 -1
  246. package/dist/ui/hooks/useAsyncData.d.ts +0 -40
  247. package/dist/ui/hooks/useAsyncData.d.ts.map +0 -1
  248. package/dist/ui/hooks/useAsyncData.js.map +0 -1
  249. package/dist/ui/hooks/useKeyboardNavigation.d.ts +0 -27
  250. package/dist/ui/hooks/useKeyboardNavigation.d.ts.map +0 -1
  251. package/dist/ui/hooks/useKeyboardNavigation.js +0 -82
  252. package/dist/ui/hooks/useKeyboardNavigation.js.map +0 -1
  253. package/dist/ui/screens/CliToolsScreen.d.ts +0 -4
  254. package/dist/ui/screens/CliToolsScreen.d.ts.map +0 -1
  255. package/dist/ui/screens/CliToolsScreen.js.map +0 -1
  256. package/dist/ui/screens/EnvVarsScreen.d.ts +0 -4
  257. package/dist/ui/screens/EnvVarsScreen.d.ts.map +0 -1
  258. package/dist/ui/screens/EnvVarsScreen.js +0 -145
  259. package/dist/ui/screens/EnvVarsScreen.js.map +0 -1
  260. package/dist/ui/screens/McpRegistryScreen.d.ts +0 -4
  261. package/dist/ui/screens/McpRegistryScreen.d.ts.map +0 -1
  262. package/dist/ui/screens/McpRegistryScreen.js.map +0 -1
  263. package/dist/ui/screens/McpScreen.d.ts +0 -4
  264. package/dist/ui/screens/McpScreen.d.ts.map +0 -1
  265. package/dist/ui/screens/McpScreen.js.map +0 -1
  266. package/dist/ui/screens/ModelSelectorScreen.d.ts +0 -4
  267. package/dist/ui/screens/ModelSelectorScreen.d.ts.map +0 -1
  268. package/dist/ui/screens/ModelSelectorScreen.js +0 -143
  269. package/dist/ui/screens/ModelSelectorScreen.js.map +0 -1
  270. package/dist/ui/screens/PluginsScreen.d.ts +0 -4
  271. package/dist/ui/screens/PluginsScreen.d.ts.map +0 -1
  272. package/dist/ui/screens/PluginsScreen.js.map +0 -1
  273. package/dist/ui/screens/StatusLineScreen.d.ts +0 -4
  274. package/dist/ui/screens/StatusLineScreen.d.ts.map +0 -1
  275. package/dist/ui/screens/StatusLineScreen.js +0 -197
  276. package/dist/ui/screens/StatusLineScreen.js.map +0 -1
  277. package/dist/ui/screens/index.d.ts +0 -8
  278. package/dist/ui/screens/index.d.ts.map +0 -1
  279. package/dist/ui/screens/index.js +0 -8
  280. package/dist/ui/screens/index.js.map +0 -1
  281. package/dist/ui/state/AppContext.d.ts +0 -40
  282. package/dist/ui/state/AppContext.d.ts.map +0 -1
  283. package/dist/ui/state/AppContext.js.map +0 -1
  284. package/dist/ui/state/DimensionsContext.d.ts +0 -27
  285. package/dist/ui/state/DimensionsContext.d.ts.map +0 -1
  286. package/dist/ui/state/DimensionsContext.js.map +0 -1
  287. package/dist/ui/state/reducer.d.ts +0 -4
  288. package/dist/ui/state/reducer.d.ts.map +0 -1
  289. package/dist/ui/state/reducer.js.map +0 -1
  290. package/dist/ui/state/types.d.ts +0 -266
  291. package/dist/ui/state/types.d.ts.map +0 -1
  292. package/dist/ui/state/types.js +0 -2
  293. package/dist/ui/state/types.js.map +0 -1
  294. package/dist/utils/command-utils.d.ts +0 -8
  295. package/dist/utils/command-utils.d.ts.map +0 -1
  296. package/dist/utils/command-utils.js.map +0 -1
  297. package/dist/utils/fuzzy-search.d.ts +0 -33
  298. package/dist/utils/fuzzy-search.d.ts.map +0 -1
  299. package/dist/utils/fuzzy-search.js.map +0 -1
  300. package/dist/utils/string-utils.d.ts +0 -24
  301. package/dist/utils/string-utils.d.ts.map +0 -1
  302. package/dist/utils/string-utils.js.map +0 -1
@@ -0,0 +1,273 @@
1
+ import type {
2
+ Marketplace,
3
+ McpServer,
4
+ StatusLineConfig,
5
+ } from "../../types/index.js";
6
+ import type { PluginInfo } from "../../services/plugin-manager.js";
7
+
8
+ // ============================================================================
9
+ // Route Types
10
+ // ============================================================================
11
+
12
+ export type Screen =
13
+ | "plugins"
14
+ | "mcp"
15
+ | "mcp-registry"
16
+ | "statusline"
17
+ | "env-vars"
18
+ | "cli-tools"
19
+ | "model-selector";
20
+
21
+ export type Route =
22
+ | { screen: "plugins" }
23
+ | { screen: "mcp" }
24
+ | { screen: "mcp-registry"; query?: string }
25
+ | { screen: "statusline" }
26
+ | { screen: "env-vars" }
27
+ | { screen: "cli-tools" }
28
+ | { screen: "model-selector" };
29
+
30
+ // ============================================================================
31
+ // Async Data Types
32
+ // ============================================================================
33
+
34
+ export type AsyncData<T> =
35
+ | { status: "idle" }
36
+ | { status: "loading" }
37
+ | { status: "success"; data: T }
38
+ | { status: "error"; error: Error };
39
+
40
+ // ============================================================================
41
+ // Modal Types
42
+ // ============================================================================
43
+
44
+ export interface SelectOption {
45
+ label: string;
46
+ value: string;
47
+ description?: string;
48
+ }
49
+
50
+ export type ModalState =
51
+ | {
52
+ type: "confirm";
53
+ title: string;
54
+ message: string;
55
+ onConfirm: () => void;
56
+ onCancel: () => void;
57
+ }
58
+ | {
59
+ type: "input";
60
+ title: string;
61
+ label: string;
62
+ defaultValue?: string;
63
+ onSubmit: (value: string) => void;
64
+ onCancel: () => void;
65
+ }
66
+ | {
67
+ type: "select";
68
+ title: string;
69
+ message: string;
70
+ options: SelectOption[];
71
+ onSelect: (value: string) => void;
72
+ onCancel: () => void;
73
+ }
74
+ | {
75
+ type: "message";
76
+ title: string;
77
+ message: string;
78
+ variant: "info" | "success" | "error";
79
+ onDismiss: () => void;
80
+ }
81
+ | {
82
+ type: "loading";
83
+ message: string;
84
+ };
85
+
86
+ // ============================================================================
87
+ // Progress Types
88
+ // ============================================================================
89
+
90
+ export interface ProgressState {
91
+ message: string;
92
+ current?: number;
93
+ total?: number;
94
+ }
95
+
96
+ // ============================================================================
97
+ // Screen State Types
98
+ // ============================================================================
99
+
100
+ export interface PluginsScreenState {
101
+ scope: "project" | "global";
102
+ collapsedMarketplaces: Set<string>;
103
+ selectedIndex: number;
104
+ searchQuery: string;
105
+ marketplaces: AsyncData<Marketplace[]>;
106
+ plugins: AsyncData<PluginInfo[]>;
107
+ }
108
+
109
+ export interface McpScreenState {
110
+ selectedIndex: number;
111
+ servers: AsyncData<McpServer[]>;
112
+ installedServers: Record<string, boolean>;
113
+ }
114
+
115
+ export interface McpRegistryScreenState {
116
+ searchQuery: string;
117
+ selectedIndex: number;
118
+ servers: AsyncData<McpServer[]>;
119
+ }
120
+
121
+ export interface StatusLineScreenState {
122
+ scope: "project" | "global";
123
+ selectedIndex: number;
124
+ presets: AsyncData<StatusLineConfig[]>;
125
+ currentPreset: string | null;
126
+ }
127
+
128
+ export interface EnvVarsScreenState {
129
+ selectedIndex: number;
130
+ variables: AsyncData<Record<string, string>>;
131
+ }
132
+
133
+ export interface CliTool {
134
+ name: string;
135
+ displayName: string;
136
+ description: string;
137
+ installCommand: string;
138
+ checkCommand: string;
139
+ homebrewFormula?: string;
140
+ npmPackage?: string;
141
+ installedVersion?: string;
142
+ latestVersion?: string;
143
+ hasUpdate?: boolean;
144
+ isInstalled: boolean;
145
+ isChecking: boolean;
146
+ }
147
+
148
+ export interface CliToolsScreenState {
149
+ selectedIndex: number;
150
+ tools: AsyncData<CliTool[]>;
151
+ }
152
+
153
+ export interface ModelSelectorScreenState {
154
+ selectedIndex: number;
155
+ searchQuery: string;
156
+ taskSize: "large" | "small";
157
+ }
158
+
159
+ // ============================================================================
160
+ // App State
161
+ // ============================================================================
162
+
163
+ export interface AppState {
164
+ // Navigation
165
+ currentRoute: Route;
166
+
167
+ // Project context
168
+ projectPath: string;
169
+
170
+ // Global UI state
171
+ modal: ModalState | null;
172
+ progress: ProgressState | null;
173
+ isSearching: boolean;
174
+
175
+ // Refresh counter - incremented when marketplace sync completes
176
+ // Screens can watch this to know when to refetch data
177
+ dataRefreshVersion: number;
178
+
179
+ // Screen-specific state
180
+ plugins: PluginsScreenState;
181
+ mcp: McpScreenState;
182
+ mcpRegistry: McpRegistryScreenState;
183
+ statusline: StatusLineScreenState;
184
+ envVars: EnvVarsScreenState;
185
+ cliTools: CliToolsScreenState;
186
+ modelSelector: ModelSelectorScreenState;
187
+ }
188
+
189
+ // ============================================================================
190
+ // Action Types
191
+ // ============================================================================
192
+
193
+ export type AppAction =
194
+ // Navigation
195
+ | { type: "NAVIGATE"; route: Route }
196
+
197
+ // Plugins screen
198
+ | { type: "PLUGINS_SET_SCOPE"; scope: "project" | "global" }
199
+ | { type: "PLUGINS_TOGGLE_SCOPE" }
200
+ | { type: "PLUGINS_TOGGLE_MARKETPLACE"; name: string }
201
+ | { type: "PLUGINS_SELECT"; index: number }
202
+ | { type: "PLUGINS_SET_SEARCH"; query: string }
203
+ | { type: "PLUGINS_DATA_LOADING" }
204
+ | {
205
+ type: "PLUGINS_DATA_SUCCESS";
206
+ marketplaces: Marketplace[];
207
+ plugins: PluginInfo[];
208
+ }
209
+ | { type: "PLUGINS_DATA_ERROR"; error: Error }
210
+
211
+ // MCP screen
212
+ | { type: "MCP_SELECT"; index: number }
213
+ | { type: "MCP_DATA_LOADING" }
214
+ | {
215
+ type: "MCP_DATA_SUCCESS";
216
+ servers: McpServer[];
217
+ installedServers: Record<string, boolean>;
218
+ }
219
+ | { type: "MCP_DATA_ERROR"; error: Error }
220
+
221
+ // MCP Registry screen
222
+ | { type: "MCP_REGISTRY_SET_QUERY"; query: string }
223
+ | { type: "MCPREGISTRY_SEARCH"; query: string } // Alias for MCP_REGISTRY_SET_QUERY
224
+ | { type: "MCPREGISTRY_SELECT"; index: number } // Alias for MCP_REGISTRY_SELECT
225
+ | { type: "MCP_REGISTRY_SELECT"; index: number }
226
+ | { type: "MCP_REGISTRY_DATA_LOADING" }
227
+ | { type: "MCP_REGISTRY_DATA_SUCCESS"; servers: McpServer[] }
228
+ | { type: "MCP_REGISTRY_DATA_ERROR"; error: Error }
229
+
230
+ // Status line screen
231
+ | { type: "STATUSLINE_SET_SCOPE"; scope: "project" | "global" }
232
+ | { type: "STATUSLINE_TOGGLE_SCOPE" }
233
+ | { type: "STATUSLINE_SELECT"; index: number }
234
+ | { type: "STATUSLINE_DATA_LOADING" }
235
+ | {
236
+ type: "STATUSLINE_DATA_SUCCESS";
237
+ presets: StatusLineConfig[];
238
+ currentPreset: string | null;
239
+ }
240
+ | { type: "STATUSLINE_DATA_ERROR"; error: Error }
241
+
242
+ // Env vars screen
243
+ | { type: "ENVVARS_SELECT"; index: number }
244
+ | { type: "ENVVARS_DATA_LOADING" }
245
+ | { type: "ENVVARS_DATA_SUCCESS"; variables: Record<string, string> }
246
+ | { type: "ENVVARS_DATA_ERROR"; error: Error }
247
+
248
+ // CLI tools screen
249
+ | { type: "CLITOOLS_SELECT"; index: number }
250
+ | { type: "CLITOOLS_DATA_LOADING" }
251
+ | { type: "CLITOOLS_DATA_SUCCESS"; tools: CliTool[] }
252
+ | { type: "CLITOOLS_DATA_ERROR"; error: Error }
253
+ | { type: "CLITOOLS_UPDATE_TOOL"; name: string; updates: Partial<CliTool> }
254
+
255
+ // Model Selector Screen
256
+ | { type: "MODEL_SELECTOR_SELECT"; index: number }
257
+ | { type: "MODEL_SELECTOR_SET_SEARCH"; query: string }
258
+ | { type: "MODEL_SELECTOR_SET_TASK_SIZE"; size: "large" | "small" }
259
+
260
+ // Modals
261
+ | { type: "SHOW_MODAL"; modal: ModalState }
262
+ | { type: "HIDE_MODAL" }
263
+
264
+ // Progress
265
+ | { type: "SHOW_PROGRESS"; state: ProgressState }
266
+ | { type: "UPDATE_PROGRESS"; state: ProgressState }
267
+ | { type: "HIDE_PROGRESS" }
268
+
269
+ // Search state (blocks global key handlers)
270
+ | { type: "SET_SEARCHING"; isSearching: boolean }
271
+
272
+ // Data refresh - triggers screens to refetch
273
+ | { type: "DATA_REFRESH_COMPLETE" };
@@ -1,5 +1,5 @@
1
- import { execFile } from 'node:child_process';
2
- import { promisify } from 'node:util';
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
3
  const execFileAsync = promisify(execFile);
4
4
  /**
5
5
  * Check if a command exists in PATH
@@ -10,11 +10,10 @@ const execFileAsync = promisify(execFile);
10
10
  export async function which(command) {
11
11
  try {
12
12
  // Use execFile with argument array to prevent command injection
13
- const { stdout } = await execFileAsync('which', [command]);
13
+ const { stdout } = await execFileAsync("which", [command]);
14
14
  return stdout.trim() || null;
15
15
  }
16
16
  catch {
17
17
  return null;
18
18
  }
19
19
  }
20
- //# sourceMappingURL=command-utils.js.map
@@ -0,0 +1,20 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+
4
+ const execFileAsync = promisify(execFile);
5
+
6
+ /**
7
+ * Check if a command exists in PATH
8
+ * Uses execFile (not exec) to avoid shell injection vulnerabilities
9
+ * @param command - Command name to check
10
+ * @returns Path to command if found, null otherwise
11
+ */
12
+ export async function which(command: string): Promise<string | null> {
13
+ try {
14
+ // Use execFile with argument array to prevent command injection
15
+ const { stdout } = await execFileAsync("which", [command]);
16
+ return stdout.trim() || null;
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
@@ -52,7 +52,7 @@ export function fuzzyMatch(query, target) {
52
52
  */
53
53
  export function fuzzyFilter(items, query, getText) {
54
54
  if (!query.trim()) {
55
- return items.map(item => ({ item, score: 1, matches: [] }));
55
+ return items.map((item) => ({ item, score: 1, matches: [] }));
56
56
  }
57
57
  const results = [];
58
58
  for (const item of items) {
@@ -79,7 +79,7 @@ export function highlightMatches(text, matches) {
79
79
  }
80
80
  const matchSet = new Set(matches);
81
81
  const segments = [];
82
- let currentSegment = '';
82
+ let currentSegment = "";
83
83
  let isHighlighted = matchSet.has(0);
84
84
  for (let i = 0; i < text.length; i++) {
85
85
  const shouldHighlight = matchSet.has(i);
@@ -99,4 +99,3 @@ export function highlightMatches(text, matches) {
99
99
  }
100
100
  return segments;
101
101
  }
102
- //# sourceMappingURL=fuzzy-search.js.map
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Simple fuzzy search utility for filtering lists
3
+ * Uses a scoring algorithm that rewards:
4
+ * - Consecutive character matches
5
+ * - Matches at word boundaries
6
+ * - Matches at start of string
7
+ */
8
+
9
+ export interface FuzzyMatch<T> {
10
+ item: T;
11
+ score: number;
12
+ matches: number[]; // indices of matched characters
13
+ }
14
+
15
+ /**
16
+ * Calculate fuzzy match score for a query against a target string
17
+ * Returns null if no match, otherwise returns score and match indices
18
+ */
19
+ export function fuzzyMatch(
20
+ query: string,
21
+ target: string,
22
+ ): { score: number; matches: number[] } | null {
23
+ if (!query) return { score: 1, matches: [] };
24
+
25
+ const queryLower = query.toLowerCase();
26
+ const targetLower = target.toLowerCase();
27
+
28
+ let queryIdx = 0;
29
+ let score = 0;
30
+ const matches: number[] = [];
31
+ let prevMatchIdx = -1;
32
+
33
+ for (let i = 0; i < targetLower.length && queryIdx < queryLower.length; i++) {
34
+ if (targetLower[i] === queryLower[queryIdx]) {
35
+ matches.push(i);
36
+
37
+ // Bonus for consecutive matches
38
+ if (prevMatchIdx === i - 1) {
39
+ score += 5;
40
+ }
41
+
42
+ // Bonus for matching at word boundary
43
+ if (i === 0 || /[\s\-_.]/.test(target[i - 1])) {
44
+ score += 10;
45
+ }
46
+
47
+ // Bonus for matching at start
48
+ if (i === 0) {
49
+ score += 15;
50
+ }
51
+
52
+ // Base score for each match
53
+ score += 1;
54
+
55
+ prevMatchIdx = i;
56
+ queryIdx++;
57
+ }
58
+ }
59
+
60
+ // All query characters must be found
61
+ if (queryIdx !== queryLower.length) {
62
+ return null;
63
+ }
64
+
65
+ // Penalty for longer strings (prefer shorter, more precise matches)
66
+ score -= Math.floor(target.length / 10);
67
+
68
+ return { score, matches };
69
+ }
70
+
71
+ /**
72
+ * Filter and sort items by fuzzy match score
73
+ */
74
+ export function fuzzyFilter<T>(
75
+ items: T[],
76
+ query: string,
77
+ getText: (item: T) => string,
78
+ ): FuzzyMatch<T>[] {
79
+ if (!query.trim()) {
80
+ return items.map((item) => ({ item, score: 1, matches: [] }));
81
+ }
82
+
83
+ const results: FuzzyMatch<T>[] = [];
84
+
85
+ for (const item of items) {
86
+ const text = getText(item);
87
+ const match = fuzzyMatch(query, text);
88
+
89
+ if (match) {
90
+ results.push({
91
+ item,
92
+ score: match.score,
93
+ matches: match.matches,
94
+ });
95
+ }
96
+ }
97
+
98
+ // Sort by score descending
99
+ return results.sort((a, b) => b.score - a.score);
100
+ }
101
+
102
+ /**
103
+ * Highlight matched characters in a string
104
+ * Returns array of { text, highlighted } segments
105
+ */
106
+ export function highlightMatches(
107
+ text: string,
108
+ matches: number[],
109
+ ): Array<{ text: string; highlighted: boolean }> {
110
+ if (matches.length === 0) {
111
+ return [{ text, highlighted: false }];
112
+ }
113
+
114
+ const matchSet = new Set(matches);
115
+ const segments: Array<{ text: string; highlighted: boolean }> = [];
116
+ let currentSegment = "";
117
+ let isHighlighted = matchSet.has(0);
118
+
119
+ for (let i = 0; i < text.length; i++) {
120
+ const shouldHighlight = matchSet.has(i);
121
+
122
+ if (shouldHighlight !== isHighlighted) {
123
+ if (currentSegment) {
124
+ segments.push({ text: currentSegment, highlighted: isHighlighted });
125
+ }
126
+ currentSegment = text[i];
127
+ isHighlighted = shouldHighlight;
128
+ } else {
129
+ currentSegment += text[i];
130
+ }
131
+ }
132
+
133
+ if (currentSegment) {
134
+ segments.push({ text: currentSegment, highlighted: isHighlighted });
135
+ }
136
+
137
+ return segments;
138
+ }
@@ -9,14 +9,14 @@ export function formatMarketplaceName(name) {
9
9
  return name
10
10
  .split(/[-_]/)
11
11
  .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
12
- .join(' ');
12
+ .join(" ");
13
13
  }
14
14
  /**
15
15
  * Validate GitHub repository format (org/repo)
16
16
  * Prevents SSRF via malicious repo strings
17
17
  */
18
18
  export function isValidGitHubRepo(repo) {
19
- if (!repo || typeof repo !== 'string') {
19
+ if (!repo || typeof repo !== "string") {
20
20
  return false;
21
21
  }
22
22
  // Must match GitHub username/repo format
@@ -25,7 +25,8 @@ export function isValidGitHubRepo(repo) {
25
25
  const GITHUB_REPO_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]\/[a-zA-Z0-9_.-]+$/;
26
26
  // Allow single-char usernames too
27
27
  const GITHUB_REPO_PATTERN_SHORT = /^[a-zA-Z0-9]\/[a-zA-Z0-9_.-]+$/;
28
- if (!GITHUB_REPO_PATTERN.test(repo) && !GITHUB_REPO_PATTERN_SHORT.test(repo)) {
28
+ if (!GITHUB_REPO_PATTERN.test(repo) &&
29
+ !GITHUB_REPO_PATTERN_SHORT.test(repo)) {
29
30
  return false;
30
31
  }
31
32
  // Blocklist suspicious patterns
@@ -40,11 +41,11 @@ export function isValidGitHubRepo(repo) {
40
41
  return !BLOCKLIST.some((pattern) => pattern.test(repo));
41
42
  }
42
43
  export function parsePluginId(pluginId) {
43
- if (!pluginId || typeof pluginId !== 'string') {
44
+ if (!pluginId || typeof pluginId !== "string") {
44
45
  return null;
45
46
  }
46
47
  const trimmed = pluginId.trim();
47
- const atIndex = trimmed.lastIndexOf('@');
48
+ const atIndex = trimmed.lastIndexOf("@");
48
49
  if (atIndex <= 0) {
49
50
  return null;
50
51
  }
@@ -59,4 +60,3 @@ export function parsePluginId(pluginId) {
59
60
  raw: trimmed,
60
61
  };
61
62
  }
62
- //# sourceMappingURL=string-utils.js.map
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Shared string utilities for claudeup
3
+ */
4
+
5
+ /**
6
+ * Format marketplace name for display
7
+ * e.g., "mag-claude-plugins" -> "Mag Claude Plugins"
8
+ */
9
+ export function formatMarketplaceName(name: string): string {
10
+ return name
11
+ .split(/[-_]/)
12
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
13
+ .join(" ");
14
+ }
15
+
16
+ /**
17
+ * Validate GitHub repository format (org/repo)
18
+ * Prevents SSRF via malicious repo strings
19
+ */
20
+ export function isValidGitHubRepo(repo: string): boolean {
21
+ if (!repo || typeof repo !== "string") {
22
+ return false;
23
+ }
24
+
25
+ // Must match GitHub username/repo format
26
+ // Username: alphanumeric, hyphens (no consecutive, not start/end)
27
+ // Repo: alphanumeric, hyphens, underscores, dots
28
+ const GITHUB_REPO_PATTERN =
29
+ /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]\/[a-zA-Z0-9_.-]+$/;
30
+
31
+ // Allow single-char usernames too
32
+ const GITHUB_REPO_PATTERN_SHORT = /^[a-zA-Z0-9]\/[a-zA-Z0-9_.-]+$/;
33
+
34
+ if (
35
+ !GITHUB_REPO_PATTERN.test(repo) &&
36
+ !GITHUB_REPO_PATTERN_SHORT.test(repo)
37
+ ) {
38
+ return false;
39
+ }
40
+
41
+ // Blocklist suspicious patterns
42
+ const BLOCKLIST = [
43
+ /\.\./, // Path traversal
44
+ /@/, // URL authority injection
45
+ /:/, // Port specification
46
+ /\s/, // Whitespace
47
+ /%/, // URL encoding
48
+ /\\/, // Backslash
49
+ ];
50
+
51
+ return !BLOCKLIST.some((pattern) => pattern.test(repo));
52
+ }
53
+
54
+ /**
55
+ * Parse plugin ID into components
56
+ * Format: pluginName@marketplaceName
57
+ */
58
+ export interface ParsedPluginId {
59
+ pluginName: string;
60
+ marketplace: string;
61
+ raw: string;
62
+ }
63
+
64
+ export function parsePluginId(pluginId: string): ParsedPluginId | null {
65
+ if (!pluginId || typeof pluginId !== "string") {
66
+ return null;
67
+ }
68
+
69
+ const trimmed = pluginId.trim();
70
+ const atIndex = trimmed.lastIndexOf("@");
71
+
72
+ if (atIndex <= 0) {
73
+ return null;
74
+ }
75
+
76
+ const pluginName = trimmed.slice(0, atIndex).trim();
77
+ const marketplace = trimmed.slice(atIndex + 1).trim();
78
+
79
+ if (!pluginName || !marketplace) {
80
+ return null;
81
+ }
82
+
83
+ return {
84
+ pluginName,
85
+ marketplace,
86
+ raw: trimmed,
87
+ };
88
+ }
@@ -1,13 +0,0 @@
1
- export interface CliTool {
2
- name: string;
3
- displayName: string;
4
- description: string;
5
- installCommand: string;
6
- checkCommand: string;
7
- website: string;
8
- category: 'ai-coding' | 'utility';
9
- packageManager: 'npm' | 'pip';
10
- packageName: string;
11
- }
12
- export declare const cliTools: CliTool[];
13
- //# sourceMappingURL=cli-tools.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli-tools.d.ts","sourceRoot":"","sources":["../../src/data/cli-tools.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,WAAW,GAAG,SAAS,CAAC;IAClC,cAAc,EAAE,KAAK,GAAG,KAAK,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,QAAQ,EAAE,OAAO,EA0H7B,CAAC"}