vimlm 0.0.6__py3-none-any.whl → 0.0.7__py3-none-any.whl
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.
- vimlm-0.0.7.dist-info/METADATA +204 -0
- vimlm-0.0.7.dist-info/RECORD +7 -0
- vimlm.py +92 -32
- vimlm-0.0.6.dist-info/METADATA +0 -158
- vimlm-0.0.6.dist-info/RECORD +0 -7
- {vimlm-0.0.6.dist-info → vimlm-0.0.7.dist-info}/LICENSE +0 -0
- {vimlm-0.0.6.dist-info → vimlm-0.0.7.dist-info}/WHEEL +0 -0
- {vimlm-0.0.6.dist-info → vimlm-0.0.7.dist-info}/entry_points.txt +0 -0
- {vimlm-0.0.6.dist-info → vimlm-0.0.7.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,204 @@
|
|
1
|
+
Metadata-Version: 2.2
|
2
|
+
Name: vimlm
|
3
|
+
Version: 0.0.7
|
4
|
+
Summary: VimLM - LLM-powered Vim assistant
|
5
|
+
Home-page: https://github.com/JosefAlbers/vimlm
|
6
|
+
Author: Josef Albers
|
7
|
+
Author-email: albersj66@gmail.com
|
8
|
+
Requires-Python: >=3.12.8
|
9
|
+
Description-Content-Type: text/markdown
|
10
|
+
License-File: LICENSE
|
11
|
+
Requires-Dist: nanollama==0.0.5
|
12
|
+
Requires-Dist: mlx_lm_utils==0.0.2
|
13
|
+
Requires-Dist: watchfiles==1.0.4
|
14
|
+
Dynamic: author
|
15
|
+
Dynamic: author-email
|
16
|
+
Dynamic: description
|
17
|
+
Dynamic: description-content-type
|
18
|
+
Dynamic: home-page
|
19
|
+
Dynamic: requires-dist
|
20
|
+
Dynamic: requires-python
|
21
|
+
Dynamic: summary
|
22
|
+
|
23
|
+
|
24
|
+
# VimLM - Local LLM-Powered Coding Assistant for Vim
|
25
|
+
|
26
|
+

|
27
|
+
|
28
|
+
LLM-powered coding companion for Vim, inspired by GitHub Copilot/Cursor. Integrates contextual code understanding, summarization, and AI assistance directly into your Vim workflow.
|
29
|
+
|
30
|
+
## Features
|
31
|
+
|
32
|
+
- **Model Agnostic** - Use any MLX-compatible model via a configuration file
|
33
|
+
- **Vim-Native UX** - Intuitive keybindings and split-window responses
|
34
|
+
- **Deep Context** - Understands code context from:
|
35
|
+
- Current file
|
36
|
+
- Visual selections
|
37
|
+
- Referenced files
|
38
|
+
- Project directory structure
|
39
|
+
- **Conversational Coding** - Iterative refinement with follow-up queries
|
40
|
+
- **Air-Gapped Security** - 100% offline - no APIs, no tracking, no data leaks
|
41
|
+
|
42
|
+
## Requirements
|
43
|
+
|
44
|
+
- Apple M-series chip
|
45
|
+
- Python 3.12.8
|
46
|
+
|
47
|
+
## Quick Start
|
48
|
+
|
49
|
+
```zsh
|
50
|
+
pip install vimlm
|
51
|
+
vimlm
|
52
|
+
```
|
53
|
+
|
54
|
+
## Basic Usage
|
55
|
+
|
56
|
+
### 1. **From Normal Mode**
|
57
|
+
**`Ctrl-l`**: Add current line + file to context
|
58
|
+
|
59
|
+
*Example prompt:* `"Regex for removing HTML tags from item.content"`
|
60
|
+
|
61
|
+
### 2. **From Visual Mode**
|
62
|
+
Select code → **`Ctrl-l`**: Add selected block + current file to context
|
63
|
+
|
64
|
+
*Example prompt:* `"Convert this to async/await syntax"`
|
65
|
+
|
66
|
+
### 3. **Follow-Up Conversations**
|
67
|
+
**`Ctrl-j`**: Continue current thread
|
68
|
+
|
69
|
+
*Example follow-up:* `"Use Manifest V3 instead"`
|
70
|
+
|
71
|
+
### 4. **Code Extraction & Replacement**
|
72
|
+
**`Ctrl-p`**: Insert code blocks from response into:
|
73
|
+
- Last visual selection (Normal mode)
|
74
|
+
- Active selection (Visual mode)
|
75
|
+
|
76
|
+
**Workflow Example**:
|
77
|
+
1. Select a block of code in Visual mode
|
78
|
+
2. Prompt with `Ctrl-l`: `"Convert this to async/await syntax"`
|
79
|
+
3. Press `Ctrl-p` to replace selection with generated code
|
80
|
+
|
81
|
+
### 5. **Inline Commands**
|
82
|
+
|
83
|
+
#### `!include` - Add External Context
|
84
|
+
```text
|
85
|
+
!include [PATH] # Add files/folders to context
|
86
|
+
```
|
87
|
+
- **`!include`** (no path): Current folder
|
88
|
+
- **`!include ~/projects/utils.py`**: Specific file
|
89
|
+
- **`!include ~/docs/api-specs/`**: Entire folder
|
90
|
+
|
91
|
+
*Example:* `"AJAX-ify this app !include ~/scrap/hypermedia-applications.summ.md"`
|
92
|
+
|
93
|
+
#### `!deploy` - Generate Project Files
|
94
|
+
```text
|
95
|
+
!deploy [DEST_DIR] # Extract code blocks to directory
|
96
|
+
```
|
97
|
+
- **`!deploy`** (no path): Current directory
|
98
|
+
- **`!deploy ./src`**: Specific directory
|
99
|
+
|
100
|
+
*Example:* `"Create REST API endpoint !deploy ./api"`
|
101
|
+
|
102
|
+
#### `!continue` - Resume Generation
|
103
|
+
```text
|
104
|
+
!continue [MAX_TOKENS] # Continue stopped response
|
105
|
+
```
|
106
|
+
- **`!continue`**: Default 2000 tokens
|
107
|
+
- **`!continue 3000`**: Custom token limit
|
108
|
+
|
109
|
+
*Example:* `"tl;dr !include large-file.txt !continue 5000"`
|
110
|
+
|
111
|
+
#### `!followup` - Thread Continuation
|
112
|
+
```text
|
113
|
+
!followup # Equivalent to Ctrl-j
|
114
|
+
```
|
115
|
+
*Example:*
|
116
|
+
|
117
|
+
Initial: `"Create Chrome extension"`
|
118
|
+
|
119
|
+
Follow-up: `"Add dark mode support !followup"`
|
120
|
+
|
121
|
+
#### **Command Combinations**
|
122
|
+
Chain multiple commands in one prompt:
|
123
|
+
```text
|
124
|
+
"Create HTMX component !include ~/lib/styles.css !deploy ./components !continue 4000"
|
125
|
+
```
|
126
|
+
|
127
|
+
### 6. **Command-Line Mode `:VimLM`**
|
128
|
+
```vim
|
129
|
+
:VimLM "prompt" [!command1] [!command2]...
|
130
|
+
```
|
131
|
+
Use predefined command chains for repetitive tasks:
|
132
|
+
|
133
|
+
**Example 1 – CI/CD Fixer Macro**:
|
134
|
+
```vim
|
135
|
+
" Debug CI failures using error logs
|
136
|
+
:VimLM Fix Dockerfile !include .gitlab-ci.yml !include $(tail -n 20 ci.log)
|
137
|
+
```
|
138
|
+
|
139
|
+
**Example 2 – Test Generation Workflow**:
|
140
|
+
```vim
|
141
|
+
" Generate unit tests for selected functions and save to test/
|
142
|
+
:VimLM Write pytest tests for this !include ./src !deploy ./test
|
143
|
+
```
|
144
|
+
|
145
|
+
**Example 3 – Documentation Helper**:
|
146
|
+
```vim
|
147
|
+
" Add docstrings to all Python functions in file
|
148
|
+
:VimLM Add Google-style docstrings !include % !continue 4000
|
149
|
+
```
|
150
|
+
|
151
|
+
### Key Bindings
|
152
|
+
|
153
|
+
| Binding | Mode | Action |
|
154
|
+
|------------|---------------|----------------------------------------|
|
155
|
+
| `Ctrl-l` | Normal/Visual | Send current file + selection to LLM |
|
156
|
+
| `Ctrl-j` | Normal | Continue conversation |
|
157
|
+
| `Ctrl-p` | Normal/Visual | Replace the selection with generated code |
|
158
|
+
| `Esc` | Prompt | Cancel input |
|
159
|
+
|
160
|
+
## Advanced Configuration
|
161
|
+
VimLM uses a JSON config file with the following configurable parameters:
|
162
|
+
```json
|
163
|
+
{
|
164
|
+
"LLM_MODEL": null,
|
165
|
+
"NUM_TOKEN": 2000,
|
166
|
+
"USE_LEADER": false,
|
167
|
+
"KEY_MAP": {},
|
168
|
+
"DO_RESET": true,
|
169
|
+
"SHOW_USER": false,
|
170
|
+
"SEP_CMD": "!",
|
171
|
+
"VERSION": "0.0.7",
|
172
|
+
"DEBUG": true
|
173
|
+
}
|
174
|
+
```
|
175
|
+
### Custom Model Setup
|
176
|
+
1. **Browse models**: [MLX Community Models on Hugging Face](https://huggingface.co/mlx-community)
|
177
|
+
2. **Edit config file**:
|
178
|
+
```json
|
179
|
+
{
|
180
|
+
"LLM_MODEL": "mlx-community/DeepSeek-R1-Distill-Qwen-7B-4bit",
|
181
|
+
"NUM_TOKEN": 9999
|
182
|
+
}
|
183
|
+
```
|
184
|
+
3. **Save to**: `~/vimlm/cfg.json`
|
185
|
+
4. **Restart VimLM**
|
186
|
+
|
187
|
+
### Custom Key Bindings
|
188
|
+
You can also configure shortcuts:
|
189
|
+
```json
|
190
|
+
{
|
191
|
+
"USE_LEADER": true, // Swap Ctrl for Leader key
|
192
|
+
"KEY_MAP": { // Remap default keys (l/j/p)
|
193
|
+
"l": "a", // <Leader>a instead of <Leader>l
|
194
|
+
"j": "s", // <Leader>s instead of <Leader>j
|
195
|
+
"p": "d" // <Leader>d instead of <Leader>p
|
196
|
+
}
|
197
|
+
}
|
198
|
+
```
|
199
|
+
|
200
|
+
## License
|
201
|
+
|
202
|
+
VimLM is licensed under the [Apache-2.0 license](LICENSE).
|
203
|
+
|
204
|
+
|
@@ -0,0 +1,7 @@
|
|
1
|
+
vimlm.py,sha256=QrboU-loWuQjTPO5HTJnt_CCxZcV6cclX6UOz438rEM,23126
|
2
|
+
vimlm-0.0.7.dist-info/LICENSE,sha256=f1xgK8fAXg_intwnbc9nLkHf7ODPLtgpHs7DetQHOro,11343
|
3
|
+
vimlm-0.0.7.dist-info/METADATA,sha256=0V2b8X4pq2uGlyuxyqCUlCYZ7tmlJhPOLZFaDjG0V6s,5668
|
4
|
+
vimlm-0.0.7.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
5
|
+
vimlm-0.0.7.dist-info/entry_points.txt,sha256=mU5V4MYsuIzCc6YB-Ro-6USSHWN5vHw8UDnTEoq0isw,36
|
6
|
+
vimlm-0.0.7.dist-info/top_level.txt,sha256=I8GjqoiP--scYsO3AfLhha-6Ax9ci3IvbWvVbPv8g94,6
|
7
|
+
vimlm-0.0.7.dist-info/RECORD,,
|
vimlm.py
CHANGED
@@ -26,13 +26,18 @@ from pathlib import Path
|
|
26
26
|
from string import Template
|
27
27
|
import re
|
28
28
|
|
29
|
-
|
30
|
-
LLM_MODEL = None # "mlx-community/DeepSeek-R1-Distill-Qwen-7B-4bit"
|
31
|
-
NUM_TOKEN = 2000
|
32
|
-
|
33
|
-
|
34
|
-
DO_RESET = True
|
35
|
-
SHOW_USER = False
|
29
|
+
DEFAULTS = dict(
|
30
|
+
LLM_MODEL = None, # "mlx-community/DeepSeek-R1-Distill-Qwen-7B-4bit"
|
31
|
+
NUM_TOKEN = 2000,
|
32
|
+
USE_LEADER = False,
|
33
|
+
KEY_MAP = {},
|
34
|
+
DO_RESET = True,
|
35
|
+
SHOW_USER = False,
|
36
|
+
SEP_CMD = '!',
|
37
|
+
VERSION = '0.0.7',
|
38
|
+
DEBUG = True,
|
39
|
+
)
|
40
|
+
|
36
41
|
VIMLM_DIR = os.path.expanduser("~/vimlm")
|
37
42
|
WATCH_DIR = os.path.expanduser("~/vimlm/watch_dir")
|
38
43
|
CFG_FILE = 'cfg.json'
|
@@ -45,6 +50,36 @@ LOG_PATH = os.path.join(VIMLM_DIR, LOG_FILE)
|
|
45
50
|
LTM_PATH = os.path.join(VIMLM_DIR, LTM_FILE)
|
46
51
|
OUT_PATH = os.path.join(WATCH_DIR, OUT_FILE)
|
47
52
|
|
53
|
+
def is_old(config):
|
54
|
+
v_str = config.get('VERSION', 0)
|
55
|
+
for min_v, usr_v in zip(DEFAULTS['VERSION'].split('.'), v_str.split('.')):
|
56
|
+
if int(min_v) < int(usr_v):
|
57
|
+
return False
|
58
|
+
elif int(min_v) > int(usr_v):
|
59
|
+
return True
|
60
|
+
return False
|
61
|
+
|
62
|
+
if os.path.exists(WATCH_DIR):
|
63
|
+
shutil.rmtree(WATCH_DIR)
|
64
|
+
os.makedirs(WATCH_DIR)
|
65
|
+
|
66
|
+
try:
|
67
|
+
with open(CFG_PATH, "r") as f:
|
68
|
+
config = json.load(f)
|
69
|
+
if is_old(config):
|
70
|
+
for p in [CFG_PATH, LOG_PATH, LTM_PATH]:
|
71
|
+
if os.path.isfile(p):
|
72
|
+
os.remove(p)
|
73
|
+
raise ValueError(f'Version mismatch')
|
74
|
+
except Exception as e:
|
75
|
+
print(e)
|
76
|
+
config = DEFAULTS
|
77
|
+
with open(CFG_PATH, 'w') as f:
|
78
|
+
json.dump(DEFAULTS, f, indent=2)
|
79
|
+
|
80
|
+
for k, v in DEFAULTS.items():
|
81
|
+
globals()[k] = config.get(k, v)
|
82
|
+
|
48
83
|
def toout(s, key=None, mode=None):
|
49
84
|
key = '' if key is None else ':'+key
|
50
85
|
mode = 'w' if mode is None else mode
|
@@ -64,23 +99,6 @@ def tolog(log, key='debug'):
|
|
64
99
|
with open(LOG_PATH, "w", encoding="utf-8") as log_f:
|
65
100
|
json.dump(logs, log_f, indent=2)
|
66
101
|
|
67
|
-
if os.path.exists(WATCH_DIR):
|
68
|
-
shutil.rmtree(WATCH_DIR)
|
69
|
-
os.makedirs(WATCH_DIR)
|
70
|
-
|
71
|
-
try:
|
72
|
-
with open(CFG_PATH, "r") as f:
|
73
|
-
config = json.load(f)
|
74
|
-
DEBUG = config.get("DEBUG", DEBUG)
|
75
|
-
LLM_MODEL = config.get("LLM_MODEL", LLM_MODEL)
|
76
|
-
NUM_TOKEN = config.get("NUM_TOKEN", NUM_TOKEN)
|
77
|
-
SEP_CMD = config.get("SEP_CMD", SEP_CMD)
|
78
|
-
USE_LEADER = config.get("USE_LEADER", USE_LEADER)
|
79
|
-
except Exception as e:
|
80
|
-
tolog(str(e))
|
81
|
-
with open(CFG_PATH, 'w') as f:
|
82
|
-
json.dump(dict(DEBUG=DEBUG, LLM_MODEL=LLM_MODEL, NUM_TOKEN=NUM_TOKEN, SEP_CMD=SEP_CMD, USE_LEADER=USE_LEADER), f, indent=2)
|
83
|
-
|
84
102
|
toout('Loading LLM...')
|
85
103
|
if LLM_MODEL is None:
|
86
104
|
from nanollama import Chat
|
@@ -326,13 +344,27 @@ def process_command(data):
|
|
326
344
|
data['include'] = ''
|
327
345
|
for cmd in cmds:
|
328
346
|
if cmd.startswith('include'):
|
329
|
-
arg = cmd.removeprefix('include').strip('(').strip(')').strip().strip('"').strip("'").strip()
|
347
|
+
arg = cmd.removeprefix('include').strip().strip('(').strip(')').strip().strip('"').strip("'").strip()
|
330
348
|
src = data['dir'] if len(arg) == 0 else arg
|
331
|
-
|
349
|
+
if arg == '%':
|
350
|
+
continue
|
351
|
+
if src.startswith('`') or src.startswith('$('):
|
352
|
+
shell_cmd = src.strip('`') if src.startswith('`') else src.strip('$()')
|
353
|
+
shell_cmd = shell_cmd.strip()
|
354
|
+
try:
|
355
|
+
result = subprocess.run(shell_cmd, shell=True, capture_output=True, text=True)
|
356
|
+
if result.returncode == 0:
|
357
|
+
data['include'] += f'--- **{shell_cmd}** ---\n```\n{result.stdout.strip()}\n```\n---\n\n'
|
358
|
+
else:
|
359
|
+
tolog(f'{shell_cmd} failed {result.stderr.strip()}')
|
360
|
+
except Exception as e:
|
361
|
+
tolog(f'Error executing {shell_cmd}: {e}')
|
362
|
+
else:
|
363
|
+
data['include'] += ingest(src)
|
332
364
|
|
333
365
|
for cmd in cmds:
|
334
366
|
if cmd.startswith('deploy'):
|
335
|
-
arg = cmd.removeprefix('deploy').strip('(').strip(')').strip().strip('"').strip("'").strip()
|
367
|
+
arg = cmd.removeprefix('deploy').strip().strip('(').strip(')').strip().strip('"').strip("'").strip()
|
336
368
|
if len(data['user_prompt']) == 0:
|
337
369
|
deploy(dest=arg)
|
338
370
|
data['user_prompt'] = ''
|
@@ -395,8 +427,10 @@ async def process_files(data):
|
|
395
427
|
if 'deploy_dest' in data:
|
396
428
|
deploy(dest=data['deploy_dest'], reformat=False)
|
397
429
|
|
398
|
-
|
399
|
-
|
430
|
+
KEYL = KEY_MAP.get('l', 'l')
|
431
|
+
KEYJ = KEY_MAP.get('j', 'j')
|
432
|
+
KEYP = KEY_MAP.get('p', 'p')
|
433
|
+
mapl, mapj, mapp = (f'<Leader>{KEYL}', f'<Leader>{KEYJ}', f'<Leader>{KEYP}') if USE_LEADER else (f'<C-{KEYL}>', f'<C-{KEYJ}>', f'<C-{KEYP}>')
|
400
434
|
VIMLMSCRIPT = Template(r"""
|
401
435
|
let s:register_names = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u']
|
402
436
|
let s:watched_dir = expand('$WATCH_DIR')
|
@@ -515,19 +549,26 @@ function! ExtractAllCodeBlocks()
|
|
515
549
|
return len(code_blocks)
|
516
550
|
endfunction
|
517
551
|
|
518
|
-
function! PasteIntoLastVisualSelection()
|
552
|
+
function! PasteIntoLastVisualSelection(...)
|
519
553
|
let num_blocks = ExtractAllCodeBlocks()
|
520
|
-
|
521
|
-
|
554
|
+
if a:0 > 0
|
555
|
+
let register_name = a:1
|
556
|
+
else
|
557
|
+
echo "Extracted " . num_blocks . " blocks into registers @a-@" . s:register_names[num_blocks - 1] . ". Enter register name: "
|
558
|
+
let register_name = nr2char(getchar())
|
559
|
+
endif
|
560
|
+
|
522
561
|
if register_name !~ '^[a-z]$'
|
523
562
|
echoerr "Invalid register name. Please enter a single lowercase letter (e.g., a, b, c)."
|
524
563
|
return
|
525
564
|
endif
|
565
|
+
|
526
566
|
let register_content = getreg(register_name)
|
527
567
|
if register_content == ''
|
528
568
|
echoerr "Register @" . register_name . " is empty."
|
529
569
|
return
|
530
570
|
endif
|
571
|
+
|
531
572
|
let current_mode = mode()
|
532
573
|
if current_mode == 'v' || current_mode == 'V' || current_mode == ''
|
533
574
|
execute 'normal! "' . register_name . 'p'
|
@@ -537,6 +578,25 @@ function! PasteIntoLastVisualSelection()
|
|
537
578
|
endif
|
538
579
|
endfunction
|
539
580
|
|
581
|
+
function! VimLM(...) range
|
582
|
+
let user_input = join(a:000, ' ')
|
583
|
+
if empty(user_input)
|
584
|
+
echo "Usage: :VimLM <prompt> [!command1] [!command2] ..."
|
585
|
+
return
|
586
|
+
endif
|
587
|
+
if line("'<") == line("'>")
|
588
|
+
silent! execute "normal! V\<ESC>"
|
589
|
+
endif
|
590
|
+
silent execute "'<,'>w! " . s:watched_dir . "/yank"
|
591
|
+
silent execute "w! " . s:watched_dir . "/context"
|
592
|
+
let user_file = s:watched_dir . '/user'
|
593
|
+
call writefile([user_input], user_file, 'w')
|
594
|
+
let current_file = expand('%:p')
|
595
|
+
let tree_file = s:watched_dir . '/tree'
|
596
|
+
call writefile([current_file], tree_file, 'w')
|
597
|
+
endfunction
|
598
|
+
|
599
|
+
command! -range -nargs=+ VimLM call VimLM(<f-args>)
|
540
600
|
nnoremap $mapp :call PasteIntoLastVisualSelection()<CR>
|
541
601
|
vnoremap $mapp <Cmd>:call PasteIntoLastVisualSelection()<CR>
|
542
602
|
vnoremap $mapl <Cmd>:call VisualPrompt()<CR>
|
vimlm-0.0.6.dist-info/METADATA
DELETED
@@ -1,158 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.2
|
2
|
-
Name: vimlm
|
3
|
-
Version: 0.0.6
|
4
|
-
Summary: VimLM - LLM-powered Vim assistant
|
5
|
-
Home-page: https://github.com/JosefAlbers/vimlm
|
6
|
-
Author: Josef Albers
|
7
|
-
Author-email: albersj66@gmail.com
|
8
|
-
Requires-Python: >=3.12.8
|
9
|
-
Description-Content-Type: text/markdown
|
10
|
-
License-File: LICENSE
|
11
|
-
Requires-Dist: nanollama==0.0.5
|
12
|
-
Requires-Dist: mlx_lm_utils==0.0.2
|
13
|
-
Requires-Dist: watchfiles==1.0.4
|
14
|
-
Dynamic: author
|
15
|
-
Dynamic: author-email
|
16
|
-
Dynamic: description
|
17
|
-
Dynamic: description-content-type
|
18
|
-
Dynamic: home-page
|
19
|
-
Dynamic: requires-dist
|
20
|
-
Dynamic: requires-python
|
21
|
-
Dynamic: summary
|
22
|
-
|
23
|
-
|
24
|
-
# VimLM - Local LLM-Powered Coding Assistant for Vim
|
25
|
-
|
26
|
-

|
27
|
-
|
28
|
-
LLM-powered coding companion for Vim, inspired by GitHub Copilot/Cursor. Integrates contextual code understanding, summarization, and AI assistance directly into your Vim workflow.
|
29
|
-
|
30
|
-
## Features
|
31
|
-
|
32
|
-
- **Model Agnostic** - Use any MLX-compatible model via a configuration file
|
33
|
-
- **Vim-Native UX** - Intuitive keybindings and split-window responses
|
34
|
-
- **Deep Context** - Understands code context from:
|
35
|
-
- Current file
|
36
|
-
- Visual selections
|
37
|
-
- Referenced files
|
38
|
-
- Project directory structure
|
39
|
-
- **Conversational Coding** - Iterative refinement with follow-up queries
|
40
|
-
- **Air-Gapped Security** - 100% offline - no APIs, no tracking, no data leaks
|
41
|
-
|
42
|
-
## Requirements
|
43
|
-
|
44
|
-
- Apple M-series chip
|
45
|
-
- Python 3.12.8
|
46
|
-
|
47
|
-
## Quick Start
|
48
|
-
|
49
|
-
Install:
|
50
|
-
|
51
|
-
```zsh
|
52
|
-
pip install vimlm
|
53
|
-
```
|
54
|
-
|
55
|
-
Launch:
|
56
|
-
|
57
|
-
```zsh
|
58
|
-
vimlm
|
59
|
-
```
|
60
|
-
|
61
|
-
or
|
62
|
-
|
63
|
-
```zsh
|
64
|
-
vimlm path/to/your_file
|
65
|
-
```
|
66
|
-
|
67
|
-
This launches Vim with the LLM in a split window, ready to assist you.
|
68
|
-
|
69
|
-
## Basic Usage
|
70
|
-
|
71
|
-
1. **From Normal Mode**:
|
72
|
-
- `Ctrl-l`: Adds current line + file to context
|
73
|
-
- Example prompt: "Regex for removing html tags from item.content"
|
74
|
-
|
75
|
-
2. **From Visual Mode**:
|
76
|
-
- Select code → `Ctrl-l`: Adds selected block + current file to context
|
77
|
-
- Example prompt: "Convert this to async/await syntax"
|
78
|
-
|
79
|
-
3. **Inline Commands**:
|
80
|
-
|
81
|
-
!include: Adds specified outside files/folders to context:
|
82
|
-
- `!include` (no path): Current folder
|
83
|
-
- `!include ~/scrap/jph00/hypermedia-applications.summ.md`: Specific folder
|
84
|
-
- `!include ~/wtm/utils.py`: Specific file
|
85
|
-
- Example prompt: "AJAX-ify this app @ ~/scrap/jph00/hypermedia-applications.summ.md"
|
86
|
-
|
87
|
-
!deploy: Extract code blocks to files in user specified dir (current dir if none specified).
|
88
|
-
|
89
|
-
!continue: Lets the LLM resume the generation from where it had halted due to length limits.
|
90
|
-
|
91
|
-
!followup: Continue the thread (equivalent to `Ctrl-j`
|
92
|
-
|
93
|
-
4. **Follow-Up**: After initial response:
|
94
|
-
- `Ctrl-j`: Continue thread
|
95
|
-
- Example follow-up: "In Manifest V3"
|
96
|
-
|
97
|
-
4. **Code Extraction: Press `Ctrl-p` to choose a code block from the response and insert them into:
|
98
|
-
- The last selected visual block (in Normal mode)
|
99
|
-
- The current selection (in Visual mode)
|
100
|
-
- Example workflow:
|
101
|
-
1. Select a block of code in Visual mode.
|
102
|
-
2. Prompt the LLM with `Ctrl-l` (e.g., "Convert this to async/await syntax").
|
103
|
-
3. Once the response is generated, press `Ctrl-p` to replace the selected block with the extracted code.
|
104
|
-
|
105
|
-
### Key Bindings
|
106
|
-
|
107
|
-
| Binding | Mode | Action |
|
108
|
-
|------------|---------------|----------------------------------------|
|
109
|
-
| `Ctrl-l` | Normal/Visual | Send current file + selection to LLM |
|
110
|
-
| `Ctrl-j` | Normal | Continue conversation |
|
111
|
-
| `Ctrl-p` | Normal/Visual | Replace the selection with generated code |
|
112
|
-
| `Esc` | Prompt | Cancel input |
|
113
|
-
|
114
|
-
## Advanced Configuration
|
115
|
-
|
116
|
-
VimLM uses a JSON config file with the following configurable parameters:
|
117
|
-
|
118
|
-
```json
|
119
|
-
{
|
120
|
-
"DEBUG": true,
|
121
|
-
"LLM_MODEL": null,
|
122
|
-
"NUM_TOKEN": 2000,
|
123
|
-
"SEP_CMD": "!",
|
124
|
-
"USE_LEADER": false
|
125
|
-
}
|
126
|
-
```
|
127
|
-
|
128
|
-
### Custom Model Setup
|
129
|
-
|
130
|
-
1. **Browse models**: [MLX Community Models on Hugging Face](https://huggingface.co/mlx-community)
|
131
|
-
|
132
|
-
2. **Edit config file**:
|
133
|
-
|
134
|
-
```json
|
135
|
-
{
|
136
|
-
"LLM_MODEL": "mlx-community/DeepSeek-R1-Distill-Qwen-7B-4bit"
|
137
|
-
}
|
138
|
-
```
|
139
|
-
3. **Save to**:
|
140
|
-
|
141
|
-
```
|
142
|
-
~/vimlm/config.json
|
143
|
-
```
|
144
|
-
|
145
|
-
4. **Restart VimLM**
|
146
|
-
|
147
|
-
|
148
|
-
### Custom Keybinding
|
149
|
-
|
150
|
-
If you prefer using `<Leader>` in place of `<Ctrl>` for the ViMLM key bindings:
|
151
|
-
|
152
|
-
```json
|
153
|
-
{
|
154
|
-
"USER_LEADER": true
|
155
|
-
}
|
156
|
-
```
|
157
|
-
|
158
|
-
|
vimlm-0.0.6.dist-info/RECORD
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
vimlm.py,sha256=_2EKVmfvM-Ikf48XzaxvC0rcLCxiHO4f6_fCslAqyCo,21181
|
2
|
-
vimlm-0.0.6.dist-info/LICENSE,sha256=f1xgK8fAXg_intwnbc9nLkHf7ODPLtgpHs7DetQHOro,11343
|
3
|
-
vimlm-0.0.6.dist-info/METADATA,sha256=INHaQDN1Id04li0LNWmlkVCIEmnKi9ABpbhAYFGokYE,4233
|
4
|
-
vimlm-0.0.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
5
|
-
vimlm-0.0.6.dist-info/entry_points.txt,sha256=mU5V4MYsuIzCc6YB-Ro-6USSHWN5vHw8UDnTEoq0isw,36
|
6
|
-
vimlm-0.0.6.dist-info/top_level.txt,sha256=I8GjqoiP--scYsO3AfLhha-6Ax9ci3IvbWvVbPv8g94,6
|
7
|
-
vimlm-0.0.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|