shmakk 1.2.4 → 1.2.5

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.
Files changed (51) hide show
  1. package/.env.example +11 -0
  2. package/README.md +75 -1
  3. package/docs/index.html +154 -16
  4. package/docs/mcp.md +78 -0
  5. package/docs/ssh.md +82 -0
  6. package/docs/vibedit-analysis.md +375 -0
  7. package/docs/vim.md +110 -0
  8. package/docs/voice.md +4 -0
  9. package/package.json +9 -5
  10. package/scripts/test-vibedit.js +45 -0
  11. package/scripts/vibedit-demo.sh +52 -0
  12. package/skills/shmakk-skill-creator.md +269 -0
  13. package/src/_check.js +7 -0
  14. package/src/_check_schema.js +5 -0
  15. package/src/_cleanup.js +18 -0
  16. package/src/_fix.js +9 -0
  17. package/src/_test_import.js +15 -0
  18. package/src/agent.js +11 -4
  19. package/src/browser-daemon.js +209 -0
  20. package/src/browser.js +10 -0
  21. package/src/cli/browserDaemon.js +60 -0
  22. package/src/cli/connectBrowser.js +137 -0
  23. package/src/cli.js +235 -8
  24. package/src/completions.js +8 -0
  25. package/src/control.js +273 -1
  26. package/src/core/browserConnector.js +523 -0
  27. package/src/electron.js +305 -0
  28. package/src/endpoints.js +74 -9
  29. package/src/index.js +24 -1
  30. package/src/llm.js +501 -61
  31. package/src/mobile.js +307 -0
  32. package/src/notify.js +51 -3
  33. package/src/orchestrator.js +35 -1
  34. package/src/pty.js +11 -6
  35. package/src/review.js +45 -11
  36. package/src/self-commands.js +153 -0
  37. package/src/session-convert.js +508 -0
  38. package/src/session-search.js +31 -0
  39. package/src/session.js +384 -46
  40. package/src/skills/browserActions.ts +984 -0
  41. package/src/skills.js +451 -24
  42. package/src/system-prompt.js +31 -25
  43. package/src/tools.js +81 -0
  44. package/src/vibedit/control.js +534 -0
  45. package/src/vibedit/electron.js +108 -0
  46. package/src/vibedit/files.js +171 -0
  47. package/src/vibedit/index.js +298 -0
  48. package/src/vibedit/overlay.js +1482 -0
  49. package/src/vibedit/prompts.js +245 -0
  50. package/src/vibedit/state.js +32 -0
  51. package/src/vim.js +410 -0
