Skip to content

Commit 2af6840

Browse files
authored
Merge branch 'main' into feat/process-instance-diagram
2 parents 27855a9 + 6758764 commit 2af6840

File tree

17 files changed

+1365
-115
lines changed

17 files changed

+1365
-115
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,7 @@ dist
140140
# Vite logs files
141141
vite.config.js.timestamp-*
142142
vite.config.ts.timestamp-*
143+
144+
# Test plugin directories
145+
c8ctl-test-*
146+
test-plugin-temp

PLUGIN-HELP.md

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,46 @@ This document describes how c8ctl plugins can provide help text that gets integr
66

77
When users load plugins, their commands automatically appear in the help text. Plugins can optionally provide descriptions for their commands to make the help more informative.
88

9+
## Global Plugin System
10+
11+
c8ctl uses a global plugin system where plugins are installed to a user-specific directory. This means:
12+
13+
- **No local package.json required**: Plugins work from any directory
14+
- **Global installation**: Plugins are installed to OS-specific directories:
15+
- **Linux**: `~/.config/c8ctl/plugins/node_modules`
16+
- **macOS**: `~/Library/Application Support/c8ctl/plugins/node_modules`
17+
- **Windows**: `%APPDATA%\c8ctl\plugins\node_modules`
18+
- **Plugin registry**: Tracked in `plugins.json` in the same parent directory
19+
- **Persistent across projects**: Once loaded, plugins are available everywhere
20+
- **Centralized management**: All plugins are managed through the c8ctl plugin registry
21+
- **Cannot override built-in commands**: Plugin commands are executed only if no built-in command matches
22+
23+
> **Note:** You can override the default data directory by setting the `C8CTL_DATA_DIR` environment variable.
24+
25+
## Plugin Registry
26+
27+
The plugin registry (`plugins.json`) maintains a list of all installed plugins with metadata:
28+
29+
```json
30+
{
31+
"plugins": [
32+
{
33+
"name": "my-plugin",
34+
"source": "my-plugin@1.0.0",
35+
"installedAt": "2024-01-15T10:30:00.000Z"
36+
}
37+
]
38+
}
39+
```
40+
41+
Registry locations by OS:
42+
- **Linux**: `~/.config/c8ctl/plugins.json`
43+
- **macOS**: `~/Library/Application Support/c8ctl/plugins.json`
44+
- **Windows**: `%APPDATA%\c8ctl\plugins.json`
45+
946
## How It Works
1047

11-
1. **Automatic Discovery**: When `c8ctl help` is invoked, it scans all loaded plugins
48+
1. **Automatic Discovery**: When `c8ctl help` is invoked, it scans all loaded plugins from the global plugins directory
1249
2. **Plugin Section**: If plugins are loaded, a "Plugin Commands" section appears at the bottom of the help text
1350
3. **Command Listing**: Each plugin command is listed with its optional description
1451

