@laststance/claude-plugin-dashboard 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +235 -0
  3. package/dist/app.d.ts +8 -0
  4. package/dist/app.js +481 -0
  5. package/dist/cli.d.ts +16 -0
  6. package/dist/cli.js +316 -0
  7. package/dist/components/ConfirmDialog.d.ts +14 -0
  8. package/dist/components/ConfirmDialog.js +14 -0
  9. package/dist/components/KeyHints.d.ts +19 -0
  10. package/dist/components/KeyHints.js +23 -0
  11. package/dist/components/MarketplaceDetail.d.ts +15 -0
  12. package/dist/components/MarketplaceDetail.js +39 -0
  13. package/dist/components/MarketplaceList.d.ts +16 -0
  14. package/dist/components/MarketplaceList.js +32 -0
  15. package/dist/components/PluginDetail.d.ts +15 -0
  16. package/dist/components/PluginDetail.js +52 -0
  17. package/dist/components/PluginList.d.ts +19 -0
  18. package/dist/components/PluginList.js +54 -0
  19. package/dist/components/SearchInput.d.ts +16 -0
  20. package/dist/components/SearchInput.js +14 -0
  21. package/dist/components/SortDropdown.d.ts +21 -0
  22. package/dist/components/SortDropdown.js +29 -0
  23. package/dist/components/StatusIcon.d.ts +20 -0
  24. package/dist/components/StatusIcon.js +25 -0
  25. package/dist/components/TabBar.d.ts +24 -0
  26. package/dist/components/TabBar.js +38 -0
  27. package/dist/services/fileService.d.ts +41 -0
  28. package/dist/services/fileService.js +104 -0
  29. package/dist/services/pluginActionsService.d.ts +21 -0
  30. package/dist/services/pluginActionsService.js +65 -0
  31. package/dist/services/pluginService.d.ts +66 -0
  32. package/dist/services/pluginService.js +188 -0
  33. package/dist/services/settingsService.d.ts +82 -0
  34. package/dist/services/settingsService.js +117 -0
  35. package/dist/tabs/DiscoverTab.d.ts +26 -0
  36. package/dist/tabs/DiscoverTab.js +25 -0
  37. package/dist/tabs/ErrorsTab.d.ts +16 -0
  38. package/dist/tabs/ErrorsTab.js +39 -0
  39. package/dist/tabs/InstalledTab.d.ts +16 -0
  40. package/dist/tabs/InstalledTab.js +24 -0
  41. package/dist/tabs/MarketplacesTab.d.ts +16 -0
  42. package/dist/tabs/MarketplacesTab.js +21 -0
  43. package/dist/types/index.d.ts +250 -0
  44. package/dist/types/index.js +5 -0
  45. package/dist/utils/paths.d.ts +40 -0
  46. package/dist/utils/paths.js +50 -0
  47. package/package.json +60 -0
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Settings service for managing plugin enable/disable state
3
+ * Reads and writes to ~/.claude/settings.json
4
+ */
5
+ import type { Settings } from '../types/index.js';
6
+ /**
7
+ * Read current settings from settings.json
8
+ * @returns Settings object
9
+ * @throws Error if settings.json doesn't exist or is invalid
10
+ * @example
11
+ * const settings = readSettings();
12
+ * console.log(settings.enabledPlugins);
13
+ */
14
+ export declare function readSettings(): Settings;
15
+ /**
16
+ * Write settings to settings.json
17
+ * @param settings - Settings object to write
18
+ * @example
19
+ * const settings = readSettings();
20
+ * settings.enabledPlugins['my-plugin@marketplace'] = true;
21
+ * writeSettings(settings);
22
+ */
23
+ export declare function writeSettings(settings: Settings): void;
24
+ /**
25
+ * Get enabled plugins map from settings
26
+ * @returns Record of plugin IDs to enabled state
27
+ * @example
28
+ * const enabled = getEnabledPlugins();
29
+ * // { "context7@claude-plugins-official": true, ... }
30
+ */
31
+ export declare function getEnabledPlugins(): Record<string, boolean>;
32
+ /**
33
+ * Check if a plugin is enabled
34
+ * @param pluginId - Plugin identifier (e.g., "context7@claude-plugins-official")
35
+ * @returns true if enabled, false if disabled or not found
36
+ * @example
37
+ * isPluginEnabled("context7@claude-plugins-official"); // true/false
38
+ */
39
+ export declare function isPluginEnabled(pluginId: string): boolean;
40
+ /**
41
+ * Set plugin enabled state
42
+ * @param pluginId - Plugin identifier
43
+ * @param enabled - Whether to enable or disable
44
+ * @example
45
+ * setPluginEnabled("context7@claude-plugins-official", true);
46
+ */
47
+ export declare function setPluginEnabled(pluginId: string, enabled: boolean): void;
48
+ /**
49
+ * Enable a plugin
50
+ * @param pluginId - Plugin identifier
51
+ * @example
52
+ * enablePlugin("context7@claude-plugins-official");
53
+ */
54
+ export declare function enablePlugin(pluginId: string): void;
55
+ /**
56
+ * Disable a plugin
57
+ * @param pluginId - Plugin identifier
58
+ * @example
59
+ * disablePlugin("context7@claude-plugins-official");
60
+ */
61
+ export declare function disablePlugin(pluginId: string): void;
62
+ /**
63
+ * Toggle plugin enabled state
64
+ * @param pluginId - Plugin identifier
65
+ * @returns New enabled state
66
+ * @example
67
+ * const newState = togglePlugin("context7@claude-plugins-official");
68
+ * console.log(`Plugin is now ${newState ? 'enabled' : 'disabled'}`);
69
+ */
70
+ export declare function togglePlugin(pluginId: string): boolean;
71
+ /**
72
+ * Get plugin statistics from settings
73
+ * @returns Object with counts of enabled/disabled plugins
74
+ * @example
75
+ * const stats = getPluginStats();
76
+ * // { total: 21, enabled: 13, disabled: 8 }
77
+ */
78
+ export declare function getPluginStats(): {
79
+ total: number;
80
+ enabled: number;
81
+ disabled: number;
82
+ };
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Settings service for managing plugin enable/disable state
3
+ * Reads and writes to ~/.claude/settings.json
4
+ */
5
+ import { readJsonFile, writeJsonFile } from './fileService.js';
6
+ import { PATHS } from '../utils/paths.js';
7
+ /**
8
+ * Read current settings from settings.json
9
+ * @returns Settings object
10
+ * @throws Error if settings.json doesn't exist or is invalid
11
+ * @example
12
+ * const settings = readSettings();
13
+ * console.log(settings.enabledPlugins);
14
+ */
15
+ export function readSettings() {
16
+ const settings = readJsonFile(PATHS.settings);
17
+ if (!settings) {
18
+ throw new Error(`settings.json not found at ${PATHS.settings}. Is Claude Code installed?`);
19
+ }
20
+ return settings;
21
+ }
22
+ /**
23
+ * Write settings to settings.json
24
+ * @param settings - Settings object to write
25
+ * @example
26
+ * const settings = readSettings();
27
+ * settings.enabledPlugins['my-plugin@marketplace'] = true;
28
+ * writeSettings(settings);
29
+ */
30
+ export function writeSettings(settings) {
31
+ writeJsonFile(PATHS.settings, settings);
32
+ }
33
+ /**
34
+ * Get enabled plugins map from settings
35
+ * @returns Record of plugin IDs to enabled state
36
+ * @example
37
+ * const enabled = getEnabledPlugins();
38
+ * // { "context7@claude-plugins-official": true, ... }
39
+ */
40
+ export function getEnabledPlugins() {
41
+ const settings = readSettings();
42
+ return settings.enabledPlugins ?? {};
43
+ }
44
+ /**
45
+ * Check if a plugin is enabled
46
+ * @param pluginId - Plugin identifier (e.g., "context7@claude-plugins-official")
47
+ * @returns true if enabled, false if disabled or not found
48
+ * @example
49
+ * isPluginEnabled("context7@claude-plugins-official"); // true/false
50
+ */
51
+ export function isPluginEnabled(pluginId) {
52
+ const enabled = getEnabledPlugins();
53
+ return enabled[pluginId] ?? false;
54
+ }
55
+ /**
56
+ * Set plugin enabled state
57
+ * @param pluginId - Plugin identifier
58
+ * @param enabled - Whether to enable or disable
59
+ * @example
60
+ * setPluginEnabled("context7@claude-plugins-official", true);
61
+ */
62
+ export function setPluginEnabled(pluginId, enabled) {
63
+ const settings = readSettings();
64
+ if (!settings.enabledPlugins) {
65
+ settings.enabledPlugins = {};
66
+ }
67
+ settings.enabledPlugins[pluginId] = enabled;
68
+ writeSettings(settings);
69
+ }
70
+ /**
71
+ * Enable a plugin
72
+ * @param pluginId - Plugin identifier
73
+ * @example
74
+ * enablePlugin("context7@claude-plugins-official");
75
+ */
76
+ export function enablePlugin(pluginId) {
77
+ setPluginEnabled(pluginId, true);
78
+ }
79
+ /**
80
+ * Disable a plugin
81
+ * @param pluginId - Plugin identifier
82
+ * @example
83
+ * disablePlugin("context7@claude-plugins-official");
84
+ */
85
+ export function disablePlugin(pluginId) {
86
+ setPluginEnabled(pluginId, false);
87
+ }
88
+ /**
89
+ * Toggle plugin enabled state
90
+ * @param pluginId - Plugin identifier
91
+ * @returns New enabled state
92
+ * @example
93
+ * const newState = togglePlugin("context7@claude-plugins-official");
94
+ * console.log(`Plugin is now ${newState ? 'enabled' : 'disabled'}`);
95
+ */
96
+ export function togglePlugin(pluginId) {
97
+ const currentState = isPluginEnabled(pluginId);
98
+ setPluginEnabled(pluginId, !currentState);
99
+ return !currentState;
100
+ }
101
+ /**
102
+ * Get plugin statistics from settings
103
+ * @returns Object with counts of enabled/disabled plugins
104
+ * @example
105
+ * const stats = getPluginStats();
106
+ * // { total: 21, enabled: 13, disabled: 8 }
107
+ */
108
+ export function getPluginStats() {
109
+ const enabled = getEnabledPlugins();
110
+ const entries = Object.entries(enabled);
111
+ const enabledCount = entries.filter(([, v]) => v === true).length;
112
+ return {
113
+ total: entries.length,
114
+ enabled: enabledCount,
115
+ disabled: entries.length - enabledCount,
116
+ };
117
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * DiscoverTab component
3
+ * Browse all available plugins from all marketplaces
4
+ */
5
+ import type { Plugin, AppState } from '../types/index.js';
6
+ interface DiscoverTabProps {
7
+ plugins: Plugin[];
8
+ selectedIndex: number;
9
+ searchQuery: string;
10
+ sortBy: AppState['sortBy'];
11
+ sortOrder: AppState['sortOrder'];
12
+ isSearchMode?: boolean;
13
+ }
14
+ /**
15
+ * Discover tab - browse all plugins
16
+ * @example
17
+ * <DiscoverTab
18
+ * plugins={filteredPlugins}
19
+ * selectedIndex={state.selectedIndex}
20
+ * searchQuery={state.searchQuery}
21
+ * sortBy={state.sortBy}
22
+ * sortOrder={state.sortOrder}
23
+ * />
24
+ */
25
+ export default function DiscoverTab({ plugins, selectedIndex, searchQuery, sortBy, sortOrder, isSearchMode, }: DiscoverTabProps): import("react/jsx-runtime").JSX.Element;
26
+ export {};
@@ -0,0 +1,25 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * DiscoverTab component
4
+ * Browse all available plugins from all marketplaces
5
+ */
6
+ import { Box, Text } from 'ink';
7
+ import PluginList from '../components/PluginList.js';
8
+ import PluginDetail from '../components/PluginDetail.js';
9
+ import SearchInput from '../components/SearchInput.js';
10
+ import SortDropdown from '../components/SortDropdown.js';
11
+ /**
12
+ * Discover tab - browse all plugins
13
+ * @example
14
+ * <DiscoverTab
15
+ * plugins={filteredPlugins}
16
+ * selectedIndex={state.selectedIndex}
17
+ * searchQuery={state.searchQuery}
18
+ * sortBy={state.sortBy}
19
+ * sortOrder={state.sortOrder}
20
+ * />
21
+ */
22
+ export default function DiscoverTab({ plugins, selectedIndex, searchQuery, sortBy, sortOrder, isSearchMode = false, }) {
23
+ const selectedPlugin = plugins[selectedIndex] ?? null;
24
+ return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { marginBottom: 1, gap: 2, children: [_jsxs(Text, { bold: true, children: ["Discover plugins (", plugins.length > 0 ? `${selectedIndex + 1}/${plugins.length}` : '0', ")"] }), _jsx(Box, { flexGrow: 1 }), _jsx(SortDropdown, { sortBy: sortBy, sortOrder: sortOrder })] }), _jsx(Box, { marginBottom: 1, children: _jsx(SearchInput, { query: searchQuery, isActive: isSearchMode, placeholder: "Type to search..." }) }), _jsxs(Box, { flexGrow: 1, children: [_jsx(Box, { width: "50%", flexDirection: "column", children: _jsx(PluginList, { plugins: plugins, selectedIndex: selectedIndex, visibleCount: 12 }) }), _jsx(Box, { width: "50%", flexDirection: "column", children: _jsx(PluginDetail, { plugin: selectedPlugin }) })] })] }));
25
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * ErrorsTab component
3
+ * Display plugin-related errors and issues
4
+ */
5
+ import type { PluginError } from '../types/index.js';
6
+ interface ErrorsTabProps {
7
+ errors: PluginError[];
8
+ selectedIndex: number;
9
+ }
10
+ /**
11
+ * Errors tab - display plugin errors
12
+ * @example
13
+ * <ErrorsTab errors={errors} selectedIndex={0} />
14
+ */
15
+ export default function ErrorsTab({ errors, selectedIndex }: ErrorsTabProps): import("react/jsx-runtime").JSX.Element;
16
+ export {};
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * ErrorsTab component
4
+ * Display plugin-related errors and issues
5
+ */
6
+ import { Box, Text } from 'ink';
7
+ /**
8
+ * Errors tab - display plugin errors
9
+ * @example
10
+ * <ErrorsTab errors={errors} selectedIndex={0} />
11
+ */
12
+ export default function ErrorsTab({ errors, selectedIndex }) {
13
+ if (errors.length === 0) {
14
+ return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Errors (0)" }) }), _jsxs(Box, { flexDirection: "column", padding: 2, borderStyle: "round", borderColor: "green", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "green", children: "\u2713" }), _jsx(Text, { bold: true, color: "green", children: "No errors to display" })] }), _jsx(Text, { dimColor: true, children: "All plugins are working correctly" })] })] }));
15
+ }
16
+ const selectedError = errors[selectedIndex] ?? null;
17
+ return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsx(Box, { marginBottom: 1, gap: 2, children: _jsxs(Text, { bold: true, color: "red", children: ["Errors (", errors.length > 0 ? `${selectedIndex + 1}/${errors.length}` : '0', ")"] }) }), _jsxs(Box, { flexGrow: 1, children: [_jsx(Box, { width: "50%", flexDirection: "column", children: errors.map((error, index) => {
18
+ const isSelected = index === selectedIndex;
19
+ return (_jsxs(Box, { paddingX: 1, children: [_jsx(Box, { width: 2, children: isSelected ? (_jsx(Text, { color: "cyan", children: '>' })) : (_jsx(Text, { children: " " })) }), _jsx(Box, { width: 2, children: _jsx(Text, { color: "red", children: "\u2717" }) }), _jsxs(Box, { flexGrow: 1, flexDirection: "column", children: [_jsx(Text, { bold: true, color: isSelected ? 'cyan' : 'white', children: error.pluginId }), _jsx(Text, { dimColor: true, wrap: "truncate", children: error.message })] })] }, `${error.pluginId}-${index}`));
20
+ }) }), _jsx(Box, { width: "50%", flexDirection: "column", children: selectedError ? (_jsxs(Box, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "red", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "red", children: selectedError.pluginId }) }), _jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "gray", children: "Type:" }), _jsx(Text, { children: selectedError.type })] }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "gray", children: "Time:" }), _jsx(Text, { children: formatDate(selectedError.timestamp) })] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "Message:" }), _jsx(Text, { wrap: "wrap", children: selectedError.message })] }), selectedError.details && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "Details:" }), _jsx(Text, { dimColor: true, wrap: "wrap", children: selectedError.details })] }))] })) : (_jsx(Box, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "gray", children: _jsx(Text, { dimColor: true, children: "Select an error to view details" }) })) })] })] }));
21
+ }
22
+ /**
23
+ * Format ISO date string to readable format
24
+ */
25
+ function formatDate(isoString) {
26
+ try {
27
+ const date = new Date(isoString);
28
+ return date.toLocaleString('en-US', {
29
+ year: 'numeric',
30
+ month: 'short',
31
+ day: 'numeric',
32
+ hour: '2-digit',
33
+ minute: '2-digit',
34
+ });
35
+ }
36
+ catch {
37
+ return isoString;
38
+ }
39
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * InstalledTab component
3
+ * View and manage installed plugins
4
+ */
5
+ import type { Plugin } from '../types/index.js';
6
+ interface InstalledTabProps {
7
+ plugins: Plugin[];
8
+ selectedIndex: number;
9
+ }
10
+ /**
11
+ * Installed tab - manage installed plugins
12
+ * @example
13
+ * <InstalledTab plugins={installedPlugins} selectedIndex={0} />
14
+ */
15
+ export default function InstalledTab({ plugins, selectedIndex, }: InstalledTabProps): import("react/jsx-runtime").JSX.Element;
16
+ export {};
@@ -0,0 +1,24 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * InstalledTab component
4
+ * View and manage installed plugins
5
+ */
6
+ import { Box, Text } from 'ink';
7
+ import PluginList from '../components/PluginList.js';
8
+ import PluginDetail from '../components/PluginDetail.js';
9
+ /**
10
+ * Installed tab - manage installed plugins
11
+ * @example
12
+ * <InstalledTab plugins={installedPlugins} selectedIndex={0} />
13
+ */
14
+ export default function InstalledTab({ plugins, selectedIndex, }) {
15
+ // Filter to installed plugins only
16
+ const installedPlugins = plugins.filter((p) => p.isInstalled);
17
+ const selectedPlugin = installedPlugins[selectedIndex] ?? null;
18
+ // Count enabled/disabled
19
+ const enabledCount = installedPlugins.filter((p) => p.isEnabled).length;
20
+ const disabledCount = installedPlugins.length - enabledCount;
21
+ return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { marginBottom: 1, gap: 2, children: [_jsxs(Text, { bold: true, children: ["Installed plugins (", installedPlugins.length > 0
22
+ ? `${selectedIndex + 1}/${installedPlugins.length}`
23
+ : '0', ")"] }), _jsx(Box, { flexGrow: 1 }), _jsxs(Box, { gap: 2, children: [_jsxs(Text, { color: "green", children: ["\u25CF ", enabledCount, " enabled"] }), _jsxs(Text, { color: "yellow", children: ["\u25D0 ", disabledCount, " disabled"] })] })] }), _jsxs(Box, { flexGrow: 1, children: [_jsx(Box, { width: "50%", flexDirection: "column", children: installedPlugins.length === 0 ? (_jsxs(Box, { padding: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "No plugins installed" }), _jsxs(Text, { dimColor: true, children: ["Use the Discover tab or", ' ', _jsx(Text, { color: "white", children: "/plugin install" }), " in Claude Code"] })] })) : (_jsx(PluginList, { plugins: installedPlugins, selectedIndex: selectedIndex, visibleCount: 12 })) }), _jsx(Box, { width: "50%", flexDirection: "column", children: _jsx(PluginDetail, { plugin: selectedPlugin }) })] })] }));
24
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * MarketplacesTab component
3
+ * View and manage marketplace sources
4
+ */
5
+ import type { Marketplace } from '../types/index.js';
6
+ interface MarketplacesTabProps {
7
+ marketplaces: Marketplace[];
8
+ selectedIndex: number;
9
+ }
10
+ /**
11
+ * Marketplaces tab - manage plugin sources
12
+ * @example
13
+ * <MarketplacesTab marketplaces={marketplaces} selectedIndex={0} />
14
+ */
15
+ export default function MarketplacesTab({ marketplaces, selectedIndex, }: MarketplacesTabProps): import("react/jsx-runtime").JSX.Element;
16
+ export {};
@@ -0,0 +1,21 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * MarketplacesTab component
4
+ * View and manage marketplace sources
5
+ */
6
+ import { Box, Text } from 'ink';
7
+ import MarketplaceList from '../components/MarketplaceList.js';
8
+ import MarketplaceDetail from '../components/MarketplaceDetail.js';
9
+ /**
10
+ * Marketplaces tab - manage plugin sources
11
+ * @example
12
+ * <MarketplacesTab marketplaces={marketplaces} selectedIndex={0} />
13
+ */
14
+ export default function MarketplacesTab({ marketplaces, selectedIndex, }) {
15
+ const selectedMarketplace = marketplaces[selectedIndex] ?? null;
16
+ // Count total plugins across all marketplaces
17
+ const totalPlugins = marketplaces.reduce((sum, m) => sum + (m.pluginCount || 0), 0);
18
+ return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [_jsxs(Box, { marginBottom: 1, gap: 2, children: [_jsxs(Text, { bold: true, children: ["Marketplaces (", marketplaces.length > 0
19
+ ? `${selectedIndex + 1}/${marketplaces.length}`
20
+ : '0', ")"] }), _jsx(Box, { flexGrow: 1 }), _jsxs(Text, { color: "gray", children: [totalPlugins, " total plugins"] })] }), _jsxs(Box, { flexGrow: 1, children: [_jsx(Box, { width: "50%", flexDirection: "column", children: marketplaces.length === 0 ? (_jsxs(Box, { padding: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "No marketplaces found" }), _jsxs(Text, { dimColor: true, children: ["Add marketplaces with", ' ', _jsx(Text, { color: "white", children: "/plugin add-marketplace" })] })] })) : (_jsx(MarketplaceList, { marketplaces: marketplaces, selectedIndex: selectedIndex })) }), _jsx(Box, { width: "50%", flexDirection: "column", children: _jsx(MarketplaceDetail, { marketplace: selectedMarketplace }) })] })] }));
21
+ }
@@ -0,0 +1,250 @@
1
+ /**
2
+ * TypeScript interfaces for Claude Code Plugin Dashboard
3
+ * These types aggregate data from multiple Claude Code configuration files
4
+ */
5
+ /**
6
+ * Aggregated plugin data from multiple sources
7
+ * Combines data from settings.json, installed_plugins.json, marketplace.json, etc.
8
+ */
9
+ export interface Plugin {
10
+ /** Unique identifier: "name@marketplace" */
11
+ id: string;
12
+ /** Plugin name */
13
+ name: string;
14
+ /** Marketplace identifier */
15
+ marketplace: string;
16
+ /** Short description */
17
+ description: string;
18
+ /** Semantic version or commit hash */
19
+ version: string;
20
+ /** Global installation count from install-counts-cache.json */
21
+ installCount: number;
22
+ /** Whether plugin is installed locally */
23
+ isInstalled: boolean;
24
+ /** Whether plugin is enabled in settings.json */
25
+ isEnabled: boolean;
26
+ /** Installation timestamp (if installed) */
27
+ installedAt?: string;
28
+ /** Last update timestamp (if installed) */
29
+ lastUpdated?: string;
30
+ /** Plugin category */
31
+ category?: string;
32
+ /** Searchable tags */
33
+ tags?: string[];
34
+ /** Author information */
35
+ author?: {
36
+ name: string;
37
+ email?: string;
38
+ };
39
+ /** Homepage URL */
40
+ homepage?: string;
41
+ /** Whether this is a local development plugin */
42
+ isLocal?: boolean;
43
+ /** Git commit SHA (if installed) */
44
+ gitCommitSha?: string;
45
+ }
46
+ /**
47
+ * Raw installed plugin data from installed_plugins.json
48
+ */
49
+ export interface InstalledPluginEntry {
50
+ scope: 'user' | 'project';
51
+ installPath: string;
52
+ version: string;
53
+ installedAt: string;
54
+ lastUpdated: string;
55
+ gitCommitSha: string;
56
+ isLocal: boolean;
57
+ }
58
+ /**
59
+ * Structure of installed_plugins.json file
60
+ */
61
+ export interface InstalledPluginsFile {
62
+ version: number;
63
+ plugins: Record<string, InstalledPluginEntry[]>;
64
+ }
65
+ /**
66
+ * Marketplace configuration from known_marketplaces.json
67
+ */
68
+ export interface Marketplace {
69
+ id: string;
70
+ name: string;
71
+ source: {
72
+ source: 'git' | 'github';
73
+ url?: string;
74
+ repo?: string;
75
+ };
76
+ installLocation: string;
77
+ lastUpdated: string;
78
+ pluginCount?: number;
79
+ }
80
+ /**
81
+ * Structure of known_marketplaces.json file
82
+ */
83
+ export interface KnownMarketplacesFile {
84
+ [marketplaceId: string]: {
85
+ source: {
86
+ source: 'git' | 'github';
87
+ url?: string;
88
+ repo?: string;
89
+ };
90
+ installLocation: string;
91
+ lastUpdated: string;
92
+ };
93
+ }
94
+ /**
95
+ * Plugin entry in marketplace.json
96
+ */
97
+ export interface MarketplacePluginEntry {
98
+ name: string;
99
+ description: string;
100
+ version?: string;
101
+ author?: {
102
+ name: string;
103
+ email?: string;
104
+ };
105
+ category?: string;
106
+ homepage?: string;
107
+ tags?: string[];
108
+ keywords?: string[];
109
+ }
110
+ /**
111
+ * Structure of marketplace.json file
112
+ */
113
+ export interface MarketplaceFile {
114
+ $schema?: string;
115
+ name: string;
116
+ description?: string;
117
+ owner?: {
118
+ name: string;
119
+ email?: string;
120
+ };
121
+ plugins: MarketplacePluginEntry[];
122
+ }
123
+ /**
124
+ * Install count entry from install-counts-cache.json
125
+ */
126
+ export interface InstallCount {
127
+ plugin: string;
128
+ unique_installs: number;
129
+ }
130
+ /**
131
+ * Structure of install-counts-cache.json file
132
+ */
133
+ export interface InstallCountsFile {
134
+ version: number;
135
+ fetchedAt: string;
136
+ counts: InstallCount[];
137
+ }
138
+ /**
139
+ * Plugin error entry
140
+ */
141
+ export interface PluginError {
142
+ pluginId: string;
143
+ type: 'installation' | 'runtime' | 'config';
144
+ message: string;
145
+ timestamp: string;
146
+ details?: string;
147
+ }
148
+ /**
149
+ * Settings.json structure (partial - only plugin-related fields)
150
+ */
151
+ export interface Settings {
152
+ enabledPlugins?: Record<string, boolean>;
153
+ [key: string]: unknown;
154
+ }
155
+ /**
156
+ * Application state for useReducer
157
+ */
158
+ export interface AppState {
159
+ /** Current active tab */
160
+ activeTab: 'discover' | 'installed' | 'marketplaces' | 'errors';
161
+ /** All plugins from all marketplaces */
162
+ plugins: Plugin[];
163
+ /** All marketplaces */
164
+ marketplaces: Marketplace[];
165
+ /** Plugin errors */
166
+ errors: PluginError[];
167
+ /** Currently selected item index in the list */
168
+ selectedIndex: number;
169
+ /** Search/filter query */
170
+ searchQuery: string;
171
+ /** Sort field */
172
+ sortBy: 'installs' | 'name' | 'date';
173
+ /** Sort direction */
174
+ sortOrder: 'asc' | 'desc';
175
+ /** Loading state */
176
+ loading: boolean;
177
+ /** Error message */
178
+ error: string | null;
179
+ /** Status message */
180
+ message: string | null;
181
+ /** Current async operation (install/uninstall) */
182
+ operation: 'idle' | 'installing' | 'uninstalling';
183
+ /** Plugin ID being operated on */
184
+ operationPluginId: string | null;
185
+ /** Whether confirmation dialog is showing */
186
+ confirmUninstall: boolean;
187
+ }
188
+ /**
189
+ * Action types for useReducer
190
+ */
191
+ export type Action = {
192
+ type: 'SET_TAB';
193
+ payload: AppState['activeTab'];
194
+ } | {
195
+ type: 'SET_PLUGINS';
196
+ payload: Plugin[];
197
+ } | {
198
+ type: 'SET_MARKETPLACES';
199
+ payload: Marketplace[];
200
+ } | {
201
+ type: 'SET_ERRORS';
202
+ payload: PluginError[];
203
+ } | {
204
+ type: 'SET_SELECTED_INDEX';
205
+ payload: number;
206
+ } | {
207
+ type: 'MOVE_SELECTION';
208
+ payload: 'up' | 'down';
209
+ } | {
210
+ type: 'SET_SEARCH_QUERY';
211
+ payload: string;
212
+ } | {
213
+ type: 'SET_SORT';
214
+ payload: {
215
+ by: AppState['sortBy'];
216
+ order: AppState['sortOrder'];
217
+ };
218
+ } | {
219
+ type: 'TOGGLE_PLUGIN_ENABLED';
220
+ payload: string;
221
+ } | {
222
+ type: 'UPDATE_PLUGIN';
223
+ payload: Plugin;
224
+ } | {
225
+ type: 'SET_LOADING';
226
+ payload: boolean;
227
+ } | {
228
+ type: 'SET_ERROR';
229
+ payload: string | null;
230
+ } | {
231
+ type: 'SET_MESSAGE';
232
+ payload: string | null;
233
+ } | {
234
+ type: 'NEXT_TAB';
235
+ } | {
236
+ type: 'PREV_TAB';
237
+ } | {
238
+ type: 'START_OPERATION';
239
+ payload: {
240
+ operation: 'installing' | 'uninstalling';
241
+ pluginId: string;
242
+ };
243
+ } | {
244
+ type: 'END_OPERATION';
245
+ } | {
246
+ type: 'SHOW_CONFIRM_UNINSTALL';
247
+ payload: string;
248
+ } | {
249
+ type: 'HIDE_CONFIRM_UNINSTALL';
250
+ };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * TypeScript interfaces for Claude Code Plugin Dashboard
3
+ * These types aggregate data from multiple Claude Code configuration files
4
+ */
5
+ export {};