vimlm 0.0.7__py3-none-any.whl → 0.0.9__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.9.dist-info/METADATA +169 -0
- vimlm-0.0.9.dist-info/RECORD +7 -0
- vimlm.py +83 -19
- vimlm-0.0.7.dist-info/METADATA +0 -204
- vimlm-0.0.7.dist-info/RECORD +0 -7
- {vimlm-0.0.7.dist-info → vimlm-0.0.9.dist-info}/LICENSE +0 -0
- {vimlm-0.0.7.dist-info → vimlm-0.0.9.dist-info}/WHEEL +0 -0
- {vimlm-0.0.7.dist-info → vimlm-0.0.9.dist-info}/entry_points.txt +0 -0
- {vimlm-0.0.7.dist-info → vimlm-0.0.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,169 @@
|
|
1
|
+
Metadata-Version: 2.2
|
2
|
+
Name: vimlm
|
3
|
+
Version: 0.0.9
|
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.3
|
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 - AI-Powered Coding Assistant for Vim
|
25
|
+
|
26
|
+

|
27
|
+
|
28
|
+
VimLM brings the power of AI directly into your Vim workflow. Maintain focus with keyboard-driven interactions while leveraging AI for code generation, refactoring, and documentation.
|
29
|
+
|
30
|
+
Get started quickly with the [tutorial](tutorial.md).
|
31
|
+
|
32
|
+
## Features
|
33
|
+
- **Native Vim Integration** - Split-window responses & intuitive keybindings
|
34
|
+
- **Offline First** - 100% local execution with MLX-compatible models
|
35
|
+
- **Contextual Awareness** - Integrates seamlessly with your codebase and external resources
|
36
|
+
- **Conversational Workflow** - Iterate on responses with follow-up queries
|
37
|
+
- **Project Scaffolding** - Generate and deploy code blocks to directories
|
38
|
+
- **Extensible** - Create custom LLM workflows with command chains
|
39
|
+
|
40
|
+
## Requirements
|
41
|
+
- Apple Silicon (M-series)
|
42
|
+
- Python 3.12.8
|
43
|
+
- Vim 9.1
|
44
|
+
|
45
|
+
## Quick Start
|
46
|
+
```bash
|
47
|
+
pip install vimlm
|
48
|
+
vimlm
|
49
|
+
```
|
50
|
+
|
51
|
+
## Basic Usage
|
52
|
+
|
53
|
+
| Key Binding | Mode | Action |
|
54
|
+
|-------------|---------------|----------------------------------------|
|
55
|
+
| `Ctrl-l` | Normal/Visual | Prompt LLM |
|
56
|
+
| `Ctrl-j` | Normal | Continue conversation |
|
57
|
+
| `Ctrl-p` | Normal/Visual | Import generated code |
|
58
|
+
| `Esc` | Prompt | Cancel input |
|
59
|
+
|
60
|
+
### 1. **Contextual Prompting**
|
61
|
+
`Ctrl-l` to prompt LLM with context:
|
62
|
+
- Normal mode: Current file + line
|
63
|
+
- Visual mode: Current file + selected block
|
64
|
+
|
65
|
+
*Example Prompt*: `Create a Chrome extension`
|
66
|
+
|
67
|
+
### 2. **Conversational Refinement**
|
68
|
+
`Ctrl-j` to continue current thread.
|
69
|
+
|
70
|
+
*Example Prompt*: `Use manifest V3 instead`
|
71
|
+
|
72
|
+
### 3. **Code Substitution**
|
73
|
+
`Ctrl-p` to insert generated code block
|
74
|
+
- In Normal mode: Into last visual selection
|
75
|
+
- In Visual mode: Into current visual selection
|
76
|
+
|
77
|
+
*Example Workflow*:
|
78
|
+
1. Select a block of code in Visual mode
|
79
|
+
2. Prompt with `Ctrl-l`: `Use regex to remove html tags from item.content`
|
80
|
+
3. Press `Ctrl-p` to replace selection with generated code
|
81
|
+
|
82
|
+
## Inline Directives
|
83
|
+
```text
|
84
|
+
:VimLM [PROMPT] [!command1] [!command2]...
|
85
|
+
```
|
86
|
+
|
87
|
+
`!` prefix to embed inline directives in prompts:
|
88
|
+
|
89
|
+
| Directive | Description |
|
90
|
+
|------------------|------------------------------------------|
|
91
|
+
| `!include PATH` | Add file/directory/shell output to context |
|
92
|
+
| `!deploy DEST` | Save code blocks to directory |
|
93
|
+
| `!continue N` | Continue stopped response |
|
94
|
+
| `!followup` | Continue conversation |
|
95
|
+
|
96
|
+
### 1. **Context Layering**
|
97
|
+
```text
|
98
|
+
!include [PATH] # Add files/folders to context
|
99
|
+
```
|
100
|
+
- **`!include`** (no path): Current folder
|
101
|
+
- **`!include ~/projects/utils.py`**: Specific file
|
102
|
+
- **`!include ~/docs/api-specs/`**: Entire folder
|
103
|
+
- **`!include $(...)`**: Shell command output
|
104
|
+
|
105
|
+
*Example*: `Summarize recent changes !include $(git log --oneline -n 50)`
|
106
|
+
|
107
|
+
### 2. **Code Deployment**
|
108
|
+
```text
|
109
|
+
!deploy [DEST_DIR] # Extract code blocks to directory
|
110
|
+
```
|
111
|
+
- **`!deploy`** (no path): Current directory
|
112
|
+
- **`!deploy ./src`**: Specific directory
|
113
|
+
|
114
|
+
*Example:* `Create REST API endpoint !deploy ./api`
|
115
|
+
|
116
|
+
### 3. **Extending Response**
|
117
|
+
```text
|
118
|
+
!continue [MAX_TOKENS] # Continue stopped response
|
119
|
+
```
|
120
|
+
- **`!continue`**: Default 2000 tokens
|
121
|
+
- **`!continue 3000`**: Custom token limit
|
122
|
+
|
123
|
+
*Example:* `tl;dr !include large-file.txt !continue 5000`
|
124
|
+
|
125
|
+
## Command-Line Mode
|
126
|
+
```vim
|
127
|
+
:VimLM prompt [!command1] [!command2]...
|
128
|
+
```
|
129
|
+
|
130
|
+
Simplify complex tasks by chaining multiple commands together into a single, reusable Vim command.
|
131
|
+
|
132
|
+
*Examples*:
|
133
|
+
```vim
|
134
|
+
" Debug CI failures using error logs
|
135
|
+
:VimLM Fix Dockerfile !include .gitlab-ci.yml !include $(tail -n 20 ci.log)
|
136
|
+
|
137
|
+
" Generate unit tests for selected functions and save to test/
|
138
|
+
:VimLM Write pytest tests for this !include ./src !deploy ./test
|
139
|
+
|
140
|
+
" Add docstrings to all Python functions in file
|
141
|
+
:VimLM Add Google-style docstrings !include % !continue 4000
|
142
|
+
```
|
143
|
+
|
144
|
+
## Configuration
|
145
|
+
|
146
|
+
### 1. **Model Settings**
|
147
|
+
Edit `~/vimlm/cfg.json`:
|
148
|
+
```json
|
149
|
+
{
|
150
|
+
"LLM_MODEL": "mlx-community/DeepSeek-R1-Distill-Qwen-7B-4bit",
|
151
|
+
"NUM_TOKEN": 32768
|
152
|
+
}
|
153
|
+
```
|
154
|
+
|
155
|
+
### 2. **Key Customization**
|
156
|
+
```json
|
157
|
+
{
|
158
|
+
"USE_LEADER": true,
|
159
|
+
"KEY_MAP": {
|
160
|
+
"l": "]",
|
161
|
+
"j": "[",
|
162
|
+
"p": "p"
|
163
|
+
}
|
164
|
+
}
|
165
|
+
```
|
166
|
+
|
167
|
+
## License
|
168
|
+
|
169
|
+
Apache 2.0 - See [LICENSE](LICENSE) for details.
|
@@ -0,0 +1,7 @@
|
|
1
|
+
vimlm.py,sha256=n0rCiqHJQ1ouKZD-0C9rcE6rAC4d1bVriKTx5303Dmo,25180
|
2
|
+
vimlm-0.0.9.dist-info/LICENSE,sha256=f1xgK8fAXg_intwnbc9nLkHf7ODPLtgpHs7DetQHOro,11343
|
3
|
+
vimlm-0.0.9.dist-info/METADATA,sha256=xhXHiDlYY44vN12JgbsZHcgRJDy01-hDOCcm05ZQHng,4942
|
4
|
+
vimlm-0.0.9.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
5
|
+
vimlm-0.0.9.dist-info/entry_points.txt,sha256=mU5V4MYsuIzCc6YB-Ro-6USSHWN5vHw8UDnTEoq0isw,36
|
6
|
+
vimlm-0.0.9.dist-info/top_level.txt,sha256=I8GjqoiP--scYsO3AfLhha-6Ax9ci3IvbWvVbPv8g94,6
|
7
|
+
vimlm-0.0.9.dist-info/RECORD,,
|
vimlm.py
CHANGED
@@ -18,7 +18,7 @@ import json
|
|
18
18
|
import os
|
19
19
|
from watchfiles import awatch
|
20
20
|
import shutil
|
21
|
-
import
|
21
|
+
from datetime import datetime
|
22
22
|
from itertools import accumulate
|
23
23
|
import argparse
|
24
24
|
import tempfile
|
@@ -34,10 +34,11 @@ DEFAULTS = dict(
|
|
34
34
|
DO_RESET = True,
|
35
35
|
SHOW_USER = False,
|
36
36
|
SEP_CMD = '!',
|
37
|
-
VERSION = '0.0.
|
38
|
-
DEBUG =
|
37
|
+
VERSION = '0.0.9',
|
38
|
+
DEBUG = False,
|
39
39
|
)
|
40
40
|
|
41
|
+
DATE_FORM = "%Y_%m_%d_%H_%M_%S"
|
41
42
|
VIMLM_DIR = os.path.expanduser("~/vimlm")
|
42
43
|
WATCH_DIR = os.path.expanduser("~/vimlm/watch_dir")
|
43
44
|
CFG_FILE = 'cfg.json'
|
@@ -70,7 +71,7 @@ try:
|
|
70
71
|
for p in [CFG_PATH, LOG_PATH, LTM_PATH]:
|
71
72
|
if os.path.isfile(p):
|
72
73
|
os.remove(p)
|
73
|
-
raise ValueError(f'
|
74
|
+
raise ValueError(f'Updating config')
|
74
75
|
except Exception as e:
|
75
76
|
print(e)
|
76
77
|
config = DEFAULTS
|
@@ -95,10 +96,22 @@ def tolog(log, key='debug'):
|
|
95
96
|
logs = json.load(log_f)
|
96
97
|
except:
|
97
98
|
logs = []
|
98
|
-
logs.append(dict(key=key, log=log, timestamp=
|
99
|
+
logs.append(dict(key=key, log=log, timestamp=datetime.now().strftime(DATE_FORM)))
|
99
100
|
with open(LOG_PATH, "w", encoding="utf-8") as log_f:
|
100
101
|
json.dump(logs, log_f, indent=2)
|
101
102
|
|
103
|
+
def print_log():
|
104
|
+
with open(LOG_PATH, 'r') as f:
|
105
|
+
logs = json.load(f)
|
106
|
+
for log in logs:
|
107
|
+
print(f'\033[37m{log["key"]} {log["timestamp"]}\033[0m')
|
108
|
+
if 'tovim' in log["key"]:
|
109
|
+
print('\033[33m')
|
110
|
+
elif 'tollm' in log["key"]:
|
111
|
+
print('\033[31m')
|
112
|
+
print(log["log"])
|
113
|
+
print('\033[0m')
|
114
|
+
|
102
115
|
toout('Loading LLM...')
|
103
116
|
if LLM_MODEL is None:
|
104
117
|
from nanollama import Chat
|
@@ -117,6 +130,7 @@ def deploy(dest=None, src=None, reformat=True):
|
|
117
130
|
with open(src, 'r') as f:
|
118
131
|
prompt_deploy = f.read().strip() + '\n\n---\n\n' + prompt_deploy
|
119
132
|
if reformat:
|
133
|
+
toout('Deploying...')
|
120
134
|
response = chat(prompt_deploy, max_new=NUM_TOKEN, verbose=False, stream=False)['text']
|
121
135
|
toout(response, 'deploy')
|
122
136
|
lines = response.splitlines()
|
@@ -371,6 +385,14 @@ def process_command(data):
|
|
371
385
|
return data
|
372
386
|
data['user_prompt'] += "\n\nEnsure that each code block is preceded by a filename in **filename.ext** format. The filename should only contain alphanumeric characters, dots, underscores, or hyphens. Ensure that any extraneous characters are removed from the filenames."
|
373
387
|
data['deploy_dest'] = arg
|
388
|
+
for cmd in cmds:
|
389
|
+
if cmd.startswith('write'):
|
390
|
+
arg = cmd.removeprefix('write').strip().strip('(').strip(')').strip().strip('"').strip("'").strip()
|
391
|
+
if len(arg) == 0:
|
392
|
+
arg = 'response'
|
393
|
+
pass
|
394
|
+
timestamp = datetime.now().strftime(DATE_FORM)
|
395
|
+
data['write_dest'] = re.sub(r"[^a-zA-Z0-9_.-]", "", f'{arg}_{timestamp}.md')
|
374
396
|
return data
|
375
397
|
|
376
398
|
async def monitor_directory():
|
@@ -423,7 +445,10 @@ async def process_files(data):
|
|
423
445
|
toout(response['text'])
|
424
446
|
else:
|
425
447
|
toout(response['text'])
|
426
|
-
tolog(response
|
448
|
+
tolog(response)
|
449
|
+
if 'write_dest' in data:
|
450
|
+
with open(data['write_dest'], 'w') as f:
|
451
|
+
f.write(response['text'])
|
427
452
|
if 'deploy_dest' in data:
|
428
453
|
deploy(dest=data['deploy_dest'], reformat=False)
|
429
454
|
|
@@ -434,6 +459,43 @@ mapl, mapj, mapp = (f'<Leader>{KEYL}', f'<Leader>{KEYJ}', f'<Leader>{KEYP}') if
|
|
434
459
|
VIMLMSCRIPT = Template(r"""
|
435
460
|
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']
|
436
461
|
let s:watched_dir = expand('$WATCH_DIR')
|
462
|
+
let s:vimlm_enabled = 1
|
463
|
+
|
464
|
+
function! ToggleVimLM()
|
465
|
+
if s:vimlm_enabled
|
466
|
+
let s:vimlm_enabled = 0
|
467
|
+
let response_path = s:watched_dir . '/response.md'
|
468
|
+
let bufnum = bufnr(response_path)
|
469
|
+
let winid = bufwinnr(bufnum)
|
470
|
+
if winid != -1
|
471
|
+
execute winid . 'wincmd c'
|
472
|
+
endif
|
473
|
+
if exists('s:monitor_timer')
|
474
|
+
call timer_stop(s:monitor_timer)
|
475
|
+
unlet s:monitor_timer
|
476
|
+
endif
|
477
|
+
echohl WarningMsg | echom "VimLM disabled" | echohl None
|
478
|
+
else
|
479
|
+
let s:vimlm_enabled = 1
|
480
|
+
silent! call Monitor()
|
481
|
+
echohl WarningMsg | echom "VimLM enabled" | echohl None
|
482
|
+
endif
|
483
|
+
endfunction
|
484
|
+
|
485
|
+
function! CheckForUpdates(timer)
|
486
|
+
if !s:vimlm_enabled
|
487
|
+
return
|
488
|
+
endif
|
489
|
+
let bufnum = bufnr(s:watched_dir . '/response.md')
|
490
|
+
let winid = bufwinnr(bufnum)
|
491
|
+
if winid == -1
|
492
|
+
call timer_stop(s:monitor_timer)
|
493
|
+
unlet s:monitor_timer
|
494
|
+
call Monitor()
|
495
|
+
else
|
496
|
+
silent! checktime
|
497
|
+
endif
|
498
|
+
endfunction
|
437
499
|
|
438
500
|
function! Monitor()
|
439
501
|
if exists('s:monitor_timer')
|
@@ -456,13 +518,16 @@ function! Monitor()
|
|
456
518
|
let s:monitor_timer = timer_start(100, 'CheckForUpdates', {'repeat': -1})
|
457
519
|
endfunction
|
458
520
|
|
459
|
-
function!
|
521
|
+
function! ScrollToTop()
|
460
522
|
let bufnum = bufnr(s:watched_dir . '/response.md')
|
461
|
-
if bufnum
|
462
|
-
|
463
|
-
|
523
|
+
if bufnum != -1
|
524
|
+
let winid = bufwinnr(bufnum)
|
525
|
+
if winid > 0
|
526
|
+
execute winid . "wincmd w"
|
527
|
+
normal! gg
|
528
|
+
wincmd p
|
529
|
+
endif
|
464
530
|
endif
|
465
|
-
silent! checktime
|
466
531
|
endfunction
|
467
532
|
|
468
533
|
function! s:CustomInput(prompt) abort
|
@@ -486,14 +551,13 @@ function! SaveUserInput(prompt)
|
|
486
551
|
let current_file = expand('%:p')
|
487
552
|
let tree_file = s:watched_dir . '/tree'
|
488
553
|
call writefile([current_file], tree_file, 'w')
|
554
|
+
call ScrollToTop()
|
489
555
|
endfunction
|
490
556
|
|
491
557
|
function! VisualPrompt()
|
492
558
|
silent! execute "normal! \<ESC>"
|
493
559
|
silent execute "'<,'>w! " . s:watched_dir . "/yank"
|
494
560
|
silent execute "w! " . s:watched_dir . "/context"
|
495
|
-
" silent! execute "normal! `<V`>"
|
496
|
-
" silent! execute "normal! \<ESC>"
|
497
561
|
call SaveUserInput('VimLM: ')
|
498
562
|
endfunction
|
499
563
|
|
@@ -501,7 +565,6 @@ function! NormalPrompt()
|
|
501
565
|
silent! execute "normal! V\<ESC>"
|
502
566
|
silent execute "'<,'>w! " . s:watched_dir . "/yank"
|
503
567
|
silent execute "w! " . s:watched_dir . "/context"
|
504
|
-
" silent! execute "normal! \<ESC>"
|
505
568
|
call SaveUserInput('VimLM: ')
|
506
569
|
endfunction
|
507
570
|
|
@@ -557,18 +620,15 @@ function! PasteIntoLastVisualSelection(...)
|
|
557
620
|
echo "Extracted " . num_blocks . " blocks into registers @a-@" . s:register_names[num_blocks - 1] . ". Enter register name: "
|
558
621
|
let register_name = nr2char(getchar())
|
559
622
|
endif
|
560
|
-
|
561
623
|
if register_name !~ '^[a-z]$'
|
562
624
|
echoerr "Invalid register name. Please enter a single lowercase letter (e.g., a, b, c)."
|
563
625
|
return
|
564
626
|
endif
|
565
|
-
|
566
627
|
let register_content = getreg(register_name)
|
567
628
|
if register_content == ''
|
568
629
|
echoerr "Register @" . register_name . " is empty."
|
569
630
|
return
|
570
631
|
endif
|
571
|
-
|
572
632
|
let current_mode = mode()
|
573
633
|
if current_mode == 'v' || current_mode == 'V' || current_mode == ''
|
574
634
|
execute 'normal! "' . register_name . 'p'
|
@@ -579,6 +639,10 @@ function! PasteIntoLastVisualSelection(...)
|
|
579
639
|
endfunction
|
580
640
|
|
581
641
|
function! VimLM(...) range
|
642
|
+
let tree_file = s:watched_dir . '/tree'
|
643
|
+
while filereadable(tree_file)
|
644
|
+
sleep 100m
|
645
|
+
endwhile
|
582
646
|
let user_input = join(a:000, ' ')
|
583
647
|
if empty(user_input)
|
584
648
|
echo "Usage: :VimLM <prompt> [!command1] [!command2] ..."
|
@@ -592,10 +656,11 @@ function! VimLM(...) range
|
|
592
656
|
let user_file = s:watched_dir . '/user'
|
593
657
|
call writefile([user_input], user_file, 'w')
|
594
658
|
let current_file = expand('%:p')
|
595
|
-
let tree_file = s:watched_dir . '/tree'
|
596
659
|
call writefile([current_file], tree_file, 'w')
|
660
|
+
call ScrollToTop()
|
597
661
|
endfunction
|
598
662
|
|
663
|
+
command! ToggleVimLM call ToggleVimLM()
|
599
664
|
command! -range -nargs=+ VimLM call VimLM(<f-args>)
|
600
665
|
nnoremap $mapp :call PasteIntoLastVisualSelection()<CR>
|
601
666
|
vnoremap $mapp <Cmd>:call PasteIntoLastVisualSelection()<CR>
|
@@ -611,7 +676,6 @@ async def main():
|
|
611
676
|
parser.add_argument("vim_args", nargs=argparse.REMAINDER, help="Vim arguments")
|
612
677
|
args = parser.parse_args()
|
613
678
|
if args.test:
|
614
|
-
test()
|
615
679
|
return
|
616
680
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.vim', delete=False) as f:
|
617
681
|
f.write(VIMLMSCRIPT)
|
vimlm-0.0.7.dist-info/METADATA
DELETED
@@ -1,204 +0,0 @@
|
|
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
|
-
|
vimlm-0.0.7.dist-info/RECORD
DELETED
@@ -1,7 +0,0 @@
|
|
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,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|