@within-7/minto 0.3.6 → 0.3.10

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 (238) hide show
  1. package/{cli.js → cli.cjs} +25 -23
  2. package/dist/commands/agents/AgentsCommand.js +459 -655
  3. package/dist/commands/agents/AgentsCommand.js.map +2 -2
  4. package/dist/commands/agents/types.js +1 -0
  5. package/dist/commands/agents/types.js.map +2 -2
  6. package/dist/commands/agents/utils/fileOperations.js +96 -36
  7. package/dist/commands/agents/utils/fileOperations.js.map +3 -3
  8. package/dist/commands/agents/utils/index.js +3 -1
  9. package/dist/commands/agents/utils/index.js.map +2 -2
  10. package/dist/commands/context.js +54 -23
  11. package/dist/commands/context.js.map +2 -2
  12. package/dist/commands/export.js +673 -93
  13. package/dist/commands/export.js.map +2 -2
  14. package/dist/commands/language.js +110 -0
  15. package/dist/commands/language.js.map +7 -0
  16. package/dist/commands/mcp-interactive.js +419 -217
  17. package/dist/commands/mcp-interactive.js.map +2 -2
  18. package/dist/commands/model.js +415 -66
  19. package/dist/commands/model.js.map +2 -2
  20. package/dist/commands/new.js +56 -0
  21. package/dist/commands/new.js.map +7 -0
  22. package/dist/commands/permissions.js +75 -49
  23. package/dist/commands/permissions.js.map +2 -2
  24. package/dist/commands/plugin.js +882 -185
  25. package/dist/commands/plugin.js.map +3 -3
  26. package/dist/commands/resume.js +251 -16
  27. package/dist/commands/resume.js.map +2 -2
  28. package/dist/commands/sandbox.js +168 -70
  29. package/dist/commands/sandbox.js.map +2 -2
  30. package/dist/commands/sessions.js +224 -0
  31. package/dist/commands/sessions.js.map +7 -0
  32. package/dist/commands/setup.js +596 -109
  33. package/dist/commands/setup.js.map +2 -2
  34. package/dist/commands/stats.js +292 -0
  35. package/dist/commands/stats.js.map +7 -0
  36. package/dist/commands/status.js +75 -7
  37. package/dist/commands/status.js.map +2 -2
  38. package/dist/commands/undo.js +154 -180
  39. package/dist/commands/undo.js.map +2 -2
  40. package/dist/commands.js +6 -0
  41. package/dist/commands.js.map +2 -2
  42. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js +3 -2
  43. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
  44. package/dist/components/Config.js +9 -8
  45. package/dist/components/Config.js.map +2 -2
  46. package/dist/components/HeaderBar.js +2 -1
  47. package/dist/components/HeaderBar.js.map +2 -2
  48. package/dist/components/Help.js +166 -32
  49. package/dist/components/Help.js.map +2 -2
  50. package/dist/components/HotkeyHelpPanel.js +46 -44
  51. package/dist/components/HotkeyHelpPanel.js.map +2 -2
  52. package/dist/components/InfoPanel/InfoPanel.js +123 -0
  53. package/dist/components/InfoPanel/InfoPanel.js.map +7 -0
  54. package/dist/components/InfoPanel/index.js +5 -0
  55. package/dist/components/InfoPanel/index.js.map +7 -0
  56. package/dist/components/InfoPanel/types.js +1 -0
  57. package/dist/components/InfoPanel/types.js.map +7 -0
  58. package/dist/components/Logo.js +5 -2
  59. package/dist/components/Logo.js.map +2 -2
  60. package/dist/components/MCPServerApprovalDialog.js +6 -5
  61. package/dist/components/MCPServerApprovalDialog.js.map +2 -2
  62. package/dist/components/MCPServerMultiselectDialog.js +5 -4
  63. package/dist/components/MCPServerMultiselectDialog.js.map +2 -2
  64. package/dist/components/MessageSelector.js +4 -3
  65. package/dist/components/MessageSelector.js.map +2 -2
  66. package/dist/components/ModelConfig.js +13 -12
  67. package/dist/components/ModelConfig.js.map +2 -2
  68. package/dist/components/ModelListManager.js +4 -3
  69. package/dist/components/ModelListManager.js.map +2 -2
  70. package/dist/components/ModelSelector/BrandTextInput.js +43 -0
  71. package/dist/components/ModelSelector/BrandTextInput.js.map +7 -0
  72. package/dist/components/ModelSelector/ModelSelector.js +419 -501
  73. package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
  74. package/dist/components/ModelSelector/WizardContainer.js +45 -0
  75. package/dist/components/ModelSelector/WizardContainer.js.map +7 -0
  76. package/dist/components/ModelSelector/index.js +1 -3
  77. package/dist/components/ModelSelector/index.js.map +2 -2
  78. package/dist/components/PromptInput.js +77 -44
  79. package/dist/components/PromptInput.js.map +2 -2
  80. package/dist/components/SensitiveFileWarning.js +12 -8
  81. package/dist/components/SensitiveFileWarning.js.map +2 -2
  82. package/dist/components/SimpleSelector/SimpleSelector.js +154 -0
  83. package/dist/components/SimpleSelector/SimpleSelector.js.map +7 -0
  84. package/dist/components/SimpleSelector/index.js +5 -0
  85. package/dist/components/SimpleSelector/index.js.map +7 -0
  86. package/dist/components/SimpleSelector/types.js +1 -0
  87. package/dist/components/SimpleSelector/types.js.map +7 -0
  88. package/dist/components/StatusOverlayContent.js +21 -0
  89. package/dist/components/StatusOverlayContent.js.map +7 -0
  90. package/dist/components/TabbedListView/ScrollableList.js +117 -0
  91. package/dist/components/TabbedListView/ScrollableList.js.map +7 -0
  92. package/dist/components/TabbedListView/SearchInput.js +23 -0
  93. package/dist/components/TabbedListView/SearchInput.js.map +7 -0
  94. package/dist/components/TabbedListView/TabBar.js +20 -0
  95. package/dist/components/TabbedListView/TabBar.js.map +7 -0
  96. package/dist/components/TabbedListView/TabbedListView.js +246 -0
  97. package/dist/components/TabbedListView/TabbedListView.js.map +7 -0
  98. package/dist/components/TabbedListView/index.js +11 -0
  99. package/dist/components/TabbedListView/index.js.map +7 -0
  100. package/dist/components/TabbedListView/types.js +1 -0
  101. package/dist/components/TabbedListView/types.js.map +7 -0
  102. package/dist/components/TodoChangeBlock.js +6 -5
  103. package/dist/components/TodoChangeBlock.js.map +3 -3
  104. package/dist/components/TodoPanel.js +6 -3
  105. package/dist/components/TodoPanel.js.map +3 -3
  106. package/dist/components/TrustDialog.js +6 -5
  107. package/dist/components/TrustDialog.js.map +2 -2
  108. package/dist/components/messages/UserToolResultMessage/UserToolCanceledMessage.js +2 -1
  109. package/dist/components/messages/UserToolResultMessage/UserToolCanceledMessage.js.map +2 -2
  110. package/dist/constants/macros.js +1 -1
  111. package/dist/constants/macros.js.map +1 -1
  112. package/dist/constants/product.js +2 -2
  113. package/dist/constants/product.js.map +1 -1
  114. package/dist/constants/prompts.js +17 -0
  115. package/dist/constants/prompts.js.map +2 -2
  116. package/dist/constants/toolInputExamples.js +5 -1
  117. package/dist/constants/toolInputExamples.js.map +2 -2
  118. package/dist/core/backupHook.js +29 -0
  119. package/dist/core/backupHook.js.map +7 -0
  120. package/dist/core/config/defaults.js +8 -2
  121. package/dist/core/config/defaults.js.map +2 -2
  122. package/dist/core/config/schema.js +14 -2
  123. package/dist/core/config/schema.js.map +2 -2
  124. package/dist/core/costTracker.js +0 -16
  125. package/dist/core/costTracker.js.map +2 -2
  126. package/dist/core/tokenStatsManager.js +5 -0
  127. package/dist/core/tokenStatsManager.js.map +2 -2
  128. package/dist/cost-tracker.js +0 -16
  129. package/dist/cost-tracker.js.map +2 -2
  130. package/dist/entrypoints/bootstrap.js +56 -0
  131. package/dist/entrypoints/bootstrap.js.map +7 -0
  132. package/dist/entrypoints/cli.js +164 -23
  133. package/dist/entrypoints/cli.js.map +3 -3
  134. package/dist/history.js +75 -15
  135. package/dist/history.js.map +2 -2
  136. package/dist/i18n/index.js +2 -2
  137. package/dist/i18n/index.js.map +2 -2
  138. package/dist/i18n/locales/en.js +582 -1
  139. package/dist/i18n/locales/en.js.map +2 -2
  140. package/dist/i18n/locales/zh-CN.js +582 -1
  141. package/dist/i18n/locales/zh-CN.js.map +2 -2
  142. package/dist/i18n/types.js.map +1 -1
  143. package/dist/index.js +1 -1
  144. package/dist/index.js.map +2 -2
  145. package/dist/messages.js +11 -0
  146. package/dist/messages.js.map +2 -2
  147. package/dist/permissions.js.map +2 -2
  148. package/dist/query.js +9 -0
  149. package/dist/query.js.map +2 -2
  150. package/dist/screens/REPL.js +45 -7
  151. package/dist/screens/REPL.js.map +2 -2
  152. package/dist/services/customCommands.js +44 -16
  153. package/dist/services/customCommands.js.map +2 -2
  154. package/dist/services/plugins/lspServers.js +1 -1
  155. package/dist/services/plugins/lspServers.js.map +2 -2
  156. package/dist/services/plugins/pluginRuntime.js +2 -1
  157. package/dist/services/plugins/pluginRuntime.js.map +2 -2
  158. package/dist/services/plugins/pluginValidation.js +10 -3
  159. package/dist/services/plugins/pluginValidation.js.map +2 -2
  160. package/dist/services/plugins/skillMarketplace.js +16 -8
  161. package/dist/services/plugins/skillMarketplace.js.map +2 -2
  162. package/dist/services/systemReminder.js +17 -6
  163. package/dist/services/systemReminder.js.map +2 -2
  164. package/dist/tools/FileEditTool/FileEditTool.js +7 -0
  165. package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
  166. package/dist/tools/FileWriteTool/FileWriteTool.js +7 -0
  167. package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
  168. package/dist/tools/MultiEditTool/MultiEditTool.js +7 -0
  169. package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
  170. package/dist/tools/NotebookEditTool/NotebookEditTool.js +2 -0
  171. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
  172. package/dist/tools/TaskTool/TaskTool.js +179 -1
  173. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  174. package/dist/tools/TodoWriteTool/prompt.js +21 -0
  175. package/dist/tools/TodoWriteTool/prompt.js.map +2 -2
  176. package/dist/tools/URLFetcherTool/prompt.js +14 -9
  177. package/dist/tools/URLFetcherTool/prompt.js.map +2 -2
  178. package/dist/tools/WebSearchTool/prompt.js +12 -6
  179. package/dist/tools/WebSearchTool/prompt.js.map +2 -2
  180. package/dist/types/PermissionMode.js +30 -1
  181. package/dist/types/PermissionMode.js.map +2 -2
  182. package/dist/types/plugin.js +2 -4
  183. package/dist/types/plugin.js.map +2 -2
  184. package/dist/utils/agentHookExecutor.js +103 -0
  185. package/dist/utils/agentHookExecutor.js.map +7 -0
  186. package/dist/utils/agentLoader.js +272 -32
  187. package/dist/utils/agentLoader.js.map +2 -2
  188. package/dist/utils/agentMemory.js +134 -0
  189. package/dist/utils/agentMemory.js.map +7 -0
  190. package/dist/utils/claudeCodeSync.js +439 -0
  191. package/dist/utils/claudeCodeSync.js.map +7 -0
  192. package/dist/utils/config.js +52 -24
  193. package/dist/utils/config.js.map +2 -2
  194. package/dist/utils/configPaths.js +199 -0
  195. package/dist/utils/configPaths.js.map +7 -0
  196. package/dist/utils/execFileNoThrow.js +2 -1
  197. package/dist/utils/execFileNoThrow.js.map +2 -2
  198. package/dist/utils/historyManager.js +234 -0
  199. package/dist/utils/historyManager.js.map +7 -0
  200. package/dist/utils/marketplaceManager.js +80 -43
  201. package/dist/utils/marketplaceManager.js.map +2 -2
  202. package/dist/utils/messages.js +13 -8
  203. package/dist/utils/messages.js.map +2 -2
  204. package/dist/utils/migration/index.js +37 -0
  205. package/dist/utils/migration/index.js.map +7 -0
  206. package/dist/utils/migration/migrateHistory.js +273 -0
  207. package/dist/utils/migration/migrateHistory.js.map +7 -0
  208. package/dist/utils/migration/migrateTodos.js +323 -0
  209. package/dist/utils/migration/migrateTodos.js.map +7 -0
  210. package/dist/utils/pasteCache.js +309 -0
  211. package/dist/utils/pasteCache.js.map +7 -0
  212. package/dist/utils/pluginInstaller.js +34 -24
  213. package/dist/utils/pluginInstaller.js.map +2 -2
  214. package/dist/utils/pluginLoader.js +54 -28
  215. package/dist/utils/pluginLoader.js.map +2 -2
  216. package/dist/utils/repoFetcher.js +110 -0
  217. package/dist/utils/repoFetcher.js.map +7 -0
  218. package/dist/utils/sessionIndex.js +192 -0
  219. package/dist/utils/sessionIndex.js.map +7 -0
  220. package/dist/utils/sessionTracker.js +170 -0
  221. package/dist/utils/sessionTracker.js.map +7 -0
  222. package/dist/utils/skillLoader.js +103 -5
  223. package/dist/utils/skillLoader.js.map +2 -2
  224. package/dist/utils/stats.js +417 -0
  225. package/dist/utils/stats.js.map +7 -0
  226. package/dist/utils/stringSubstitution.js +106 -0
  227. package/dist/utils/stringSubstitution.js.map +7 -0
  228. package/dist/utils/teamConfig.js +156 -14
  229. package/dist/utils/teamConfig.js.map +2 -2
  230. package/dist/utils/terminal.js +1 -1
  231. package/dist/utils/terminal.js.map +2 -2
  232. package/dist/utils/todoStorage.js +51 -19
  233. package/dist/utils/todoStorage.js.map +2 -2
  234. package/dist/utils/tooling/safeRender.js.map +2 -2
  235. package/dist/version.js +2 -2
  236. package/dist/version.js.map +1 -1
  237. package/package.json +71 -28
  238. package/scripts/{postinstall.js → postinstall.cjs} +1 -1
