shmakk 1.2.1 → 1.2.3
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 +40 -0
- package/package.json +1 -1
- package/src/agent.js +54 -9
- package/src/cli.js +193 -78
- package/src/correction.js +6 -0
- package/src/endpoints.js +6 -0
- package/src/hooks/bash.js +17 -2
- package/src/hooks/fish.js +21 -2
- package/src/hooks/zsh.js +31 -2
- package/src/index.js +11 -2
- package/src/llm.js +2 -2
- package/src/mcp-client.js +7 -1
- package/src/notify.js +6 -3
- package/src/pty.js +2 -2
- package/src/review.js +3 -3
- package/src/self-commands.js +96 -16
- package/src/session.js +14 -5
- package/src/shell.js +39 -19
- package/src/ssh.js +255 -0
- package/src/system-prompt.js +3 -1
- package/src/tools.js +105 -1
package/README.md
CHANGED
|
@@ -170,6 +170,46 @@ The coordinator system enables complex, multi-step task execution with plan-firs
|
|
|
170
170
|
- Secrets (`.env`, keys, tokens) are never sent to the AI
|
|
171
171
|
- Workspace root is enforced — tools can't access files outside it
|
|
172
172
|
|
|
173
|
+
## Remote SSH
|
|
174
|
+
|
|
175
|
+
The agent can run commands and transfer files on remote hosts via SSH. Configure hosts in `.shmakk/hosts.json` (per-project) or `~/.config/shmakk/hosts.json` (global):
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"hosts": {
|
|
180
|
+
"devbox": {
|
|
181
|
+
"host": "marcus@192.168.1.100",
|
|
182
|
+
"port": 22,
|
|
183
|
+
"auto_approve": false,
|
|
184
|
+
"timeout_sec": 30
|
|
185
|
+
},
|
|
186
|
+
"staging": {
|
|
187
|
+
"host": "deploy@10.0.0.5",
|
|
188
|
+
"port": 2247
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
"allow_ssh_config": false,
|
|
192
|
+
"default_timeout_sec": 30
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
| Tool | Description |
|
|
197
|
+
|------|-------------|
|
|
198
|
+
| `ssh_run` | Run a shell command on a remote host |
|
|
199
|
+
| `ssh_push` | Copy a local workspace file to a remote host |
|
|
200
|
+
| `ssh_pull` | Copy a remote file into the local workspace |
|
|
201
|
+
|
|
202
|
+
SSH key auth via `~/.ssh` is assumed. For persistent connections (avoid re-auth on every call), add to `~/.ssh/config`:
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
Host *
|
|
206
|
+
ControlMaster auto
|
|
207
|
+
ControlPath ~/.ssh/controlmasters/%r@%h:%p
|
|
208
|
+
ControlPersist 600
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Then `mkdir -p ~/.ssh/controlmasters` once.
|
|
212
|
+
|
|
173
213
|
## How it works
|
|
174
214
|
|
|
175
215
|
shmakk wraps your shell in a PTY (pseudo-terminal). Every command that fails is checked against a deterministic correction engine (no LLM, no API call). If a correction matches and the fixed command succeeds, shmakk feeds the agent your **original input** (not the fixed command) so the agent can address your full intent — not just the typo. You can also give task instructions in natural language — shmakk uses tools to read files, write code, list directories, and run commands, all constrained to your workspace.
|
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
|
-
const { makeClient, modelFor, isConfigured, getDeepSeekOptions } = require('./llm');
|
|
10
|
+
const { makeClient, modelFor, isConfigured, getDeepSeekOptions, supportsVision } = require('./llm');
|
|
11
11
|
const {
|
|
12
12
|
sanitizeAssistantContent,
|
|
13
13
|
isLeakedToolMarkup,
|
|
@@ -87,13 +87,16 @@ function clearTaskJournal(root) {
|
|
|
87
87
|
const { renderBlock } = require('./markdown');
|
|
88
88
|
|
|
89
89
|
// Tiny spinner so the user sees "the agent is thinking" while we wait on
|
|
90
|
-
// the model. Erased when stop() is called.
|
|
90
|
+
// the model. Erased when stop() is called. Also updates the terminal tab
|
|
91
|
+
// title so you can see agent activity even when looking at another tab.
|
|
91
92
|
function startSpinner(write, label = 'thinking') {
|
|
92
93
|
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
93
94
|
let i = 0; let line = '';
|
|
94
95
|
const draw = () => {
|
|
95
96
|
line = `\x1b[2m${frames[i % frames.length]} ${label}…\x1b[0m`;
|
|
96
97
|
write('\r' + line);
|
|
98
|
+
// Update terminal tab/window title so activity is visible from other tabs
|
|
99
|
+
write(`\x1b]0;${frames[i % frames.length]} ${label} — shmakk\x07`);
|
|
97
100
|
i++;
|
|
98
101
|
};
|
|
99
102
|
draw();
|
|
@@ -101,6 +104,8 @@ function startSpinner(write, label = 'thinking') {
|
|
|
101
104
|
return () => {
|
|
102
105
|
clearInterval(tm);
|
|
103
106
|
write('\r' + ' '.repeat(line.replace(/\x1b\[[0-9;]*m/g, '').length + 2) + '\r\r');
|
|
107
|
+
// Clear the terminal title — shell will restore normal title on next prompt
|
|
108
|
+
write('\x1b]0;\x07');
|
|
104
109
|
};
|
|
105
110
|
}
|
|
106
111
|
|
|
@@ -251,6 +256,7 @@ async function runAgent({ input, roots, glossary, confirmTool, write, signal, hi
|
|
|
251
256
|
mcpToolHint,
|
|
252
257
|
userRulesText,
|
|
253
258
|
userMemoryText,
|
|
259
|
+
supportsVision: supportsVision(),
|
|
254
260
|
});
|
|
255
261
|
|
|
256
262
|
const boundedHistory = trimForContext(history, runtimeProfile.historyEntries);
|
|
@@ -292,14 +298,22 @@ async function runAgent({ input, roots, glossary, confirmTool, write, signal, hi
|
|
|
292
298
|
// Tool loop. Streams content as it arrives; prints each tool call.
|
|
293
299
|
let producedAnything = false;
|
|
294
300
|
const runState = { _dsmlLeakRetries: 0 };
|
|
301
|
+
let taskSpinnerStop = null;
|
|
302
|
+
const ensureSpinner = () => {
|
|
303
|
+
if (!taskSpinnerStop) taskSpinnerStop = startSpinner(write, 'thinking');
|
|
304
|
+
};
|
|
305
|
+
const stopTaskSpinner = () => {
|
|
306
|
+
if (taskSpinnerStop) { taskSpinnerStop(); taskSpinnerStop = null; }
|
|
307
|
+
};
|
|
295
308
|
for (let i = 0; i < dynamicToolBudget; i++) {
|
|
296
|
-
if (signal && signal.aborted) return messages.slice(1);
|
|
309
|
+
if (signal && signal.aborted) { stopTaskSpinner(); return messages.slice(1); }
|
|
297
310
|
|
|
298
311
|
// Stream the response so the user sees text as it generates.
|
|
299
312
|
const cacheKey = promptCacheEnabled ? promptCache.makeKey({ model: modelFor('agent'), messages, toolChoice: 'auto' }) : null;
|
|
300
313
|
if (promptCacheEnabled && cacheKey) {
|
|
301
314
|
const hit = promptCache.get(roots[0], cacheKey);
|
|
302
315
|
if (hit && hit.content) {
|
|
316
|
+
stopTaskSpinner();
|
|
303
317
|
write(dim('[shmakk] prompt cache hit', colors) + '\n');
|
|
304
318
|
write(highlightCodeBlocks(hit.content, colors));
|
|
305
319
|
if (!hit.content.endsWith('\n')) write('\n');
|
|
@@ -309,7 +323,7 @@ async function runAgent({ input, roots, glossary, confirmTool, write, signal, hi
|
|
|
309
323
|
}
|
|
310
324
|
}
|
|
311
325
|
|
|
312
|
-
|
|
326
|
+
ensureSpinner();
|
|
313
327
|
const allTools = mcpManager ? [...TOOLS, ...mcpManager.getToolDefinitions()] : TOOLS;
|
|
314
328
|
let stream;
|
|
315
329
|
try {
|
|
@@ -324,14 +338,13 @@ async function runAgent({ input, roots, glossary, confirmTool, write, signal, hi
|
|
|
324
338
|
...getDeepSeekOptions('tool_loop'),
|
|
325
339
|
}, { signal });
|
|
326
340
|
} catch (e) {
|
|
327
|
-
|
|
341
|
+
stopTaskSpinner();
|
|
328
342
|
throw e;
|
|
329
343
|
}
|
|
330
344
|
|
|
331
345
|
let content = '';
|
|
332
346
|
let reasoningContent = '';
|
|
333
347
|
const toolCalls = []; // [{id, type:'function', function:{name, arguments}}]
|
|
334
|
-
let spinnerStopped = false;
|
|
335
348
|
let streamingContentOk = true; // flipped to false on leak
|
|
336
349
|
try {
|
|
337
350
|
for await (const chunk of stream) {
|
|
@@ -370,7 +383,6 @@ async function runAgent({ input, roots, glossary, confirmTool, write, signal, hi
|
|
|
370
383
|
reasoningContent += delta.reasoning_content;
|
|
371
384
|
}
|
|
372
385
|
if (delta.tool_calls) {
|
|
373
|
-
if (!spinnerStopped) { stop(); spinnerStopped = true; }
|
|
374
386
|
for (const tc of delta.tool_calls) {
|
|
375
387
|
const idx = tc.index ?? 0;
|
|
376
388
|
if (!toolCalls[idx]) toolCalls[idx] = { id: tc.id || '', type: 'function', function: { name: '', arguments: '' } };
|
|
@@ -382,7 +394,8 @@ async function runAgent({ input, roots, glossary, confirmTool, write, signal, hi
|
|
|
382
394
|
}
|
|
383
395
|
}
|
|
384
396
|
} finally {
|
|
385
|
-
|
|
397
|
+
// Spinner runs continuously from loop start — stopped below when
|
|
398
|
+
// we're about to show output or dispatch tools that may need input.
|
|
386
399
|
}
|
|
387
400
|
|
|
388
401
|
// ── DSML leak detection (after stream completes) ────────────────────
|
|
@@ -427,6 +440,7 @@ async function runAgent({ input, roots, glossary, confirmTool, write, signal, hi
|
|
|
427
440
|
// Max retries exceeded — strip and show what we can.
|
|
428
441
|
content = sanitized.visibleText;
|
|
429
442
|
if (!content) {
|
|
443
|
+
stopTaskSpinner();
|
|
430
444
|
write(dim('[shmakk] response contained only leaked tool markup — blocked.', colors) + '\n');
|
|
431
445
|
clearTaskJournal(roots[0]);
|
|
432
446
|
return messages.slice(1);
|
|
@@ -477,6 +491,7 @@ async function runAgent({ input, roots, glossary, confirmTool, write, signal, hi
|
|
|
477
491
|
|
|
478
492
|
// No tools → done.
|
|
479
493
|
if (!normalizedToolCalls.length) {
|
|
494
|
+
stopTaskSpinner();
|
|
480
495
|
if (content) {
|
|
481
496
|
write(renderBlock(content, { enabled: markdown, colors }));
|
|
482
497
|
if (!content.endsWith('\n')) write('\n');
|
|
@@ -497,6 +512,7 @@ async function runAgent({ input, roots, glossary, confirmTool, write, signal, hi
|
|
|
497
512
|
// Dispatch tool calls.
|
|
498
513
|
// Reads/lists are noisy — collect them silently and show a single dim summary.
|
|
499
514
|
// Writes, edits, runs, and errors always print clearly.
|
|
515
|
+
stopTaskSpinner();
|
|
500
516
|
const SILENT_TOOLS = new Set(['read_file', 'list_dir', 'web_search', 'fetch_url']);
|
|
501
517
|
let iterProgress = false;
|
|
502
518
|
let silentReads = [];
|
|
@@ -534,7 +550,34 @@ async function runAgent({ input, roots, glossary, confirmTool, write, signal, hi
|
|
|
534
550
|
write(dim(`→ ${c.function.name}(${shortArgs(args)})${summary ? ' — ' + summary : ''}`, colors) + '\n');
|
|
535
551
|
}
|
|
536
552
|
|
|
537
|
-
|
|
553
|
+
// Build tool result message. If the result includes images with real
|
|
554
|
+
// base64 data AND the current endpoint has `vision: true`, send them as
|
|
555
|
+
// vision content blocks in a follow-up user message. Otherwise just
|
|
556
|
+
// include image metadata in the text. Keep the tool message text-only.
|
|
557
|
+
const toolImages = Array.isArray(result.images) ? result.images.filter((img) => img.data) : [];
|
|
558
|
+
const toolText = result.content || '';
|
|
559
|
+
const toolMeta = Object.fromEntries(
|
|
560
|
+
Object.entries(result || {}).filter(([k]) => !['content', 'images', 'error', 'isRetryable'].includes(k)),
|
|
561
|
+
);
|
|
562
|
+
let toolContent = (toolText + (Object.keys(toolMeta).length ? ' ' + JSON.stringify(toolMeta) : '')).trim();
|
|
563
|
+
if (toolImages.length > 0 && !supportsVision()) {
|
|
564
|
+
// Endpoint doesn't support vision — include image metadata as text
|
|
565
|
+
const imgDesc = toolImages.map((img, i) => `[Image #${i + 1}: ${img.mimeType}, base64=${img.dataLength} chars${img.truncated ? ', truncated' : ''}]`).join(', ');
|
|
566
|
+
toolContent = toolContent ? `${toolContent} ${imgDesc}` : imgDesc;
|
|
567
|
+
}
|
|
568
|
+
messages.push({ role: 'tool', tool_call_id: c.id, content: toolContent.slice(0, 8000) });
|
|
569
|
+
if (toolImages.length > 0 && supportsVision()) {
|
|
570
|
+
messages.push({
|
|
571
|
+
role: 'user',
|
|
572
|
+
content: [
|
|
573
|
+
{ type: 'text', text: `[Images returned by ${c.function.name}${toolImages.some((img) => img.truncated) ? ' (some truncated to ~2MB base64)' : ''}]` },
|
|
574
|
+
...toolImages.map((img) => ({
|
|
575
|
+
type: 'image_url',
|
|
576
|
+
image_url: { url: `data:${img.mimeType};base64,${img.data}`, detail: 'auto' },
|
|
577
|
+
})),
|
|
578
|
+
],
|
|
579
|
+
});
|
|
580
|
+
}
|
|
538
581
|
producedAnything = true;
|
|
539
582
|
persistJournal('running');
|
|
540
583
|
if (signal && signal.aborted) return messages.slice(1);
|
|
@@ -553,6 +596,7 @@ async function runAgent({ input, roots, glossary, confirmTool, write, signal, hi
|
|
|
553
596
|
}
|
|
554
597
|
|
|
555
598
|
if (repeatedSignatureCount >= runtimeProfile.stallRepeat && noProgressRepeats >= 2) {
|
|
599
|
+
stopTaskSpinner();
|
|
556
600
|
break;
|
|
557
601
|
}
|
|
558
602
|
}
|
|
@@ -589,6 +633,7 @@ async function runAgent({ input, roots, glossary, confirmTool, write, signal, hi
|
|
|
589
633
|
}
|
|
590
634
|
} catch {}
|
|
591
635
|
|
|
636
|
+
stopTaskSpinner();
|
|
592
637
|
write(dim('[shmakk] paused after several tool rounds. Resume later continues from task journal; try `shmakk --reset` to clear.', colors) + '\n');
|
|
593
638
|
persistJournal('paused');
|
|
594
639
|
return messages.slice(1);
|
package/src/cli.js
CHANGED
|
@@ -41,8 +41,10 @@ function parseArgs(argv) {
|
|
|
41
41
|
voiceSilenceStartSec: null,
|
|
42
42
|
voicePadStartSec: null,
|
|
43
43
|
ttsVoice: null,
|
|
44
|
-
notify:
|
|
44
|
+
notify: true,
|
|
45
45
|
completion: null,
|
|
46
|
+
helpCategory: null,
|
|
47
|
+
shell: null,
|
|
46
48
|
unknown: [],
|
|
47
49
|
};
|
|
48
50
|
|
|
@@ -53,7 +55,13 @@ function parseArgs(argv) {
|
|
|
53
55
|
case '--yes-files': opts.yesFiles = true; break;
|
|
54
56
|
case '--update-command-glossary': opts.updateGlossary = true; break;
|
|
55
57
|
case '-h':
|
|
56
|
-
case '--help':
|
|
58
|
+
case '--help':
|
|
59
|
+
opts.help = true;
|
|
60
|
+
// Capture optional category: shmakk --help voice
|
|
61
|
+
if (i + 1 < argv.length && !argv[i + 1].startsWith('-')) {
|
|
62
|
+
opts.helpCategory = argv[++i];
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
57
65
|
case '--debug': opts.debug = true; break;
|
|
58
66
|
case '--no-ai': opts.noAi = true; break;
|
|
59
67
|
case '--no-correction': opts.noCorrection = true; break;
|
|
@@ -102,34 +110,67 @@ function parseArgs(argv) {
|
|
|
102
110
|
case '--markdown': opts.markdown = argv[++i] || null; break;
|
|
103
111
|
case '--endpoint': opts.endpoint = argv[++i] || null; break;
|
|
104
112
|
case '--model-recommendation': opts.modelRecommendation = true; break;
|
|
113
|
+
case '--shell':
|
|
114
|
+
{
|
|
115
|
+
const v = argv[++i];
|
|
116
|
+
if (!v || !['fish', 'bash', 'zsh'].includes(v)) {
|
|
117
|
+
process.stderr.write('[shmakk] invalid --shell. Use: fish|bash|zsh\n');
|
|
118
|
+
process.exit(2);
|
|
119
|
+
}
|
|
120
|
+
opts.shell = v;
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
105
123
|
default: opts.unknown.push(a);
|
|
106
124
|
}
|
|
107
125
|
}
|
|
108
126
|
return opts;
|
|
109
127
|
}
|
|
110
128
|
|
|
111
|
-
|
|
129
|
+
// ── Help: category-based navigation ──────────────────────────────────────────
|
|
130
|
+
|
|
131
|
+
const HELP_SUMMARY = `shmakk - AI-supervised terminal wrapper
|
|
112
132
|
|
|
113
133
|
Launch shmakk, then type commands as usual. shmakk watches the shell, catches
|
|
114
134
|
failures, and calls an LLM to fix them, plan tasks, and edit files.
|
|
115
135
|
|
|
116
|
-
You can also type natural-language self-commands directly into the session
|
|
117
|
-
(e.g. "list skills", "agent overview", "compact"). See SELF-COMMANDS below.
|
|
118
|
-
|
|
119
136
|
Type "help" inside a session to see this same text.
|
|
120
137
|
|
|
138
|
+
Usage: shmakk [--flag ...]
|
|
139
|
+
shmakk --help [category]
|
|
140
|
+
|
|
141
|
+
Categories (shmakk --help <name> for details):
|
|
142
|
+
|
|
143
|
+
launch Startup modes, profiles, tuning flags
|
|
144
|
+
session Status, stats, restart, exit, control signals
|
|
145
|
+
skills Skill discovery, loading, listing, management
|
|
146
|
+
models Provider configuration, endpoint presets
|
|
147
|
+
voice Speech-to-Text / Text-to-Speech options
|
|
148
|
+
env Environment variable reference
|
|
149
|
+
mcp MCP servers and browser automation
|
|
150
|
+
ssh Remote host execution
|
|
151
|
+
self Natural-language self-commands (inside a session)
|
|
152
|
+
|
|
153
|
+
`;
|
|
154
|
+
|
|
155
|
+
const HELP_SECTIONS = {};
|
|
156
|
+
|
|
157
|
+
HELP_SECTIONS.launch = `═══════════════════════════════════════════════════════════════════════════
|
|
158
|
+
LAUNCH OPTIONS (shmakk --flag from outside a session)
|
|
121
159
|
═══════════════════════════════════════════════════════════════════════════
|
|
122
|
-
|
|
123
|
-
|
|
160
|
+
|
|
161
|
+
These flags only apply when starting a new shmakk session. They are
|
|
162
|
+
ignored if you are already inside shmakk (SHMAKK=1).
|
|
124
163
|
|
|
125
164
|
shmakk Launch in auto mode (AI acts on failures)
|
|
126
165
|
shmakk --review Launch in review mode (confirm every AI action)
|
|
127
166
|
shmakk --yes-files Auto-accept file writes, edits, mkdir
|
|
128
167
|
|
|
129
|
-
shmakk --help Show this
|
|
168
|
+
shmakk --help Show overview (this text)
|
|
169
|
+
shmakk --help <category> Show detailed help for a category
|
|
130
170
|
shmakk --build-history [files] Parse shell history for better corrections
|
|
131
171
|
shmakk --update-command-glossary Scan PATH and build local command glossary
|
|
132
172
|
|
|
173
|
+
--shell <fish|bash|zsh> Use a specific shell (default: current $SHELL)
|
|
133
174
|
--no-ai Disable AI entirely (pure passthrough)
|
|
134
175
|
--no-correction Disable command correction
|
|
135
176
|
--debug Verbose logging to stderr
|
|
@@ -140,8 +181,9 @@ const HELP = `shmakk - AI-supervised terminal wrapper
|
|
|
140
181
|
--colors <true|false> Enable or disable ANSI colors
|
|
141
182
|
--markdown <true|false> Enable or disable markdown rendering
|
|
142
183
|
--notify Desktop notifications for Y/n prompts
|
|
184
|
+
`;
|
|
143
185
|
|
|
144
|
-
|
|
186
|
+
HELP_SECTIONS.models = `═══════════════════════════════════════════════════════════════════════════
|
|
145
187
|
MODEL PROVIDERS
|
|
146
188
|
═══════════════════════════════════════════════════════════════════════════
|
|
147
189
|
|
|
@@ -159,8 +201,9 @@ const HELP = `shmakk - AI-supervised terminal wrapper
|
|
|
159
201
|
"model":"qwen/qwen3.5-9b" }
|
|
160
202
|
}
|
|
161
203
|
}
|
|
204
|
+
`;
|
|
162
205
|
|
|
163
|
-
|
|
206
|
+
HELP_SECTIONS.session = `═══════════════════════════════════════════════════════════════════════════
|
|
164
207
|
SESSION CONTROL (shmakk --flag from another terminal)
|
|
165
208
|
═══════════════════════════════════════════════════════════════════════════
|
|
166
209
|
|
|
@@ -176,6 +219,11 @@ const HELP = `shmakk - AI-supervised terminal wrapper
|
|
|
176
219
|
shmakk --exit Cleanly exit the parent shmakk
|
|
177
220
|
|
|
178
221
|
shmakk --profile-set <name> Switch profile and restart
|
|
222
|
+
`;
|
|
223
|
+
|
|
224
|
+
HELP_SECTIONS.skills = `═══════════════════════════════════════════════════════════════════════════
|
|
225
|
+
SKILLS
|
|
226
|
+
═══════════════════════════════════════════════════════════════════════════
|
|
179
227
|
|
|
180
228
|
shmakk --load-skill <name> Load a skill into workspace state
|
|
181
229
|
shmakk --install-skill <url> Download skill markdown from URL, validate, load
|
|
@@ -183,71 +231,9 @@ const HELP = `shmakk - AI-supervised terminal wrapper
|
|
|
183
231
|
shmakk --list-skills List all registered skills (workspace + global)
|
|
184
232
|
shmakk --skill-status Active skill and registry status
|
|
185
233
|
shmakk --unload-skill <name> Remove skill from whichever registry has it
|
|
234
|
+
`;
|
|
186
235
|
|
|
187
|
-
|
|
188
|
-
SELF-COMMANDS (type inside an shmakk session)
|
|
189
|
-
═══════════════════════════════════════════════════════════════════════════
|
|
190
|
-
|
|
191
|
-
── Skills ──
|
|
192
|
-
list skills List all registered skills
|
|
193
|
-
list skills <category> List skills in a specific category
|
|
194
|
-
list skill categories Show available skill categories
|
|
195
|
-
find skills <query> Search skills by name/description
|
|
196
|
-
load skill <name> Load a skill into the active workspace
|
|
197
|
-
unload skill <name> Remove a skill from its registry
|
|
198
|
-
skill status Show active skill and registry state
|
|
199
|
-
|
|
200
|
-
── Agents & Team ──
|
|
201
|
-
agent overview Show all agents and their specialisms
|
|
202
|
-
agent skills List all agent skills
|
|
203
|
-
agent <name> Show detail for a specific agent
|
|
204
|
-
list agents Alias for agent overview
|
|
205
|
-
|
|
206
|
-
── Context & Session ──
|
|
207
|
-
status Show session status
|
|
208
|
-
stats Show session/task statistics
|
|
209
|
-
resume status Show task journal for resume continuity
|
|
210
|
-
show plan Display current plan and progress
|
|
211
|
-
compact Clear conversation + task journal
|
|
212
|
-
reset Clear AI conversation history
|
|
213
|
-
|
|
214
|
-
── Memory & Search ──
|
|
215
|
-
recall <query> Search past sessions by content
|
|
216
|
-
find session <query> Find a session by topic
|
|
217
|
-
last sessions Show recent sessions
|
|
218
|
-
search db status Display session search DB info
|
|
219
|
-
show memory List stored memories
|
|
220
|
-
forget <query> Remove matching memories
|
|
221
|
-
|
|
222
|
-
── Configuration ──
|
|
223
|
-
show config Print resolved configuration
|
|
224
|
-
mcp status Show MCP servers and tools
|
|
225
|
-
show rules Display active workspace rules
|
|
226
|
-
list endpoints List configured model endpoints
|
|
227
|
-
use endpoint <name> Switch to a named model endpoint
|
|
228
|
-
set model to <name> Change the active model
|
|
229
|
-
set url to <url> Change the base URL
|
|
230
|
-
set api key to <key> Change the API key
|
|
231
|
-
|
|
232
|
-
── Toggles ──
|
|
233
|
-
enable review | disable review
|
|
234
|
-
enable correction | disable correction
|
|
235
|
-
enable yes-files | disable yes-files
|
|
236
|
-
enable colors | disable colors
|
|
237
|
-
enable debug | disable debug
|
|
238
|
-
|
|
239
|
-
── Workflows ──
|
|
240
|
-
list workflows Show available automation workflows
|
|
241
|
-
run workflow <name> Execute a named workflow
|
|
242
|
-
|
|
243
|
-
── Edits ──
|
|
244
|
-
review edits Step through pending file changes
|
|
245
|
-
|
|
246
|
-
── Meta ──
|
|
247
|
-
sidebar <query> Out-of-band agent query (not added to history)
|
|
248
|
-
help Show this help
|
|
249
|
-
|
|
250
|
-
═══════════════════════════════════════════════════════════════════════════
|
|
236
|
+
HELP_SECTIONS.voice = `═══════════════════════════════════════════════════════════════════════════
|
|
251
237
|
VOICE (Speech-to-Text / Text-to-Speech)
|
|
252
238
|
═══════════════════════════════════════════════════════════════════════════
|
|
253
239
|
|
|
@@ -266,8 +252,9 @@ const HELP = `shmakk - AI-supervised terminal wrapper
|
|
|
266
252
|
STT: Whisper-base ONNX in-process. No Python, no server, no API key.
|
|
267
253
|
TTS: kokoro-js (Kokoro-82M ONNX, ~334MB fp16). Auto-download on first use.
|
|
268
254
|
Requires aplay, paplay, or afplay for audio. 28 voices, rotated daily.
|
|
255
|
+
`;
|
|
269
256
|
|
|
270
|
-
|
|
257
|
+
HELP_SECTIONS.env = `═══════════════════════════════════════════════════════════════════════════
|
|
271
258
|
ENVIRONMENT
|
|
272
259
|
═══════════════════════════════════════════════════════════════════════════
|
|
273
260
|
|
|
@@ -287,8 +274,9 @@ const HELP = `shmakk - AI-supervised terminal wrapper
|
|
|
287
274
|
SHMAKK_VOICE_SILENCE_SEC VAD silence threshold seconds
|
|
288
275
|
SHMAKK_VOICE_SILENCE_THRESHOLD VAD amplitude threshold
|
|
289
276
|
SHMAKK_VOICE_PAD_START_SEC Start-of-recording padding
|
|
277
|
+
`;
|
|
290
278
|
|
|
291
|
-
|
|
279
|
+
HELP_SECTIONS.mcp = `═══════════════════════════════════════════════════════════════════════════
|
|
292
280
|
MCP & BROWSER
|
|
293
281
|
═══════════════════════════════════════════════════════════════════════════
|
|
294
282
|
|
|
@@ -299,7 +287,134 @@ const HELP = `shmakk - AI-supervised terminal wrapper
|
|
|
299
287
|
npm install playwright && npx playwright install chromium
|
|
300
288
|
Tools: navigate, click, type, read_page, screenshot, evaluate, select,
|
|
301
289
|
wait, scroll, close.
|
|
290
|
+
`;
|
|
291
|
+
|
|
292
|
+
HELP_SECTIONS.ssh = `═══════════════════════════════════════════════════════════════════════════
|
|
293
|
+
REMOTE HOSTS (SSH)
|
|
294
|
+
═══════════════════════════════════════════════════════════════════════════
|
|
295
|
+
|
|
296
|
+
The agent can run commands on remote hosts and transfer files via SSH.
|
|
297
|
+
Configure hosts in .shmakk/hosts.json or ~/.config/shmakk/hosts.json:
|
|
298
|
+
|
|
299
|
+
{
|
|
300
|
+
"hosts": {
|
|
301
|
+
"devbox": {
|
|
302
|
+
"host": "user@192.168.1.100",
|
|
303
|
+
"port": 22,
|
|
304
|
+
"auto_approve": false,
|
|
305
|
+
"timeout_sec": 30
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
"allow_ssh_config": false,
|
|
309
|
+
"default_timeout_sec": 30
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
Agent tools: ssh_run (run command), ssh_push (upload), ssh_pull (download).
|
|
313
|
+
For persistent connections, use ControlMaster in ~/.ssh/config:
|
|
314
|
+
Host *
|
|
315
|
+
ControlMaster auto
|
|
316
|
+
ControlPath ~/.ssh/controlmasters/%r@%h:%p
|
|
317
|
+
ControlPersist 600
|
|
318
|
+
`;
|
|
319
|
+
|
|
320
|
+
HELP_SECTIONS.self = `═══════════════════════════════════════════════════════════════════════════
|
|
321
|
+
SELF-COMMANDS (type inside an shmakk session)
|
|
322
|
+
═══════════════════════════════════════════════════════════════════════════
|
|
323
|
+
|
|
324
|
+
Self-commands work with a prefix — /cmd or shmakk cmd.
|
|
325
|
+
Bare words like "status" or "stats" go to the shell, not shmakk.
|
|
326
|
+
|
|
327
|
+
-- Session --
|
|
328
|
+
/status | shmakk status Show session status
|
|
329
|
+
/stats | shmakk stats Show session/task statistics
|
|
330
|
+
/sessions | shmakk sessions Show recent sessions
|
|
331
|
+
show sessions | last sessions (same as /sessions)
|
|
332
|
+
resume status Show task journal for resume continuity
|
|
333
|
+
show plan Display current plan and progress
|
|
334
|
+
/compact | shmakk compact Clear conversation + task journal
|
|
335
|
+
/reset | shmakk reset Clear AI conversation history
|
|
336
|
+
|
|
337
|
+
-- Skills --
|
|
338
|
+
list skills List all registered skills
|
|
339
|
+
list skills <category> List skills in a specific category
|
|
340
|
+
list skill categories Show available skill categories
|
|
341
|
+
find skills <query> Search skills by name/description
|
|
342
|
+
load skill <name> Load a skill into the active workspace
|
|
343
|
+
unload skill <name> Remove a skill from its registry
|
|
344
|
+
skill status Show active skill and registry state
|
|
345
|
+
|
|
346
|
+
-- Agents & Team --
|
|
347
|
+
agent overview Show all agents and their specialisms
|
|
348
|
+
agent skills List all agent skills
|
|
349
|
+
agent <name> Show detail for a specific agent
|
|
350
|
+
list agents Alias for agent overview
|
|
351
|
+
|
|
352
|
+
-- Memory & Search --
|
|
353
|
+
recall <query> Search past sessions by content
|
|
354
|
+
find session <query> Find a session by topic
|
|
355
|
+
search db status Display session search DB info
|
|
356
|
+
show memory List stored memories
|
|
357
|
+
forget <query> Remove matching memories
|
|
358
|
+
|
|
359
|
+
-- Configuration --
|
|
360
|
+
show config Print resolved configuration
|
|
361
|
+
mcp status Show MCP servers and tools
|
|
362
|
+
show rules Display active workspace rules
|
|
363
|
+
list endpoints List configured model endpoints
|
|
364
|
+
use endpoint <name> Switch to a named model endpoint
|
|
365
|
+
set model to <name> Change the active model
|
|
366
|
+
set url to <url> Change the base URL
|
|
367
|
+
set api key to <key> Change the API key
|
|
368
|
+
|
|
369
|
+
-- Toggles --
|
|
370
|
+
enable review | disable review
|
|
371
|
+
enable correction | disable correction
|
|
372
|
+
enable yes-files | disable yes-files
|
|
373
|
+
enable colors | disable colors
|
|
374
|
+
enable debug | disable debug
|
|
375
|
+
|
|
376
|
+
-- Workflows --
|
|
377
|
+
list workflows Show available automation workflows
|
|
378
|
+
run workflow <name> Execute a named workflow
|
|
379
|
+
|
|
380
|
+
-- Edits --
|
|
381
|
+
review edits Step through pending file changes
|
|
382
|
+
|
|
383
|
+
-- Meta --
|
|
384
|
+
sidebar <query> Out-of-band agent query (not added to history)
|
|
385
|
+
help Show this help
|
|
386
|
+
`;
|
|
387
|
+
|
|
388
|
+
// Resolve: returns the full old HELP string for backward compat, or the category
|
|
389
|
+
// text, or the summary.
|
|
390
|
+
function resolveHelp(category) {
|
|
391
|
+
if (category) {
|
|
392
|
+
const key = category.toLowerCase();
|
|
393
|
+
if (HELP_SECTIONS[key]) return HELP_SECTIONS[key];
|
|
394
|
+
// Unknown category: show summary + available categories
|
|
395
|
+
const available = Object.keys(HELP_SECTIONS).join(', ');
|
|
396
|
+
return HELP_SUMMARY + `Unknown category "${category}". Available: ${available}\n`;
|
|
397
|
+
}
|
|
398
|
+
return HELP_SUMMARY;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Help text shown INSIDE a session (no launch flags — you're already running)
|
|
402
|
+
const HELP_SESSION_SUMMARY = `shmakk — inside a session
|
|
403
|
+
|
|
404
|
+
Type commands as usual. shmakk watches the shell, catches failures, and
|
|
405
|
+
calls an LLM to fix them, plan tasks, and edit files.
|
|
406
|
+
|
|
407
|
+
Self-commands use a prefix: /cmd or shmakk cmd
|
|
408
|
+
Examples: /status /sessions shmakk status shmakk show sessions
|
|
409
|
+
|
|
410
|
+
Multi-word natural language also works: "show help" "list skills"
|
|
411
|
+
|
|
412
|
+
For the full reference: shmakk --help self (from outside the session)
|
|
413
|
+
For launch flags: shmakk --help launch
|
|
302
414
|
|
|
303
415
|
`;
|
|
304
416
|
|
|
305
|
-
|
|
417
|
+
// Full legacy HELP for backward compat
|
|
418
|
+
const HELP = HELP_SUMMARY + Object.values(HELP_SECTIONS).join('\n');
|
|
419
|
+
|
|
420
|
+
module.exports = { parseArgs, HELP, resolveHelp, HELP_SUMMARY, HELP_SESSION_SUMMARY };
|
package/src/correction.js
CHANGED
|
@@ -115,6 +115,12 @@ function preserveCase(original, corrected) {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
async function correct({ input, glossary, signal: _unused }) {
|
|
118
|
+
// /-prefixed and "shmakk ..." commands are shmakk self-commands.
|
|
119
|
+
// They should never reach the correction engine — bail out immediately.
|
|
120
|
+
if (/^\//.test(input) || /^shmakk\s/i.test(input)) {
|
|
121
|
+
return { category: 'not_a_correction', proposed: null, safety: 'uncertain', reason: 'shmakk self-command prefix — not a shell command' };
|
|
122
|
+
}
|
|
123
|
+
|
|
118
124
|
// Pre-filter natural language
|
|
119
125
|
if (looksLikeNaturalLanguage(input)) {
|
|
120
126
|
return { category: 'not_a_correction', proposed: null, safety: 'uncertain', reason: 'looks like natural language' };
|
package/src/endpoints.js
CHANGED
|
@@ -73,6 +73,7 @@ function normalizeModelConfig(name, cfg) {
|
|
|
73
73
|
headers: cfg.headers || cfg.headears || null,
|
|
74
74
|
registry: cfg.registry || null,
|
|
75
75
|
main: !!cfg.main,
|
|
76
|
+
vision: !!cfg.vision,
|
|
76
77
|
};
|
|
77
78
|
}
|
|
78
79
|
|
|
@@ -143,6 +144,10 @@ function getCurrentEndpointName() {
|
|
|
143
144
|
return currentEndpointName;
|
|
144
145
|
}
|
|
145
146
|
|
|
147
|
+
function supportsVision() {
|
|
148
|
+
return !!(currentEndpointConfig && currentEndpointConfig.vision);
|
|
149
|
+
}
|
|
150
|
+
|
|
146
151
|
function listEndpoints(cwd) {
|
|
147
152
|
return Object.keys(loadModelRegistry(cwd).models);
|
|
148
153
|
}
|
|
@@ -160,5 +165,6 @@ module.exports = {
|
|
|
160
165
|
listEndpoints,
|
|
161
166
|
getCurrentEndpoint,
|
|
162
167
|
getCurrentEndpointName,
|
|
168
|
+
supportsVision,
|
|
163
169
|
getModelRegistry,
|
|
164
170
|
};
|