@@ -83,6 +120,7 @@ export const commands = {
83120
## Help Output Example
84121

85122
Without plugins loaded:
123+
86124
```
87125
c8ctl - Camunda 8 CLI v2.0.0
88126
@@ -95,6 +133,7 @@ Commands:
95133
```
96134

97135
With plugins loaded:
136+
98137
```
99138
c8ctl - Camunda 8 CLI v2.0.0
100139
@@ -121,6 +160,7 @@ The plugin loader ([src/plugin-loader.ts](src/plugin-loader.ts)) provides:
121160
- `getPluginCommandNames()`: Returns array of command names
122161
- `getPluginCommandsInfo()`: Returns detailed info including descriptions
123162
- Automatic metadata extraction during plugin loading
163+
- Scans the [global plugins directory](#global-plugin-system) for installed plugins
124164

125165
### Help Command
126166

@@ -150,25 +190,59 @@ interface PluginMetadata {
150190
2. **Keep descriptions concise**: Aim for one line (< 60 characters)
151191
3. **Use imperative verbs**: Start with action words (Analyze, Deploy, Check, etc.)
152192
4. **Match command names**: Ensure metadata command names match exported functions
153-
5. **TypeScript plugins**: The `c8ctl-plugin.js` entry point must be JavaScript. Node.js doesn't support type stripping in `node_modules`. Transpile TypeScript to JavaScript before publishing your plugin.
193+
5. **Use unique command names**: Plugin commands cannot override built-in commands (see [Command Precedence](#command-precedence))
194+
6. **TypeScript plugins**: The `c8ctl-plugin.js` entry point must be JavaScript. Node.js doesn't support type stripping in `node_modules`. Transpile TypeScript to JavaScript before publishing your plugin.
195+
196+
## Command Precedence
197+
198+
**Important:** Plugin commands cannot override built-in c8ctl commands. Built-in commands always take precedence.
199+
200+
When c8ctl processes a command, it follows this order:
201+
202+
1. Check for built-in commands (list, get, create, deploy, etc.)
203+
2. If no built-in command matches, check plugin commands
204+
3. Execute the matched command
205+
206+
### Example
207+
208+
If a plugin exports a command named `list`:
209+
210+
```javascript
211+
export const commands = {
212+
'list': async (args) => {
213+
console.log('This will NEVER execute');
214+
}
215+
};
216+
```
217+
218+
When users run `c8ctl list profiles`, the built-in `list` command will execute, not the plugin version.
219+
220+
### Recommendation
221+
222+
Choose descriptive, unique names for your plugin commands that don't conflict with built-in commands. For example:
223+
-`analyze-process`, `export-data`, `sync-resources`
224+
-`list`, `get`, `create`, `deploy`
154225

155226
## Testing
156227

157228
See [tests/unit/plugin-loader.test.ts](tests/unit/plugin-loader.test.ts) for unit tests that verify:
229+
158230
- `getPluginCommandsInfo()` returns correct structure
159231
- Help text includes plugin commands
160232
- Metadata is properly parsed
161233

162234
## Example Plugin Development Flow
163235

164236
1. Create plugin with commands:
237+
165238
```typescript
166239
export const commands = {
167240
myCommand: async () => { /* ... */ }
168241
};
169242
```
170243

171-
2. Add metadata for help:
244+
1. Add metadata for help:
245+
172246
```typescript
173247
export const metadata = {
174248
commands: {
@@ -179,12 +253,14 @@ export const metadata = {
179253
};
180254
```
181255

182-
3. Load plugin:
256+
1. Load plugin:
257+
183258
```bash
184259
c8ctl load plugin my-plugin
185260
```
186261

187-
4. Verify help includes your command:
262+
1. Verify help includes your command:
263+
188264
```bash
189265
c8ctl help
190266
```

README.md

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,24 @@ Debug output is written to stderr with timestamps and won't interfere with norma
308308

309309
### Plugin Management
310310

311-
c8ctl supports a plugin system that allows extending the CLI with custom commands via npm packages. Plugins are tracked in a registry file (`~/.config/c8ctl/plugins.json` on Linux, similar locations on other platforms) for persistence across npm operations.
311+
c8ctl supports a global plugin system that allows extending the CLI with custom commands via npm packages. Plugins are installed globally to a user-specific directory and tracked in a registry file.
312+
313+
**Plugin Storage Locations:**
314+
315+
The plugin system uses OS-specific directories:
316+
317+
| OS | Plugins Directory | Registry File |
318+
|----|-------------------|---------------|
319+
| **Linux** | `~/.config/c8ctl/plugins/node_modules` | `~/.config/c8ctl/plugins.json` |
320+
| **macOS** | `~/Library/Application Support/c8ctl/plugins/node_modules` | `~/Library/Application Support/c8ctl/plugins.json` |
321+
| **Windows** | `%APPDATA%\c8ctl\plugins\node_modules` | `%APPDATA%\c8ctl\plugins.json` |
322+
323+
> **Note:** You can override the data directory with the `C8CTL_DATA_DIR` environment variable.
312324
313325
```bash
326+
# Create a new plugin from template
327+
c8ctl init plugin my-plugin
328+
314329
# Load a plugin from npm registry
315330
c8ctl load plugin <package-name>
316331

@@ -320,7 +335,14 @@ c8ctl load plugin --from file:///path/to/plugin
320335
c8ctl load plugin --from https://github.com/user/repo
321336
c8ctl load plugin --from git://github.com/user/repo.git
322337

323-
# Unload a plugin (wraps npm uninstall)
338+
# Upgrade a plugin to latest or specific version
339+
c8ctl upgrade plugin <package-name>
340+
c8ctl upgrade plugin <package-name> 1.2.3
341+
342+
# Downgrade a plugin to a specific version
343+
c8ctl downgrade plugin <package-name> 1.0.0
344+
345+
# Unload a plugin
324346
c8ctl unload plugin <package-name>
325347

326348
# List installed plugins (shows sync status)
@@ -335,21 +357,32 @@ c8ctl sync plugins
335357
c8ctl help
336358
```
337359

338-
**Plugin Registry:**
339-
- Plugins are tracked independently of `package.json` in a registry file
340-
- The registry serves as the source of truth (local precedence)
360+
**Global Plugin System:**
361+
- Plugins are installed to a global directory (OS-specific, see table above)
362+
- Plugin registry file (`plugins.json`) tracks all installed plugins
363+
- No local `package.json` is required in your working directory
364+
- Plugins are available globally from any directory
365+
- The registry serves as the source of truth for installed plugins
366+
- Default plugins are bundled with c8ctl and loaded automatically
367+
- **Plugin commands cannot override built-in commands** - built-in commands always take precedence
341368
- `c8ctl list plugins` shows sync status:
342369
- `✓ Installed` - Plugin is in registry and installed
343-
- `⚠ Not installed` - Plugin is in registry but not in node_modules (run `sync`)
344-
- `⚠ Not in registry` - Plugin is in package.json but not tracked in registry
370+
- `⚠ Not installed` - Plugin is in registry but not in global directory (run `sync`)
371+
- `⚠ Not in registry` - Plugin is installed but not tracked in registry
345372
- `c8ctl sync plugins` synchronizes plugins from the registry, rebuilding or reinstalling as needed
346373

374+
**Plugin Development:**
375+
- Use `c8ctl init plugin <name>` to scaffold a new plugin with TypeScript template
376+
- Generated scaffold includes all necessary files and build configuration
377+
- Plugins have access to the c8ctl runtime via `globalThis.c8ctl`
378+
- See the bundled `hello-world` plugin in `default-plugins/` for a complete example
379+
347380
**Plugin Requirements:**
348381
- Plugin packages must be regular Node.js modules
349382
- They must include a `c8ctl-plugin.js` or `c8ctl-plugin.ts` file in the root directory
350383
- The plugin file must export a `commands` object
351384
- Optionally export a `metadata` object to provide help text
352-
- Plugins are installed in `node_modules` like regular npm packages
385+
- Plugins are installed globally and work from any directory
353386
- The runtime object `c8ctl` provides environment information to plugins
354387
- **Important**: `c8ctl-plugin.js` must be JavaScript. Node.js doesn't support type stripping in `node_modules`. If writing in TypeScript, transpile to JS before publishing.
355388

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# c8ctl-plugin-hello-world
2+
3+
A default "hello world" plugin for c8ctl that demonstrates the complete plugin API and lifecycle.
4+
5+
## Purpose
6+
7+
This plugin serves multiple purposes:
8+
9+
1. **Example**: Shows how to create a fully compliant c8ctl plugin
10+
2. **Documentation**: Demonstrates all plugin API features
11+
3. **Testing**: Validates the plugin system is working correctly
12+
4. **Template**: Provides a reference implementation for new plugins
13+
14+
## Features
15+
16+
- ✅ Complete metadata for help integration
17+
- ✅ Command that displays runtime information
18+
- ✅ Argument handling demonstration
19+
- ✅ Compliant with plugin API requirements
20+
- ✅ Loaded by default as an essential plugin
21+
22+
## Usage
23+
24+
This plugin is loaded automatically with c8ctl. Try it:
25+
26+
```bash
27+
c8ctl hello-world
28+
c8ctl hello-world arg1 arg2 arg3
29+
```
30+
31+
## Plugin Structure
32+
33+
```
34+
hello-world/
35+
├── package.json # Package metadata with c8ctl keywords
36+
├── c8ctl-plugin.js # Plugin implementation (ES6 module)
37+
└── README.md # This file
38+
```
39+
40+
## Requirements
41+
42+
- Package must have `c8ctl` or `c8ctl-plugin` in keywords
43+
- Must export `commands` object with command functions
44+
- Optionally export `metadata` object for help text
45+
- Entry point must be `c8ctl-plugin.js` (or .ts)
46+
47+
## Plugin API
48+
49+
### Storage Locations
50+
51+
Plugins are stored in OS-specific directories:
52+
53+
| OS | Plugin Directory | Registry File |
54+
|----|-----------------|---------------|
55+
| **Linux** | `~/.config/c8ctl/plugins/node_modules` | `~/.config/c8ctl/plugins.json` |
56+
| **macOS** | `~/Library/Application Support/c8ctl/plugins/node_modules` | `~/Library/Application Support/c8ctl/plugins.json` |
57+
| **Windows** | `%APPDATA%\c8ctl\plugins\node_modules` | `%APPDATA%\c8ctl\plugins.json` |
58+
59+
> **Note:** Override with `C8CTL_DATA_DIR` environment variable if needed.
60+
61+
### Runtime Access
62+
63+
The plugin can access c8ctl runtime via `globalThis.c8ctl`:
64+
65+
```javascript
66+
globalThis.c8ctl = {
67+
version: string; // c8ctl version
68+
nodeVersion: string; // Node.js version
69+
platform: string; // OS platform
70+
arch: string; // CPU architecture
71+
cwd: string; // Current working directory
72+
outputMode: 'text' | 'json'; // Output format
73+
activeProfile?: string; // Active connection profile
74+
activeTenant?: string; // Active tenant
75+
};
76+
```
77+
78+
### Command Implementation
79+
80+
Commands receive arguments and return promises:
81+
82+
```javascript
83+
export const commands = {
84+
'command-name': async (args) => {
85+
// args is an array of strings
86+
// Use console.log for output
87+
// Return a promise or async function
88+
},
89+
};
90+
```
91+
92+
### Metadata
93+
94+
Provide help text via metadata export:
95+
96+
```javascript
97+
export const metadata = {
98+
name: 'plugin-name',
99+
description: 'Plugin description',
100+
commands: {
101+
'command-name': {
102+
description: 'Command description shown in help',
103+
},
104+
},
105+
};
106+
```
107+
108+
## Important Limitations
109+
110+
### Command Precedence
111+
112+
**Plugin commands cannot override built-in commands.** The c8ctl CLI always checks for built-in commands first. If a plugin exports a command with the same name as a built-in command (e.g., `list`, `get`, `create`), the built-in command will always execute, and the plugin command will be ignored.
113+
114+
For example, if a plugin tries to export a `list` command:
115+
116+
```javascript
117+
export const commands = {
118+
'list': async (args) => {
119+
console.log('This will NEVER execute');
120+
}
121+
};
122+
```
123+
124+
When users run `c8ctl list`, the built-in `list` command will execute instead. Choose unique command names for your plugins to avoid conflicts.
125+
126+
## Development
127+
128+
To create a similar plugin:
129+
130+
1. Use the scaffolding command:
131+
132+
```bash
133+
c8ctl init plugin my-plugin
134+
```
135+
136+
2. Or manually create the structure following this example
137+
138+
3. Test locally:
139+
140+
```bash
141+
c8ctl load plugin --from file:///path/to/plugin
142+
```
143+
144+
4. Publish to npm:
145+
146+
```bash
147+
npm publish
148+
```
149+
150+
5. Users can install:
151+
152+
```bash
153+
c8ctl load plugin your-plugin-name
154+
```
155+
156+
## License
157+
158+
You decide, but our [own Drink-ware](/LICENSE.md) is of course the most fitting :)

0 commit comments

Comments
 (0)