@@ -1,10 +1,13 @@
1
- import React, { useState, useEffect } from "react";
2
- import { Box, Text, useInput } from "ink";
1
+ import React, { useState, useEffect, useMemo, useCallback } from "react";
2
+ import { Box, Text } from "ink";
3
3
  import { SimpleSpinner } from "../components/Spinner.js";
4
4
  import {
5
5
  loadAllPlugins,
6
6
  getPlugin,
7
- listPlugins
7
+ listPlugins,
8
+ togglePluginEnabled,
9
+ enablePlugin,
10
+ disablePlugin
8
11
  } from "../utils/pluginLoader.js";
9
12
  import {
10
13
  addMarketplace,
@@ -13,19 +16,19 @@ import {
13
16
  listMarketplaces,
14
17
  installPluginFromMarketplace
15
18
  } from "../utils/marketplaceManager.js";
19
+ import { uninstallPlugin } from "../utils/pluginInstaller.js";
20
+ import {
21
+ syncFromClaudeCode,
22
+ hasClaudeCodeInstallation
23
+ } from "../utils/claudeCodeSync.js";
24
+ import { gt as semverGt, valid as semverValid } from "semver";
16
25
  import { MarketplaceError } from "../types/marketplace.js";
17
26
  import { rmSync } from "fs";
18
27
  import { getTheme } from "../utils/theme.js";
19
28
  import { SEMANTIC_COLORS } from "../constants/colors.js";
20
- import { MainMenu } from "./plugin/MainMenu.js";
21
- import { MarketplaceSelector } from "./plugin/MarketplaceSelector.js";
22
- import { PluginBrowser } from "./plugin/PluginBrowser.js";
23
- import { PluginDetailsInstall } from "./plugin/PluginDetailsInstall.js";
24
- import { InstalledPluginsManager } from "./plugin/InstalledPluginsManager.js";
25
- import { InstalledPluginsByMarketplace } from "./plugin/InstalledPluginsByMarketplace.js";
26
- import { PluginDetailsManage } from "./plugin/PluginDetailsManage.js";
27
- import { AddMarketplaceForm } from "./plugin/AddMarketplaceForm.js";
28
- import { MarketplaceManager } from "./plugin/MarketplaceManager.js";
29
+ import { TabbedListView } from "../components/TabbedListView/TabbedListView.js";
30
+ import { t } from "../i18n/index.js";
31
+ import { formatMarketplacePath, formatTimeAgo } from "./plugin/utils.js";
29
32
  const PluginList = ({ onDone }) => {
30
33
  const [plugins, setPlugins] = useState([]);
31
34
  const [loading, setLoading] = useState(true);
@@ -66,7 +69,7 @@ const PluginList = ({ onDone }) => {
66
69
  flexDirection: "column",
67
70
  marginBottom: 1
68
71
  },
69
- /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: theme.primary, bold: true }, plugin2.manifest.displayName || plugin2.manifest.name), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", "v", plugin2.manifest.version), !plugin2.enabled && /* @__PURE__ */ React.createElement(Text, { color: theme.warning }, " (disabled)")),
72
+ /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: theme.primary, bold: true }, plugin2.manifest.displayName || plugin2.manifest.name), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", "v", plugin2.manifest.version), plugin2.source.type === "claude-code" && /* @__PURE__ */ React.createElement(Text, { color: "#5DADE2" }, " [CC]"), !plugin2.enabled && /* @__PURE__ */ React.createElement(Text, { color: theme.warning }, " (disabled)")),
70
73
  plugin2.manifest.description && /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", plugin2.manifest.description),