@@ -0,0 +1,137 @@
1
+ // CLI entry point for `shmakk connect-browser`.
2
+ // Connects to a running Chrome instance via CDP, enabling the agent
3
+ // to interact with the user's own authenticated browser sessions.
4
+ //
5
+ // Usage:
6
+ // shmakk connect-browser # auto-detect CDP port
7
+ // shmakk connect-browser --port 9222 # connect to specific port
8
+ // shmakk connect-browser --disconnect # disconnect
9
+ // shmakk connect-browser --status # show connection status
10
+
11
+ const path = require('path');
12
+ const fs = require('fs');
13
+
14
+ function resolveConnector() {
15
+ try {
16
+ return require('../core/browserConnector');
17
+ } catch (e) {
18
+ process.stderr.write(
19
+ '[shmakk] Browser connector unavailable.\n' +
20
+ '[shmakk] Install playwright: npm install playwright\n' +
21
+ `[shmakk] Details: ${e.message}\n`,
22
+ );
23
+ process.exit(1);
24
+ }
25
+ }
26
+
27
+ function parseArgs(argv) {
28
+ const args = {
29
+ port: null,
30
+ disconnect: false,
31
+ status: false,
32
+ help: false,
33
+ };
34
+
35
+ let i = 0;
36
+ while (i < argv.length) {
37
+ const a = argv[i];
38
+ if (a === '--port' || a === '-p') {
39
+ args.port = parseInt(argv[++i], 10);
40
+ if (isNaN(args.port) || args.port < 1 || args.port > 65535) {
41
+ process.stderr.write(`[shmakk] invalid port: ${argv[i]}\n`);
42
+ process.exit(2);
43
+ }
44
+ } else if (a === '--disconnect' || a === '-d') {
45
+ args.disconnect = true;
46
+ } else if (a === '--status' || a === '-s') {
47
+ args.status = true;
48
+ } else if (a === '--help' || a === '-h') {
49
+ args.help = true;
50
+ } else if (a === 'connect-browser') {
51
+ // skip the subcommand name itself
52
+ } else {
53
+ process.stderr.write(`[shmakk] connect-browser: unknown option: ${a}\n`);
54
+ args.help = true;
55
+ }
56
+ i++;
57
+ }
58
+
59
+ return args;
60
+ }
61
+
62
+ const HELP = `shmakk connect-browser — connect to a running Chrome instance via CDP
63
+
64
+ Usage:
65
+ shmakk connect-browser [options]
66
+
67
+ Options:
68
+ --port, -p <port> Connect to Chrome on a specific CDP port (default: auto-detect)
69
+ --disconnect, -d Disconnect from Chrome
70
+ --status, -s Show connection status
71
+ --help, -h Show this help
72
+
73
+ Before running this command, start Chrome with remote debugging enabled:
74
+ google-chrome-stable --remote-debugging-port=9222
75
+
76
+ When connected, the agent's browser tool will interact with your
77
+ Chrome instance, preserving logins, cookies, and extensions.
78
+ `;
79
+
80
+ async function main() {
81
+ const rawArgs = process.argv.slice(2);
82
+
83
+ // If no subcommand-like arg, default to connect.
84
+ const args = parseArgs(rawArgs);
85
+
86
+ if (args.help) {
87
+ process.stdout.write(HELP);
88
+ process.exit(0);
89
+ }
90
+
91
+ const bc = resolveConnector();
92
+
93
+ if (!bc.isAvailable()) {
94
+ process.stderr.write(
95
+ '[shmakk] playwright is required for browser CDP connection.\n' +
96
+ '[shmakk] Run: npm install playwright && npx playwright install chromium\n',
97
+ );
98
+ process.exit(1);
99
+ }
100
+
101
+ if (args.disconnect) {
102
+ const result = await bc.disconnect();
103
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
104
+ process.exit(0);
105
+ }
106
+
107
+ if (args.status) {
108
+ const result = await bc.getStatus();
109
+ process.stdout.write(JSON.stringify(result, null, 2) + '\n');
110
+ process.exit(0);
111
+ }
112
+
113
+ // Default: connect
114
+ const connectArgs = {};
115
+ if (args.port) connectArgs.port = args.port;
116
+
117
+ process.stdout.write('[shmakk] connecting to Chrome via CDP...\n');
118
+
119
+ const result = await bc.connect(connectArgs);
120
+
121
+ if (result.ok) {
122
+ process.stdout.write(
123
+ `[shmakk] connected to Chrome (port ${result.port})\n` +
124
+ `[shmakk] current page: ${result.url || '(about:blank)'}\n` +
125
+ `[shmakk] title: ${result.title || '(none)'}\n`,
126
+ );
127
+ process.exit(0);
128
+ } else {
129
+ process.stderr.write(`[shmakk] connection failed: ${result.error}\n`);
130
+ process.exit(1);
131
+ }
132
+ }
133
+
134
+ main().catch((err) => {
135
+ process.stderr.write(`[shmakk] connect-browser fatal: ${err && err.stack || err}\n`);
136
+ process.exit(1);
137
+ });
package/src/cli.js CHANGED
@@ -21,7 +21,9 @@ function parseArgs(argv) {
21
21
  globalSkills: false,
22
22
  resumeStatus: false,
23
23
  showPlan: false,
24
+ newSession: false,
24
25
  mcpStatus: false,
26
+ consolidateWorkspace: false,
25
27
  exitNow: false,
26
28
  restart: false,
27
29
  profile: null,
@@ -30,6 +32,10 @@ function parseArgs(argv) {
30
32
  markdown: null,
31
33
  endpoint: null,
32
34
  modelRecommendation: false,
35
+ vim: 'vi',
36
+ vimEditor: null,
37
+ vimReal: null,
38
+ vimAi: null,
33
39
  voice: false,
34
40
  stt: false,
35
41
  tts: false,
@@ -45,12 +51,35 @@ function parseArgs(argv) {
45
51
  completion: null,
46
52
  helpCategory: null,
47
53
  shell: null,
54
+ browserDaemon: false,
55
+ browserDaemonPort: null,
56
+ // connect-browser subcommand
57
+ connectBrowser: false,
58
+ connectBrowserPort: null,
59
+ connectBrowserDisconnect: false,
60
+ connectBrowserStatus: false,
48
61
  unknown: [],
49
62
  };
63
+ const setVoiceCliMode = (mode) => {
64
+ if (mode === 'stt') {
65
+ opts.stt = true;
66
+ opts.voice = true;
67
+ } else if (mode === 'tts') {
68
+ opts.tts = true;
69
+ } else if (mode === 'sts') {
70
+ opts.sts = true;
71
+ opts.voice = true;
72
+ opts.tts = true;
73
+ }
74
+ };
50
75
 
51
76
  for (let i = 0; i < argv.length; i++) {
52
77
  const a = argv[i];
53
78
  switch (a) {
79
+ case '--':
80
+ opts.unknown.push(...argv.slice(i + 1));
81
+ i = argv.length;
82
+ break;
54
83
  case '--review': opts.review = true; break;
55
84
  case '--yes-files': opts.yesFiles = true; break;
56
85
  case '--update-command-glossary': opts.updateGlossary = true; break;
@@ -79,7 +108,9 @@ function parseArgs(argv) {
79
108
  case '--install-skill': opts.installSkill = argv[++i] || null; break;
80
109
  case '--resume-status': opts.resumeStatus = true; break;
81
110
  case '--show-plan': opts.showPlan = true; break;
111
+ case '--new-session': opts.newSession = true; break;
82
112
  case '--mcp-status': opts.mcpStatus = true; break;
113
+ case '--consolidate-workspace': opts.consolidateWorkspace = true; break;
83
114
  case '--exit': opts.exitNow = true; break;
84
115
  case '--restart': opts.restart = true; break;
85
116
  case '--reset': opts.reset = true; break;
@@ -93,10 +124,10 @@ function parseArgs(argv) {
93
124
  }
94
125
  if (!opts.buildHistory.length) opts.buildHistory = null; // flag with no files = auto-detect
95
126
  break;
96
- case '--stt': opts.stt = true; opts.voice = true; break;
97
- case '--tts': opts.tts = true; break;
98
- case '--sts': opts.sts = true; opts.stt = true; opts.tts = true; opts.voice = true; break;
99
- case '--voice': opts.stt = true; opts.voice = true; break;
127
+ case '--stt': setVoiceCliMode('stt'); break;
128
+ case '--tts': setVoiceCliMode('tts'); break;
129
+ case '--sts': setVoiceCliMode('sts'); break;
130
+ case '--voice': setVoiceCliMode('stt'); break;
100
131
  case '--voice-language': opts.voiceLanguage = argv[++i] || null; break;
101
132
  case '--voice-max-sec': opts.voiceMaxDuration = parseInt(argv[++i], 10) || null; break;
102
133
  case '--voice-silence-sec': opts.voiceSilenceSec = argv[++i] || null; break;
@@ -110,6 +141,19 @@ function parseArgs(argv) {
110
141
  case '--markdown': opts.markdown = argv[++i] || null; break;
111
142
  case '--endpoint': opts.endpoint = argv[++i] || null; break;
112
143
  case '--model-recommendation': opts.modelRecommendation = true; break;
144
+ case '--vim':
145
+ {
146
+ const v = (argv[++i] || 'vi').toLowerCase();
147
+ if (!['vi', 'vim', 'disable', 'enable'].includes(v)) {
148
+ process.stderr.write('[shmakk] invalid --vim. Use: vi|vim|disable\n');
149
+ process.exit(2);
150
+ }
151
+ opts.vim = v === 'enable' ? 'vim' : v;
152
+ }
153
+ break;
154
+ case '--vim-editor': opts.vimEditor = argv[++i] || null; break;
155
+ case '--vim-real': opts.vimReal = argv[++i] || null; break;
156
+ case '--vim-ai': opts.vimAi = argv[++i] || null; break;
113
157
  case '--shell':
114
158
  {
115
159
  const v = argv[++i];
@@ -120,6 +164,53 @@ function parseArgs(argv) {
120
164
  opts.shell = v;
121
165
  }
122
166
  break;
167
+ case 'connect-browser':
168
+ opts.connectBrowser = true;
169
+ // Consume subsequent flags that belong to connect-browser
170
+ while (i + 1 < argv.length) {
171
+ const n = argv[i + 1];
172
+ if (n === '--port' || n === '-p') {
173
+ i++;
174
+ opts.connectBrowserPort = parseInt(argv[++i], 10);
175
+ if (isNaN(opts.connectBrowserPort)) {
176
+ process.stderr.write(`[shmakk] connect-browser: invalid port: ${argv[i]}\n`);
177
+ process.exit(2);
178
+ }
179
+ } else if (n === '--disconnect' || n === '-d') {
180
+ i++;
181
+ opts.connectBrowserDisconnect = true;
182
+ } else if (n === '--status' || n === '-s') {
183
+ i++;
184
+ opts.connectBrowserStatus = true;
185
+ } else if (n === '--help' || n === '-h') {
186
+ i++;
187
+ opts.help = true;
188
+ opts.helpCategory = 'mcp';
189
+ } else {
190
+ break; // unknown or next subcommand
191
+ }
192
+ }
193
+ break;
194
+ case 'browser-daemon':
195
+ opts.browserDaemon = true;
196
+ while (i + 1 < argv.length) {
197
+ const n = argv[i + 1];
198
+ if (n === '--port' || n === '-p') {
199
+ i++;
200
+ opts.browserDaemonPort = parseInt(argv[++i], 10);
201
+ if (isNaN(opts.browserDaemonPort)) {
202
+ process.stderr.write(`[shmakk] browser-daemon: invalid port: ${argv[i]}\n`);
203
+ process.exit(2);
204
+ }
205
+ } else if (n === '--help' || n === '-h') {
206
+ i++;
207
+ opts.help = true;
208
+ opts.helpCategory = 'mcp';
209
+ } else {
210
+ break;
211
+ }
212
+ }
213
+ break;
123
214
  default: opts.unknown.push(a);
124
215
  }
125
216
  }
@@ -144,10 +235,12 @@ Categories (shmakk --help <name> for details):
144
235
  session Status, stats, restart, exit, control signals
145
236
  skills Skill discovery, loading, listing, management
146
237
  models Provider configuration, endpoint presets
238
+ vim Vim/vi integration and AI suggestions
147
239
  voice Speech-to-Text / Text-to-Speech options
148
240
  env Environment variable reference
149
241
  mcp MCP servers and browser automation
150
242
  ssh Remote host execution
243
+ vibedit Visual editing overlay (web + Electron)
151
244
  self Natural-language self-commands (inside a session)
152
245
 
153
246
  `;
@@ -177,9 +270,11 @@ HELP_SECTIONS.launch = `══════════════════
177
270
  --print-config Print resolved configuration and exit
178
271
 
179
272
  --workspace <path> Override workspace root
273
+ --new-session Force a new session instead of resuming
180
274
  --profile <name> Startup profile: tiny|balanced|deep|builder|large-app
181
275
  --colors <true|false> Enable or disable ANSI colors
182
276
  --markdown <true|false> Enable or disable markdown rendering
277
+ --vim <vi|vim|disable> Intercept vi/vim inside shmakk (default: vi)
183
278
  --notify Desktop notifications for Y/n prompts
184
279
  `;
185
280
 
@@ -190,17 +285,62 @@ HELP_SECTIONS.models = `══════════════════
190
285
  --endpoint <name> Use model preset from ~/.config/shmakk/endpoints.json
191
286
  --model-recommendation Main model chooses best model per call
192
287
 
193
- Providers: openai-compatible | codex | anthropic | google
288
+ Providers: openai-compatible | codex | anthropic | google | nvidia
194
289
  Configure in ~/.config/shmakk/endpoints.json:
195
290
  {
196
291
  "main": "claude",
292
+ "fast": "flash",
197
293
  "models": {
198
294
  "claude": { "provider":"anthropic", "model":"claude-sonnet-4-5-...", "api_key":"..." },
295
+ "flash": { "provider":"google", "model":"gemini-flash", "api_key":"..." },
199
296
  "gpt5": { "provider":"codex", "model":"gpt-5-codex", "api_key":"..." },
297
+ "kimi": { "provider":"nvidia", "model":"moonshotai/kimi-k2.6", "api_key":"nvapi-..." },
200
298
  "local-qwen": { "provider":"openai-compatible", "base_url":"http://127.0.0.1:1234/v1",
201
299
  "model":"qwen/qwen3.5-9b" }
202
300
  }
203
301
  }
302
+
303
+ main is used for normal agent work. fast is used for low-latency paths such
304
+ as Vim suggestions. Override with SHMAKK_FAST_ENDPOINT or
305
+ SHMAKK_VIM_SUGGEST_ENDPOINT.
306
+ `;
307
+
308
+ HELP_SECTIONS.vim = `═══════════════════════════════════════════════════════════════════════════
309
+ VIM / VI
310
+ ═══════════════════════════════════════════════════════════════════════════
311
+
312
+ --vim <vi|vim|disable> Intercept vi/vim inside shmakk (default: vi)
313
+
314
+ When enabled, shmakk puts a temporary vi or vim shim at the front of PATH
315
+ inside the shmakk shell. The shim launches your real editor, loads your
316
+ normal vimrc/plugins/colors first, then sources a small shmakk plugin.
317
+
318
+ Commands inside Vim:
319
+ :G <prompt> Generate code at the cursor
320
+ :Tw <prompt> Write prose or documentation at the cursor
321
+ :Cmd <command> Run a shell command in a scratch buffer
322
+ :ShmakkSuggest Ask for a full-block suggestion
323
+ :ShmakkAccept Preview and accept pending auto-suggestion
324
+ :ShmakkPreview Preview pending auto-suggestion
325
+ :ShmakkDeny Clear pending auto-suggestion
326
+
327
+ Mappings:
328
+ <C-Space> Manual full-block suggestion with preview
329
+ <leader>sa Accept pending auto-suggestion
330
+ <leader>sp Preview pending auto-suggestion
331
+ <leader>sd Deny pending auto-suggestion
332
+
333
+ Lowercase :g is not overridden; it remains Vim's native :global command.
334
+ Use :G for shmakk generation. Native commands such as :%s/foo/bar/g still
335
+ work normally.
336
+
337
+ Optional auto-suggest in vimrc:
338
+ let g:shmakk_auto_suggest = 1
339
+ let g:shmakk_auto_suggest_delay_ms = 2000
340
+ let g:shmakk_auto_suggest_min_chars = 20
341
+
342
+ Suggestions prefer SHMAKK_VIM_SUGGEST_ENDPOINT, then SHMAKK_FAST_ENDPOINT,
343
+ then the endpoint registry's fast model, then the current model.
204
344
  `;
205
345
 
206
346
  HELP_SECTIONS.session = `═══════════════════════════════════════════════════════════════════════════
@@ -214,6 +354,7 @@ HELP_SECTIONS.session = `══════════════════
214
354
  shmakk --mcp-status MCP servers and their tools
215
355
 
216
356
  shmakk --compact Clear conversation + task journal
357
+ shmakk --consolidate-workspace Merge nested .shmakk dirs into root
217
358
  shmakk --reset Clear AI conversation history (keep session)
218
359
  shmakk --restart Restart the inner shell (keeps window)
219
360
  shmakk --exit Cleanly exit the parent shmakk
@@ -240,6 +381,7 @@ HELP_SECTIONS.voice = `═══════════════════
240
381
  --sts Speech-to-Speech: always-on mic + TTS
241
382
  --stt Speech-to-Text: mic input, text output
242
383
  --tts Text-to-Speech: text input, spoken output
384
+ These modes are exclusive; the last one wins.
243
385
 
244
386
  --voice-language <code> Language hint (e.g. en, es, fr)
245
387
  --voice-max-sec <sec> Max recording seconds (default: 30)
@@ -261,10 +403,17 @@ HELP_SECTIONS.env = `═══════════════════
261
403
  SHMAKK_BASE_URL OpenAI-compatible base URL
262
404
  SHMAKK_API_KEY API key
263
405
  SHMAKK_MODEL Default model
264
- SHMAKK_PROVIDER Provider: openai-compatible|codex|anthropic|google
406
+ SHMAKK_FAST_ENDPOINT Named endpoint for low-latency tasks
407
+ SHMAKK_VIM_SUGGEST_ENDPOINT Named endpoint for Vim suggestions
408
+ SHMAKK_PROVIDER Provider: openai-compatible|codex|anthropic|google|nvidia
265
409
  SHMAKK_HEADERS Extra headers: k=v,k=v
266
410
  SHMAKK_REGISTRY Model registry filter (comma-separated)
267
411
  SHMAKK_MODEL_RECOMMENDATION Set to 1 to let main model choose per call
412
+ SHMAKK_VIM_SHIM_DIR Internal PATH shim for enhanced vi/vim
413
+ SHMAKK_REAL_PATH Original PATH before shmakk vim shims
414
+ SHMAKK_VIM_SUGGEST_MAX_CHARS Max context chars sent for Vim suggestions
415
+ SHMAKK_VIM_SUGGEST_BEFORE_LINES Lines before cursor for Vim suggestions
416
+ SHMAKK_VIM_SUGGEST_AFTER_LINES Lines after cursor for Vim suggestions
268
417
 
269
418
  SHMAKK_HF_CACHE HuggingFace cache directory (voice models)
270
419
  SHMAKK_TTS_VOICE Pin a specific TTS voice
@@ -281,12 +430,37 @@ HELP_SECTIONS.mcp = `═══════════════════
281
430
  ═══════════════════════════════════════════════════════════════════════════
282
431
 
283
432
  MCP servers: configure in ~/.config/shmakk/mcp.json or .shmakk/mcp.json
284
- { "mcpServers": { "name": { "command": "...", "args": [...] } } }
433
+ {
434
+ "mcpServers": {
435
+ "name": {
436
+ "command": "...",
437
+ "args": ["..."],
438
+ "env": { "TOKEN": "\${TOKEN}" },
439
+ "safety": "uncertain",
440
+ "safeTools": ["read"],
441
+ "unsafeTools": ["delete"],
442
+ "timeout": 30000,
443
+ "disabled": false
444
+ }
445
+ }
446
+ }
285
447
 
286
- Browser automation: requires playwright
448
+ Agent browser tool: headless Playwright for non-interactive browsing.
287
449
  npm install playwright && npx playwright install chromium
288
450
  Tools: navigate, click, type, read_page, screenshot, evaluate, select,
289
451
  wait, scroll, close.
452
+
453
+ Extension automation for the current Chrome tab:
454
+ shmakk browser-daemon run global extension backend
455
+ shmakk browser-daemon --port 3947 use a custom extension port
456
+
457
+ CDP connection is for live debugging / Electron-style targets:
458
+ shmakk connect-browser auto-detect and connect
459
+ shmakk connect-browser --port 9222 connect to specific port
460
+ shmakk connect-browser --status show connection status
461
+ shmakk connect-browser --disconnect disconnect
462
+
463
+ shmakk --mcp-status Show configured servers and discovered tools
290
464
  `;
291
465
 
292
466
  HELP_SECTIONS.ssh = `═══════════════════════════════════════════════════════════════════════════
@@ -317,6 +491,50 @@ HELP_SECTIONS.ssh = `═══════════════════
317
491
  ControlPersist 600
318
492
  `;
319
493
 
494
+ HELP_SECTIONS['vibedit'] = `═══════════════════════════════════════════════════════════════════════════
495
+ VIBEDIT (web + Electron)
496
+ ═══════════════════════════════════════════════════════════════════════════
497
+
498
+ Vibedit is the visual editing overlay. It opens a Chromium browser tab,
499
+ injects a chat panel with a shadow-DOM overlay, captures screenshots for
500
+ vision-model analysis, and applies code changes directly to your project.
501
+
502
+ ── Web mode ──
503
+
504
+ Inside a session:
505
+ /vibedit <url | file | package.json | dir>
506
+
507
+ Opens a Playwright Chromium browser, injects the overlay, and auto-starts
508
+ a dev server if given a package.json or project directory. The overlay
509
+ appears as a puck in the bottom-right corner.
510
+
511
+ ── Electron mode ──
512
+
513
+ Inside a session:
514
+ /vibedit-electron <debug-port>
515
+ /ve <debug-port> (short alias)
516
+
517
+ Connects to an already-running Electron app via Chrome DevTools Protocol.
518
+ The app must be launched with --remote-debugging-port=<port>.
519
+ No new browser window is opened — the overlay is injected into the
520
+ existing Electron window.
521
+
522
+ ── Specs ──
523
+
524
+ Use the overlay's Save button to capture a spec (screenshot + description).
525
+ The spec is immediately sent to the agent for implementation. If the
526
+ agent is already busy, the spec is queued and applied on the next run.
527
+
528
+ ── Dependencies ──
529
+
530
+ Both modes require: npm install playwright && npx playwright install chromium
531
+ Electron mode also needs the target app's debug port accessible.
532
+
533
+ Project-local state: .shmakk/state/vibedit-*
534
+ Extension daemon state: ~/.config/shmakk/browser-daemon.json
535
+ Vibedit control port: auto-assigned per overlay session
536
+ `;
537
+
320
538
  HELP_SECTIONS.self = `═══════════════════════════════════════════════════════════════════════════
321
539
  SELF-COMMANDS (type inside an shmakk session)
322
540
  ═══════════════════════════════════════════════════════════════════════════
@@ -372,6 +590,15 @@ HELP_SECTIONS.self = `═══════════════════
372
590
  enable yes-files | disable yes-files
373
591
  enable colors | disable colors
374
592
  enable debug | disable debug
593
+ enable stt | disable stt Ctrl+O voice input; disables TTS/STS
594
+ enable tts | disable tts Spoken agent replies; disables STT/STS
595
+ enable sts | disable sts Always-on speech-to-speech; disables STT/TTS
596
+
597
+ -- Vibedit --
598
+ /vibedit <url | path> Launch visual editing overlay on a web app
599
+ /vibedit-electron <port> Connect overlay to an Electron app via CDP
600
+ /ve <port> Short alias for /vibedit-electron
601
+ shmakk browser-daemon Run Chrome extension automation backend
375
602
 
376
603
  -- Workflows --
377
604
  list workflows Show available automation workflows
@@ -41,6 +41,7 @@ const FLAGS = [
41
41
  { flag: '--profile-set', arg: '<name>', desc: 'Switch profile and restart' },
42
42
  { flag: '--endpoint', arg: '<name>', desc: 'Use model preset from ~/.config/shmakk/endpoints.json' },
43
43
  { flag: '--colors', arg: '<true|false>', desc: 'Toggle ANSI colors' },
44
+ { flag: '--vim', arg: '<vi|vim|disable>', desc: 'Intercept vi/vim with shmakk Vim mode' },
44
45
  { flag: '--load-skill', arg: '<name>', desc: 'Load a skill into workspace state' },
45
46
  { flag: '--unload-skill', arg: '<name>', desc: 'Remove skill from registry' },
46
47
  { flag: '--install-skill', arg: '<url>', desc: 'Download and install skill from URL' },
@@ -79,6 +80,9 @@ function bash() {
79
80
  } else if (f.flag === '--colors') {
80
81
  lines.push(' COMPREPLY=($(compgen -W "true false" -- "$cur"))');
81
82
  lines.push(' return');
83
+ } else if (f.flag === '--vim') {
84
+ lines.push(' COMPREPLY=($(compgen -W "vi vim disable" -- "$cur"))');
85
+ lines.push(' return');
82
86
  } else {
83
87
  lines.push(' COMPREPLY=()');
84
88
  lines.push(' return');
@@ -122,6 +126,8 @@ function zsh() {
122
126
  lines.push(` "${f.flag}[${f.desc}]:shell:(bash zsh fish)" \\`);
123
127
  } else if (f.flag === '--colors') {
124
128
  lines.push(` "${f.flag}[${f.desc}]:value:(true false)" \\`);
129
+ } else if (f.flag === '--vim') {
130
+ lines.push(` "${f.flag}[${f.desc}]:mode:(vi vim disable)" \\`);
125
131
  } else {
126
132
  lines.push(` "${f.flag}[${f.desc}]: :" \\`);
127
133
  }
@@ -148,6 +154,8 @@ function fish() {
148
154
  lines.push(`complete -c shmakk -l ${f.flag.slice(2)} -d '${f.desc}' -xa 'bash zsh fish'`);
149
155
  } else if (f.flag === '--colors') {
150
156
  lines.push(`complete -c shmakk -l ${f.flag.slice(2)} -d '${f.desc}' -xa 'true false'`);
157
+ } else if (f.flag === '--vim') {
158
+ lines.push(`complete -c shmakk -l ${f.flag.slice(2)} -d '${f.desc}' -xa 'vi vim disable'`);
151
159
  } else {
152
160
  // arg but no specific completions
153
161
  lines.push(`complete -c shmakk -l ${f.flag.slice(2)} -d '${f.desc}' -r`);