@laststance/claude-plugin-dashboard 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/dist/app.d.ts +7 -1
- package/dist/app.js +231 -84
- package/dist/cli.js +58 -67
- package/dist/components/ComponentBadges.d.ts +3 -3
- package/dist/components/ComponentBadges.js +12 -11
- package/dist/components/ComponentList.d.ts +53 -0
- package/dist/components/ComponentList.js +193 -0
- package/dist/components/KeyHints.js +25 -28
- package/dist/components/MarketplaceActionMenu.d.ts +41 -0
- package/dist/components/MarketplaceActionMenu.js +68 -0
- package/dist/components/MarketplaceDetail.d.ts +10 -3
- package/dist/components/MarketplaceDetail.js +10 -4
- package/dist/components/PluginDetail.d.ts +3 -0
- package/dist/components/PluginDetail.js +28 -4
- package/dist/components/PluginList.js +19 -7
- package/dist/services/componentService.d.ts +12 -1
- package/dist/services/componentService.js +238 -0
- package/dist/services/marketplaceActionsService.d.ts +17 -0
- package/dist/services/marketplaceActionsService.js +18 -0
- package/dist/services/pluginService.js +78 -2
- package/dist/tabs/DiscoverTab.js +1 -1
- package/dist/tabs/EnabledTab.js +2 -2
- package/dist/tabs/InstalledTab.js +2 -2
- package/dist/tabs/MarketplacesTab.d.ts +15 -2
- package/dist/tabs/MarketplacesTab.js +13 -4
- package/dist/types/index.d.ts +110 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -32,7 +32,12 @@ Built with [Ink](https://github.com/vadimdemedes/ink) (React for CLI).
|
|
|
32
32
|
## Installation
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
|
|
35
|
+
npx @laststance/claude-plugin-dashboard@latest
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install -g @laststance/claude-plugin-dashboard@latest
|
|
36
41
|
```
|
|
37
42
|
|
|
38
43
|
## Usage
|
|
@@ -244,6 +249,17 @@ MIT © [Laststance.io](https://github.com/laststance)
|
|
|
244
249
|
|
|
245
250
|
## Changelog
|
|
246
251
|
|
|
252
|
+
### v0.2.2
|
|
253
|
+
|
|
254
|
+
- Claude Code v2.1.3 compatibility (unified Skills/Commands model)
|
|
255
|
+
|
|
256
|
+
### v0.2.1
|
|
257
|
+
|
|
258
|
+
- **Marketplace Management**: Add/remove/refresh marketplaces directly from dashboard
|
|
259
|
+
- **Plugin Component Types**: Display skills, agents, hooks, MCP servers in detail view
|
|
260
|
+
- **Bug Fix**: Search filter now works correctly in Enabled and Installed tabs
|
|
261
|
+
- **CI/CD**: GitHub Actions workflow, CI and Codecov badges
|
|
262
|
+
|
|
247
263
|
### v0.2.0
|
|
248
264
|
|
|
249
265
|
- **Enabled tab**: New default view showing active plugins (installed AND enabled)
|
package/dist/app.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Main App component for Claude Code Plugin Dashboard
|
|
3
3
|
* Interactive TUI to browse and manage Claude Code plugins
|
|
4
4
|
*/
|
|
5
|
-
import type { AppState, Action, Plugin, FocusZone } from './types/index.js';
|
|
5
|
+
import type { AppState, Action, Plugin, Marketplace, FocusZone } from './types/index.js';
|
|
6
6
|
/**
|
|
7
7
|
* Initial application state
|
|
8
8
|
*/
|
|
@@ -31,6 +31,12 @@ export declare function getItemsForTab(state: AppState): unknown[];
|
|
|
31
31
|
* Get filtered and sorted plugins for discover tab
|
|
32
32
|
*/
|
|
33
33
|
export declare function getFilteredPlugins(state: AppState): Plugin[];
|
|
34
|
+
/**
|
|
35
|
+
* Get filtered marketplaces based on search query
|
|
36
|
+
* @param state - Current app state
|
|
37
|
+
* @returns Filtered array of marketplaces
|
|
38
|
+
*/
|
|
39
|
+
export declare function getFilteredMarketplaces(state: AppState): Marketplace[];
|
|
34
40
|
/**
|
|
35
41
|
* Main App component
|
|
36
42
|
*/
|
package/dist/app.js
CHANGED
|
@@ -5,6 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
*/
|
|
6
6
|
import { useEffect, useReducer } from 'react';
|
|
7
7
|
import { Box, Text, useInput, useApp } from 'ink';
|
|
8
|
+
import { match, P } from 'ts-pattern';
|
|
8
9
|
import TabBar, { getNextTab } from './components/TabBar.js';
|
|
9
10
|
import KeyHints from './components/KeyHints.js';
|
|
10
11
|
import DiscoverTab from './tabs/DiscoverTab.js';
|
|
@@ -15,7 +16,7 @@ import ErrorsTab from './tabs/ErrorsTab.js';
|
|
|
15
16
|
import { loadAllPlugins, loadMarketplaces, searchPlugins, searchMarketplaces, sortPlugins, } from './services/pluginService.js';
|
|
16
17
|
import { togglePlugin } from './services/settingsService.js';
|
|
17
18
|
import { installPlugin, uninstallPlugin, } from './services/pluginActionsService.js';
|
|
18
|
-
import { addMarketplace, removeMarketplace, updateMarketplace, } from './services/marketplaceActionsService.js';
|
|
19
|
+
import { addMarketplace, removeMarketplace, updateMarketplace, toggleAutoUpdate, } from './services/marketplaceActionsService.js';
|
|
19
20
|
import AddMarketplaceDialog from './components/AddMarketplaceDialog.js';
|
|
20
21
|
import ConfirmDialog from './components/ConfirmDialog.js';
|
|
21
22
|
import HelpOverlay from './components/HelpOverlay.js';
|
|
@@ -45,6 +46,8 @@ export const initialState = {
|
|
|
45
46
|
confirmRemoveMarketplace: false,
|
|
46
47
|
showAddMarketplaceDialog: false,
|
|
47
48
|
addMarketplaceError: null,
|
|
49
|
+
showMarketplaceActionMenu: false,
|
|
50
|
+
actionMenuSelectedIndex: 0,
|
|
48
51
|
};
|
|
49
52
|
/**
|
|
50
53
|
* Get available focus zones for the current tab
|
|
@@ -65,16 +68,12 @@ export function getAvailableZones(activeTab) {
|
|
|
65
68
|
* @returns Display message for the operation
|
|
66
69
|
*/
|
|
67
70
|
function getMarketplaceOperationMessage(operation, marketplaceId) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return `Updating ${marketplaceId || 'marketplaces'}...`;
|
|
75
|
-
default:
|
|
76
|
-
return '';
|
|
77
|
-
}
|
|
71
|
+
return match(operation)
|
|
72
|
+
.with('adding', () => 'Adding marketplace...')
|
|
73
|
+
.with('removing', () => `Removing ${marketplaceId}...`)
|
|
74
|
+
.with('updating', () => `Updating ${marketplaceId || 'marketplaces'}...`)
|
|
75
|
+
.with('idle', () => '')
|
|
76
|
+
.exhaustive();
|
|
78
77
|
}
|
|
79
78
|
/**
|
|
80
79
|
* State reducer for application state management
|
|
@@ -89,6 +88,8 @@ export function appReducer(state, action) {
|
|
|
89
88
|
selectedIndex: 0,
|
|
90
89
|
searchQuery: '',
|
|
91
90
|
message: null,
|
|
91
|
+
showMarketplaceActionMenu: false,
|
|
92
|
+
actionMenuSelectedIndex: 0,
|
|
92
93
|
};
|
|
93
94
|
case 'NEXT_TAB':
|
|
94
95
|
return {
|
|
@@ -98,6 +99,8 @@ export function appReducer(state, action) {
|
|
|
98
99
|
selectedIndex: 0,
|
|
99
100
|
searchQuery: '',
|
|
100
101
|
message: null,
|
|
102
|
+
showMarketplaceActionMenu: false,
|
|
103
|
+
actionMenuSelectedIndex: 0,
|
|
101
104
|
};
|
|
102
105
|
case 'PREV_TAB':
|
|
103
106
|
return {
|
|
@@ -107,6 +110,8 @@ export function appReducer(state, action) {
|
|
|
107
110
|
selectedIndex: 0,
|
|
108
111
|
searchQuery: '',
|
|
109
112
|
message: null,
|
|
113
|
+
showMarketplaceActionMenu: false,
|
|
114
|
+
actionMenuSelectedIndex: 0,
|
|
110
115
|
};
|
|
111
116
|
case 'SET_FOCUS_ZONE':
|
|
112
117
|
return {
|
|
@@ -279,6 +284,33 @@ export function appReducer(state, action) {
|
|
|
279
284
|
marketplaceOperation: 'idle',
|
|
280
285
|
operationMarketplaceId: null,
|
|
281
286
|
};
|
|
287
|
+
case 'SHOW_MARKETPLACE_ACTION_MENU':
|
|
288
|
+
return {
|
|
289
|
+
...state,
|
|
290
|
+
showMarketplaceActionMenu: true,
|
|
291
|
+
actionMenuSelectedIndex: 0,
|
|
292
|
+
};
|
|
293
|
+
case 'HIDE_MARKETPLACE_ACTION_MENU':
|
|
294
|
+
return {
|
|
295
|
+
...state,
|
|
296
|
+
showMarketplaceActionMenu: false,
|
|
297
|
+
actionMenuSelectedIndex: 0,
|
|
298
|
+
};
|
|
299
|
+
case 'SET_ACTION_MENU_INDEX':
|
|
300
|
+
return {
|
|
301
|
+
...state,
|
|
302
|
+
actionMenuSelectedIndex: action.payload,
|
|
303
|
+
};
|
|
304
|
+
case 'MOVE_ACTION_MENU_SELECTION': {
|
|
305
|
+
const maxIndex = 3; // 4 actions: browse, update, autoUpdate, remove
|
|
306
|
+
const newIndex = action.payload === 'up'
|
|
307
|
+
? Math.max(0, state.actionMenuSelectedIndex - 1)
|
|
308
|
+
: Math.min(maxIndex, state.actionMenuSelectedIndex + 1);
|
|
309
|
+
return {
|
|
310
|
+
...state,
|
|
311
|
+
actionMenuSelectedIndex: newIndex,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
282
314
|
default:
|
|
283
315
|
return state;
|
|
284
316
|
}
|
|
@@ -292,28 +324,23 @@ export function appReducer(state, action) {
|
|
|
292
324
|
* // => Only installed plugins matching 'su'
|
|
293
325
|
*/
|
|
294
326
|
export function getItemsForTab(state) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
case 'errors':
|
|
313
|
-
return state.errors;
|
|
314
|
-
default:
|
|
315
|
-
return [];
|
|
316
|
-
}
|
|
327
|
+
return match(state.activeTab)
|
|
328
|
+
.with('enabled', () => {
|
|
329
|
+
const enabledPlugins = state.plugins.filter((p) => p.isInstalled && p.isEnabled);
|
|
330
|
+
return state.searchQuery
|
|
331
|
+
? searchPlugins(state.searchQuery, enabledPlugins)
|
|
332
|
+
: enabledPlugins;
|
|
333
|
+
})
|
|
334
|
+
.with('installed', () => {
|
|
335
|
+
const installedPlugins = state.plugins.filter((p) => p.isInstalled);
|
|
336
|
+
return state.searchQuery
|
|
337
|
+
? searchPlugins(state.searchQuery, installedPlugins)
|
|
338
|
+
: installedPlugins;
|
|
339
|
+
})
|
|
340
|
+
.with('discover', () => getFilteredPlugins(state))
|
|
341
|
+
.with('marketplaces', () => state.marketplaces)
|
|
342
|
+
.with('errors', () => state.errors)
|
|
343
|
+
.otherwise(() => []);
|
|
317
344
|
}
|
|
318
345
|
/**
|
|
319
346
|
* Get filtered and sorted plugins for discover tab
|
|
@@ -328,6 +355,19 @@ export function getFilteredPlugins(state) {
|
|
|
328
355
|
plugins = sortPlugins(plugins, state.sortBy, state.sortOrder);
|
|
329
356
|
return plugins;
|
|
330
357
|
}
|
|
358
|
+
/**
|
|
359
|
+
* Get filtered marketplaces based on search query
|
|
360
|
+
* @param state - Current app state
|
|
361
|
+
* @returns Filtered array of marketplaces
|
|
362
|
+
*/
|
|
363
|
+
export function getFilteredMarketplaces(state) {
|
|
364
|
+
let marketplaces = state.marketplaces;
|
|
365
|
+
// Apply search filter
|
|
366
|
+
if (state.searchQuery) {
|
|
367
|
+
marketplaces = searchMarketplaces(state.searchQuery, marketplaces);
|
|
368
|
+
}
|
|
369
|
+
return marketplaces;
|
|
370
|
+
}
|
|
331
371
|
/**
|
|
332
372
|
* Main App component
|
|
333
373
|
*/
|
|
@@ -488,6 +528,46 @@ export default function App() {
|
|
|
488
528
|
});
|
|
489
529
|
}
|
|
490
530
|
}
|
|
531
|
+
/**
|
|
532
|
+
* Handle toggling auto-update for a marketplace
|
|
533
|
+
* @param marketplaceId - ID of the marketplace
|
|
534
|
+
* @param currentValue - Current auto-update state
|
|
535
|
+
*/
|
|
536
|
+
async function handleToggleAutoUpdate(marketplaceId, currentValue) {
|
|
537
|
+
dispatch({
|
|
538
|
+
type: 'START_MARKETPLACE_OPERATION',
|
|
539
|
+
payload: { operation: 'updating', marketplaceId },
|
|
540
|
+
});
|
|
541
|
+
const result = await toggleAutoUpdate(marketplaceId, currentValue);
|
|
542
|
+
dispatch({ type: 'END_MARKETPLACE_OPERATION' });
|
|
543
|
+
if (result.success) {
|
|
544
|
+
// Reload marketplaces to get fresh state
|
|
545
|
+
const marketplaces = loadMarketplaces();
|
|
546
|
+
dispatch({ type: 'SET_MARKETPLACES', payload: marketplaces });
|
|
547
|
+
dispatch({ type: 'SET_MESSAGE', payload: `✅ ${result.message}` });
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
dispatch({
|
|
551
|
+
type: 'SET_MESSAGE',
|
|
552
|
+
payload: `❌ ${result.message}${result.error ? `: ${result.error}` : ''}`,
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Handle browsing plugins for a specific marketplace
|
|
558
|
+
* Switches to Discover tab with marketplace filter applied
|
|
559
|
+
* @param marketplaceId - ID of the marketplace to browse
|
|
560
|
+
*/
|
|
561
|
+
function handleBrowseMarketplacePlugins(marketplaceId) {
|
|
562
|
+
// Switch to Discover tab
|
|
563
|
+
dispatch({ type: 'SET_TAB', payload: 'discover' });
|
|
564
|
+
// Set search query to filter by marketplace
|
|
565
|
+
dispatch({ type: 'SET_SEARCH_QUERY', payload: marketplaceId });
|
|
566
|
+
dispatch({
|
|
567
|
+
type: 'SET_MESSAGE',
|
|
568
|
+
payload: `Browsing plugins from ${marketplaceId}`,
|
|
569
|
+
});
|
|
570
|
+
}
|
|
491
571
|
// Keyboard input handling
|
|
492
572
|
useInput((input, key) => {
|
|
493
573
|
// Block all input during operations (plugin or marketplace)
|
|
@@ -513,7 +593,7 @@ export default function App() {
|
|
|
513
593
|
}
|
|
514
594
|
// Handle plugin uninstall confirmation dialog
|
|
515
595
|
if (state.confirmUninstall && state.operationPluginId) {
|
|
516
|
-
if (input === 'y' || input === 'Y') {
|
|
596
|
+
if (input === 'y' || input === 'Y' || key.return) {
|
|
517
597
|
dispatch({ type: 'HIDE_CONFIRM_UNINSTALL' });
|
|
518
598
|
handleUninstall(state.operationPluginId);
|
|
519
599
|
return;
|
|
@@ -527,7 +607,7 @@ export default function App() {
|
|
|
527
607
|
}
|
|
528
608
|
// Handle marketplace remove confirmation dialog
|
|
529
609
|
if (state.confirmRemoveMarketplace && state.operationMarketplaceId) {
|
|
530
|
-
if (input === 'y' || input === 'Y') {
|
|
610
|
+
if (input === 'y' || input === 'Y' || key.return) {
|
|
531
611
|
dispatch({ type: 'HIDE_CONFIRM_REMOVE_MARKETPLACE' });
|
|
532
612
|
handleRemoveMarketplace(state.operationMarketplaceId);
|
|
533
613
|
return;
|
|
@@ -570,6 +650,54 @@ export default function App() {
|
|
|
570
650
|
}
|
|
571
651
|
return;
|
|
572
652
|
}
|
|
653
|
+
// Handle marketplace action menu
|
|
654
|
+
if (state.showMarketplaceActionMenu) {
|
|
655
|
+
const selectedMarketplace = getFilteredMarketplaces(state)[state.selectedIndex];
|
|
656
|
+
if (!selectedMarketplace) {
|
|
657
|
+
dispatch({ type: 'HIDE_MARKETPLACE_ACTION_MENU' });
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
// Up/Down arrow navigation
|
|
661
|
+
if (key.upArrow || (key.ctrl && input === 'p')) {
|
|
662
|
+
dispatch({ type: 'MOVE_ACTION_MENU_SELECTION', payload: 'up' });
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (key.downArrow || (key.ctrl && input === 'n')) {
|
|
666
|
+
dispatch({ type: 'MOVE_ACTION_MENU_SELECTION', payload: 'down' });
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
// Execute action on Enter
|
|
670
|
+
if (key.return) {
|
|
671
|
+
dispatch({ type: 'HIDE_MARKETPLACE_ACTION_MENU' });
|
|
672
|
+
const actionIndex = state.actionMenuSelectedIndex;
|
|
673
|
+
if (actionIndex === 0) {
|
|
674
|
+
// Browse plugins
|
|
675
|
+
handleBrowseMarketplacePlugins(selectedMarketplace.id);
|
|
676
|
+
}
|
|
677
|
+
else if (actionIndex === 1) {
|
|
678
|
+
// Update marketplace
|
|
679
|
+
handleUpdateMarketplace(selectedMarketplace.id);
|
|
680
|
+
}
|
|
681
|
+
else if (actionIndex === 2) {
|
|
682
|
+
// Toggle auto-update
|
|
683
|
+
handleToggleAutoUpdate(selectedMarketplace.id, selectedMarketplace.autoUpdate ?? false);
|
|
684
|
+
}
|
|
685
|
+
else if (actionIndex === 3) {
|
|
686
|
+
// Remove marketplace (show confirmation)
|
|
687
|
+
dispatch({
|
|
688
|
+
type: 'SHOW_CONFIRM_REMOVE_MARKETPLACE',
|
|
689
|
+
payload: selectedMarketplace.id,
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
// Close menu on Escape
|
|
695
|
+
if (key.escape) {
|
|
696
|
+
dispatch({ type: 'HIDE_MARKETPLACE_ACTION_MENU' });
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
573
701
|
// Search mode input (when focusZone is 'search')
|
|
574
702
|
if (state.focusZone === 'search') {
|
|
575
703
|
// Up arrow: move focus to tabbar
|
|
@@ -766,6 +894,14 @@ export default function App() {
|
|
|
766
894
|
}
|
|
767
895
|
// Marketplace-specific key bindings
|
|
768
896
|
if (state.activeTab === 'marketplaces' && state.focusZone === 'list') {
|
|
897
|
+
// Open action menu (Enter or m key)
|
|
898
|
+
if (key.return || input === 'm') {
|
|
899
|
+
const selectedMarketplace = filteredMarketplaces[state.selectedIndex];
|
|
900
|
+
if (selectedMarketplace) {
|
|
901
|
+
dispatch({ type: 'SHOW_MARKETPLACE_ACTION_MENU' });
|
|
902
|
+
}
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
769
905
|
// Add marketplace (a key)
|
|
770
906
|
if (input === 'a') {
|
|
771
907
|
dispatch({ type: 'SHOW_ADD_MARKETPLACE_DIALOG' });
|
|
@@ -853,56 +989,67 @@ export default function App() {
|
|
|
853
989
|
const filteredMarketplaces = state.searchQuery
|
|
854
990
|
? searchMarketplaces(state.searchQuery, state.marketplaces)
|
|
855
991
|
: state.marketplaces;
|
|
856
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, gap: 2, children: [_jsx(Text, { bold: true, color: "magenta", children: "\u26A1 Claude Code Plugin Dashboard" }), _jsx(Box, { flexGrow: 1 }), _jsxs(Text, { dimColor: true, children: ["v", packageJson.version] })] }), _jsx(TabBar, { activeTab: state.activeTab, isFocused: state.focusZone === 'tabbar' }),
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
992
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, gap: 2, children: [_jsx(Text, { bold: true, color: "magenta", children: "\u26A1 Claude Code Plugin Dashboard" }), _jsx(Box, { flexGrow: 1 }), _jsxs(Text, { dimColor: true, children: ["v", packageJson.version] })] }), _jsx(TabBar, { activeTab: state.activeTab, isFocused: state.focusZone === 'tabbar' }), _jsx(Box, { flexGrow: 1, flexDirection: "column", children: match(state.activeTab)
|
|
993
|
+
.with('enabled', () => (_jsx(EnabledTab, { plugins: enabledPlugins, selectedIndex: state.selectedIndex, searchQuery: state.searchQuery, focusZone: state.focusZone }, "enabled")))
|
|
994
|
+
.with('installed', () => (_jsx(InstalledTab, { plugins: installedPlugins, selectedIndex: state.selectedIndex, searchQuery: state.searchQuery, focusZone: state.focusZone }, "installed")))
|
|
995
|
+
.with('discover', () => (_jsx(DiscoverTab, { plugins: filteredPlugins, selectedIndex: state.selectedIndex, searchQuery: state.searchQuery, sortBy: state.sortBy, sortOrder: state.sortOrder, focusZone: state.focusZone }, "discover")))
|
|
996
|
+
.with('marketplaces', () => (_jsx(MarketplacesTab, { marketplaces: filteredMarketplaces, selectedIndex: state.selectedIndex, searchQuery: state.searchQuery, focusZone: state.focusZone, showActionMenu: state.showMarketplaceActionMenu, actionMenuSelectedIndex: state.actionMenuSelectedIndex }, "marketplaces")))
|
|
997
|
+
.with('errors', () => (_jsx(ErrorsTab, { errors: state.errors, selectedIndex: state.selectedIndex }, "errors")))
|
|
998
|
+
.exhaustive() }), state.confirmUninstall && state.operationPluginId && (_jsx(ConfirmDialog, { message: `Uninstall ${state.operationPluginId}?` })), state.confirmRemoveMarketplace && state.operationMarketplaceId && (_jsx(ConfirmDialog, { message: `Remove marketplace ${state.operationMarketplaceId}?` })), state.showAddMarketplaceDialog && (_jsx(AddMarketplaceDialog, { value: state.searchQuery, error: state.addMarketplaceError ?? undefined })), _jsx(HelpOverlay, { isVisible: state.showHelp }), state.message && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "yellow", children: state.message }) })), _jsx(KeyHints, { focusZone: state.focusZone, extraHints: match([state.focusZone, state.activeTab])
|
|
999
|
+
.with(['search', P._], () => undefined)
|
|
1000
|
+
.with(['tabbar', P._], () => undefined)
|
|
1001
|
+
.with(['list', 'enabled'], () => {
|
|
1002
|
+
const hints = [
|
|
1003
|
+
{ key: '/', action: 'search' },
|
|
1004
|
+
{ key: 'i', action: 'install' },
|
|
1005
|
+
{ key: 'u', action: 'uninstall' },
|
|
1006
|
+
];
|
|
1007
|
+
const selectedPlugin = enabledPlugins[state.selectedIndex];
|
|
1008
|
+
if (selectedPlugin) {
|
|
1009
|
+
hints.push({
|
|
1010
|
+
key: 'Enter',
|
|
1011
|
+
action: selectedPlugin.isInstalled ? 'toggle' : 'install',
|
|
1012
|
+
});
|
|
864
1013
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
?
|
|
878
|
-
|
|
879
|
-
? installedPlugins
|
|
880
|
-
: filteredPlugins;
|
|
881
|
-
const selectedPlugin = items[state.selectedIndex];
|
|
882
|
-
// Add contextual Enter hint
|
|
883
|
-
if (selectedPlugin) {
|
|
884
|
-
if (!selectedPlugin.isInstalled) {
|
|
885
|
-
hints.push({ key: 'Enter', action: 'install' });
|
|
886
|
-
}
|
|
887
|
-
else {
|
|
888
|
-
hints.push({ key: 'Enter', action: 'toggle' });
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
// Add sort hint for discover tab
|
|
892
|
-
if (state.activeTab === 'discover') {
|
|
893
|
-
hints.push({ key: 's', action: 'sort' });
|
|
894
|
-
}
|
|
895
|
-
return hints;
|
|
1014
|
+
return hints;
|
|
1015
|
+
})
|
|
1016
|
+
.with(['list', 'installed'], () => {
|
|
1017
|
+
const hints = [
|
|
1018
|
+
{ key: '/', action: 'search' },
|
|
1019
|
+
{ key: 'i', action: 'install' },
|
|
1020
|
+
{ key: 'u', action: 'uninstall' },
|
|
1021
|
+
];
|
|
1022
|
+
const selectedPlugin = installedPlugins[state.selectedIndex];
|
|
1023
|
+
if (selectedPlugin) {
|
|
1024
|
+
hints.push({
|
|
1025
|
+
key: 'Enter',
|
|
1026
|
+
action: selectedPlugin.isInstalled ? 'toggle' : 'install',
|
|
1027
|
+
});
|
|
896
1028
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1029
|
+
return hints;
|
|
1030
|
+
})
|
|
1031
|
+
.with(['list', 'discover'], () => {
|
|
1032
|
+
const hints = [
|
|
1033
|
+
{ key: '/', action: 'search' },
|
|
1034
|
+
{ key: 'i', action: 'install' },
|
|
1035
|
+
{ key: 'u', action: 'uninstall' },
|
|
1036
|
+
];
|
|
1037
|
+
const selectedPlugin = filteredPlugins[state.selectedIndex];
|
|
1038
|
+
if (selectedPlugin) {
|
|
1039
|
+
hints.push({
|
|
1040
|
+
key: 'Enter',
|
|
1041
|
+
action: selectedPlugin.isInstalled ? 'toggle' : 'install',
|
|
1042
|
+
});
|
|
905
1043
|
}
|
|
906
|
-
|
|
907
|
-
|
|
1044
|
+
hints.push({ key: 's', action: 'sort' });
|
|
1045
|
+
return hints;
|
|
1046
|
+
})
|
|
1047
|
+
.with(['list', 'marketplaces'], () => [
|
|
1048
|
+
{ key: '/', action: 'search' },
|
|
1049
|
+
{ key: 'a', action: 'add' },
|
|
1050
|
+
{ key: 'd', action: 'remove' },
|
|
1051
|
+
{ key: 'u', action: 'update' },
|
|
1052
|
+
])
|
|
1053
|
+
.with(['list', 'errors'], () => undefined)
|
|
1054
|
+
.otherwise(() => undefined) })] }));
|
|
908
1055
|
}
|
package/dist/cli.js
CHANGED
|
@@ -14,12 +14,14 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
14
14
|
* claude-plugin-dashboard toggle <plugin-id> # Toggle plugin
|
|
15
15
|
* claude-plugin-dashboard help # Show help
|
|
16
16
|
*/
|
|
17
|
-
import {
|
|
17
|
+
import { withFullScreen } from 'fullscreen-ink';
|
|
18
|
+
import { match, P } from 'ts-pattern';
|
|
18
19
|
import App from './app.js';
|
|
19
20
|
import { loadAllPlugins, loadInstalledPlugins, loadMarketplaces, getPluginStatistics, getPluginById, } from './services/pluginService.js';
|
|
20
21
|
import { enablePlugin, disablePlugin, togglePlugin, } from './services/settingsService.js';
|
|
21
22
|
import { fileExists } from './services/fileService.js';
|
|
22
23
|
import { PATHS } from './utils/paths.js';
|
|
24
|
+
import packageJson from '../package.json' with { type: 'json' };
|
|
23
25
|
const args = process.argv.slice(2);
|
|
24
26
|
const command = args[0];
|
|
25
27
|
const subCommand = args[1];
|
|
@@ -243,66 +245,57 @@ function checkClaudeCodeInstalled() {
|
|
|
243
245
|
if (command) {
|
|
244
246
|
// Non-interactive mode
|
|
245
247
|
checkClaudeCodeInstalled();
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (!subCommand) {
|
|
266
|
-
console.error('Usage: claude-plugin-dashboard info <plugin-id>');
|
|
267
|
-
process.exit(1);
|
|
268
|
-
}
|
|
269
|
-
showPluginInfo(subCommand);
|
|
270
|
-
break;
|
|
271
|
-
case 'enable':
|
|
272
|
-
if (!subCommand) {
|
|
273
|
-
console.error('Usage: claude-plugin-dashboard enable <plugin-id>');
|
|
274
|
-
process.exit(1);
|
|
275
|
-
}
|
|
276
|
-
handleEnable(subCommand);
|
|
277
|
-
break;
|
|
278
|
-
case 'disable':
|
|
279
|
-
if (!subCommand) {
|
|
280
|
-
console.error('Usage: claude-plugin-dashboard disable <plugin-id>');
|
|
281
|
-
process.exit(1);
|
|
282
|
-
}
|
|
283
|
-
handleDisable(subCommand);
|
|
284
|
-
break;
|
|
285
|
-
case 'toggle':
|
|
286
|
-
if (!subCommand) {
|
|
287
|
-
console.error('Usage: claude-plugin-dashboard toggle <plugin-id>');
|
|
288
|
-
process.exit(1);
|
|
289
|
-
}
|
|
290
|
-
handleToggle(subCommand);
|
|
291
|
-
break;
|
|
292
|
-
case 'help':
|
|
293
|
-
case '-h':
|
|
294
|
-
case '--help':
|
|
295
|
-
showHelp();
|
|
296
|
-
break;
|
|
297
|
-
case '-v':
|
|
298
|
-
case '--version':
|
|
299
|
-
console.log('claude-plugin-dashboard v0.1.0');
|
|
300
|
-
break;
|
|
301
|
-
default:
|
|
302
|
-
console.error(`Unknown command: ${command}`);
|
|
303
|
-
console.log('Run "claude-plugin-dashboard help" for usage information.');
|
|
248
|
+
match(command)
|
|
249
|
+
.with('status', () => showStatus())
|
|
250
|
+
.with('list', () => {
|
|
251
|
+
if (subCommand === '--installed' || args.includes('--installed')) {
|
|
252
|
+
listPlugins({ installed: true });
|
|
253
|
+
}
|
|
254
|
+
else if (subCommand === '--marketplace' ||
|
|
255
|
+
args.includes('--marketplace')) {
|
|
256
|
+
const marketplaceIndex = args.indexOf('--marketplace');
|
|
257
|
+
const marketplace = args[marketplaceIndex + 1];
|
|
258
|
+
listPlugins({ marketplace });
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
listPlugins({});
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
.with('info', () => {
|
|
265
|
+
if (!subCommand) {
|
|
266
|
+
console.error('Usage: claude-plugin-dashboard info <plugin-id>');
|
|
304
267
|
process.exit(1);
|
|
305
|
-
|
|
268
|
+
}
|
|
269
|
+
showPluginInfo(subCommand);
|
|
270
|
+
})
|
|
271
|
+
.with('enable', () => {
|
|
272
|
+
if (!subCommand) {
|
|
273
|
+
console.error('Usage: claude-plugin-dashboard enable <plugin-id>');
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
handleEnable(subCommand);
|
|
277
|
+
})
|
|
278
|
+
.with('disable', () => {
|
|
279
|
+
if (!subCommand) {
|
|
280
|
+
console.error('Usage: claude-plugin-dashboard disable <plugin-id>');
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
handleDisable(subCommand);
|
|
284
|
+
})
|
|
285
|
+
.with('toggle', () => {
|
|
286
|
+
if (!subCommand) {
|
|
287
|
+
console.error('Usage: claude-plugin-dashboard toggle <plugin-id>');
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
handleToggle(subCommand);
|
|
291
|
+
})
|
|
292
|
+
.with(P.union('help', '-h', '--help'), () => showHelp())
|
|
293
|
+
.with(P.union('-v', '--version'), () => console.log(`claude-plugin-dashboard v${packageJson.version}`))
|
|
294
|
+
.otherwise((cmd) => {
|
|
295
|
+
console.error(`Unknown command: ${cmd}`);
|
|
296
|
+
console.log('Run "claude-plugin-dashboard help" for usage information.');
|
|
297
|
+
process.exit(1);
|
|
298
|
+
});
|
|
306
299
|
}
|
|
307
300
|
else {
|
|
308
301
|
// Interactive mode
|
|
@@ -312,11 +305,9 @@ else {
|
|
|
312
305
|
console.log('Use "claude-plugin-dashboard help" for non-interactive commands.');
|
|
313
306
|
process.exit(1);
|
|
314
307
|
}
|
|
315
|
-
|
|
316
|
-
//
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
process.stdout.write('\x1b[2J\x1b[H');
|
|
321
|
-
});
|
|
308
|
+
// Use fullscreen-ink for alternate screen buffer management
|
|
309
|
+
// This prevents rendering artifacts when switching tabs
|
|
310
|
+
const ink = withFullScreen(_jsx(App, {}));
|
|
311
|
+
ink.start();
|
|
312
|
+
ink.waitUntilExit();
|
|
322
313
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ComponentBadges component
|
|
3
|
-
* Displays plugin component type badges with
|
|
4
|
-
*
|
|
3
|
+
* Displays plugin component type badges with readable text labels and counts
|
|
4
|
+
* Labels: Skills, Slash, Agents, Hooks, MCP, LSP
|
|
5
5
|
*/
|
|
6
6
|
import type { PluginComponents } from '../types/index.js';
|
|
7
7
|
/**
|
|
@@ -18,7 +18,7 @@ export interface ComponentBadgesProps {
|
|
|
18
18
|
* @returns Badges component or null if no components
|
|
19
19
|
* @example
|
|
20
20
|
* <ComponentBadges components={{ skills: 5, commands: 2 }} />
|
|
21
|
-
* // Renders:
|
|
21
|
+
* // Renders: Skills:5 Slash:2
|
|
22
22
|
*/
|
|
23
23
|
export default function ComponentBadges({ components, }: ComponentBadgesProps): React.ReactNode;
|
|
24
24
|
/**
|