71
74
  componentCounts.length > 0 && /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", "Components: ", componentCounts.join(", ")),
72
75
  /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", "Location:", " ", plugin2.source.type === "local" ? plugin2.source.path : plugin2.location)
@@ -156,7 +159,7 @@ const PluginUninstall = ({
156
159
  const [message, setMessage] = useState("");
157
160
  const theme = getTheme();
158
161
  useEffect(() => {
159
- const uninstallPlugin = () => {
162
+ const uninstallPlugin2 = () => {
160
163
  try {
161
164
  setMessage(`Uninstalling plugin "${pluginName}"...`);
162
165
  const plugin2 = getPlugin(pluginName);
@@ -175,7 +178,7 @@ const PluginUninstall = ({
175
178
  onDone();
176
179
  }
177
180
  };
178
- uninstallPlugin();
181
+ uninstallPlugin2();
179
182
  }, [pluginName, onDone]);
180
183
  const color = status === "success" ? theme.success : status === "error" ? theme.error : theme.primary;
181
184
  return /* @__PURE__ */ React.createElement(Text, { color }, message);
@@ -242,6 +245,68 @@ const PluginValidate = ({
242
245
  }
243
246
  return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true, underline: true, color: theme.primary }, "Plugin Validation Results"), /* @__PURE__ */ React.createElement(Text, null, ""), results.map((result) => /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: result.valid ? theme.success : theme.error }, result.valid ? "\u2713" : "\u2717"), /* @__PURE__ */ React.createElement(Text, null, " ", result.plugin), result.valid && /* @__PURE__ */ React.createElement(Text, { color: theme.success }, " - Valid")), result.errors.length > 0 && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginLeft: 2 }, result.errors.map((error, idx) => /* @__PURE__ */ React.createElement(Text, { color: theme.error }, "\u2022 ", error))))));
244
247
  };
