Skill
Edit files with the djinnvim CLI — vim-style, pattern-anchored keyhole editing that never reads whole files. Use for any file editing when djinnvim is installed, especially when file/shell tools are restricted or files are large.From its SKILL.md
npx -y skills add anschnapp/djinnvim --skill skillAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 16 days oldThe repository was created 16 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
9.0 KB, ~2.5k tokens by cl100k_base, as published. Nobody here has run it
djinnvim: keyhole editing from the shell
You edit through small viewports, the way a vim user does: search by pattern, look at the echo, edit. Never read a whole file — every command returns the few lines around what it did, and that echo IS your verification (no re-reads needed).
Reach for it when finding or changing something would otherwise mean reading a whole file, when a change repeats across many sites, or when file and shell tools are restricted. Creating a file or rewriting one wholesale is not its job.
Setup — every command, two rules
-
Pin the sandbox root in the same shell command (shell env does not persist between your calls):
export DJINNVIM_ROOTS=/abs/path/to/project; djinnvim open src/app.py -
Pass each editor command as ONE quoted argument. Single-quote it; switch to double quotes only when the text itself contains a single quote:
djinnvim edit 'at /old_name/ ciw new_name' djinnvim substitute ":%s/'eu-west'/'us-east'/" -
-f PATHnames the file to work on, opening it if needed, soedit,substitute,print,matchesandwriteneed no separateopencall:djinnvim edit -f src/app.py 'at /old/ ciw new'. Without it they use the active buffer. A switch is announced in the echo ([now on ... ]).
State (open buffers, cursor, registers, undo) persists across your shell
calls via an auto-spawned per-session daemon — nothing to start or manage.
djinnvim status shows it, djinnvim shutdown stops it (unwritten buffers
die with it; only write touches disk). Exit codes: 1 = the editor said
error: ... (read it — the buffer is untouched), 2 = usage/daemon problem.
The seven verbs
djinnvim open PATH— open/switch the active buffer and show its head. Optional, given-fabove; use it when you want the file's metadata. Relative paths resolve against the root, not your cwd.djinnvim motion CMD— move the cursor, one motion per call:/pattern(regex, forward),?pattern(back),n/N(next/prev — n is ALWAYS forward, N ALWAYS backward, unlike vim),:80(line),gg/G,fx/Fx(char on the cursor line). Search is strictly after the cursor and wraps, reportingmatch i of n (wrapped).djinnvim matches PATTERN [-C 1]— grep-style listing of every match (capped at 50). Call this before any rename-like edit to see all sites and decoys.djinnvim edit CMD— vim normal-mode edit (details below).djinnvim substitute CMD— ex command (details below).djinnvim print [CMD]— read-only window print (ed/vim:p), the reading tool:'p'(current line, cursor stays),':80 p'/':/def load/ p'(cursor MOVES there, prints it),':10,25 p'(range; cursor to its last line). Widen withabove/below/aroundtiny(8) /middle(25) /long(50) or a number —':/def load/ p around middle';aroundcounts EACH side. Max ~100 lines per call; page by re-addressing a gutter line number.
djinnvim write— save the active buffer; reports lines changed.write --previewshows the full pending buffer-vs-disk ±diff without writing — the final review before committing.
edit
Anchored form (preferred): at /pattern/ <cmd> (regex) or
at "literal text" <cmd> (matched literally — no escaping, use it when
the anchor has parens, dots or other regex punctuation; it cannot contain
a double quote). Ordinal and offset work on both: at 2nd /pat/ <cmd>.
The anchor lands at the START of the match — anchor on the
exact text to change: at /15\)/ ciw 60 changes the 15 in
retries(15); at /retries=15/ ciw 60 would change retries.
A +N/-N after the closing slash moves the anchor N whole lines
(cursor at column 0): at /# Merge logic/-1 O text inserts above the
line ABOVE the match — e.g. above a comment banner the match sits inside.
at each /pattern/ <cmd> applies one edit command at EVERY match
(transactional: any failure changes nothing; one undo step; returns a
±diff). Text objects make it structural: at each /# obsolete/ dap
deletes every marked paragraph whole — no line counting. To go
match-by-match instead, reissue the same at /pat/ <cmd>; it anchors on
the NEXT match each time.
Commands: ciw/caw TEXT, ci(/{/[/"/' TEXT (di/da
delete), dip/dap (paragraph, delete only), dd, cc TEXT, D,
C TEXT, x, r<char>, o/O TEXT (line below/above; multi-line OK),
A/I TEXT (line end/start), i/a TEXT (before/after the cursor
char), cs"' / ds" / ysiw" (surround). Changes need TEXT, deletes
take none; everything after the first space is TEXT, verbatim. Newlines
in TEXT must be real newline characters (one Enter each, vim-exact) —
the two characters backslash-n are not translated, they stay as typed, so
source like print("a\nb") inserts correctly. (This is the opposite of
substitute, where \n in the replacement does produce a newline.) o/O are line-wise — to
insert below a multi-line statement, anchor on its LAST line, not its
first. dd deletes the cursor line; address it by pattern
(at /pattern/ dd) — edit takes no ex addresses.
Indentation is vim autoindent. The line-wise inserts o, O and cc
take the reference line's indent and your TEXT's own leading whitespace
stacks on top, so pass only the indent BEYOND the anchor's: o x = 1
after a 4-space line lands at 6 spaces, and cc members = sorted(raw)
inside an 8-space block stays at 8. o!/O!/cc! opt out and insert
TEXT literally, for a block that is already absolutely indented.
(substitute replacements are the other way round — always literal, so
capture the indent there with ^( +) and \1.) o/O also echo
pre-edit blank-line counts next to where they landed (2 blank line(s) above insertion point, 0 below) — read it to match a file's blank-line
convention (e.g. 2 lines between top-level defs) without counting from
the viewport.
Blank lines are ordinary edits, one call each: bare o/O with no TEXT
inserts exactly one empty line, and at /pattern/ dd on a blank line
removes one. Don't reach for substitute (:/pat/s/^/\n/, :N,Nd) to
fix spacing — take the counts from the o/O echo and correct with a
single anchored call.
Registers: yy / y<i|a><obj> yank, p/P paste. "name prefix
composes with the anchor: at /def helper/ "fn dap cuts the function,
"fn p pastes it (works across files). Only "name-prefixed deletes write
registers; a wrong name on p lists them all.
u undoes the last buffer change (repeat to go further; crosses writes;
no redo). Any bad echo → u reverts it whole.
substitute
Ex forms: :%s/old/new/g (file), :s/old/new/ (cursor line),
:10,40s/foo/bar/, :/start/,/end/s/x/y/g, :g/pat/d (delete matching
lines). Flags g, i. Regex and replacement are Python re syntax
(\1 groups): escape parens in the PATTERN (send_request\(x\)) but
write them plainly in the replacement. Both range addresses are
inclusive; any address takes +N/-N — end on /pat/-1 for "up to but
not including". Zero matches is a loud error, never a silent no-op.
Numeric addresses go stale after every edit; prefer pattern addresses.
To rewrite one line into several without retyping its indentation,
capture it: :s/^( +)old_tail/\1new\n\1 second line/ (\n in the
replacement inserts a line break). Replacements are always literal here —
this capture is only needed in substitute; edit's o/O/cc supply
the indent for you.
Line-shaped only: to remove whole blocks at every match use
edit 'at each /pat/ dap', not hand-counted ranges. Register ranges for
blocks text objects can't grab (function with internal blank lines):
:/def helper/,/^def /-1d fn, then paste with at /def target/ "fn P.
Workflow
openthe file (echo shows size — you never need more than that).matchesthe symbols you'll touch: counts expose decoys (fetch_recordsvsfetch_records_cached→ use\b...\b) and plan scoping before any edit.- Edit smallest-first tool: one-site → anchored
edit; many-line regex →substitute; many-site structural →at each. - Read every echo. The diff/viewport is the verification; a wrong
echo →
edit uimmediately. (The cursor line's→prefix is exactly as wide as other lines' two-space prefix — indentation shown is exact. Where the caret appears it also states the line's indentation relative to the line above — read that instead of counting leading spaces.) write --previewfor a final buffer-vs-disk diff, thenwrite, and check the reported changed-line count against what you expect.- Write before running anything against the file. Tests, linters, and file reads see only the disk — never unwritten buffer state. (A disk change under an open buffer fails loudly on the next edit/write; re-open to continue.)
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.