opencode-plugin-selector 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # opencode-plugin-selector
2
+
3
+ > Interactive plugin selector for OpenCode - easily manage your plugins with a beautiful TUI
4
+
5
+ [![npm version](https://badge.fury.io/js/opencode-plugin-selector.svg)](https://badge.fury.io/js/opencode-plugin-selector)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ - 🎨 **Beautiful TUI** - Interactive multiselect interface powered by @clack/prompts
11
+ - ⚡ **Fast** - Instant loading with known plugins list
12
+ - ✓ **Status indicators** - See which plugins are installed/active at a glance
13
+ - 💾 **Auto-sync** - Automatically updates your `opencode.json` configuration
14
+ - 🔄 **Preserves versions** - Maintains version specifiers for existing plugins
15
+ - 🌍 **Cross-platform** - Works on Windows, macOS, and Linux
16
+
17
+ ## Installation
18
+
19
+ ### From npm (Recommended)
20
+
21
+ Add to your OpenCode configuration:
22
+
23
+ ```json
24
+ {
25
+ "plugin": ["opencode-plugin-selector"]
26
+ }
27
+ ```
28
+
29
+ OpenCode will automatically install it on startup.
30
+
31
+ ### Manual Installation
32
+
33
+ ```bash
34
+ npm install -g opencode-plugin-selector
35
+ ```
36
+
37
+ Or add to your project:
38
+
39
+ ```bash
40
+ npm install opencode-plugin-selector
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ### In OpenCode
46
+
47
+ Just type the command:
48
+
49
+ ```
50
+ /plugins
51
+ ```
52
+
53
+ This opens an interactive menu where you can:
54
+
55
+ - **↑↓** - Navigate through plugins
56
+ - **Space** - Toggle plugin selection
57
+ - **Enter** - Confirm selection
58
+ - **Esc** - Cancel
59
+
60
+ ### Example
61
+
62
+ ```
63
+ ╭─────────────────────────────────────╮
64
+ │ OpenCode Plugin Selector │
65
+ ├─────────────────────────────────────┤
66
+ │ ◉ opencode-firecrawl ✓ │
67
+ │ Web scraping via Firecrawl │
68
+ │ │
69
+ │ ◯ oh-my-opencode ✓ │
70
+ │ Background agents, LSP tools │
71
+ │ │
72
+ │ ◯ opencode-wakatime ✗ │
73
+ │ Track usage with Wakatime │
74
+ ╰─────────────────────────────────────╯
75
+
76
+ [↑↓ Navigate] [Space Toggle] [Enter Confirm]
77
+ ```
78
+
79
+ ## How It Works
80
+
81
+ 1. **Plugin Discovery** - Loads known plugins from the OpenCode ecosystem
82
+ 2. **Status Check** - Scans `~/.cache/opencode/node_modules/` for installed plugins
83
+ 3. **Config Sync** - Reads `~/.config/opencode/opencode.json` for active plugins
84
+ 4. **Interactive Selection** - Presents a multiselect TUI
85
+ 5. **Config Update** - Saves your selection back to `opencode.json`
86
+ 6. **Restart Required** - Restart OpenCode to apply changes
87
+
88
+ ## Supported Plugins
89
+
90
+ The selector includes all known plugins from the OpenCode ecosystem:
91
+
92
+ - **Auth plugins**: `opencode-antigravity-auth`, `opencode-openai-codex-auth`, `opencode-gemini-auth`
93
+ - **Development**: `opencode-daytona`, `opencode-devcontainers`, `opencode-worktree`
94
+ - **Monitoring**: `opencode-helicone-session`, `opencode-wakatime`, `opencode-sentry-monitor`
95
+ - **Utilities**: `opencode-firecrawl`, `opencode-pty`, `opencode-scheduler`
96
+ - **Agents**: `oh-my-opencode`, `opencode-background-agents`, `opencode-workspace`
97
+ - And many more...
98
+
99
+ ## Configuration
100
+
101
+ The plugin respects your existing `opencode.json` configuration:
102
+
103
+ ```json
104
+ {
105
+ "plugin": [
106
+ "opencode-plugin-selector",
107
+ "opencode-firecrawl@1.2.0",
108
+ "oh-my-opencode@latest"
109
+ ]
110
+ }
111
+ ```
112
+
113
+ Version specifiers are preserved when you update your selection.
114
+
115
+ ## Development
116
+
117
+ ### Prerequisites
118
+
119
+ - Node.js 18+
120
+ - Bun (for running OpenCode)
121
+
122
+ ### Setup
123
+
124
+ ```bash
125
+ git clone https://github.com/yourusername/opencode-plugin-selector.git
126
+ cd opencode-plugin-selector
127
+ npm install
128
+ ```
129
+
130
+ ### Build
131
+
132
+ ```bash
133
+ npm run build
134
+ ```
135
+
136
+ ### Test Locally
137
+
138
+ Add to your `opencode.json`:
139
+
140
+ ```json
141
+ {
142
+ "plugin": ["./path/to/opencode-plugin-selector"]
143
+ }
144
+ ```
145
+
146
+ ## API Reference
147
+
148
+ ### Plugin Hook
149
+
150
+ The plugin implements the `tui.command.execute` hook:
151
+
152
+ ```typescript
153
+ {
154
+ 'tui.command.execute': async (input, output) => {
155
+ if (input.command === 'plugins') {
156
+ // Show interactive selector
157
+ }
158
+ }
159
+ }
160
+ ```
161
+
162
+ ### Functions
163
+
164
+ - `getAllPlugins()` - Get all available plugins with status
165
+ - `getActivePlugins()` - Get currently enabled plugins
166
+ - `updatePlugins(plugins: string[])` - Update configuration
167
+ - `getInstalledPlugins()` - Get plugins installed in cache
168
+
169
+ ## Troubleshooting
170
+
171
+ ### Plugin not loading?
172
+
173
+ 1. Check your `opencode.json` syntax
174
+ 2. Ensure the plugin is in the `plugin` array
175
+ 3. Restart OpenCode
176
+
177
+ ### Config not updating?
178
+
179
+ 1. Check file permissions on `~/.config/opencode/opencode.json`
180
+ 2. Ensure the file is valid JSON
181
+ 3. Check OpenCode logs for errors
182
+
183
+ ## Contributing
184
+
185
+ Contributions are welcome! Please feel free to submit a Pull Request.
186
+
187
+ 1. Fork the repository
188
+ 2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
189
+ 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
190
+ 4. Push to the branch (`git push origin feature/AmazingFeature`)
191
+ 5. Open a Pull Request
192
+
193
+ ## License
194
+
195
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
196
+
197
+ ## Acknowledgments
198
+
199
+ - [OpenCode](https://opencode.ai) - The AI coding assistant
200
+ - [@clack/prompts](https://github.com/natemoo-re/clack) - Beautiful CLI prompts
201
+ - [picocolors](https://github.com/alexeyraspopov/picocolors) - Terminal colors
202
+
203
+ ## Related
204
+
205
+ - [OpenCode Documentation](https://opencode.ai/docs)
206
+ - [OpenCode Ecosystem](https://opencode.ai/docs/ecosystem)
207
+ - [Plugin Development Guide](https://opencode.ai/docs/plugins)
208
+
209
+ ---
210
+
211
+ Made with ❤️ for the OpenCode community
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "opencode-plugin-selector",
3
+ "version": "1.0.0",
4
+ "description": "Interactive plugin selector for OpenCode - easily manage your plugins with a beautiful TUI",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
+ "type": "module",
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "dev": "tsc --watch"
11
+ },
12
+ "keywords": [
13
+ "opencode",
14
+ "opencode-plugin",
15
+ "plugin",
16
+ "selector",
17
+ "tui",
18
+ "cli"
19
+ ],
20
+ "author": "",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/illusionaireal/opencode-plugin-selector.git"
25
+ },
26
+ "files": [
27
+ "src"
28
+ ],
29
+ "dependencies": {
30
+ "@clack/prompts": "^0.7.0",
31
+ "picocolors": "^1.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "@opencode-ai/plugin": "latest",
35
+ "@types/bun": "^1.3.11",
36
+ "@types/node": "^25.5.1",
37
+ "typescript": "^6.0.2"
38
+ },
39
+ "peerDependencies": {
40
+ "@opencode-ai/plugin": "latest"
41
+ }
42
+ }
package/src/config.ts ADDED
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Configuration file management for OpenCode
3
+ */
4
+
5
+ import { readFile, writeFile } from 'fs/promises'
6
+ import { join } from 'path'
7
+ import { homedir } from 'os'
8
+ import type { OpenCodeConfig } from './types.js'
9
+
10
+ const CONFIG_PATH = join(homedir(), '.config', 'opencode', 'opencode.json')
11
+
12
+ /**
13
+ * Read OpenCode configuration file
14
+ */
15
+ export async function readConfig(): Promise<OpenCodeConfig> {
16
+ try {
17
+ const content = await readFile(CONFIG_PATH, 'utf-8')
18
+ return JSON.parse(content)
19
+ } catch (error) {
20
+ // If file doesn't exist, return default config
21
+ if ((error as any).code === 'ENOENT') {
22
+ return { plugin: [] }
23
+ }
24
+ throw error
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Write OpenCode configuration file
30
+ */
31
+ export async function writeConfig(config: OpenCodeConfig): Promise<void> {
32
+ const content = JSON.stringify(config, null, 2)
33
+ await writeFile(CONFIG_PATH, content, 'utf-8')
34
+ }
35
+
36
+ /**
37
+ * Get current active plugins from config
38
+ */
39
+ export async function getActivePlugins(): Promise<string[]> {
40
+ const config = await readConfig()
41
+ return config.plugin || []
42
+ }
43
+
44
+ /**
45
+ * Update plugin list in config
46
+ * Preserves version specifiers for existing plugins
47
+ */
48
+ export async function updatePlugins(selectedPlugins: string[]): Promise<void> {
49
+ const config = await readConfig()
50
+
51
+ // Build version map from existing plugins
52
+ const versionMap = new Map<string, string>()
53
+ if (config.plugin) {
54
+ for (const p of config.plugin) {
55
+ const name = stripVersion(p)
56
+ versionMap.set(name, p)
57
+ }
58
+ }
59
+
60
+ // Apply new selection with preserved versions
61
+ config.plugin = selectedPlugins.map(name => {
62
+ return versionMap.get(name) || name
63
+ })
64
+
65
+ await writeConfig(config)
66
+ }
67
+
68
+ /**
69
+ * Strip version specifier from plugin name
70
+ * @example 'plugin@1.0.0' -> 'plugin'
71
+ */
72
+ export function stripVersion(name: string): string {
73
+ return name.replace(/@.*$/, '')
74
+ }
75
+
76
+ /**
77
+ * Get config file path
78
+ */
79
+ export function getConfigPath(): string {
80
+ return CONFIG_PATH
81
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Plugin discovery - finds available OpenCode plugins from multiple sources
3
+ */
4
+
5
+ import { readdir } from 'fs/promises'
6
+ import { join } from 'path'
7
+ import { homedir } from 'os'
8
+ import type { PluginInfo } from './types.js'
9
+ import { getActivePlugins, stripVersion } from './config.js'
10
+
11
+ const CACHE_DIR = join(homedir(), '.cache', 'opencode', 'node_modules')
12
+
13
+ /**
14
+ * Known plugins from the OpenCode ecosystem
15
+ * Sourced from https://opencode.ai/docs/ecosystem/
16
+ */
17
+ const KNOWN_PLUGINS: Array<{ name: string; description: string }> = [
18
+ { name: 'opencode-daytona', description: 'Run sessions in isolated Daytona sandboxes with git sync' },
19
+ { name: 'opencode-helicone-session', description: 'Inject Helicone session headers for request grouping' },
20
+ { name: 'opencode-type-inject', description: 'Auto-inject TypeScript/Svelte types into file reads' },
21
+ { name: 'opencode-openai-codex-auth', description: 'Use ChatGPT Plus/Pro subscription instead of API credits' },
22
+ { name: 'opencode-gemini-auth', description: 'Use existing Gemini plan instead of API billing' },
23
+ { name: 'opencode-antigravity-auth', description: 'Use Antigravity free models instead of API billing' },
24
+ { name: 'opencode-devcontainers', description: 'Multi-branch devcontainer isolation with shallow clones' },
25
+ { name: 'opencode-google-antigravity-auth', description: 'Google Antigravity OAuth with Google Search support' },
26
+ { name: 'opencode-dynamic-context-pruning', description: 'Optimize token usage by pruning obsolete tool outputs' },
27
+ { name: 'opencode-vibeguard', description: 'Redact secrets/PII into VibeGuard placeholders before LLM calls' },
28
+ { name: 'opencode-websearch-cited', description: 'Native websearch with Google grounded style citations' },
29
+ { name: 'opencode-pty', description: 'Run background processes in a PTY with interactive input' },
30
+ { name: 'opencode-shell-strategy', description: 'Instructions for non-interactive shell commands' },
31
+ { name: 'opencode-wakatime', description: 'Track OpenCode usage with Wakatime' },
32
+ { name: 'opencode-md-table-formatter', description: 'Clean up markdown tables produced by LLMs' },
33
+ { name: 'opencode-morph-plugin', description: 'Fast Apply editing, WarpGrep search, context compaction' },
34
+ { name: 'oh-my-opencode', description: 'Background agents, LSP/AST/MCP tools, Claude Code compatible' },
35
+ { name: 'opencode-notificator', description: 'Desktop notifications and sound alerts for sessions' },
36
+ { name: 'opencode-notifier', description: 'Notifications for permission, completion, error events' },
37
+ { name: 'opencode-zellij-namer', description: 'AI-powered Zellij session naming from context' },
38
+ { name: 'opencode-skillful', description: 'Lazy load prompts on demand with skill discovery' },
39
+ { name: 'opencode-supermemory', description: 'Persistent memory across sessions via Supermemory' },
40
+ { name: 'plannotator-opencode', description: 'Interactive plan review with visual annotation' },
41
+ { name: 'openspoon-subtask2', description: 'Extend /commands into orchestration with flow control' },
42
+ { name: 'opencode-scheduler', description: 'Schedule recurring jobs with cron syntax (launchd/systemd)' },
43
+ { name: 'micode', description: 'Structured Brainstorm-Plan-Implement workflow with continuity' },
44
+ { name: 'octto', description: 'Interactive browser UI for AI brainstorming' },
45
+ { name: 'opencode-background-agents', description: 'Claude Code-style background agents with async delegation' },
46
+ { name: 'opencode-notify', description: 'Native OS notifications for task completion' },
47
+ { name: 'opencode-workspace', description: 'Multi-agent orchestration harness - 16 components' },
48
+ { name: 'opencode-worktree', description: 'Zero-friction git worktrees for OpenCode' },
49
+ { name: 'opencode-sentry-monitor', description: 'Trace and debug AI agents with Sentry' },
50
+ { name: 'opencode-firecrawl', description: 'Web scraping, crawling, and search via Firecrawl' }
51
+ ]
52
+
53
+ /**
54
+ * Get installed plugins from cache directory
55
+ */
56
+ export async function getInstalledPlugins(): Promise<Set<string>> {
57
+ try {
58
+ const dirs = await readdir(CACHE_DIR)
59
+ return new Set(dirs)
60
+ } catch {
61
+ return new Set()
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Search npm for additional OpenCode plugins
67
+ * Returns plugin names
68
+ */
69
+ export async function searchNpmPlugins(): Promise<string[]> {
70
+ try {
71
+ // Use Bun's shell API or spawn process to run npm search
72
+ const proc = Bun.spawn(['npm', 'search', 'opencode-plugin', '--json'])
73
+ const text = await new Response(proc.stdout).text()
74
+ await proc.exited
75
+
76
+ if (text) {
77
+ const results = JSON.parse(text)
78
+ return results.map((pkg: any) => pkg.name)
79
+ }
80
+ } catch (error) {
81
+ // Silently fail - npm search is optional enhancement
82
+ }
83
+ return []
84
+ }
85
+
86
+ /**
87
+ * Get all available plugins with their status
88
+ */
89
+ export async function getAllPlugins(): Promise<PluginInfo[]> {
90
+ const [installed, activeList] = await Promise.all([
91
+ getInstalledPlugins(),
92
+ getActivePlugins()
93
+ ])
94
+
95
+ const activeSet = new Set(activeList.map(stripVersion))
96
+
97
+ // Start with known plugins
98
+ const plugins: PluginInfo[] = KNOWN_PLUGINS.map(p => ({
99
+ name: p.name,
100
+ description: p.description,
101
+ installed: installed.has(p.name),
102
+ active: activeSet.has(p.name)
103
+ }))
104
+
105
+ // Add any additional plugins from npm search (in background, non-blocking)
106
+ // For now, we'll skip this to keep the UI fast
107
+ // Could be added as an async enhancement later
108
+
109
+ return plugins
110
+ }
package/src/index.ts ADDED
@@ -0,0 +1,117 @@
1
+ /**
2
+ * opencode-plugin-selector
3
+ * Interactive plugin selector for OpenCode
4
+ */
5
+
6
+ import * as p from '@clack/prompts'
7
+ import pc from 'picocolors'
8
+ import type { PluginContext, PluginHooks, PluginInfo } from './types.js'
9
+ import { getAllPlugins } from './discovery.js'
10
+ import { updatePlugins, stripVersion, getActivePlugins } from './config.js'
11
+
12
+ /**
13
+ * Main plugin export
14
+ */
15
+ export const PluginSelector = async (ctx: PluginContext): Promise<PluginHooks> => {
16
+ return {
17
+ /**
18
+ * Handle TUI command execution
19
+ * Triggers on /plugins command
20
+ */
21
+ 'tui.command.execute': async (input, output) => {
22
+ if (input.command === 'plugins') {
23
+ await handlePluginsCommand(ctx)
24
+ }
25
+ }
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Handle the /plugins command
31
+ */
32
+ async function handlePluginsCommand(ctx: PluginContext): Promise<void> {
33
+ console.clear()
34
+
35
+ p.intro(pc.bgCyan(pc.black(' OpenCode Plugin Selector ')))
36
+
37
+ const spinner = p.spinner()
38
+ spinner.start('Loading plugins...')
39
+
40
+ try {
41
+ // Get all plugins with their status
42
+ const plugins = await getAllPlugins()
43
+
44
+ spinner.stop(`Found ${plugins.length} plugins`)
45
+
46
+ // Build options for multiselect
47
+ const options = plugins.map(p => ({
48
+ value: p.name,
49
+ label: `${p.name} ${p.installed ? pc.green('✓') : pc.red('✗')}`,
50
+ hint: p.description
51
+ }))
52
+
53
+ // Get currently active plugins
54
+ const activePlugins = await getActivePlugins()
55
+ const initialValues = activePlugins.map(stripVersion)
56
+
57
+ // Show interactive selector
58
+ const selected = await p.multiselect({
59
+ message: 'Select plugins to enable (Space to toggle, Enter to confirm):',
60
+ options,
61
+ initialValues
62
+ })
63
+
64
+ if (p.isCancel(selected)) {
65
+ p.outro(pc.yellow('Cancelled'))
66
+ return
67
+ }
68
+
69
+ // Update configuration
70
+ spinner.start('Updating configuration...')
71
+
72
+ await updatePlugins(selected as string[])
73
+
74
+ spinner.stop('Configuration updated')
75
+
76
+ // Show summary
77
+ const enabledCount = (selected as string[]).length
78
+ p.outro(pc.green(`✓ ${enabledCount} plugins enabled`))
79
+
80
+ // Show what changed
81
+ const enabled = selected as string[]
82
+ const previouslyActive = new Set(initialValues)
83
+
84
+ const newlyEnabled = enabled.filter(n => !previouslyActive.has(n))
85
+ const newlyDisabled = initialValues.filter(n => !enabled.includes(n))
86
+
87
+ if (newlyEnabled.length > 0) {
88
+ console.log(pc.green('\nEnabled:'))
89
+ newlyEnabled.forEach(n => console.log(pc.green(` + ${n}`)))
90
+ }
91
+
92
+ if (newlyDisabled.length > 0) {
93
+ console.log(pc.red('\nDisabled:'))
94
+ newlyDisabled.forEach(n => console.log(pc.red(` - ${n}`)))
95
+ }
96
+
97
+ // Suggest restart
98
+ console.log(pc.dim('\nRestart OpenCode to apply changes.'))
99
+
100
+ } catch (error) {
101
+ spinner.stop('Failed')
102
+ p.outro(pc.red(`Error: ${(error as Error).message}`))
103
+
104
+ // Log error via OpenCode client
105
+ await ctx.client.app.log({
106
+ body: {
107
+ service: 'plugin-selector',
108
+ level: 'error',
109
+ message: 'Failed to load plugins',
110
+ extra: { error: (error as Error).message }
111
+ }
112
+ })
113
+ }
114
+ }
115
+
116
+ // Default export for OpenCode plugin system
117
+ export default PluginSelector
package/src/types.ts ADDED
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Type definitions for opencode-plugin-selector
3
+ */
4
+
5
+ /**
6
+ * Plugin metadata
7
+ */
8
+ export interface PluginInfo {
9
+ name: string
10
+ description: string
11
+ installed: boolean
12
+ active: boolean
13
+ }
14
+
15
+ /**
16
+ * OpenCode configuration structure
17
+ */
18
+ export interface OpenCodeConfig {
19
+ $schema?: string
20
+ plugin?: string[]
21
+ [key: string]: any
22
+ }
23
+
24
+ /**
25
+ * Plugin context provided by OpenCode
26
+ */
27
+ export interface PluginContext {
28
+ project: any
29
+ client: any
30
+ $: any
31
+ directory: string
32
+ worktree: string
33
+ }
34
+
35
+ /**
36
+ * Plugin hooks
37
+ */
38
+ export interface PluginHooks {
39
+ [event: string]: (input: any, output: any) => Promise<void> | void
40
+ }
41
+
42
+ /**
43
+ * Plugin function signature
44
+ */
45
+ export type PluginFunction = (ctx: PluginContext) => Promise<PluginHooks>
46
+
47
+ /**
48
+ * TUI command input
49
+ */
50
+ export interface TUICommandInput {
51
+ command: string
52
+ args: string[]
53
+ }
54
+
55
+ /**
56
+ * TUI command output
57
+ */
58
+ export interface TUICommandOutput {
59
+ result?: any
60
+ error?: Error
61
+ }