248
+ const PluginSync = ({
249
+ onDone,
250
+ force = false,
251
+ clean = false,
252
+ dryRun = false
253
+ }) => {
254
+ const [status, setStatus] = useState("syncing");
255
+ const [progress, setProgress] = useState(t("commands.plugin.syncStarting"));
256
+ const [result, setResult] = useState(null);
257
+ const [error, setError] = useState(null);
258
+ const theme = getTheme();
259
+ useEffect(() => {
260
+ const doSync = async () => {
261
+ if (!hasClaudeCodeInstallation()) {
262
+ setError(t("commands.plugin.syncNotFound"));
263
+ setStatus("error");
264
+ onDone();
265
+ return;
266
+ }
267
+ try {
268
+ const syncResult = await syncFromClaudeCode({
269
+ force,
270
+ clean,
271
+ dryRun,
272
+ onProgress: (msg) => setProgress(msg)
273
+ });
274
+ setResult(syncResult);
275
+ setStatus("done");
276
+ } catch (err) {
277
+ setError(err instanceof Error ? err.message : String(err));
278
+ setStatus("error");
279
+ } finally {
280
+ onDone();
281
+ }
282
+ };
283
+ doSync();
284
+ }, [force, clean, dryRun, onDone]);
285
+ if (status === "error") {
286
+ return /* @__PURE__ */ React.createElement(Text, { color: theme.error }, error);
287
+ }
288
+ if (status === "syncing") {
289
+ return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(SimpleSpinner, null), /* @__PURE__ */ React.createElement(Text, { color: theme.primary }, " ", progress));
290
+ }
291
+ if (!result) return null;
292
+ const hasChanges = result.installed.length > 0 || result.updated.length > 0 || result.cleaned.length > 0 || result.marketplaces.registered.length > 0;
293
+ if (!hasChanges && result.failed.length === 0) {
294
+ return /* @__PURE__ */ React.createElement(Text, { color: theme.success }, t("commands.plugin.syncNoChanges"));
295
+ }
296
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, dryRun && /* @__PURE__ */ React.createElement(Text, { color: theme.warning, bold: true }, t("commands.plugin.syncDryRun")), result.installed.length > 0 && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "Installed (", result.installed.length, "):"), result.installed.map((name) => /* @__PURE__ */ React.createElement(Box, { key: name }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " + ", name)))), result.updated.length > 0 && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { color: theme.primary }, "Updated (", result.updated.length, "):"), result.updated.map((name) => /* @__PURE__ */ React.createElement(Box, { key: name }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ~ ", name)))), result.cleaned.length > 0 && /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, t("commands.plugin.syncCleaned").replace(
297
+ "${count}",
298
+ String(result.cleaned.length)
299
+ )), result.marketplaces.registered.length > 0 && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, t("commands.plugin.syncMarketplacesRegistered").replace(
300
+ "${count}",
301
+ String(result.marketplaces.registered.length)
302
+ )), result.marketplaces.registered.map((name) => /* @__PURE__ */ React.createElement(Box, { key: name }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " + ", name)))), result.marketplaces.failed.length > 0 && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { color: theme.error }, t("commands.plugin.syncMarketplacesFailed").replace(
303
+ "${count}",
304
+ String(result.marketplaces.failed.length)
305
+ )), result.marketplaces.failed.map((f) => /* @__PURE__ */ React.createElement(Box, { key: f.name }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", "! ", f.name, ": ", f.error)))), result.failed.length > 0 && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { color: theme.error }, t("commands.plugin.syncFailed").replace(
306
+ "${count}",
307
+ String(result.failed.length)
308
+ )), result.failed.map((f) => /* @__PURE__ */ React.createElement(Box, { key: f.name }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", "! ", f.name, ": ", f.error)))), /* @__PURE__ */ React.createElement(Text, { color: theme.success }, t("commands.plugin.syncComplete").replace("${installed}", String(result.installed.length)).replace("${updated}", String(result.updated.length)).replace("${skipped}", String(result.skipped.length))));
309
+ };
245
310
  const MarketplaceListView = ({ onDone }) => {
246
311
  const [marketplaces, setMarketplaces] = useState([]);
247
312
  const [loading, setLoading] = useState(true);
@@ -374,192 +439,813 @@ const ErrorMessage = ({
374
439
  }, [onDone]);
375
440
  return /* @__PURE__ */ React.createElement(Text, { color: theme.error }, "Error: ", message);
376
441
  };
377
- const MarketplaceDetails = ({ marketplace: marketplaceName, onNavigate, onBack, onDone }) => {
378
- const theme = getTheme();
379
- const [removing, setRemoving] = useState(false);
380
- const [updating, setUpdating] = useState(false);
381
- const [error, setError] = useState();
382
- const marketplace = listMarketplaces().find(
383
- (m) => m.name === marketplaceName
384
- );
385
- useInput((input, key) => {
386
- if (key.escape) onBack();
387
- else if (input === "r" && !removing && !updating) {
388
- setRemoving(true);
389
- removeMarketplaceAction();
390
- } else if (input === "u" && !removing && !updating) {
391
- setUpdating(true);
392
- updateMarketplaceAction();
393
- } else if (input === "p" && !removing && !updating) {
394
- onNavigate({
395
- screen: "installed-plugins-by-marketplace",
396
- marketplace: marketplaceName
397
- });
398
- }
399
- });
400
- const removeMarketplaceAction = () => {
442
+ function compareVersions(installedVersion, marketplaceVersion) {
443
+ if (!installedVersion || !marketplaceVersion) return null;
444
+ if (!semverValid(installedVersion) || !semverValid(marketplaceVersion))
445
+ return null;
446
+ try {
447
+ return {
448
+ hasUpdate: semverGt(marketplaceVersion, installedVersion),
449
+ installed: installedVersion,
450
+ available: marketplaceVersion
451
+ };
452
+ } catch {
453
+ return null;
454
+ }
455
+ }
456
+ const InteractivePluginCommand = ({
457
+ onDone
458
+ }) => {
459
+ const [phase, setPhase] = useState({ type: "main" });
460
+ const [activeTab, setActiveTab] = useState("browse");
461
+ const [searchQuery, setSearchQuery] = useState("");
462
+ const [statusOverlay, setStatusOverlay] = useState(null);
463
+ const [marketplaces, setMarketplaces] = useState([]);
464
+ const [installedPlugins, setInstalledPlugins] = useState([]);
465
+ const [selectedIds, setSelectedIds] = useState(/* @__PURE__ */ new Set());
466
+ const reload = useCallback(() => {
401
467
  try {
402
- removeMarketplace(marketplaceName);
403
- onBack();
404
- } catch (err) {
405
- setError(err instanceof Error ? err.message : String(err));
406
- setRemoving(false);
468
+ setMarketplaces(listMarketplaces());
469
+ } catch {
470
+ setMarketplaces([]);
407
471
  }
408
- };
409
- const updateMarketplaceAction = async () => {
410
472
  try {
411
- await updateMarketplace(marketplaceName);
412
- setUpdating(false);
413
- } catch (err) {
414
- setError(err instanceof Error ? err.message : String(err));
415
- setUpdating(false);
473
+ setInstalledPlugins(loadAllPlugins());
474
+ } catch {
475
+ setInstalledPlugins([]);
416
476
  }
417
- };
418
- if (!marketplace) {
419
- return /* @__PURE__ */ React.createElement(Box, { padding: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.error }, "Marketplace not found: ", marketplaceName));
420
- }
421
- if (removing) {
422
- return /* @__PURE__ */ React.createElement(Box, { padding: 1 }, /* @__PURE__ */ React.createElement(SimpleSpinner, null), /* @__PURE__ */ React.createElement(Text, { color: theme.primary }, " Removing marketplace..."));
423
- }
424
- if (updating) {
425
- return /* @__PURE__ */ React.createElement(Box, { padding: 1 }, /* @__PURE__ */ React.createElement(SimpleSpinner, null), /* @__PURE__ */ React.createElement(Text, { color: theme.primary }, " Updating marketplace..."));
426
- }
427
- return /* @__PURE__ */ React.createElement(
428
- Box,
429
- {
430
- flexDirection: "column",
431
- borderStyle: "round",
432
- borderColor: theme.primary,
433
- padding: 1
477
+ }, []);
478
+ useEffect(() => {
479
+ if (phase.type === "main") {
480
+ reload();
481
+ setSearchQuery("");
482
+ setSelectedIds(/* @__PURE__ */ new Set());
483
+ }
484
+ }, [phase, reload]);
485
+ const installedNames = useMemo(
486
+ () => new Set(installedPlugins.map((p) => p.name)),
487
+ [installedPlugins]
488
+ );
489
+ const findPluginMarketplace = useCallback(
490
+ (pluginName) => {
491
+ for (const m of marketplaces) {
492
+ if (m.manifest.plugins.some((p) => p.name === pluginName)) {
493
+ return m.name;
494
+ }
495
+ }
496
+ return void 0;
434
497
  },
435
- /* @__PURE__ */ React.createElement(Text, { bold: true, color: theme.success }, marketplace.name),
436
- marketplace.manifest.metadata?.description && /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, marketplace.manifest.metadata.description)),
437
- /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Plugins: "), /* @__PURE__ */ React.createElement(Text, { color: theme.primary }, marketplace.manifest.plugins.length)),
438
- /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Owner: "), /* @__PURE__ */ React.createElement(Text, null, marketplace.manifest.owner.name)),
439
- /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Source: "), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, marketplace.source.type, marketplace.source.type === "github" && ` (${marketplace.source.repo})`, marketplace.source.type === "url" && ` (${marketplace.source.url})`, marketplace.source.type === "local" && ` (${marketplace.source.path})`)),
440
- /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Last updated: "), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, new Date(marketplace.lastUpdated).toLocaleString())),
441
- error && /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.error }, "Error: ", error)),
442
- /* @__PURE__ */ React.createElement(Box, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "p"), " to view plugins from this marketplace"), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "u"), " to update marketplace"), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "r"), " to remove marketplace"), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press ", /* @__PURE__ */ React.createElement(Text, { bold: true }, "Esc"), " to go back"))
498
+ [marketplaces]
443
499
  );
