@within-7/minto 0.3.9 → 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 (141) hide show
  1. package/dist/commands/agents/AgentsCommand.js +459 -655
  2. package/dist/commands/agents/AgentsCommand.js.map +2 -2
  3. package/dist/commands/agents/types.js +1 -0
  4. package/dist/commands/agents/types.js.map +2 -2
  5. package/dist/commands/agents/utils/fileOperations.js +96 -36
  6. package/dist/commands/agents/utils/fileOperations.js.map +3 -3
  7. package/dist/commands/agents/utils/index.js +3 -1
  8. package/dist/commands/agents/utils/index.js.map +2 -2
  9. package/dist/commands/context.js +54 -23
  10. package/dist/commands/context.js.map +2 -2
  11. package/dist/commands/export.js +673 -93
  12. package/dist/commands/export.js.map +2 -2
  13. package/dist/commands/language.js +19 -46
  14. package/dist/commands/language.js.map +2 -2
  15. package/dist/commands/mcp-interactive.js +419 -217
  16. package/dist/commands/mcp-interactive.js.map +2 -2
  17. package/dist/commands/model.js +415 -66
  18. package/dist/commands/model.js.map +2 -2
  19. package/dist/commands/permissions.js +75 -49
  20. package/dist/commands/permissions.js.map +2 -2
  21. package/dist/commands/plugin.js +882 -185
  22. package/dist/commands/plugin.js.map +3 -3
  23. package/dist/commands/resume.js +1 -1
  24. package/dist/commands/resume.js.map +1 -1
  25. package/dist/commands/sandbox.js +168 -70
  26. package/dist/commands/sandbox.js.map +2 -2
  27. package/dist/commands/setup.js +593 -107
  28. package/dist/commands/setup.js.map +2 -2
  29. package/dist/commands/stats.js +188 -131
  30. package/dist/commands/stats.js.map +2 -2
  31. package/dist/commands/status.js +75 -13
  32. package/dist/commands/status.js.map +2 -2
  33. package/dist/commands/undo.js +138 -174
  34. package/dist/commands/undo.js.map +2 -2
  35. package/dist/commands.js.map +1 -1
  36. package/dist/components/Help.js +165 -32
  37. package/dist/components/Help.js.map +2 -2
  38. package/dist/components/InfoPanel/InfoPanel.js +123 -0
  39. package/dist/components/InfoPanel/InfoPanel.js.map +7 -0
  40. package/dist/components/InfoPanel/index.js +5 -0
  41. package/dist/components/InfoPanel/index.js.map +7 -0
  42. package/dist/components/InfoPanel/types.js +1 -0
  43. package/dist/components/InfoPanel/types.js.map +7 -0
  44. package/dist/components/ModelSelector/BrandTextInput.js +43 -0
  45. package/dist/components/ModelSelector/BrandTextInput.js.map +7 -0
  46. package/dist/components/ModelSelector/ModelSelector.js +419 -501
  47. package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
  48. package/dist/components/ModelSelector/WizardContainer.js +45 -0
  49. package/dist/components/ModelSelector/WizardContainer.js.map +7 -0
  50. package/dist/components/ModelSelector/index.js +1 -3
  51. package/dist/components/ModelSelector/index.js.map +2 -2
  52. package/dist/components/PromptInput.js +5 -5
  53. package/dist/components/PromptInput.js.map +2 -2
  54. package/dist/components/SimpleSelector/SimpleSelector.js +154 -0
  55. package/dist/components/SimpleSelector/SimpleSelector.js.map +7 -0
  56. package/dist/components/SimpleSelector/index.js +5 -0
  57. package/dist/components/SimpleSelector/index.js.map +7 -0
  58. package/dist/components/SimpleSelector/types.js +1 -0
  59. package/dist/components/SimpleSelector/types.js.map +7 -0
  60. package/dist/components/StatusOverlayContent.js +21 -0
  61. package/dist/components/StatusOverlayContent.js.map +7 -0
  62. package/dist/components/TabbedListView/ScrollableList.js +31 -5
  63. package/dist/components/TabbedListView/ScrollableList.js.map +2 -2
  64. package/dist/components/TabbedListView/TabbedListView.js +122 -47
  65. package/dist/components/TabbedListView/TabbedListView.js.map +2 -2
  66. package/dist/core/backupHook.js +29 -0
  67. package/dist/core/backupHook.js.map +7 -0
  68. package/dist/core/config/defaults.js +8 -2
  69. package/dist/core/config/defaults.js.map +2 -2
  70. package/dist/core/config/schema.js +14 -2
  71. package/dist/core/config/schema.js.map +2 -2
  72. package/dist/core/costTracker.js +0 -16
  73. package/dist/core/costTracker.js.map +2 -2
  74. package/dist/cost-tracker.js +0 -16
  75. package/dist/cost-tracker.js.map +2 -2
  76. package/dist/entrypoints/bootstrap.js +3 -1
  77. package/dist/entrypoints/bootstrap.js.map +2 -2
  78. package/dist/entrypoints/cli.js +32 -0
  79. package/dist/entrypoints/cli.js.map +2 -2
  80. package/dist/i18n/locales/en.js +300 -1
  81. package/dist/i18n/locales/en.js.map +2 -2
  82. package/dist/i18n/locales/zh-CN.js +301 -2
  83. package/dist/i18n/locales/zh-CN.js.map +2 -2
  84. package/dist/i18n/types.js.map +1 -1
  85. package/dist/services/customCommands.js +30 -8
  86. package/dist/services/customCommands.js.map +2 -2
  87. package/dist/services/plugins/lspServers.js +1 -1
  88. package/dist/services/plugins/lspServers.js.map +2 -2
  89. package/dist/services/plugins/pluginRuntime.js +2 -1
  90. package/dist/services/plugins/pluginRuntime.js.map +2 -2
  91. package/dist/services/plugins/pluginValidation.js +10 -3
  92. package/dist/services/plugins/pluginValidation.js.map +2 -2
  93. package/dist/services/plugins/skillMarketplace.js +16 -8
  94. package/dist/services/plugins/skillMarketplace.js.map +2 -2
  95. package/dist/services/systemReminder.js +17 -6
  96. package/dist/services/systemReminder.js.map +2 -2
  97. package/dist/tools/FileEditTool/FileEditTool.js +7 -0
  98. package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
  99. package/dist/tools/FileWriteTool/FileWriteTool.js +7 -0
  100. package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
  101. package/dist/tools/MultiEditTool/MultiEditTool.js +7 -0
  102. package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
  103. package/dist/tools/NotebookEditTool/NotebookEditTool.js +2 -0
  104. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
  105. package/dist/tools/TaskTool/TaskTool.js +9 -6
  106. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  107. package/dist/types/PermissionMode.js.map +1 -1
  108. package/dist/types/plugin.js +2 -4
  109. package/dist/types/plugin.js.map +2 -2
  110. package/dist/utils/agentHookExecutor.js +1 -4
  111. package/dist/utils/agentHookExecutor.js.map +2 -2
  112. package/dist/utils/agentLoader.js +67 -13
  113. package/dist/utils/agentLoader.js.map +2 -2
  114. package/dist/utils/agentMemory.js.map +2 -2
  115. package/dist/utils/claudeCodeSync.js +439 -0
  116. package/dist/utils/claudeCodeSync.js.map +7 -0
  117. package/dist/utils/config.js +1 -23
  118. package/dist/utils/config.js.map +2 -2
  119. package/dist/utils/execFileNoThrow.js +2 -1
  120. package/dist/utils/execFileNoThrow.js.map +2 -2
  121. package/dist/utils/marketplaceManager.js +80 -43
  122. package/dist/utils/marketplaceManager.js.map +2 -2
  123. package/dist/utils/messages.js +2 -2
  124. package/dist/utils/messages.js.map +2 -2
  125. package/dist/utils/pluginInstaller.js +34 -24
  126. package/dist/utils/pluginInstaller.js.map +2 -2
  127. package/dist/utils/pluginLoader.js +48 -25
  128. package/dist/utils/pluginLoader.js.map +2 -2
  129. package/dist/utils/repoFetcher.js +110 -0
  130. package/dist/utils/repoFetcher.js.map +7 -0
  131. package/dist/utils/skillLoader.js +18 -6
  132. package/dist/utils/skillLoader.js.map +2 -2
  133. package/dist/utils/stringSubstitution.js +4 -5
  134. package/dist/utils/stringSubstitution.js.map +2 -2
  135. package/dist/utils/teamConfig.js +153 -13
  136. package/dist/utils/teamConfig.js.map +2 -2
  137. package/dist/utils/terminal.js +1 -1
  138. package/dist/utils/terminal.js.map +2 -2
  139. package/dist/version.js +2 -2
  140. package/dist/version.js.map +1 -1
  141. package/package.json +6 -6
@@ -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 });