@austinthesing/magic-shell 0.2.27 → 0.2.28
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 +13 -8
- package/dist/cli.js +221 -1
- package/dist/tui.js +221 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -145,7 +145,7 @@ Launch with `mshell` for a full interactive experience.
|
|
|
145
145
|
|
|
146
146
|
### Keyboard Shortcuts
|
|
147
147
|
|
|
148
|
-
Magic Shell follows OpenCode-style TUI shortcuts where they
|
|
148
|
+
Magic Shell follows OpenCode-style TUI shortcuts where they fit cleanly:
|
|
149
149
|
|
|
150
150
|
| Shortcut | Action |
|
|
151
151
|
| ---------- | ---------------------- |
|
|
@@ -154,23 +154,28 @@ Magic Shell follows OpenCode-style TUI shortcuts where they map cleanly:
|
|
|
154
154
|
| `Ctrl+X T` | Change theme |
|
|
155
155
|
| `Ctrl+X Q` | Exit |
|
|
156
156
|
| `Ctrl+T` | Cycle thinking level |
|
|
157
|
-
| `Ctrl+C` | Exit /
|
|
157
|
+
| `Ctrl+C` | Exit, or cancel/close the active dialog first |
|
|
158
158
|
| `Ctrl+D` | Exit |
|
|
159
|
-
| `Esc` | Close dialogs
|
|
159
|
+
| `Esc` | Close dialogs or cancel pending actions |
|
|
160
160
|
|
|
161
|
-
###
|
|
161
|
+
### Slash Commands in TUI
|
|
162
162
|
|
|
163
|
-
|
|
163
|
+
Typing `/` opens a slash command menu under the prompt. Type to filter, use arrow keys to move, `Tab` to complete, and `Enter` to run the selected command.
|
|
164
|
+
|
|
165
|
+
You can also submit slash commands directly:
|
|
164
166
|
|
|
165
167
|
- `!help` or `/help` - Show help
|
|
166
|
-
- `!model` or `/
|
|
167
|
-
- `!provider` or `/
|
|
168
|
+
- `!model` or `/models` - Change model
|
|
169
|
+
- `!provider` or `/providers` - Switch provider
|
|
170
|
+
- `!theme` or `/themes` - Change theme
|
|
168
171
|
- `!dry` or `/dry` - Toggle dry-run mode
|
|
172
|
+
- `!thinking` or `/thinking` - Cycle thinking level
|
|
169
173
|
- `!config` or `/config` - Show current configuration
|
|
170
174
|
- `!history` or `/history` - Show command history
|
|
171
175
|
- `!clear` or `/clear` - Clear output
|
|
176
|
+
- `!exit` or `/exit` - Exit the TUI
|
|
172
177
|
|
|
173
|
-
|
|
178
|
+
Both `!` and `/` prefixes work for these commands.
|
|
174
179
|
|
|
175
180
|
## AI Providers
|
|
176
181
|
|
package/dist/cli.js
CHANGED
|
@@ -76817,6 +76817,11 @@ var inputHintText;
|
|
|
76817
76817
|
var helpBarText;
|
|
76818
76818
|
var modelSelector = null;
|
|
76819
76819
|
var providerSelector = null;
|
|
76820
|
+
var slashCommandContainer = null;
|
|
76821
|
+
var slashCommandSelector = null;
|
|
76822
|
+
var slashCommandMatches = [];
|
|
76823
|
+
var slashCommandSelectedIndex = 0;
|
|
76824
|
+
var slashCommandSyncQueued = false;
|
|
76820
76825
|
var pendingMessageId = null;
|
|
76821
76826
|
var awaitingConfirmation = false;
|
|
76822
76827
|
function generateMessageId() {
|
|
@@ -77119,6 +77124,7 @@ function createMainUI() {
|
|
|
77119
77124
|
});
|
|
77120
77125
|
mainContainer.add(helpBarText);
|
|
77121
77126
|
renderer.keyInput.on("keypress", handleKeypress);
|
|
77127
|
+
renderer.keyInput.on("keypress", queueSlashCommandSync);
|
|
77122
77128
|
inputField.focus();
|
|
77123
77129
|
}
|
|
77124
77130
|
function getStatusBarContent() {
|
|
@@ -77428,13 +77434,28 @@ function refreshThemeColors() {
|
|
|
77428
77434
|
if (inputHintText) {
|
|
77429
77435
|
inputHintText.content = getInputHintContent();
|
|
77430
77436
|
}
|
|
77437
|
+
if (slashCommandContainer) {
|
|
77438
|
+
slashCommandContainer.borderColor = theme.colors.border;
|
|
77439
|
+
slashCommandContainer.backgroundColor = theme.colors.backgroundPanel;
|
|
77440
|
+
}
|
|
77441
|
+
if (slashCommandSelector) {
|
|
77442
|
+
slashCommandSelector.selectedBackgroundColor = theme.colors.backgroundElement;
|
|
77443
|
+
slashCommandSelector.textColor = theme.colors.text;
|
|
77444
|
+
slashCommandSelector.selectedTextColor = theme.colors.primary;
|
|
77445
|
+
slashCommandSelector.descriptionColor = theme.colors.textMuted;
|
|
77446
|
+
slashCommandSelector.selectedDescriptionColor = theme.colors.textMuted;
|
|
77447
|
+
}
|
|
77431
77448
|
}
|
|
77432
77449
|
async function handleInput(value) {
|
|
77433
77450
|
const input = value.trim();
|
|
77434
77451
|
if (!input)
|
|
77435
77452
|
return;
|
|
77436
77453
|
inputField.setText("");
|
|
77437
|
-
|
|
77454
|
+
closeSlashCommandMenu();
|
|
77455
|
+
if (input.startsWith("/") && await tryHandleSlashCommand(input)) {
|
|
77456
|
+
return;
|
|
77457
|
+
}
|
|
77458
|
+
if (input.startsWith("!")) {
|
|
77438
77459
|
await handleSpecialCommand(input);
|
|
77439
77460
|
return;
|
|
77440
77461
|
}
|
|
@@ -77445,6 +77466,18 @@ async function handleInput(value) {
|
|
|
77445
77466
|
}
|
|
77446
77467
|
await translateAndProcess(input);
|
|
77447
77468
|
}
|
|
77469
|
+
async function tryHandleSlashCommand(input) {
|
|
77470
|
+
if (!input.startsWith("/"))
|
|
77471
|
+
return false;
|
|
77472
|
+
const slashBody = input.slice(1).trim();
|
|
77473
|
+
if (!slashBody.includes(" ") && slashCommandMatches.length > 0) {
|
|
77474
|
+
const selected = slashCommandMatches[Math.min(slashCommandSelectedIndex, slashCommandMatches.length - 1)];
|
|
77475
|
+
await handleSpecialCommand(`/${selected.slash}`);
|
|
77476
|
+
return true;
|
|
77477
|
+
}
|
|
77478
|
+
await handleSpecialCommand(input);
|
|
77479
|
+
return true;
|
|
77480
|
+
}
|
|
77448
77481
|
function isDirectCommand(input) {
|
|
77449
77482
|
const directCommands = ["ls", "pwd", "cd", "cat", "echo", "mkdir", "touch", "rm", "cp", "mv", "git", "npm", "bun", "node", "python", "pip", "brew", "apt", "docker", "kubectl"];
|
|
77450
77483
|
const firstWord = input.split(/\s+/)[0].toLowerCase();
|
|
@@ -77620,6 +77653,7 @@ async function handleSpecialCommand(input) {
|
|
|
77620
77653
|
}
|
|
77621
77654
|
}
|
|
77622
77655
|
function clearChat() {
|
|
77656
|
+
closeSlashCommandMenu();
|
|
77623
77657
|
for (const msg of chatMessages) {
|
|
77624
77658
|
chatScrollBox.remove(`msg-${msg.id}`);
|
|
77625
77659
|
}
|
|
@@ -78031,6 +78065,166 @@ function getCommandPaletteOptions() {
|
|
|
78031
78065
|
}
|
|
78032
78066
|
];
|
|
78033
78067
|
}
|
|
78068
|
+
function getSlashCommands() {
|
|
78069
|
+
return [
|
|
78070
|
+
{
|
|
78071
|
+
slash: "help",
|
|
78072
|
+
name: "Help",
|
|
78073
|
+
description: "Show commands and shortcuts",
|
|
78074
|
+
action: () => showHelp()
|
|
78075
|
+
},
|
|
78076
|
+
{
|
|
78077
|
+
slash: "models",
|
|
78078
|
+
name: "Models",
|
|
78079
|
+
description: `Change model · ${currentModel.name}`,
|
|
78080
|
+
action: () => showModelSelector()
|
|
78081
|
+
},
|
|
78082
|
+
{
|
|
78083
|
+
slash: "providers",
|
|
78084
|
+
name: "Providers",
|
|
78085
|
+
description: `Switch provider · ${getProviderDisplayName(config2.provider)}`,
|
|
78086
|
+
action: () => switchProvider()
|
|
78087
|
+
},
|
|
78088
|
+
{
|
|
78089
|
+
slash: "themes",
|
|
78090
|
+
name: "Themes",
|
|
78091
|
+
description: `Change theme · ${getTheme().name}`,
|
|
78092
|
+
action: () => showThemeSelector()
|
|
78093
|
+
},
|
|
78094
|
+
{
|
|
78095
|
+
slash: "dry",
|
|
78096
|
+
name: "Dry Run",
|
|
78097
|
+
description: dryRunMode ? "Turn dry-run off" : "Turn dry-run on",
|
|
78098
|
+
action: () => {
|
|
78099
|
+
dryRunMode = !dryRunMode;
|
|
78100
|
+
statusBarText.content = getStatusBarContent();
|
|
78101
|
+
addSystemMessage(`Dry-run mode: ${dryRunMode ? "ON" : "OFF"}`);
|
|
78102
|
+
}
|
|
78103
|
+
},
|
|
78104
|
+
{
|
|
78105
|
+
slash: "thinking",
|
|
78106
|
+
name: "Thinking",
|
|
78107
|
+
description: `Cycle thinking level · ${config2.thinkingLevel}`,
|
|
78108
|
+
action: () => cycleThinkingLevel()
|
|
78109
|
+
},
|
|
78110
|
+
{
|
|
78111
|
+
slash: "config",
|
|
78112
|
+
name: "Config",
|
|
78113
|
+
description: "Show current configuration",
|
|
78114
|
+
action: () => showConfig()
|
|
78115
|
+
},
|
|
78116
|
+
{
|
|
78117
|
+
slash: "history",
|
|
78118
|
+
name: "History",
|
|
78119
|
+
description: `${history.length} commands`,
|
|
78120
|
+
action: () => showHistory()
|
|
78121
|
+
},
|
|
78122
|
+
{
|
|
78123
|
+
slash: "clear",
|
|
78124
|
+
name: "Clear",
|
|
78125
|
+
description: "Clear the chat history",
|
|
78126
|
+
action: () => clearChat()
|
|
78127
|
+
},
|
|
78128
|
+
{
|
|
78129
|
+
slash: "exit",
|
|
78130
|
+
name: "Exit",
|
|
78131
|
+
description: "Close magic-shell",
|
|
78132
|
+
action: () => {
|
|
78133
|
+
renderer.destroy();
|
|
78134
|
+
process.exit(0);
|
|
78135
|
+
}
|
|
78136
|
+
}
|
|
78137
|
+
];
|
|
78138
|
+
}
|
|
78139
|
+
function getSlashCommandMatches(inputText) {
|
|
78140
|
+
if (!inputText.startsWith("/"))
|
|
78141
|
+
return [];
|
|
78142
|
+
const slashBody = inputText.slice(1);
|
|
78143
|
+
if (slashBody.includes(" "))
|
|
78144
|
+
return [];
|
|
78145
|
+
const query = slashBody.trim().toLowerCase();
|
|
78146
|
+
return getSlashCommands().filter((cmd) => {
|
|
78147
|
+
if (!query)
|
|
78148
|
+
return true;
|
|
78149
|
+
return `${cmd.slash} ${cmd.name} ${cmd.description}`.toLowerCase().includes(query);
|
|
78150
|
+
});
|
|
78151
|
+
}
|
|
78152
|
+
function ensureSlashCommandMenu() {
|
|
78153
|
+
if (slashCommandContainer && slashCommandSelector)
|
|
78154
|
+
return;
|
|
78155
|
+
slashCommandContainer = new BoxRenderable(renderer, {
|
|
78156
|
+
id: "slash-command-container",
|
|
78157
|
+
width: "100%",
|
|
78158
|
+
border: true,
|
|
78159
|
+
borderColor: getTheme().colors.border,
|
|
78160
|
+
borderStyle: "single",
|
|
78161
|
+
backgroundColor: getTheme().colors.backgroundPanel,
|
|
78162
|
+
paddingLeft: 1,
|
|
78163
|
+
paddingRight: 1,
|
|
78164
|
+
paddingTop: 0,
|
|
78165
|
+
paddingBottom: 0,
|
|
78166
|
+
marginTop: 1
|
|
78167
|
+
});
|
|
78168
|
+
inputContainer.add(slashCommandContainer);
|
|
78169
|
+
slashCommandSelector = new SelectRenderable(renderer, {
|
|
78170
|
+
id: "slash-command-select",
|
|
78171
|
+
width: "100%",
|
|
78172
|
+
height: 6,
|
|
78173
|
+
options: [],
|
|
78174
|
+
backgroundColor: "transparent",
|
|
78175
|
+
focusedBackgroundColor: "transparent",
|
|
78176
|
+
selectedBackgroundColor: getTheme().colors.backgroundElement,
|
|
78177
|
+
textColor: getTheme().colors.text,
|
|
78178
|
+
selectedTextColor: getTheme().colors.primary,
|
|
78179
|
+
descriptionColor: getTheme().colors.textMuted,
|
|
78180
|
+
selectedDescriptionColor: getTheme().colors.textMuted,
|
|
78181
|
+
showDescription: true,
|
|
78182
|
+
wrapSelection: true
|
|
78183
|
+
});
|
|
78184
|
+
slashCommandContainer.add(slashCommandSelector);
|
|
78185
|
+
}
|
|
78186
|
+
function updateSlashCommandMenuOptions() {
|
|
78187
|
+
if (!slashCommandSelector)
|
|
78188
|
+
return;
|
|
78189
|
+
slashCommandSelector.options = slashCommandMatches.map((cmd) => ({
|
|
78190
|
+
name: `/${cmd.slash}`,
|
|
78191
|
+
description: cmd.description,
|
|
78192
|
+
value: cmd
|
|
78193
|
+
}));
|
|
78194
|
+
slashCommandSelector.height = Math.min(Math.max(slashCommandMatches.length, 1), 6);
|
|
78195
|
+
slashCommandSelector.setSelectedIndex(slashCommandSelectedIndex);
|
|
78196
|
+
}
|
|
78197
|
+
function closeSlashCommandMenu() {
|
|
78198
|
+
if (slashCommandContainer) {
|
|
78199
|
+
inputContainer.remove("slash-command-container");
|
|
78200
|
+
slashCommandContainer = null;
|
|
78201
|
+
slashCommandSelector = null;
|
|
78202
|
+
}
|
|
78203
|
+
slashCommandMatches = [];
|
|
78204
|
+
slashCommandSelectedIndex = 0;
|
|
78205
|
+
}
|
|
78206
|
+
function syncSlashCommandMenu() {
|
|
78207
|
+
const inputText = inputField.editBuffer.getText();
|
|
78208
|
+
const matches = getSlashCommandMatches(inputText);
|
|
78209
|
+
if (matches.length === 0) {
|
|
78210
|
+
closeSlashCommandMenu();
|
|
78211
|
+
return;
|
|
78212
|
+
}
|
|
78213
|
+
ensureSlashCommandMenu();
|
|
78214
|
+
slashCommandMatches = matches;
|
|
78215
|
+
slashCommandSelectedIndex = Math.min(slashCommandSelectedIndex, slashCommandMatches.length - 1);
|
|
78216
|
+
updateSlashCommandMenuOptions();
|
|
78217
|
+
}
|
|
78218
|
+
function queueSlashCommandSync() {
|
|
78219
|
+
if (slashCommandSyncQueued)
|
|
78220
|
+
return;
|
|
78221
|
+
slashCommandSyncQueued = true;
|
|
78222
|
+
setTimeout(() => {
|
|
78223
|
+
slashCommandSyncQueued = false;
|
|
78224
|
+
if (inputField)
|
|
78225
|
+
syncSlashCommandMenu();
|
|
78226
|
+
}, 0);
|
|
78227
|
+
}
|
|
78034
78228
|
function showCommandPalette() {
|
|
78035
78229
|
if (commandPalette) {
|
|
78036
78230
|
return;
|
|
@@ -78118,6 +78312,24 @@ function closeCommandPalette() {
|
|
|
78118
78312
|
}
|
|
78119
78313
|
function handleKeypress(key) {
|
|
78120
78314
|
const commands = getCommandPaletteOptions();
|
|
78315
|
+
if (slashCommandMatches.length > 0) {
|
|
78316
|
+
if (key.name === "up") {
|
|
78317
|
+
slashCommandSelectedIndex = slashCommandSelectedIndex === 0 ? slashCommandMatches.length - 1 : slashCommandSelectedIndex - 1;
|
|
78318
|
+
updateSlashCommandMenuOptions();
|
|
78319
|
+
return;
|
|
78320
|
+
}
|
|
78321
|
+
if (key.name === "down") {
|
|
78322
|
+
slashCommandSelectedIndex = (slashCommandSelectedIndex + 1) % slashCommandMatches.length;
|
|
78323
|
+
updateSlashCommandMenuOptions();
|
|
78324
|
+
return;
|
|
78325
|
+
}
|
|
78326
|
+
if (key.name === "tab") {
|
|
78327
|
+
inputField.setText(`/${slashCommandMatches[slashCommandSelectedIndex].slash}`);
|
|
78328
|
+
syncSlashCommandMenu();
|
|
78329
|
+
inputField.focus();
|
|
78330
|
+
return;
|
|
78331
|
+
}
|
|
78332
|
+
}
|
|
78121
78333
|
if (key.ctrl && key.name === "p") {
|
|
78122
78334
|
showCommandPalette();
|
|
78123
78335
|
return;
|
|
@@ -78162,6 +78374,10 @@ function handleKeypress(key) {
|
|
|
78162
78374
|
closeCommandPalette();
|
|
78163
78375
|
return;
|
|
78164
78376
|
}
|
|
78377
|
+
if (slashCommandMatches.length > 0) {
|
|
78378
|
+
closeSlashCommandMenu();
|
|
78379
|
+
return;
|
|
78380
|
+
}
|
|
78165
78381
|
if (modelSelector) {
|
|
78166
78382
|
renderer.root.remove("model-selector-container");
|
|
78167
78383
|
modelSelector = null;
|
|
@@ -78189,6 +78405,10 @@ function handleKeypress(key) {
|
|
|
78189
78405
|
closeCommandPalette();
|
|
78190
78406
|
return;
|
|
78191
78407
|
}
|
|
78408
|
+
if (slashCommandMatches.length > 0) {
|
|
78409
|
+
closeSlashCommandMenu();
|
|
78410
|
+
return;
|
|
78411
|
+
}
|
|
78192
78412
|
if (modelSelector) {
|
|
78193
78413
|
renderer.root.remove("model-selector-container");
|
|
78194
78414
|
modelSelector = null;
|
package/dist/tui.js
CHANGED
|
@@ -76817,6 +76817,11 @@ var inputHintText;
|
|
|
76817
76817
|
var helpBarText;
|
|
76818
76818
|
var modelSelector = null;
|
|
76819
76819
|
var providerSelector = null;
|
|
76820
|
+
var slashCommandContainer = null;
|
|
76821
|
+
var slashCommandSelector = null;
|
|
76822
|
+
var slashCommandMatches = [];
|
|
76823
|
+
var slashCommandSelectedIndex = 0;
|
|
76824
|
+
var slashCommandSyncQueued = false;
|
|
76820
76825
|
var pendingMessageId = null;
|
|
76821
76826
|
var awaitingConfirmation = false;
|
|
76822
76827
|
function generateMessageId() {
|
|
@@ -77119,6 +77124,7 @@ function createMainUI() {
|
|
|
77119
77124
|
});
|
|
77120
77125
|
mainContainer.add(helpBarText);
|
|
77121
77126
|
renderer.keyInput.on("keypress", handleKeypress);
|
|
77127
|
+
renderer.keyInput.on("keypress", queueSlashCommandSync);
|
|
77122
77128
|
inputField.focus();
|
|
77123
77129
|
}
|
|
77124
77130
|
function getStatusBarContent() {
|
|
@@ -77428,13 +77434,28 @@ function refreshThemeColors() {
|
|
|
77428
77434
|
if (inputHintText) {
|
|
77429
77435
|
inputHintText.content = getInputHintContent();
|
|
77430
77436
|
}
|
|
77437
|
+
if (slashCommandContainer) {
|
|
77438
|
+
slashCommandContainer.borderColor = theme.colors.border;
|
|
77439
|
+
slashCommandContainer.backgroundColor = theme.colors.backgroundPanel;
|
|
77440
|
+
}
|
|
77441
|
+
if (slashCommandSelector) {
|
|
77442
|
+
slashCommandSelector.selectedBackgroundColor = theme.colors.backgroundElement;
|
|
77443
|
+
slashCommandSelector.textColor = theme.colors.text;
|
|
77444
|
+
slashCommandSelector.selectedTextColor = theme.colors.primary;
|
|
77445
|
+
slashCommandSelector.descriptionColor = theme.colors.textMuted;
|
|
77446
|
+
slashCommandSelector.selectedDescriptionColor = theme.colors.textMuted;
|
|
77447
|
+
}
|
|
77431
77448
|
}
|
|
77432
77449
|
async function handleInput(value) {
|
|
77433
77450
|
const input = value.trim();
|
|
77434
77451
|
if (!input)
|
|
77435
77452
|
return;
|
|
77436
77453
|
inputField.setText("");
|
|
77437
|
-
|
|
77454
|
+
closeSlashCommandMenu();
|
|
77455
|
+
if (input.startsWith("/") && await tryHandleSlashCommand(input)) {
|
|
77456
|
+
return;
|
|
77457
|
+
}
|
|
77458
|
+
if (input.startsWith("!")) {
|
|
77438
77459
|
await handleSpecialCommand(input);
|
|
77439
77460
|
return;
|
|
77440
77461
|
}
|
|
@@ -77445,6 +77466,18 @@ async function handleInput(value) {
|
|
|
77445
77466
|
}
|
|
77446
77467
|
await translateAndProcess(input);
|
|
77447
77468
|
}
|
|
77469
|
+
async function tryHandleSlashCommand(input) {
|
|
77470
|
+
if (!input.startsWith("/"))
|
|
77471
|
+
return false;
|
|
77472
|
+
const slashBody = input.slice(1).trim();
|
|
77473
|
+
if (!slashBody.includes(" ") && slashCommandMatches.length > 0) {
|
|
77474
|
+
const selected = slashCommandMatches[Math.min(slashCommandSelectedIndex, slashCommandMatches.length - 1)];
|
|
77475
|
+
await handleSpecialCommand(`/${selected.slash}`);
|
|
77476
|
+
return true;
|
|
77477
|
+
}
|
|
77478
|
+
await handleSpecialCommand(input);
|
|
77479
|
+
return true;
|
|
77480
|
+
}
|
|
77448
77481
|
function isDirectCommand(input) {
|
|
77449
77482
|
const directCommands = ["ls", "pwd", "cd", "cat", "echo", "mkdir", "touch", "rm", "cp", "mv", "git", "npm", "bun", "node", "python", "pip", "brew", "apt", "docker", "kubectl"];
|
|
77450
77483
|
const firstWord = input.split(/\s+/)[0].toLowerCase();
|
|
@@ -77620,6 +77653,7 @@ async function handleSpecialCommand(input) {
|
|
|
77620
77653
|
}
|
|
77621
77654
|
}
|
|
77622
77655
|
function clearChat() {
|
|
77656
|
+
closeSlashCommandMenu();
|
|
77623
77657
|
for (const msg of chatMessages) {
|
|
77624
77658
|
chatScrollBox.remove(`msg-${msg.id}`);
|
|
77625
77659
|
}
|
|
@@ -78031,6 +78065,166 @@ function getCommandPaletteOptions() {
|
|
|
78031
78065
|
}
|
|
78032
78066
|
];
|
|
78033
78067
|
}
|
|
78068
|
+
function getSlashCommands() {
|
|
78069
|
+
return [
|
|
78070
|
+
{
|
|
78071
|
+
slash: "help",
|
|
78072
|
+
name: "Help",
|
|
78073
|
+
description: "Show commands and shortcuts",
|
|
78074
|
+
action: () => showHelp()
|
|
78075
|
+
},
|
|
78076
|
+
{
|
|
78077
|
+
slash: "models",
|
|
78078
|
+
name: "Models",
|
|
78079
|
+
description: `Change model · ${currentModel.name}`,
|
|
78080
|
+
action: () => showModelSelector()
|
|
78081
|
+
},
|
|
78082
|
+
{
|
|
78083
|
+
slash: "providers",
|
|
78084
|
+
name: "Providers",
|
|
78085
|
+
description: `Switch provider · ${getProviderDisplayName(config2.provider)}`,
|
|
78086
|
+
action: () => switchProvider()
|
|
78087
|
+
},
|
|
78088
|
+
{
|
|
78089
|
+
slash: "themes",
|
|
78090
|
+
name: "Themes",
|
|
78091
|
+
description: `Change theme · ${getTheme().name}`,
|
|
78092
|
+
action: () => showThemeSelector()
|
|
78093
|
+
},
|
|
78094
|
+
{
|
|
78095
|
+
slash: "dry",
|
|
78096
|
+
name: "Dry Run",
|
|
78097
|
+
description: dryRunMode ? "Turn dry-run off" : "Turn dry-run on",
|
|
78098
|
+
action: () => {
|
|
78099
|
+
dryRunMode = !dryRunMode;
|
|
78100
|
+
statusBarText.content = getStatusBarContent();
|
|
78101
|
+
addSystemMessage(`Dry-run mode: ${dryRunMode ? "ON" : "OFF"}`);
|
|
78102
|
+
}
|
|
78103
|
+
},
|
|
78104
|
+
{
|
|
78105
|
+
slash: "thinking",
|
|
78106
|
+
name: "Thinking",
|
|
78107
|
+
description: `Cycle thinking level · ${config2.thinkingLevel}`,
|
|
78108
|
+
action: () => cycleThinkingLevel()
|
|
78109
|
+
},
|
|
78110
|
+
{
|
|
78111
|
+
slash: "config",
|
|
78112
|
+
name: "Config",
|
|
78113
|
+
description: "Show current configuration",
|
|
78114
|
+
action: () => showConfig()
|
|
78115
|
+
},
|
|
78116
|
+
{
|
|
78117
|
+
slash: "history",
|
|
78118
|
+
name: "History",
|
|
78119
|
+
description: `${history.length} commands`,
|
|
78120
|
+
action: () => showHistory()
|
|
78121
|
+
},
|
|
78122
|
+
{
|
|
78123
|
+
slash: "clear",
|
|
78124
|
+
name: "Clear",
|
|
78125
|
+
description: "Clear the chat history",
|
|
78126
|
+
action: () => clearChat()
|
|
78127
|
+
},
|
|
78128
|
+
{
|
|
78129
|
+
slash: "exit",
|
|
78130
|
+
name: "Exit",
|
|
78131
|
+
description: "Close magic-shell",
|
|
78132
|
+
action: () => {
|
|
78133
|
+
renderer.destroy();
|
|
78134
|
+
process.exit(0);
|
|
78135
|
+
}
|
|
78136
|
+
}
|
|
78137
|
+
];
|
|
78138
|
+
}
|
|
78139
|
+
function getSlashCommandMatches(inputText) {
|
|
78140
|
+
if (!inputText.startsWith("/"))
|
|
78141
|
+
return [];
|
|
78142
|
+
const slashBody = inputText.slice(1);
|
|
78143
|
+
if (slashBody.includes(" "))
|
|
78144
|
+
return [];
|
|
78145
|
+
const query = slashBody.trim().toLowerCase();
|
|
78146
|
+
return getSlashCommands().filter((cmd) => {
|
|
78147
|
+
if (!query)
|
|
78148
|
+
return true;
|
|
78149
|
+
return `${cmd.slash} ${cmd.name} ${cmd.description}`.toLowerCase().includes(query);
|
|
78150
|
+
});
|
|
78151
|
+
}
|
|
78152
|
+
function ensureSlashCommandMenu() {
|
|
78153
|
+
if (slashCommandContainer && slashCommandSelector)
|
|
78154
|
+
return;
|
|
78155
|
+
slashCommandContainer = new BoxRenderable(renderer, {
|
|
78156
|
+
id: "slash-command-container",
|
|
78157
|
+
width: "100%",
|
|
78158
|
+
border: true,
|
|
78159
|
+
borderColor: getTheme().colors.border,
|
|
78160
|
+
borderStyle: "single",
|
|
78161
|
+
backgroundColor: getTheme().colors.backgroundPanel,
|
|
78162
|
+
paddingLeft: 1,
|
|
78163
|
+
paddingRight: 1,
|
|
78164
|
+
paddingTop: 0,
|
|
78165
|
+
paddingBottom: 0,
|
|
78166
|
+
marginTop: 1
|
|
78167
|
+
});
|
|
78168
|
+
inputContainer.add(slashCommandContainer);
|
|
78169
|
+
slashCommandSelector = new SelectRenderable(renderer, {
|
|
78170
|
+
id: "slash-command-select",
|
|
78171
|
+
width: "100%",
|
|
78172
|
+
height: 6,
|
|
78173
|
+
options: [],
|
|
78174
|
+
backgroundColor: "transparent",
|
|
78175
|
+
focusedBackgroundColor: "transparent",
|
|
78176
|
+
selectedBackgroundColor: getTheme().colors.backgroundElement,
|
|
78177
|
+
textColor: getTheme().colors.text,
|
|
78178
|
+
selectedTextColor: getTheme().colors.primary,
|
|
78179
|
+
descriptionColor: getTheme().colors.textMuted,
|
|
78180
|
+
selectedDescriptionColor: getTheme().colors.textMuted,
|
|
78181
|
+
showDescription: true,
|
|
78182
|
+
wrapSelection: true
|
|
78183
|
+
});
|
|
78184
|
+
slashCommandContainer.add(slashCommandSelector);
|
|
78185
|
+
}
|
|
78186
|
+
function updateSlashCommandMenuOptions() {
|
|
78187
|
+
if (!slashCommandSelector)
|
|
78188
|
+
return;
|
|
78189
|
+
slashCommandSelector.options = slashCommandMatches.map((cmd) => ({
|
|
78190
|
+
name: `/${cmd.slash}`,
|
|
78191
|
+
description: cmd.description,
|
|
78192
|
+
value: cmd
|
|
78193
|
+
}));
|
|
78194
|
+
slashCommandSelector.height = Math.min(Math.max(slashCommandMatches.length, 1), 6);
|
|
78195
|
+
slashCommandSelector.setSelectedIndex(slashCommandSelectedIndex);
|
|
78196
|
+
}
|
|
78197
|
+
function closeSlashCommandMenu() {
|
|
78198
|
+
if (slashCommandContainer) {
|
|
78199
|
+
inputContainer.remove("slash-command-container");
|
|
78200
|
+
slashCommandContainer = null;
|
|
78201
|
+
slashCommandSelector = null;
|
|
78202
|
+
}
|
|
78203
|
+
slashCommandMatches = [];
|
|
78204
|
+
slashCommandSelectedIndex = 0;
|
|
78205
|
+
}
|
|
78206
|
+
function syncSlashCommandMenu() {
|
|
78207
|
+
const inputText = inputField.editBuffer.getText();
|
|
78208
|
+
const matches = getSlashCommandMatches(inputText);
|
|
78209
|
+
if (matches.length === 0) {
|
|
78210
|
+
closeSlashCommandMenu();
|
|
78211
|
+
return;
|
|
78212
|
+
}
|
|
78213
|
+
ensureSlashCommandMenu();
|
|
78214
|
+
slashCommandMatches = matches;
|
|
78215
|
+
slashCommandSelectedIndex = Math.min(slashCommandSelectedIndex, slashCommandMatches.length - 1);
|
|
78216
|
+
updateSlashCommandMenuOptions();
|
|
78217
|
+
}
|
|
78218
|
+
function queueSlashCommandSync() {
|
|
78219
|
+
if (slashCommandSyncQueued)
|
|
78220
|
+
return;
|
|
78221
|
+
slashCommandSyncQueued = true;
|
|
78222
|
+
setTimeout(() => {
|
|
78223
|
+
slashCommandSyncQueued = false;
|
|
78224
|
+
if (inputField)
|
|
78225
|
+
syncSlashCommandMenu();
|
|
78226
|
+
}, 0);
|
|
78227
|
+
}
|
|
78034
78228
|
function showCommandPalette() {
|
|
78035
78229
|
if (commandPalette) {
|
|
78036
78230
|
return;
|
|
@@ -78118,6 +78312,24 @@ function closeCommandPalette() {
|
|
|
78118
78312
|
}
|
|
78119
78313
|
function handleKeypress(key) {
|
|
78120
78314
|
const commands = getCommandPaletteOptions();
|
|
78315
|
+
if (slashCommandMatches.length > 0) {
|
|
78316
|
+
if (key.name === "up") {
|
|
78317
|
+
slashCommandSelectedIndex = slashCommandSelectedIndex === 0 ? slashCommandMatches.length - 1 : slashCommandSelectedIndex - 1;
|
|
78318
|
+
updateSlashCommandMenuOptions();
|
|
78319
|
+
return;
|
|
78320
|
+
}
|
|
78321
|
+
if (key.name === "down") {
|
|
78322
|
+
slashCommandSelectedIndex = (slashCommandSelectedIndex + 1) % slashCommandMatches.length;
|
|
78323
|
+
updateSlashCommandMenuOptions();
|
|
78324
|
+
return;
|
|
78325
|
+
}
|
|
78326
|
+
if (key.name === "tab") {
|
|
78327
|
+
inputField.setText(`/${slashCommandMatches[slashCommandSelectedIndex].slash}`);
|
|
78328
|
+
syncSlashCommandMenu();
|
|
78329
|
+
inputField.focus();
|
|
78330
|
+
return;
|
|
78331
|
+
}
|
|
78332
|
+
}
|
|
78121
78333
|
if (key.ctrl && key.name === "p") {
|
|
78122
78334
|
showCommandPalette();
|
|
78123
78335
|
return;
|
|
@@ -78162,6 +78374,10 @@ function handleKeypress(key) {
|
|
|
78162
78374
|
closeCommandPalette();
|
|
78163
78375
|
return;
|
|
78164
78376
|
}
|
|
78377
|
+
if (slashCommandMatches.length > 0) {
|
|
78378
|
+
closeSlashCommandMenu();
|
|
78379
|
+
return;
|
|
78380
|
+
}
|
|
78165
78381
|
if (modelSelector) {
|
|
78166
78382
|
renderer.root.remove("model-selector-container");
|
|
78167
78383
|
modelSelector = null;
|
|
@@ -78189,6 +78405,10 @@ function handleKeypress(key) {
|
|
|
78189
78405
|
closeCommandPalette();
|
|
78190
78406
|
return;
|
|
78191
78407
|
}
|
|
78408
|
+
if (slashCommandMatches.length > 0) {
|
|
78409
|
+
closeSlashCommandMenu();
|
|
78410
|
+
return;
|
|
78411
|
+
}
|
|
78192
78412
|
if (modelSelector) {
|
|
78193
78413
|
renderer.root.remove("model-selector-container");
|
|
78194
78414
|
modelSelector = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@austinthesing/magic-shell",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.28",
|
|
4
4
|
"description": "Natural language to terminal commands with safety features. Supports OpenCode Zen, OpenRouter, AI gateways, Workers AI, and custom models.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|