444
- };
445
- const InteractivePluginCommand = ({
446
- onDone
447
- }) => {
448
- const [navigationStack, setNavigationStack] = useState([
449
- { screen: "main-menu" }
450
- ]);
451
- const currentState = navigationStack[navigationStack.length - 1];
452
- const handleNavigate = (newState) => {
453
- setNavigationStack((prev) => [...prev, newState]);
454
- };
455
- const handleBack = () => {
456
- if (navigationStack.length > 1) {
457
- setNavigationStack((prev) => prev.slice(0, -1));
458
- } else {
459
- onDone();
500
+ const multiSelectActionLabel = useMemo(() => {
501
+ if (phase.type !== "main" || selectedIds.size === 0) return void 0;
502
+ if (activeTab === "browse" || activeTab === "installed") {
503
+ return `${t("commands.plugin.batchAction")} (${selectedIds.size})`;
460
504
  }
461
- };
462
- switch (currentState.screen) {
463
- case "main-menu":
464
- return /* @__PURE__ */ React.createElement(
465
- MainMenu,
466
- {
467
- onNavigate: handleNavigate,
468
- onBack: handleBack,
469
- onDone
505
+ return void 0;
506
+ }, [phase, activeTab, selectedIds.size]);
507
+ const multiSelectEnabled = phase.type === "main" && (activeTab === "browse" || activeTab === "installed");
508
+ const { tabs, items, title, footerHint, searchEnabled, searchPlaceholder } = useMemo(() => {
509
+ switch (phase.type) {
510
+ case "main": {
511
+ const mainTabs = [
512
+ { id: "browse", label: t("commands.plugin.tabBrowse") },
513
+ { id: "installed", label: t("commands.plugin.tabInstalled") },
514
+ { id: "marketplaces", label: t("commands.plugin.tabMarketplaces") }
515
+ ];
516
+ let mainItems = [];
517
+ if (activeTab === "browse") {
518
+ for (const m of marketplaces) {
519
+ for (const p of m.manifest.plugins) {
520
+ const installed = installedPlugins.find(
521
+ (ip) => ip.name === p.name
522
+ );
523
+ let metadata;
524
+ if (installed) {
525
+ const versionInfo = compareVersions(
526
+ installed.manifest.version,
527
+ p.version
528
+ );
529
+ if (versionInfo?.hasUpdate) {
530
+ metadata = t("commands.plugin.updateAvailable").replace("{installed}", versionInfo.installed).replace("{available}", versionInfo.available);
531
+ } else {
532
+ metadata = "installed";
533
+ }
534
+ }
535
+ mainItems.push({
536
+ id: `${m.name}:${p.name}`,
537
+ label: p.name,
538
+ description: p.description || "",
539
+ category: m.name,
540
+ status: installedNames.has(p.name) ? "enabled" : void 0,
541
+ metadata,
542
+ data: { pluginName: p.name, marketplaceName: m.name }
543
+ });
544
+ }
545
+ }
546
+ } else if (activeTab === "installed") {
547
+ for (const p of installedPlugins) {
548
+ const mName = p.source.type === "claude-code" ? p.source.marketplace : p.source.type === "marketplace" ? p.source.marketplace : findPluginMarketplace(p.name);
549
+ const sourceTag = p.source.type === "claude-code" ? " [CC]" : "";
550
+ mainItems.push({
551
+ id: p.name,
552
+ label: `${p.manifest.displayName || p.name}${sourceTag}`,
553
+ description: p.manifest.description || "",
554
+ status: p.enabled ? "enabled" : "disabled",
555
+ category: mName || t("commands.plugin.local"),
556
+ data: { pluginName: p.name, source: p.source }
557
+ });
558
+ }
559
+ } else if (activeTab === "marketplaces") {
560
+ for (const m of marketplaces) {
561
+ mainItems.push({
562
+ id: m.name,
563
+ label: m.name,
564
+ description: `${m.manifest.plugins.length} plugins \xB7 ${formatMarketplacePath(m.source)} \xB7 ${formatTimeAgo(m.lastUpdated)}`,
565
+ status: m.enabled ? "enabled" : "disabled",
566
+ data: { marketplaceName: m.name }
567
+ });
568
+ }
569
+ mainItems.push({
570
+ id: "add-new",
571
+ label: `+ ${t("commands.plugin.addMarketplace")}`,
572
+ description: "Register a new plugin marketplace"
573
+ });
470
574
  }
471
- );
472
- case "marketplace-selector":
473
- return /* @__PURE__ */ React.createElement(
474
- MarketplaceSelector,
475
- {
476
- onNavigate: handleNavigate,
477
- onBack: handleBack,
478
- onDone
575
+ const isMultiSelectTab = activeTab === "browse" || activeTab === "installed";
576
+ return {
577
+ tabs: mainTabs,
578
+ items: mainItems,
579
+ title: "Plugins",
580
+ footerHint: isMultiSelectTab ? t("commands.plugin.footerHintMultiSelect") : t("commands.plugin.footerHint"),
581
+ searchEnabled: true,
582
+ searchPlaceholder: void 0
583
+ };
584
+ }
585
+ case "plugin-actions": {
586
+ const actionItems = [];
587
+ if (phase.from === "browse") {
588
+ actionItems.push({
589
+ id: "install",
590
+ label: t("commands.plugin.install"),
591
+ description: `Install from ${phase.marketplace || "marketplace"}`
592
+ });
593
+ } else {
594
+ const plugin2 = installedPlugins.find((p) => p.name === phase.plugin);
595
+ const isEnabled = plugin2?.enabled ?? true;
596
+ actionItems.push({
597
+ id: "toggle",
598
+ label: isEnabled ? t("commands.plugin.disable") : t("commands.plugin.enable"),
599
+ description: isEnabled ? "Disable this plugin" : "Enable this plugin"
600
+ });
601
+ if (phase.marketplace) {
602
+ actionItems.push({
603
+ id: "update",
604
+ label: t("commands.plugin.update"),
605
+ description: "Re-install from marketplace source"
606
+ });
607
+ }
608
+ actionItems.push({
609
+ id: "uninstall",
610
+ label: t("commands.plugin.uninstall"),
611
+ description: "Remove this plugin"
612
+ });
479
613
  }
480
- );
481
- case "plugin-browser":
482
- return /* @__PURE__ */ React.createElement(
483
- PluginBrowser,
484
- {
485
- marketplace: currentState.marketplace,
486
- onNavigate: handleNavigate,
487
- onBack: handleBack,
488
- onDone
614
+ actionItems.push({
615
+ id: "back",
616
+ label: t("commands.plugin.back")
617
+ });
618
+ return {
619
+ tabs: [
620
+ { id: "actions", label: t("commands.plugin.actions") }
621
+ ],
622
+ items: actionItems,
623
+ title: phase.plugin,
624
+ footerHint: "\u2191\u2193 Navigate \xB7 Enter Select \xB7 Esc Back",
625
+ searchEnabled: false,
626
+ searchPlaceholder: void 0
627
+ };
628
+ }
629
+ case "marketplace-actions": {
630
+ const mActionItems = [
631
+ {
632
+ id: "update",
633
+ label: t("commands.plugin.marketplaceUpdate"),
634
+ description: "Re-fetch marketplace manifest"
635
+ },
636
+ {
637
+ id: "browse",
638
+ label: t("commands.plugin.browsePlugins"),
639
+ description: "View plugins from this marketplace"
640
+ },
641
+ {
642
+ id: "remove",
643
+ label: t("commands.plugin.marketplaceRemove"),
644
+ description: "Unregister this marketplace"
645
+ },
646
+ {
647
+ id: "back",
648
+ label: t("commands.plugin.back")
649
+ }
650
+ ];
651
+ return {
652
+ tabs: [
653
+ { id: "actions", label: t("commands.plugin.actions") }
654
+ ],
655
+ items: mActionItems,
656
+ title: phase.marketplace,
657
+ footerHint: "\u2191\u2193 Navigate \xB7 Enter Select \xB7 Esc Back",
658
+ searchEnabled: false,
659
+ searchPlaceholder: void 0
660
+ };
661
+ }
662
+ case "batch-action-picker": {
663
+ const pickerItems = [];
664
+ const sel = phase.selectedItems;
665
+ if (phase.from === "installed") {
666
+ const updatableCount = sel.filter((item) => {
667
+ const pluginName = item.data?.pluginName;
668
+ return findPluginMarketplace(pluginName) !== void 0;
669
+ }).length;
670
+ if (updatableCount > 0) {
671
+ pickerItems.push({
672
+ id: "batch-update",
673
+ label: t("commands.plugin.batchUpdateAction").replace(
674
+ "{count}",
675
+ String(updatableCount)
676
+ )
677
+ });
678
+ }
679
+ pickerItems.push({
680
+ id: "batch-uninstall",
681
+ label: t("commands.plugin.batchDeleteAction").replace(
682
+ "{count}",
683
+ String(sel.length)
684
+ )
685
+ });
686
+ const disabledCount = sel.filter((item) => {
687
+ const plugin2 = installedPlugins.find(
688
+ (p) => p.name === item.data?.pluginName
689
+ );
690
+ return plugin2 && !plugin2.enabled;
691
+ }).length;
692
+ if (disabledCount > 0) {
693
+ pickerItems.push({
694
+ id: "batch-enable",
695
+ label: t("commands.plugin.batchEnableAction").replace(
696
+ "{count}",
697
+ String(disabledCount)
698
+ )
699
+ });
700
+ }
701
+ const enabledCount = sel.filter((item) => {
702
+ const plugin2 = installedPlugins.find(
703
+ (p) => p.name === item.data?.pluginName
704
+ );
705
+ return plugin2 && plugin2.enabled;
706
+ }).length;
707
+ if (enabledCount > 0) {
708
+ pickerItems.push({
709
+ id: "batch-disable",
710
+ label: t("commands.plugin.batchDisableAction").replace(
711
+ "{count}",
712
+ String(enabledCount)
713
+ )
714
+ });
715
+ }
716
+ } else {
717
+ const notInstalledCount = sel.filter(
718
+ (item) => !installedNames.has(item.data?.pluginName)
719
+ ).length;
720
+ if (notInstalledCount > 0) {
721
+ pickerItems.push({
722
+ id: "batch-install",
723
+ label: t("commands.plugin.batchInstallAction").replace(
724
+ "{count}",
725
+ String(notInstalledCount)
726
+ )
727
+ });
728
+ }
729
+ const alreadyInstalledCount = sel.filter(
730
+ (item) => installedNames.has(item.data?.pluginName)
731
+ ).length;
732
+ if (alreadyInstalledCount > 0) {
733
+ pickerItems.push({
734
+ id: "batch-update",
735
+ label: t("commands.plugin.batchUpdateAction").replace(
736
+ "{count}",
737
+ String(alreadyInstalledCount)
738
+ )
739
+ });
740
+ }
489
741
  }
490
- );
491
- case "plugin-details-install":
492
- return /* @__PURE__ */ React.createElement(
493
- PluginDetailsInstall,
494
- {
495
- marketplace: currentState.marketplace,
496
- plugin: currentState.plugin,
497
- onNavigate: handleNavigate,
498
- onBack: handleBack,
499
- onDone
742
+ pickerItems.push({
743
+ id: "back",
744
+ label: t("commands.plugin.back")
745
+ });
746
+ return {
747
+ tabs: [
748
+ {
749
+ id: "actions",
750
+ label: t("commands.plugin.batchActionPickerTitle")
751
+ }
752
+ ],
753
+ items: pickerItems,
754
+ title: t("commands.plugin.batchActionPickerTitle"),
755
+ footerHint: "\u2191\u2193 Navigate \xB7 Enter Select \xB7 Esc Back",
756
+ searchEnabled: false,
757
+ searchPlaceholder: void 0
758
+ };
759
+ }
760
+ case "batch-progress": {
761
+ return {
762
+ tabs: [
763
+ { id: "progress", label: t("common.loading") }
764
+ ],
765
+ items: [],
766
+ title: "Plugins",
767
+ footerHint: "",
768
+ searchEnabled: false,
769
+ searchPlaceholder: void 0
770
+ };
771
+ }
772
+ case "add-marketplace": {
773
+ const helpItems = [
774
+ {
775
+ id: "example-github",
776
+ label: "owner/repo",
777
+ description: "GitHub shorthand"
778
+ },
779
+ {
780
+ id: "example-https",
781
+ label: "https://github.com/owner/repo.git",
782
+ description: "HTTPS URL"
783
+ },
784
+ {
785
+ id: "example-local",
786
+ label: "./path/to/local",
787
+ description: "Local path"
788
+ }
789
+ ];
790
+ return {
791
+ tabs: [
792
+ {
793
+ id: "add",
794
+ label: t("commands.plugin.marketplaceAdd")
795
+ }
796
+ ],
797
+ items: helpItems,
798
+ title: t("commands.plugin.addMarketplace"),
799
+ footerHint: t("commands.plugin.addFooterHint"),
800
+ searchEnabled: true,
801
+ searchPlaceholder: t("commands.plugin.addMarketplacePlaceholder")
802
+ };
803
+ }
804
+ }
805
+ }, [
806
+ phase,
807
+ activeTab,
808
+ marketplaces,
809
+ installedPlugins,
810
+ installedNames,
811
+ findPluginMarketplace
812
+ ]);
813
+ const handleTabChange = useCallback((tabId) => {
814
+ setActiveTab(tabId);
815
+ setSearchQuery("");
816
+ setSelectedIds(/* @__PURE__ */ new Set());
817
+ }, []);
818
+ const handleSelectionChange = useCallback(
819
+ (itemId, selected) => {
820
+ setSelectedIds((prev) => {
821
+ const next = new Set(prev);
822
+ if (selected) {
823
+ next.add(itemId);
824
+ } else {
825
+ next.delete(itemId);
500
826
  }
501
- );
502
- case "installed-plugins-manager":
503
- return /* @__PURE__ */ React.createElement(
504
- InstalledPluginsManager,
505
- {
506
- key: navigationStack.length,
507
- onNavigate: handleNavigate,
508
- onBack: handleBack,
509
- onDone
827
+ return next;
828
+ });
829
+ },
830
+ []
831
+ );
832
+ const handleMultiSelect = useCallback(
833
+ (selectedItems) => {
834
+ if (activeTab === "browse" || activeTab === "installed") {
835
+ setPhase({
836
+ type: "batch-action-picker",
837
+ selectedItems,
838
+ from: activeTab
839
+ });
840
+ }
841
+ },
842
+ [activeTab]
843
+ );
844
+ useEffect(() => {
845
+ if (phase.type !== "batch-progress") return;
846
+ let cancelled = false;
847
+ const { action, plugins } = phase;
848
+ const executeBatch = async () => {
849
+ let successCount = 0;
850
+ let failCount = 0;
851
+ const errors = [];
852
+ for (let i = 0; i < plugins.length; i++) {
853
+ if (cancelled) return;
854
+ const plugin2 = plugins[i];
855
+ const progressKey = action === "install" ? "commands.plugin.batchInstalling" : action === "update" ? "commands.plugin.batchUpdating" : "commands.plugin.batchUninstalling";
856
+ setStatusOverlay({
857
+ type: "loading",
858
+ message: t(progressKey).replace("{current}", String(i + 1)).replace("{total}", String(plugins.length)).replace("{name}", plugin2.name)
859
+ });
860
+ try {
861
+ if (action === "uninstall") {
862
+ await uninstallPlugin(plugin2.name);
863
+ } else {
864
+ await installPluginFromMarketplace(plugin2.name, plugin2.marketplace);
865
+ }
866
+ successCount++;
867
+ } catch (err) {
868
+ failCount++;
869
+ const msg = err instanceof Error ? err.message : String(err);
870
+ if (errors.length < 3) {
871
+ errors.push(`${plugin2.name}: ${msg}`);
872
+ }
510
873
  }
511
- );
512
- case "installed-plugins-by-marketplace":
513
- return /* @__PURE__ */ React.createElement(
514
- InstalledPluginsByMarketplace,
515
- {
516
- marketplace: currentState.marketplace,
517
- onNavigate: handleNavigate,
518
- onBack: handleBack,
519
- onDone
874
+ }
875
+ if (cancelled) return;
876
+ const parts = [];
877
+ if (successCount > 0) {
878
+ const countKey = action === "install" ? "commands.plugin.batchInstalledCount" : action === "update" ? "commands.plugin.batchUpdatedCount" : "commands.plugin.batchUninstalledCount";
879
+ parts.push(t(countKey).replace("{count}", String(successCount)));
880
+ }
881
+ if (failCount > 0) {
882
+ parts.push(
883
+ t("commands.plugin.batchFailedCount").replace(
884
+ "{count}",
885
+ String(failCount)
886
+ )
887
+ );
888
+ if (errors.length > 0) {
889
+ parts.push(errors.join("\n"));
520
890
  }
521
- );
522
- case "plugin-details-manage":
523
- return /* @__PURE__ */ React.createElement(
524
- PluginDetailsManage,
525
- {
526
- plugin: currentState.plugin,
527
- onNavigate: handleNavigate,
528
- onBack: handleBack,
529
- onDone
891
+ }
892
+ setStatusOverlay({
893
+ type: failCount === 0 ? "success" : "error",
894
+ message: parts.join("\n")
895
+ });
896
+ setSelectedIds(/* @__PURE__ */ new Set());
897
+ };
898
+ executeBatch();
899
+ return () => {
900
+ cancelled = true;
901
+ };
902
+ }, [phase]);
903
+ const handleStatusDismiss = useCallback(() => {
904
+ setStatusOverlay(null);
905
+ setPhase({ type: "main" });
906
+ }, []);
907
+ const handleBack = useCallback(() => {
908
+ if (statusOverlay && statusOverlay.type !== "loading") {
909
+ handleStatusDismiss();
910
+ return;
911
+ }
912
+ if (phase.type === "main") {
913
+ onDone();
914
+ } else {
915
+ setPhase({ type: "main" });
916
+ setSearchQuery("");
917
+ }
918
+ }, [phase, statusOverlay, onDone, handleStatusDismiss]);
919
+ const handleClose = useCallback(() => {
920
+ if (statusOverlay && statusOverlay.type !== "loading") {
921
+ handleStatusDismiss();
922
+ return;
923
+ }
924
+ if (phase.type === "main") {
925
+ onDone();
926
+ } else {
927
+ setPhase({ type: "main" });
928
+ setSearchQuery("");
929
+ }
930
+ }, [phase, statusOverlay, onDone, handleStatusDismiss]);
931
+ const handleSearchSubmit = useCallback(
932
+ async (query) => {
933
+ if (phase.type !== "add-marketplace" || !query.trim()) return;
934
+ setStatusOverlay({
935
+ type: "loading",
936
+ message: t("commands.plugin.marketplaceAdding")
937
+ });
938
+ try {
939
+ await addMarketplace(query.trim());
940
+ setStatusOverlay({
941
+ type: "success",
942
+ message: t("commands.plugin.marketplaceAddSuccess")
943
+ });
944
+ } catch (err) {
945
+ const msg = err instanceof Error ? err.message : String(err);
946
+ setStatusOverlay({
947
+ type: "error",
948
+ message: `${t("commands.plugin.marketplaceAddFailed")}: ${msg}`
949
+ });
950
+ }
951
+ },
952
+ [phase]
953
+ );
954
+ const handleSelect = useCallback(
955
+ async (item) => {
956
+ switch (phase.type) {
957
+ case "main": {
958
+ if (activeTab === "browse") {
959
+ const data = item.data;
960
+ setPhase({
961
+ type: "plugin-actions",
962
+ plugin: data.pluginName,
963
+ marketplace: data.marketplaceName,
964
+ from: "browse"
965
+ });
966
+ } else if (activeTab === "installed") {
967
+ const data = item.data;
968
+ const mName = findPluginMarketplace(data.pluginName);
969
+ setPhase({
970
+ type: "plugin-actions",
971
+ plugin: data.pluginName,
972
+ marketplace: mName,
973
+ from: "installed"
974
+ });
975
+ } else if (activeTab === "marketplaces") {
976
+ if (item.id === "add-new") {
977
+ setPhase({ type: "add-marketplace" });
978
+ setSearchQuery("");
979
+ } else {
980
+ const data = item.data;
981
+ setPhase({
982
+ type: "marketplace-actions",
983
+ marketplace: data.marketplaceName
984
+ });
985
+ }
986
+ }
987
+ break;
530
988
  }
531
- );
532
- case "add-marketplace":
533
- return /* @__PURE__ */ React.createElement(
534
- AddMarketplaceForm,
535
- {
536
- onNavigate: handleNavigate,
537
- onBack: handleBack,
538
- onDone
989
+ case "plugin-actions": {
990
+ if (item.id === "back") {
991
+ setPhase({ type: "main" });
992
+ return;
993
+ }
994
+ if (item.id === "install") {
995
+ setStatusOverlay({
996
+ type: "loading",
997
+ message: t("commands.plugin.installing")
998
+ });
999
+ try {
1000
+ await installPluginFromMarketplace(
1001
+ phase.plugin,
1002
+ phase.marketplace
1003
+ );
1004
+ setStatusOverlay({
1005
+ type: "success",
1006
+ message: t("commands.plugin.installSuccess")
1007
+ });
1008
+ } catch (err) {
1009
+ const msg = err instanceof Error ? err.message : String(err);
1010
+ setStatusOverlay({
1011
+ type: "error",
1012
+ message: `${t("commands.plugin.installFailed")}: ${msg}`
1013
+ });
1014
+ }
1015
+ } else if (item.id === "toggle") {
1016
+ try {
1017
+ togglePluginEnabled(phase.plugin);
1018
+ setStatusOverlay({
1019
+ type: "success",
1020
+ message: t("commands.plugin.toggleSuccess")
1021
+ });
1022
+ } catch (err) {
1023
+ const msg = err instanceof Error ? err.message : String(err);
1024
+ setStatusOverlay({ type: "error", message: msg });
1025
+ }
1026
+ } else if (item.id === "update") {
1027
+ setStatusOverlay({
1028
+ type: "loading",
1029
+ message: t("commands.plugin.updating")
1030
+ });
1031
+ try {
1032
+ await installPluginFromMarketplace(
1033
+ phase.plugin,
1034
+ phase.marketplace
1035
+ );
1036
+ setStatusOverlay({
1037
+ type: "success",
1038
+ message: t("commands.plugin.updateSuccess")
1039
+ });
1040
+ } catch (err) {
1041
+ const msg = err instanceof Error ? err.message : String(err);
1042
+ setStatusOverlay({
1043
+ type: "error",
1044
+ message: `${t("commands.plugin.updateFailed")}: ${msg}`
1045
+ });
1046
+ }
1047
+ } else if (item.id === "uninstall") {
1048
+ setStatusOverlay({
1049
+ type: "loading",
1050
+ message: t("commands.plugin.uninstalling")
1051
+ });
1052
+ try {
1053
+ await uninstallPlugin(phase.plugin);
1054
+ setStatusOverlay({
1055
+ type: "success",
1056
+ message: t("commands.plugin.uninstallSuccess")
1057
+ });
1058
+ } catch (err) {
1059
+ const msg = err instanceof Error ? err.message : String(err);
1060
+ setStatusOverlay({
1061
+ type: "error",
1062
+ message: `${t("commands.plugin.uninstallFailed")}: ${msg}`
1063
+ });
1064
+ }
1065
+ }
1066
+ break;
539
1067
  }
540
- );
541
- case "marketplace-manager":
542
- return /* @__PURE__ */ React.createElement(
543
- MarketplaceManager,
544
- {
545
- onNavigate: handleNavigate,
546
- onBack: handleBack,
547
- onDone
1068
+ case "marketplace-actions": {
1069
+ if (item.id === "back") {
1070
+ setPhase({ type: "main" });
1071
+ return;
1072
+ }
1073
+ if (item.id === "update") {
1074
+ setStatusOverlay({
1075
+ type: "loading",
1076
+ message: t("commands.plugin.marketplaceUpdating")
1077
+ });
1078
+ try {
1079
+ await updateMarketplace(phase.marketplace);
1080
+ setStatusOverlay({
1081
+ type: "success",
1082
+ message: t("commands.plugin.marketplaceUpdateSuccess")
1083
+ });
1084
+ } catch (err) {
1085
+ const msg = err instanceof Error ? err.message : String(err);
1086
+ setStatusOverlay({
1087
+ type: "error",
1088
+ message: `${t("commands.plugin.marketplaceUpdateFailed")}: ${msg}`
1089
+ });
1090
+ }
1091
+ } else if (item.id === "browse") {
1092
+ setPhase({ type: "main" });
1093
+ setActiveTab("browse");
1094
+ setSearchQuery(phase.marketplace);
1095
+ } else if (item.id === "remove") {
1096
+ setStatusOverlay({
1097
+ type: "loading",
1098
+ message: t("commands.plugin.marketplaceRemoving")
1099
+ });
1100
+ try {
1101
+ removeMarketplace(phase.marketplace);
1102
+ setStatusOverlay({
1103
+ type: "success",
1104
+ message: t("commands.plugin.marketplaceRemoveSuccess")
1105
+ });
1106
+ } catch (err) {
1107
+ const msg = err instanceof Error ? err.message : String(err);
1108
+ setStatusOverlay({ type: "error", message: msg });
1109
+ }
1110
+ }
1111
+ break;
548
1112
  }
549
- );
550
- case "marketplace-details":
551
- return /* @__PURE__ */ React.createElement(
552
- MarketplaceDetails,
553
- {
554
- marketplace: currentState.marketplace,
555
- onNavigate: handleNavigate,
556
- onBack: handleBack,
557
- onDone
1113
+ case "batch-action-picker": {
1114
+ if (item.id === "back") {
1115
+ setPhase({ type: "main" });
1116
+ return;
1117
+ }
1118
+ const sel = phase.selectedItems;
1119
+ if (item.id === "batch-install") {
1120
+ const toInstall = sel.filter((si) => !installedNames.has(si.data?.pluginName)).map((si) => ({
1121
+ name: si.data.pluginName,
1122
+ marketplace: si.data.marketplaceName
1123
+ }));
1124
+ if (toInstall.length === 0) {
1125
+ setStatusOverlay({
1126
+ type: "error",
1127
+ message: t("commands.plugin.batchAllAlreadyInstalled")
1128
+ });
1129
+ return;
1130
+ }
1131
+ setPhase({
1132
+ type: "batch-progress",
1133
+ action: "install",
1134
+ plugins: toInstall
1135
+ });
1136
+ } else if (item.id === "batch-update") {
1137
+ const toUpdate = sel.filter((si) => {
1138
+ const pluginName = si.data?.pluginName;
1139
+ return findPluginMarketplace(pluginName) !== void 0;
1140
+ }).map((si) => {
1141
+ const pluginName = si.data.pluginName;
1142
+ return {
1143
+ name: pluginName,
1144
+ marketplace: findPluginMarketplace(pluginName)
1145
+ };
1146
+ });
1147
+ if (toUpdate.length === 0) {
1148
+ setStatusOverlay({
1149
+ type: "error",
1150
+ message: t("commands.plugin.batchNoUpdatable")
1151
+ });
1152
+ return;
1153
+ }
1154
+ setPhase({
1155
+ type: "batch-progress",
1156
+ action: "update",
1157
+ plugins: toUpdate
1158
+ });
1159
+ } else if (item.id === "batch-uninstall") {
1160
+ const toUninstall = sel.map((si) => ({
1161
+ name: si.data.pluginName
1162
+ }));
1163
+ setPhase({
1164
+ type: "batch-progress",
1165
+ action: "uninstall",
1166
+ plugins: toUninstall
1167
+ });
1168
+ } else if (item.id === "batch-enable") {
1169
+ let count = 0;
1170
+ for (const si of sel) {
1171
+ const pluginName = si.data?.pluginName;
1172
+ const plugin2 = installedPlugins.find((p) => p.name === pluginName);
1173
+ if (plugin2 && !plugin2.enabled) {
1174
+ try {
1175
+ enablePlugin(pluginName);
1176
+ count++;
1177
+ } catch {
1178
+ }
1179
+ }
1180
+ }
1181
+ setStatusOverlay({
1182
+ type: "success",
1183
+ message: t("commands.plugin.batchEnabledCount").replace(
1184
+ "{count}",
1185
+ String(count)
1186
+ )
1187
+ });
1188
+ setSelectedIds(/* @__PURE__ */ new Set());
1189
+ } else if (item.id === "batch-disable") {
1190
+ let count = 0;
1191
+ for (const si of sel) {
1192
+ const pluginName = si.data?.pluginName;
1193
+ const plugin2 = installedPlugins.find((p) => p.name === pluginName);
1194
+ if (plugin2 && plugin2.enabled) {
1195
+ try {
1196
+ disablePlugin(pluginName);
1197
+ count++;
1198
+ } catch {
1199
+ }
1200
+ }
1201
+ }
1202
+ setStatusOverlay({
1203
+ type: "success",
1204
+ message: t("commands.plugin.batchDisabledCount").replace(
1205
+ "{count}",
1206
+ String(count)
1207
+ )
1208
+ });
1209
+ setSelectedIds(/* @__PURE__ */ new Set());
1210
+ }
1211
+ break;
558
1212
  }
559
- );
560
- default:
561
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { color: "red" }, "Unknown screen state"), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Press Esc to exit"));
562
- }
1213
+ case "add-marketplace": {
1214
+ setSearchQuery(item.label);
1215
+ break;
1216
+ }
1217
+ }
1218
+ },
1219
+ [phase, activeTab, findPluginMarketplace, installedPlugins, installedNames]
1220
+ );
1221
+ const currentTab = phase.type === "main" ? activeTab : phase.type === "plugin-actions" || phase.type === "marketplace-actions" || phase.type === "batch-action-picker" ? "actions" : phase.type === "batch-progress" ? "progress" : "add";
1222
+ return /* @__PURE__ */ React.createElement(
1223
+ TabbedListView,
1224
+ {
1225
+ title,
1226
+ tabs,
1227
+ activeTab: currentTab,
1228
+ onTabChange: handleTabChange,
1229
+ items,
1230
+ searchEnabled,
1231
+ searchPlaceholder,
1232
+ searchQuery,
1233
+ onSearchChange: setSearchQuery,
1234
+ onSearchSubmit: phase.type === "add-marketplace" ? handleSearchSubmit : void 0,
1235
+ onSelect: handleSelect,
1236
+ onClose: handleClose,
1237
+ onBack: handleBack,
1238
+ statusOverlay,
1239
+ footerHint,
1240
+ groupByCategory: activeTab === "browse" || activeTab === "installed",
1241
+ statusDismissHint: "Enter Continue \xB7 Esc Back",
1242
+ multiSelect: multiSelectEnabled,
1243
+ selectedIds,
1244
+ onSelectionChange: handleSelectionChange,
1245
+ onMultiSelect: handleMultiSelect,
1246
+ multiSelectActionLabel
1247
+ }
1248
+ );
563
1249
  };
564
1250
  const LegacyPluginCommand = ({
565
1251
  args,
@@ -575,7 +1261,7 @@ const LegacyPluginCommand = ({
575
1261
  }
576
1262
  }, [subcommand, onDone]);
577
1263
  if (!subcommand || subcommand === "help" || subcommand === "--help") {
578
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true, underline: true, color: theme.primary }, "Plugin Management Commands"), /* @__PURE__ */ React.createElement(Text, null, ""), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin list"), " - List all installed plugins"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin info <name>"), " - Show detailed plugin information"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin install <name>[@marketplace]"), " ", "- Install plugin from marketplace"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin uninstall <name>"), " - Remove installed plugin"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin validate [name]"), " - Validate plugin manifest and components"), /* @__PURE__ */ React.createElement(Text, null, ""), /* @__PURE__ */ React.createElement(Text, { bold: true, color: theme.primary }, "Marketplace Commands:"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin marketplace add <source>"), " ", "- Register new marketplace"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin marketplace list"), " - List registered marketplaces"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin marketplace update <name>"), " ", "- Update marketplace manifest"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin marketplace remove <name>"), " ", "- Unregister marketplace"), /* @__PURE__ */ React.createElement(Text, null, ""), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Examples:"), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " /plugin list"), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " /plugin info my-plugin"), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", "/plugin install awesome-plugin@official"), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", "/plugin marketplace add owner/repo"));
1264
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true, underline: true, color: theme.primary }, "Plugin Management Commands"), /* @__PURE__ */ React.createElement(Text, null, ""), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin list"), " - List all installed plugins"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin info <name>"), " - Show detailed plugin information"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin install <name>[@marketplace]"), " ", "- Install plugin from marketplace"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin uninstall <name>"), " - Remove installed plugin"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin validate [name]"), " - Validate plugin manifest and components"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin sync [--force] [--clean] [--dry-run]"), " ", "- ", t("commands.plugin.syncDesc")), /* @__PURE__ */ React.createElement(Text, null, ""), /* @__PURE__ */ React.createElement(Text, { bold: true, color: theme.primary }, "Marketplace Commands:"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin marketplace add <source>"), " ", "- Register new marketplace"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin marketplace list"), " - List registered marketplaces"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin marketplace update <name>"), " ", "- Update marketplace manifest"), /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: theme.success }, "/plugin marketplace remove <name>"), " ", "- Unregister marketplace"), /* @__PURE__ */ React.createElement(Text, null, ""), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, "Examples:"), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " /plugin list"), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " /plugin info my-plugin"), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", "/plugin install awesome-plugin@official"), /* @__PURE__ */ React.createElement(Text, { color: SEMANTIC_COLORS.dim }, " ", "/plugin marketplace add owner/repo"));
579
1265
  }
