@austinthesing/magic-shell 0.1.3 → 0.2.1
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 +1 -1
- package/README.md +19 -29
- package/dist/cli.js +147 -13
- package/dist/index.js +390 -36
- package/dist/tui.js +23423 -0
- package/package.json +4 -3
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 Magic Shell Contributors
|
|
3
|
+
Copyright (c) 2026 austin-thesing and Magic Shell Contributors
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -8,8 +8,10 @@ Magic Shell is an open-source CLI tool that translates plain English (or any nat
|
|
|
8
8
|
|
|
9
9
|
- **Natural Language Translation**: Describe what you want to do in plain English
|
|
10
10
|
- **Multiple AI Providers**: OpenCode Zen (with free models!) and OpenRouter
|
|
11
|
+
- **Project Context Aware**: Opt-in detection of package.json scripts, Makefile targets, etc.
|
|
11
12
|
- **Interactive TUI Mode**: Full-featured terminal interface with themes
|
|
12
13
|
- **Command Safety Analysis**: Multi-level safety checks before executing commands
|
|
14
|
+
- **Auto Updates**: Automatic update checking with one-command upgrades
|
|
13
15
|
- **Cross-Platform**: macOS, Linux, and Windows support
|
|
14
16
|
- **Shell-Aware**: Automatically detects and adapts to your shell (bash, zsh, fish, PowerShell, etc.)
|
|
15
17
|
- **Secure Credential Storage**: Uses system keychain (macOS Keychain, Linux secret-tool, Windows Credential Manager)
|
|
@@ -18,38 +20,20 @@ Magic Shell is an open-source CLI tool that translates plain English (or any nat
|
|
|
18
20
|
|
|
19
21
|
## Installation
|
|
20
22
|
|
|
21
|
-
###
|
|
23
|
+
### Via Package Manager (Recommended)
|
|
22
24
|
|
|
23
|
-
**macOS / Linux:**
|
|
24
25
|
```bash
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
**Windows (PowerShell):**
|
|
29
|
-
```powershell
|
|
30
|
-
irm https://raw.githubusercontent.com/austin-thesing/magic-shell/main/install.ps1 | iex
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### Via Package Manager
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
# Install globally with bun (recommended)
|
|
26
|
+
# bun (recommended)
|
|
37
27
|
bun add -g @austinthesing/magic-shell
|
|
38
28
|
|
|
39
|
-
#
|
|
29
|
+
# npm
|
|
40
30
|
npm install -g @austinthesing/magic-shell
|
|
41
31
|
|
|
42
|
-
#
|
|
43
|
-
yarn global add @austinthesing/magic-shell
|
|
44
|
-
|
|
45
|
-
# Or with pnpm
|
|
32
|
+
# pnpm
|
|
46
33
|
pnpm add -g @austinthesing/magic-shell
|
|
47
|
-
```
|
|
48
34
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
brew install austin-thesing/tap/magic-shell
|
|
35
|
+
# yarn
|
|
36
|
+
yarn global add @austinthesing/magic-shell
|
|
53
37
|
```
|
|
54
38
|
|
|
55
39
|
### From Source
|
|
@@ -83,7 +67,7 @@ export OPENCODE_ZEN_API_KEY="your-key-here"
|
|
|
83
67
|
|
|
84
68
|
## Usage
|
|
85
69
|
|
|
86
|
-
Magic Shell can be invoked using `magic-shell`, `msh`, or `ms`.
|
|
70
|
+
Magic Shell can be invoked using `magic-shell`, `msh`, or `ms`. For TUI mode directly, use `mshell`.
|
|
87
71
|
|
|
88
72
|
### Basic Commands
|
|
89
73
|
|
|
@@ -100,8 +84,8 @@ msh -n "delete all node_modules folders"
|
|
|
100
84
|
|
|
101
85
|
# Launch interactive TUI mode
|
|
102
86
|
msh -i
|
|
103
|
-
# or
|
|
104
|
-
|
|
87
|
+
# or directly with
|
|
88
|
+
mshell
|
|
105
89
|
```
|
|
106
90
|
|
|
107
91
|
### Command Reference
|
|
@@ -118,6 +102,10 @@ msh
|
|
|
118
102
|
| `msh --provider <name>` | Set provider (opencode-zen or openrouter) |
|
|
119
103
|
| `msh --themes` | List available themes |
|
|
120
104
|
| `msh --theme <name>` | Set color theme |
|
|
105
|
+
| `msh --repo-context` | Enable project context detection |
|
|
106
|
+
| `msh -r <query>` | Use project context for single query |
|
|
107
|
+
| `msh --version` | Show version |
|
|
108
|
+
| `msh --check-update` | Check for updates |
|
|
121
109
|
| `msh --help` | Show help |
|
|
122
110
|
|
|
123
111
|
### Examples
|
|
@@ -147,7 +135,7 @@ msh "compress this folder to a zip file" | pbcopy
|
|
|
147
135
|
|
|
148
136
|
## Interactive TUI Mode
|
|
149
137
|
|
|
150
|
-
Launch with `
|
|
138
|
+
Launch with `mshell` or `msh -i` for a full interactive experience.
|
|
151
139
|
|
|
152
140
|
### Keyboard Shortcuts
|
|
153
141
|
|
|
@@ -160,6 +148,7 @@ All shortcuts use the `Ctrl+X` chord (press Ctrl+X, then the key):
|
|
|
160
148
|
| `Ctrl+X S` | Switch provider |
|
|
161
149
|
| `Ctrl+X D` | Toggle dry-run mode |
|
|
162
150
|
| `Ctrl+X T` | Change theme |
|
|
151
|
+
| `Ctrl+X R` | Toggle project context |
|
|
163
152
|
| `Ctrl+X H` | Show history |
|
|
164
153
|
| `Ctrl+X C` | Show config |
|
|
165
154
|
| `Ctrl+X L` | Clear output |
|
|
@@ -257,9 +246,10 @@ Configuration is stored in `~/.magic-shell/config.json`.
|
|
|
257
246
|
```json
|
|
258
247
|
{
|
|
259
248
|
"provider": "opencode-zen",
|
|
260
|
-
"defaultModel": "
|
|
249
|
+
"defaultModel": "gemini-3-flash",
|
|
261
250
|
"safetyLevel": "moderate",
|
|
262
251
|
"dryRunByDefault": false,
|
|
252
|
+
"repoContext": false,
|
|
263
253
|
"theme": "opencode",
|
|
264
254
|
"blockedCommands": [...],
|
|
265
255
|
"confirmedDangerousPatterns": [...]
|
package/dist/cli.js
CHANGED
|
@@ -21116,7 +21116,7 @@ var init_config = __esm(() => {
|
|
|
21116
21116
|
provider: "opencode-zen",
|
|
21117
21117
|
openrouterApiKey: "",
|
|
21118
21118
|
opencodeZenApiKey: "",
|
|
21119
|
-
defaultModel: "
|
|
21119
|
+
defaultModel: "gemini-3-flash",
|
|
21120
21120
|
safetyLevel: "moderate",
|
|
21121
21121
|
dryRunByDefault: false,
|
|
21122
21122
|
blockedCommands: [
|
|
@@ -21127,7 +21127,8 @@ var init_config = __esm(() => {
|
|
|
21127
21127
|
"chmod -R 777 /",
|
|
21128
21128
|
"chown -R"
|
|
21129
21129
|
],
|
|
21130
|
-
confirmedDangerousPatterns: []
|
|
21130
|
+
confirmedDangerousPatterns: [],
|
|
21131
|
+
repoContext: false
|
|
21131
21132
|
};
|
|
21132
21133
|
});
|
|
21133
21134
|
|
|
@@ -21472,6 +21473,112 @@ function getPlatformPaths(platform) {
|
|
|
21472
21473
|
}
|
|
21473
21474
|
var init_shell = () => {};
|
|
21474
21475
|
|
|
21476
|
+
// src/lib/repo-context.ts
|
|
21477
|
+
import { existsSync as existsSync6, readFileSync as readFileSync2 } from "fs";
|
|
21478
|
+
import { join as join3 } from "path";
|
|
21479
|
+
function detectRepoContext(cwd) {
|
|
21480
|
+
const context = {
|
|
21481
|
+
type: "unknown"
|
|
21482
|
+
};
|
|
21483
|
+
let detected = false;
|
|
21484
|
+
if (existsSync6(join3(cwd, ".git"))) {
|
|
21485
|
+
context.hasGit = true;
|
|
21486
|
+
detected = true;
|
|
21487
|
+
}
|
|
21488
|
+
if (existsSync6(join3(cwd, "Dockerfile")) || existsSync6(join3(cwd, "docker-compose.yml")) || existsSync6(join3(cwd, "docker-compose.yaml"))) {
|
|
21489
|
+
context.hasDocker = true;
|
|
21490
|
+
detected = true;
|
|
21491
|
+
}
|
|
21492
|
+
const packageJsonPath = join3(cwd, "package.json");
|
|
21493
|
+
if (existsSync6(packageJsonPath)) {
|
|
21494
|
+
detected = true;
|
|
21495
|
+
context.type = "node";
|
|
21496
|
+
try {
|
|
21497
|
+
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
21498
|
+
if (existsSync6(join3(cwd, "bun.lockb")) || existsSync6(join3(cwd, "bun.lock"))) {
|
|
21499
|
+
context.packageManager = "bun";
|
|
21500
|
+
} else if (existsSync6(join3(cwd, "pnpm-lock.yaml"))) {
|
|
21501
|
+
context.packageManager = "pnpm";
|
|
21502
|
+
} else if (existsSync6(join3(cwd, "yarn.lock"))) {
|
|
21503
|
+
context.packageManager = "yarn";
|
|
21504
|
+
} else if (existsSync6(join3(cwd, "package-lock.json"))) {
|
|
21505
|
+
context.packageManager = "npm";
|
|
21506
|
+
} else if (packageJson.packageManager) {
|
|
21507
|
+
const pm = packageJson.packageManager.split("@")[0];
|
|
21508
|
+
context.packageManager = pm;
|
|
21509
|
+
}
|
|
21510
|
+
if (packageJson.scripts && typeof packageJson.scripts === "object") {
|
|
21511
|
+
context.scripts = Object.keys(packageJson.scripts);
|
|
21512
|
+
}
|
|
21513
|
+
} catch {}
|
|
21514
|
+
}
|
|
21515
|
+
const makefilePath = join3(cwd, "Makefile");
|
|
21516
|
+
if (existsSync6(makefilePath)) {
|
|
21517
|
+
detected = true;
|
|
21518
|
+
if (context.type === "unknown")
|
|
21519
|
+
context.type = "make";
|
|
21520
|
+
try {
|
|
21521
|
+
const makefile = readFileSync2(makefilePath, "utf-8");
|
|
21522
|
+
const targetRegex = /^([a-zA-Z_][a-zA-Z0-9_-]*)\s*:/gm;
|
|
21523
|
+
const targets = [];
|
|
21524
|
+
let match;
|
|
21525
|
+
while ((match = targetRegex.exec(makefile)) !== null) {
|
|
21526
|
+
if (!match[1].startsWith(".") && !match[1].startsWith("_")) {
|
|
21527
|
+
targets.push(match[1]);
|
|
21528
|
+
}
|
|
21529
|
+
}
|
|
21530
|
+
if (targets.length > 0) {
|
|
21531
|
+
context.makeTargets = [...new Set(targets)];
|
|
21532
|
+
}
|
|
21533
|
+
} catch {}
|
|
21534
|
+
}
|
|
21535
|
+
if (existsSync6(join3(cwd, "Cargo.toml"))) {
|
|
21536
|
+
detected = true;
|
|
21537
|
+
context.type = "rust";
|
|
21538
|
+
context.cargoCommands = ["build", "run", "test", "check", "clippy", "fmt", "doc"];
|
|
21539
|
+
}
|
|
21540
|
+
if (existsSync6(join3(cwd, "pyproject.toml")) || existsSync6(join3(cwd, "setup.py")) || existsSync6(join3(cwd, "requirements.txt"))) {
|
|
21541
|
+
detected = true;
|
|
21542
|
+
if (context.type === "unknown")
|
|
21543
|
+
context.type = "python";
|
|
21544
|
+
}
|
|
21545
|
+
if (existsSync6(join3(cwd, "go.mod"))) {
|
|
21546
|
+
detected = true;
|
|
21547
|
+
if (context.type === "unknown")
|
|
21548
|
+
context.type = "go";
|
|
21549
|
+
}
|
|
21550
|
+
return detected ? context : null;
|
|
21551
|
+
}
|
|
21552
|
+
function formatRepoContext(context) {
|
|
21553
|
+
const lines = [];
|
|
21554
|
+
lines.push(`Project type: ${context.type}`);
|
|
21555
|
+
if (context.packageManager) {
|
|
21556
|
+
lines.push(`Package manager: ${context.packageManager}`);
|
|
21557
|
+
}
|
|
21558
|
+
if (context.scripts && context.scripts.length > 0) {
|
|
21559
|
+
const displayScripts = context.scripts.slice(0, 15);
|
|
21560
|
+
const suffix = context.scripts.length > 15 ? ` (+${context.scripts.length - 15} more)` : "";
|
|
21561
|
+
lines.push(`Available scripts: ${displayScripts.join(", ")}${suffix}`);
|
|
21562
|
+
}
|
|
21563
|
+
if (context.makeTargets && context.makeTargets.length > 0) {
|
|
21564
|
+
const displayTargets = context.makeTargets.slice(0, 15);
|
|
21565
|
+
const suffix = context.makeTargets.length > 15 ? ` (+${context.makeTargets.length - 15} more)` : "";
|
|
21566
|
+
lines.push(`Make targets: ${displayTargets.join(", ")}${suffix}`);
|
|
21567
|
+
}
|
|
21568
|
+
if (context.cargoCommands) {
|
|
21569
|
+
lines.push(`Cargo commands: ${context.cargoCommands.join(", ")}`);
|
|
21570
|
+
}
|
|
21571
|
+
if (context.hasDocker) {
|
|
21572
|
+
lines.push(`Docker: available`);
|
|
21573
|
+
}
|
|
21574
|
+
if (context.hasGit) {
|
|
21575
|
+
lines.push(`Git: initialized`);
|
|
21576
|
+
}
|
|
21577
|
+
return lines.join(`
|
|
21578
|
+
`);
|
|
21579
|
+
}
|
|
21580
|
+
var init_repo_context = () => {};
|
|
21581
|
+
|
|
21475
21582
|
// src/lib/api.ts
|
|
21476
21583
|
function getZenApiType(modelId) {
|
|
21477
21584
|
if (modelId.startsWith("gpt-")) {
|
|
@@ -21485,11 +21592,21 @@ function getZenApiType(modelId) {
|
|
|
21485
21592
|
}
|
|
21486
21593
|
return "openai-compatible";
|
|
21487
21594
|
}
|
|
21488
|
-
function buildSystemPrompt(cwd, history, shellInfo) {
|
|
21595
|
+
function buildSystemPrompt(cwd, history, shellInfo, repoContextEnabled) {
|
|
21489
21596
|
const historyContext = formatHistory(history);
|
|
21490
21597
|
const platformPaths = getPlatformPaths(shellInfo.platform);
|
|
21491
21598
|
const shellHints = getShellSyntaxHints(shellInfo.shell);
|
|
21492
21599
|
const platformName = shellInfo.platform === "macos" ? "macOS" : shellInfo.platform === "windows" ? "Windows" : shellInfo.platform === "linux" ? shellInfo.isWSL ? "Linux (WSL)" : "Linux" : "Unknown";
|
|
21600
|
+
let projectContextSection = "";
|
|
21601
|
+
if (repoContextEnabled) {
|
|
21602
|
+
const repoContext = detectRepoContext(cwd);
|
|
21603
|
+
if (repoContext) {
|
|
21604
|
+
projectContextSection = `
|
|
21605
|
+
Project context:
|
|
21606
|
+
${formatRepoContext(repoContext)}
|
|
21607
|
+
`;
|
|
21608
|
+
}
|
|
21609
|
+
}
|
|
21493
21610
|
return `You are a shell command translator. Convert the user's natural language request into a shell command.
|
|
21494
21611
|
|
|
21495
21612
|
Current environment:
|
|
@@ -21498,7 +21615,7 @@ Current environment:
|
|
|
21498
21615
|
- Working directory: ${cwd}
|
|
21499
21616
|
- Home directory: ${shellInfo.homeDir}
|
|
21500
21617
|
${shellInfo.terminalEmulator ? `- Terminal: ${shellInfo.terminalEmulator}` : ""}
|
|
21501
|
-
|
|
21618
|
+
${projectContextSection}
|
|
21502
21619
|
${shellHints}
|
|
21503
21620
|
|
|
21504
21621
|
Recent command history:
|
|
@@ -21509,7 +21626,8 @@ Rules:
|
|
|
21509
21626
|
- No explanations, no markdown, no backticks, no code blocks
|
|
21510
21627
|
- Use the correct syntax for the detected shell (${shellInfo.shell})
|
|
21511
21628
|
- If the request is unclear, make a reasonable assumption
|
|
21512
|
-
- Prefer simple, common commands over complex one-liners
|
|
21629
|
+
- Prefer simple, common commands over complex one-liners${repoContextEnabled ? `
|
|
21630
|
+
- Use project-specific commands when relevant (e.g., use the detected package manager and available scripts)` : ""}
|
|
21513
21631
|
- Use the command history for context (e.g., "do that again", "undo", "delete the file I just created")
|
|
21514
21632
|
- If the user asks something that can't be done with a shell command, output a command that prints a helpful message
|
|
21515
21633
|
- For file operations, prefer safer alternatives when possible
|
|
@@ -21781,9 +21899,9 @@ function getShellInfo() {
|
|
|
21781
21899
|
}
|
|
21782
21900
|
return cachedShellInfo;
|
|
21783
21901
|
}
|
|
21784
|
-
async function translateToCommand(apiKey, model, userInput, cwd, history = []) {
|
|
21902
|
+
async function translateToCommand(apiKey, model, userInput, cwd, history = [], repoContextEnabled) {
|
|
21785
21903
|
const shellInfo = getShellInfo();
|
|
21786
|
-
const systemPrompt = buildSystemPrompt(cwd, history, shellInfo);
|
|
21904
|
+
const systemPrompt = buildSystemPrompt(cwd, history, shellInfo, repoContextEnabled);
|
|
21787
21905
|
let rawCommand;
|
|
21788
21906
|
if (model.provider === "openrouter") {
|
|
21789
21907
|
rawCommand = await callOpenRouter(apiKey, model.id, systemPrompt, userInput);
|
|
@@ -21809,6 +21927,7 @@ async function translateToCommand(apiKey, model, userInput, cwd, history = []) {
|
|
|
21809
21927
|
var DEBUG_API, cachedShellInfo = null;
|
|
21810
21928
|
var init_api = __esm(() => {
|
|
21811
21929
|
init_shell();
|
|
21930
|
+
init_repo_context();
|
|
21812
21931
|
DEBUG_API = process.env.DEBUG_API === "1";
|
|
21813
21932
|
});
|
|
21814
21933
|
|
|
@@ -22306,7 +22425,8 @@ function getStatusBarContent() {
|
|
|
22306
22425
|
const theme = getTheme();
|
|
22307
22426
|
const providerName = config.provider === "opencode-zen" ? "OpenCode Zen" : "OpenRouter";
|
|
22308
22427
|
const safeModeIndicator = dryRunMode ? fg(theme.colors.warning)("[DRY RUN]") : fg(theme.colors.success)("Safe");
|
|
22309
|
-
|
|
22428
|
+
const repoContextIndicator = config.repoContext ? fg(theme.colors.info)("[Repo]") : "";
|
|
22429
|
+
return t`${fg(theme.colors.textMuted)("Provider:")} ${fg(theme.colors.text)(providerName)} ${fg(theme.colors.textMuted)("Model:")} ${fg(theme.colors.text)(currentModel.name)} ${safeModeIndicator}${repoContextIndicator ? " " : ""}${repoContextIndicator}`;
|
|
22310
22430
|
}
|
|
22311
22431
|
function getHelpBarContent() {
|
|
22312
22432
|
const theme = getTheme();
|
|
@@ -22636,7 +22756,7 @@ async function translateAndProcess(input) {
|
|
|
22636
22756
|
}
|
|
22637
22757
|
const loadingMsg = addSystemMessage("Translating...");
|
|
22638
22758
|
try {
|
|
22639
|
-
const command = await translateToCommand(apiKey, currentModel, input, currentCwd, history);
|
|
22759
|
+
const command = await translateToCommand(apiKey, currentModel, input, currentCwd, history, config.repoContext);
|
|
22640
22760
|
chatScrollBox.remove(`msg-${loadingMsg.id}`);
|
|
22641
22761
|
chatMessages = chatMessages.filter((m) => m.id !== loadingMsg.id);
|
|
22642
22762
|
const safety = analyzeCommand(command, config);
|
|
@@ -22786,9 +22906,10 @@ function showHelp() {
|
|
|
22786
22906
|
const helpText = `Keyboard Shortcuts (Ctrl+X then...):
|
|
22787
22907
|
P Command palette M Change model
|
|
22788
22908
|
S Switch provider D Toggle dry-run
|
|
22789
|
-
T Change theme
|
|
22790
|
-
|
|
22791
|
-
? This help
|
|
22909
|
+
T Change theme R Toggle repo context
|
|
22910
|
+
H Show history L Clear chat
|
|
22911
|
+
C Show config ? This help
|
|
22912
|
+
Q Exit
|
|
22792
22913
|
|
|
22793
22914
|
Other:
|
|
22794
22915
|
Ctrl+C Exit / Cancel Esc Close palette
|
|
@@ -22796,7 +22917,7 @@ Ctrl+C Exit / Cancel Esc Close palette
|
|
|
22796
22917
|
Tips:
|
|
22797
22918
|
- Type naturally: "list all files" -> ls -la
|
|
22798
22919
|
- Reference history: "do that again", "undo"
|
|
22799
|
-
-
|
|
22920
|
+
- Enable repo context to use project scripts (Ctrl+X R)`;
|
|
22800
22921
|
addSystemMessage(helpText);
|
|
22801
22922
|
}
|
|
22802
22923
|
async function showConfig() {
|
|
@@ -22817,6 +22938,7 @@ Shell: ${shellInfo.shell} (${shellInfo.shellPath})
|
|
|
22817
22938
|
Platform: ${shellInfo.platform}${shellInfo.isWSL ? " (WSL)" : ""}
|
|
22818
22939
|
Safety: ${config.safetyLevel}
|
|
22819
22940
|
Dry-run: ${dryRunMode ? "ON" : "OFF"}
|
|
22941
|
+
Repo context: ${config.repoContext ? "ON" : "OFF"}
|
|
22820
22942
|
API Key: ${apiKeyStatus}
|
|
22821
22943
|
History: ${history.length} commands`;
|
|
22822
22944
|
addSystemMessage(configText);
|
|
@@ -23079,6 +23201,18 @@ function getCommandPaletteOptions() {
|
|
|
23079
23201
|
addSystemMessage(`Dry-run mode: ${dryRunMode ? "ON" : "OFF"}`);
|
|
23080
23202
|
}
|
|
23081
23203
|
},
|
|
23204
|
+
{
|
|
23205
|
+
name: "Toggle Project Context",
|
|
23206
|
+
description: config.repoContext ? "Currently ON (sends script names to AI)" : "Currently OFF",
|
|
23207
|
+
key: "r",
|
|
23208
|
+
chord: "r",
|
|
23209
|
+
action: () => {
|
|
23210
|
+
config.repoContext = !config.repoContext;
|
|
23211
|
+
saveConfig(config);
|
|
23212
|
+
statusBarText.content = getStatusBarContent();
|
|
23213
|
+
addSystemMessage(`Project context: ${config.repoContext ? "ON - AI can see your package.json scripts, Makefile targets, etc." : "OFF"}`);
|
|
23214
|
+
}
|
|
23215
|
+
},
|
|
23082
23216
|
{
|
|
23083
23217
|
name: "Show Config",
|
|
23084
23218
|
description: "View current configuration",
|