580
1266
  switch (subcommand) {
581
1267
  case "list":
@@ -615,6 +1301,16 @@ const LegacyPluginCommand = ({
615
1301
  return /* @__PURE__ */ React.createElement(PluginUninstall, { pluginName: subArgs[0], onDone });
616
1302
  case "validate":
617
1303
  return /* @__PURE__ */ React.createElement(PluginValidate, { pluginName: subArgs[0], onDone });
1304
+ case "sync":
1305
+ return /* @__PURE__ */ React.createElement(
1306
+ PluginSync,
1307
+ {
1308
+ onDone,
1309
+ force: subArgs.includes("--force"),
1310
+ clean: subArgs.includes("--clean"),
1311
+ dryRun: subArgs.includes("--dry-run")
1312
+ }
1313
+ );
618
1314
  case "marketplace":
619
1315
  const marketplaceSubcommand = subArgs[0];
620
1316
  const marketplaceArgs = subArgs.slice(1);
@@ -689,6 +1385,7 @@ const plugin = {
689
1385
  description: "Manage plugins and plugin marketplaces (interactive)",
690
1386
  isEnabled: true,
691
1387
  isHidden: false,
1388
+ hidePromptInput: true,
692
1389
  aliases: ["plugins"],
693
1390
  async call(onDone, _context) {
694
1391
  return /* @__PURE__ */ React.createElement(PluginCommand, { args: "", onDone });