[Chore] cleanup
This commit is contained in:
@@ -1,12 +1,3 @@
|
|||||||
# Search config for THIS repo only (ripgrep / fd / telescope read .ignore).
|
# Search config for THIS repo only (ripgrep / fd / telescope read .ignore).
|
||||||
#
|
|
||||||
# Everything this repo holds lives in dotfiles and dotdirs — common/.config, gui/.config,
|
|
||||||
# common/.pi, common/.bashrc — which rg and fd skip by default, so an unqualified search
|
|
||||||
# here returned nothing at all. Un-hide them, then put back the two dirs that would swamp
|
|
||||||
# every result: negating .* alone drags in the whole of .git, and .claude/worktrees holds
|
|
||||||
# entire checkouts of this same repo (background sessions create them), so every hit would
|
|
||||||
# appear several times over. Scoped to this directory tree; searches elsewhere on the
|
|
||||||
# machine are unaffected.
|
|
||||||
!.*
|
!.*
|
||||||
.git/
|
.git/
|
||||||
.claude/
|
|
||||||
|
|||||||
@@ -1,174 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# reconcile-hyde.sh — reconcile a HyDE update with your dots-tracked configs.
|
|
||||||
#
|
|
||||||
# HyDE's updater (`Scripts/install.sh -r`) overwrites every config listed in
|
|
||||||
# Scripts/restore_cfg.psv — including ~/.config/hypr/*. It backs the old ones up to
|
|
||||||
# ~/.config/cfg_backups and REPLACES your stow symlinks with real files (HyDE's new
|
|
||||||
# defaults). This tool, run ON THE GUI MACHINE right after that update, shows per file:
|
|
||||||
# your dots version vs HyDE's new one
|
|
||||||
# lets you keep / take / merge each, then removes the severed real files and re-runs
|
|
||||||
# install.sh so everything is symlinked back to the repo.
|
|
||||||
#
|
|
||||||
# ./bin/reconcile-hyde.sh [host] report only — safe, changes nothing (default)
|
|
||||||
# ./bin/reconcile-hyde.sh -i [host] interactive: decide each conflicting file
|
|
||||||
# ./bin/reconcile-hyde.sh --relink [host] skip decisions: back up severed files,
|
|
||||||
# then re-stow (repo versions win)
|
|
||||||
#
|
|
||||||
# host defaults to `hostname -s`. Commit your repo first so choices are reviewable/revertable.
|
|
||||||
set -uo pipefail
|
|
||||||
|
|
||||||
# Config subtrees where HyDE and your dots overlap. Add/remove as your setup needs.
|
|
||||||
SUBTREES=(.config/hypr .config/waybar)
|
|
||||||
|
|
||||||
# --- args ---
|
|
||||||
MODE=report
|
|
||||||
HOST=""
|
|
||||||
for a in "$@"; do
|
|
||||||
case "$a" in
|
|
||||||
-i|--interactive) MODE=interactive ;;
|
|
||||||
--relink) MODE=relink ;;
|
|
||||||
-h|--help) awk 'NR > 1 && !/^#/ { exit } NR > 1' "$0"; exit 0 ;;
|
|
||||||
-*) echo "unknown option: $a" >&2; exit 2 ;;
|
|
||||||
*) HOST="$a" ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
[ -z "$HOST" ] && HOST="$(hostname -s)"
|
|
||||||
skip_relink=() # files the user chose to skip during interactive reconcile
|
|
||||||
|
|
||||||
DOTS="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/.." && pwd)" # repo root (script in bin/)
|
|
||||||
[ -x "$DOTS/install.sh" ] || { echo "error: $DOTS/install.sh not found — run from inside the dots repo" >&2; exit 1; }
|
|
||||||
|
|
||||||
# repo package search order: host overlay wins, then gui, then common
|
|
||||||
PKGS=("$HOST" gui common)
|
|
||||||
repo_src() { # repo_src <rel-path> -> print the repo file stow would link, or nothing
|
|
||||||
local rel="$1" p
|
|
||||||
for p in "${PKGS[@]}"; do
|
|
||||||
[ -f "$DOTS/$p/$rel" ] && { printf '%s\n' "$DOTS/$p/$rel"; return 0; }
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "host=$HOST dots=$DOTS mode=$MODE"
|
|
||||||
if [ -n "$(git -C "$DOTS" status --porcelain 2>/dev/null)" ]; then
|
|
||||||
echo "note: $DOTS has uncommitted changes — commit first so reconcile choices are reviewable." >&2
|
|
||||||
fi
|
|
||||||
[ -d "$HOME/.config/cfg_backups" ] && echo "HyDE's pre-update backups are in ~/.config/cfg_backups"
|
|
||||||
echo
|
|
||||||
|
|
||||||
# --- classify every live file under the managed subtrees ---
|
|
||||||
linked=0; foreign=0
|
|
||||||
same=(); conflicts=(); new_files=()
|
|
||||||
for st in "${SUBTREES[@]}"; do
|
|
||||||
[ -d "$HOME/$st" ] || continue
|
|
||||||
while IFS= read -r -d '' live; do
|
|
||||||
rel="${live#"$HOME"/}"
|
|
||||||
if [ -L "$live" ]; then # still a symlink
|
|
||||||
tgt="$(readlink -f "$live" 2>/dev/null || true)"
|
|
||||||
case "$tgt" in
|
|
||||||
"$DOTS"/*) linked=$((linked+1)) ;; # intact -> HyDE didn't sever it
|
|
||||||
*) foreign=$((foreign+1)); echo " ? $rel -> symlink outside repo: $tgt" ;;
|
|
||||||
esac
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
# real file: HyDE overwrote (severed) it, or it is brand new
|
|
||||||
src="$(repo_src "$rel" || true)"
|
|
||||||
if [ -z "$src" ]; then
|
|
||||||
new_files+=("$rel")
|
|
||||||
elif diff -q "$src" "$live" >/dev/null 2>&1; then
|
|
||||||
same+=("$rel") # content == repo; just relink
|
|
||||||
else
|
|
||||||
conflicts+=("$rel") # HyDE's new content differs from yours
|
|
||||||
fi
|
|
||||||
done < <(find "$HOME/$st" \( -type f -o -type l \) -print0)
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "== summary =="
|
|
||||||
echo " intact symlinks (untouched): $linked"
|
|
||||||
echo " severed but identical (relink): ${#same[@]}"
|
|
||||||
echo " severed & CHANGED (need decision): ${#conflicts[@]}"
|
|
||||||
echo " new HyDE files not tracked by dots: ${#new_files[@]}"
|
|
||||||
[ "$foreign" -gt 0 ] && echo " foreign symlinks flagged above: $foreign"
|
|
||||||
echo
|
|
||||||
if [ "$linked" -gt 0 ]; then
|
|
||||||
echo "For intact symlinks, check whether HyDE wrote THROUGH them into the repo:"
|
|
||||||
echo " git -C $DOTS diff -- '*/.config/hypr/*' '*/.config/waybar/*'"
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
if [ "${#new_files[@]}" -gt 0 ]; then
|
|
||||||
echo "New HyDE files (left as-is; add to a package + re-stow if you want them tracked):"
|
|
||||||
printf ' %s\n' "${new_files[@]}"
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
if [ "${#conflicts[@]}" -gt 0 ]; then
|
|
||||||
echo "Changed files (your dots version vs HyDE's new default):"
|
|
||||||
for rel in "${conflicts[@]}"; do
|
|
||||||
src="$(repo_src "$rel")"
|
|
||||||
printf ' %-45s %s changed lines\n' "$rel" "$(diff "$src" "$HOME/$rel" | grep -cE '^[<>]')"
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
|
|
||||||
# report-only ends here
|
|
||||||
if [ "$MODE" = report ]; then
|
|
||||||
if [ "${#conflicts[@]}" -gt 0 ]; then
|
|
||||||
echo "Re-run with -i to decide each changed file, or --relink to keep your repo versions wholesale."
|
|
||||||
elif [ "${#same[@]}" -gt 0 ]; then
|
|
||||||
echo "No content conflicts. Re-run with --relink to drop the severed files and re-symlink."
|
|
||||||
else
|
|
||||||
echo "Nothing to reconcile."
|
|
||||||
fi
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- interactive: decide each changed file, writing the choice into the repo copy ---
|
|
||||||
if [ "$MODE" = interactive ] && [ "${#conflicts[@]}" -gt 0 ]; then
|
|
||||||
for rel in "${conflicts[@]}"; do
|
|
||||||
src="$(repo_src "$rel")"; live="$HOME/$rel"
|
|
||||||
echo "──────── $rel ────────"
|
|
||||||
echo " repo (yours): ${src#"$DOTS"/}"
|
|
||||||
diff "$src" "$live" | sed 's/^/ /' | head -60
|
|
||||||
echo " [k]eep yours (default) [t]ake HyDE's [m]erge in \$EDITOR -d [s]kip"
|
|
||||||
read -r -p " choice> " ans </dev/tty || ans=k
|
|
||||||
case "$ans" in
|
|
||||||
t|take) cp "$live" "$src"; echo " -> took HyDE's version into $src" ;;
|
|
||||||
m|merge) "${EDITOR:-nvim}" -d "$src" "$live" </dev/tty >/dev/tty 2>&1; echo " -> merged (saved $src)" ;;
|
|
||||||
s|skip) echo " -> skipped (left severed; not relinked)"; skip_relink+=("$rel") ;;
|
|
||||||
*) echo " -> kept your repo version" ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- relink: back up severed files that the repo owns, remove them, re-stow ---
|
|
||||||
BK="$HOME/.config/hyde-reconcile-backup-$(date +%Y%m%d-%H%M%S)"
|
|
||||||
to_relink=()
|
|
||||||
for st in "${SUBTREES[@]}"; do
|
|
||||||
[ -d "$HOME/$st" ] || continue
|
|
||||||
while IFS= read -r -d '' live; do
|
|
||||||
[ -L "$live" ] && continue # only real (severed) files
|
|
||||||
rel="${live#"$HOME"/}"
|
|
||||||
repo_src "$rel" >/dev/null || continue # only files the repo can provide
|
|
||||||
for s in "${skip_relink[@]}"; do [ "$s" = "$rel" ] && continue 2; done
|
|
||||||
to_relink+=("$rel")
|
|
||||||
done < <(find "$HOME/$st" -type f -print0)
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "${#to_relink[@]}" -eq 0 ]; then
|
|
||||||
echo "nothing to relink."; exit 0
|
|
||||||
fi
|
|
||||||
echo "will back up + remove ${#to_relink[@]} severed file(s), then re-stow so they link to the repo:"
|
|
||||||
printf ' %s\n' "${to_relink[@]}"
|
|
||||||
read -r -p "proceed? [y/N] " go </dev/tty || go=n
|
|
||||||
case "$go" in y|Y|yes) ;; *) echo "aborted — nothing changed."; exit 0 ;; esac
|
|
||||||
|
|
||||||
for rel in "${to_relink[@]}"; do
|
|
||||||
mkdir -p "$BK/$(dirname "$rel")"
|
|
||||||
cp -a "$HOME/$rel" "$BK/$rel"
|
|
||||||
rm -f "$HOME/$rel"
|
|
||||||
done
|
|
||||||
echo "backed up to $BK; removed severed files. re-stowing…"
|
|
||||||
"$DOTS/install.sh" "$HOST"
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "done. verify a couple of links resolve into the repo, e.g.:"
|
|
||||||
echo " readlink ~/.config/hypr/keybindings.conf"
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
# CLAUDE.md
|
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
||||||
|
|
||||||
## What this is
|
|
||||||
|
|
||||||
This is **not application source** — it's the on-disk configuration tree for `pi`, the TUI coding agent from [earendil-works/pi](https://github.com/earendil-works/pi) (binary at `/usr/bin/pi`). It lives inside the user's dots repo (`~/.dots`) and is surfaced into `$HOME` via GNU Stow's per-file symlinks (`--no-folding`), e.g. `~/.pi/agent/extensions/notify.ts -> ../../../.dots/common/.pi/agent/extensions/notify.ts`. **Editing** an existing file edits the live config directly — no deploy step. But **adding a new file** (e.g. a new extension) is not surfaced until you re-stow: run `./install.sh` (or `stow --no-folding -R -d ~/.dots -t ~ common`) to create its symlink, on each machine.
|
|
||||||
|
|
||||||
Because it's JSON + markdown config, there are no tests/lint/build. The only useful "validation" is JSON well-formedness, e.g. `jq . settings.json` or `python -m json.tool < settings.json`.
|
|
||||||
|
|
||||||
## Config directory — single source of truth
|
|
||||||
|
|
||||||
**All live config is `.pi/agent/`** (surfaced as `~/.pi/agent`). The pi coding agent reads `$PI_CODING_AGENT_DIR`, which defaults to `~/.pi/agent` and is not overridden here; per pi's docs, settings/auth/trust/sessions/extensions/AGENTS.md all resolve under that one directory. Nothing reads `~/.pi/*` at the top level or `~/.config/pi`.
|
|
||||||
|
|
||||||
> History: there used to be three drifted copies of this config (`.pi/*` top-level, `.pi/agent/`, and `~/.config/pi/`). The two unread mirrors were deleted and the dangling `~/.config/pi` stow symlink removed, leaving `.pi/agent/` as the only copy. Don't reintroduce mirrors — edit `.pi/agent/` directly.
|
|
||||||
|
|
||||||
## Layout of `.pi/agent/` (the live config)
|
|
||||||
|
|
||||||
- `settings.json` — runtime settings: `defaultProvider`/`defaultModel`/`defaultThinkingLevel`, `theme`, `enabledModels` (glob allowlist for the Ctrl+P model picker), `compaction` (auto-summarize long sessions), `retry`, HTTP timeouts, `npmCommand` (pinned to `fnm exec --using=22 -- npm`), `packages` (installed extension packages, currently `npm:pi-vim`) and their config blocks (`piVim` — see Vim input editor).
|
|
||||||
- `models.json` — the provider + model catalog (see below).
|
|
||||||
- `auth.json` — maps each provider to its credential. Values are **references, not secrets** (`$DUSKADIY_API_KEY`, `no-key-required`); pi resolves `$VAR` from the environment. **Gitignored** — the tracked copy is `auth.json.example`; a new host needs `cp auth.json.example auth.json` before pi can resolve a provider (stow links whatever is present, tracked or not, so the live file keeps working here).
|
|
||||||
- `prompts/*.md` — custom slash commands (pi "prompt templates"). `/commit` and `/review` are defined here. Format: YAML frontmatter (`description`, `argument-hint`) + body, with `${1:-default}` positional-arg substitution.
|
|
||||||
- `themes/*.json` — color themes following the schema at `earendil-works/pi .../theme/theme-schema.json`: a `vars` palette referenced by semantic `colors` keys, plus an `export` block for HTML session export.
|
|
||||||
- `sessions/` — runtime session transcripts (`.jsonl`), **gitignored**. One subdir per project cwd; each line is an event (`session`, `model_change`, `message`, …).
|
|
||||||
- `extensions/*/index.ts` — auto-discovered TypeScript extensions, loaded via [jiti](https://github.com/unjs/jiti) (no build step). `import type` from `@earendil-works/*` is erased at runtime; value imports resolve against pi's own bundled packages, so a vendored extension needs no `node_modules`. Editor TS "cannot find module" warnings on these imports are therefore expected noise.
|
|
||||||
- `npm/` — extension **packages** installed by `pi install` (currently `pi-vim`), **gitignored**; `settings.json > packages` is the tracked source of truth. On a fresh host run `pi install npm:pi-vim` once — plus pi-vim's manual peer-dep install (see Vim input editor), which `pi install` does not do — then `pi update npm:pi-vim` to bump. A package needs no re-stow — unlike a new file under `extensions/`.
|
|
||||||
- `keybindings.json` — key remaps. A user entry **replaces** the default keys for that action (it does not merge). Action ids and defaults are listed in `/opt/pi-coding-agent/docs/keybindings.md`.
|
|
||||||
|
|
||||||
## Plan mode (Shift+Tab)
|
|
||||||
|
|
||||||
`extensions/plan-mode/` is pi's bundled plan-mode example (pi has no built-in plan mode), **vendored here and rebound from its upstream `Ctrl+Alt+P` to Shift+Tab**. In plan mode it disables `edit`/`write` and restricts `bash` to a read-only allowlist (footer shows `⏸ plan`); `/plan` also toggles it, `--plan` starts in it. The rebind is two coupled edits — keep them together:
|
|
||||||
|
|
||||||
- `extensions/plan-mode/index.ts` (~line 157): `pi.registerShortcut("shift+tab", …)`, and the upstream `import { Key } … }` is removed. This file is a **local fork** of `/opt/pi-coding-agent/examples/extensions/plan-mode/`; on a pi upgrade, re-pull from there and re-apply these two edits.
|
|
||||||
- `keybindings.json` moves `app.thinking.cycle` off Shift+Tab to `ctrl+shift+t`, so the toggle fires deterministically (otherwise it collides with the built-in thinking-cycle binding).
|
|
||||||
|
|
||||||
After editing an extension or `keybindings.json`, run `/reload` in pi to apply without restarting.
|
|
||||||
|
|
||||||
## Status bar (custom footer)
|
|
||||||
|
|
||||||
`extensions/statusbar.ts` replaces pi's default footer via `ctx.ui.setFooter()`. It
|
|
||||||
mirrors the Claude Code status line (`~/.config/claude/statusline.py`) but swaps the
|
|
||||||
emoji for **Nerd Font (Material Design) glyphs** — the same family already used in
|
|
||||||
tmux (waybar cpu/mem/net glyphs) and nvim — so it renders natively in kitty
|
|
||||||
(CaskaydiaCove Nerd Font Mono). Colors come from the active theme, not raw ANSI.
|
|
||||||
|
|
||||||
Icon legend (each glyph echoes the Claude emoji it stands in for):
|
|
||||||
|
|
||||||
| Glyph | Codepoint | Segment | ~ Claude |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `` | `U+F024B` nf-md-folder | cwd (`~`-collapsed) | 📁 |
|
|
||||||
| `` | `U+F062C` nf-md-source_branch | git branch | 🌿 |
|
|
||||||
| `` | `U+F06A9` nf-md-robot | model id + `· thinking` | 🤖 |
|
|
||||||
| `` | `U+F021A` nf-md-text_box | context-window % used (+ tokens) | 📝 |
|
|
||||||
| `` | `U+F04C5` nf-md-speedometer | last response's decode throughput (t/s) | — |
|
|
||||||
| `` | `U+F002A` nf-md-alert_outline | context-budget warning widget (above editor, ≥80%) | — |
|
|
||||||
| `↑ ↓` | — | session input / output tokens | 💰 |
|
|
||||||
|
|
||||||
Two extras beyond the footer line: a themed "breathing" pulse **working-indicator**
|
|
||||||
(the streaming spinner), and a **context-budget warning widget** above the editor
|
|
||||||
that appears once the window is ≥80% full (`warning`, then `error` ≥90%) nudging a
|
|
||||||
`/compact`. Both are reset when the footer is toggled off.
|
|
||||||
|
|
||||||
Segments render left→right and truncate at the terminal edge. git only shows inside
|
|
||||||
a git repo; context, t/s and tokens only appear after the first response (t/s is
|
|
||||||
`usage.output ÷ (message_end − first streamed token)`, so prompt-eval time is
|
|
||||||
excluded). Extension statuses (e.g. plan-mode's `⏸ plan`) are preserved at the far
|
|
||||||
left. The Claude bar's system row (RAM/CPU/temp/disk) is intentionally omitted —
|
|
||||||
that data isn't in the footer API, and the tmux bar below pi already shows
|
|
||||||
cpu/mem/net. `/statusbar` toggles it off (restores the built-in footer) and back on;
|
|
||||||
`/reload` picks up edits to this file (a *new* extension needs a re-stow first — see
|
|
||||||
top).
|
|
||||||
|
|
||||||
## Vim input editor
|
|
||||||
|
|
||||||
The modal (vim-like) input editor is the npm package
|
|
||||||
**[`pi-vim`](https://github.com/lajarre/pi-vim)** (pinned by `settings.json > packages`,
|
|
||||||
installed into the gitignored `.pi/agent/npm/`, configured under `settings.json > piVim`;
|
|
||||||
v0.14.1 at adoption). It replaced the hand-rolled local fork on 2026-08-05; the fork was parked
|
|
||||||
as `extensions/vim-editor.ts.disabled` and **deleted 2026-08-09** once the package had proven
|
|
||||||
itself in daily use. `git show c6566af^:common/.pi/agent/extensions/vim-editor.ts` brings it back
|
|
||||||
if it is ever wanted.
|
|
||||||
**That swap is the whole point: no more re-pull-and-re-apply on every pi upgrade** — use
|
|
||||||
`pi update npm:pi-vim`, and `pi remove npm:pi-vim` to back out.
|
|
||||||
|
|
||||||
⚠️ **This pi build needs pi-vim's peer dep installed by hand.** `pi` here is the AUR
|
|
||||||
`pi-coding-agent`: a Bun-compiled ELF at `/opt/pi-coding-agent/pi` with the
|
|
||||||
`@earendil-works/*` packages *embedded* as virtual modules, not present on disk
|
|
||||||
(`/opt/pi-coding-agent/node_modules/` holds only `@mariozechner`). pi-vim's
|
|
||||||
`clipboard-mirror.ts` calls `import.meta.resolve("@earendil-works/pi-coding-agent")` at
|
|
||||||
**module top level** — it bakes that URL into the source of a spawned clipboard helper —
|
|
||||||
so after a bare `pi install npm:pi-vim` the resolve throws and the whole extension fails
|
|
||||||
to load: `Cannot find module '@earendil-works/pi-coding-agent'`. No setting dodges it;
|
|
||||||
`clipboardMirror: "never"` can't, because the call runs at import time. Fix, once per host:
|
|
||||||
|
|
||||||
```fish
|
|
||||||
cd ~/.pi/agent/npm
|
|
||||||
fnm exec --using=22 -- npm install --save-exact @earendil-works/pi-coding-agent@0.83.0 # match `pi --version`
|
|
||||||
```
|
|
||||||
|
|
||||||
pi's own `pi install` deliberately does *not* pull peers (it would duplicate the runtime),
|
|
||||||
so this is manual, costs ~170 MB inside the gitignored `npm/`, and wants re-pinning after a
|
|
||||||
pi upgrade (hence `--save-exact`: npm's default `^` range would let a later `npm install`
|
|
||||||
drift the peer off `pi --version` on its own). The static
|
|
||||||
`CustomEditor`/`SettingsManager`/`matchesKey` imports still bind to pi's embedded copy —
|
|
||||||
verified in the TUI — so the on-disk copy stays inert apart from the spawned helper. Upstream has no issue filed for this (checked 2026-08-05); the proper fix is
|
|
||||||
making that resolve lazy. `@burneikis/pi-vim` and `pi-vimmode` use no `import.meta.resolve`
|
|
||||||
and need no peer install, if this ever gets annoying.
|
|
||||||
|
|
||||||
Five modes — INSERT, NORMAL, VISUAL, V-LINE, EX. The active one renders as a word label
|
|
||||||
(` NORMAL `) at the **bottom-right** of the editor border, doubling as a pending-command
|
|
||||||
display: `3d2w`, `ci"`, `25gg` appear as you type them (` NORMAL 3d2w_ `). That's a
|
|
||||||
**position change from the fork**, which put a single `[N]` tag bottom-*left*.
|
|
||||||
|
|
||||||
- **motions** — `hjkl` · `w`/`b`/`e` + `W`/`B`/`E` · `0`/`^`/`$` · `{`/`}` paragraph ·
|
|
||||||
`gg`/`G` with counts (`25gg`) · `f`/`F`/`t`/`T` + `;` repeat · `%` matching pair
|
|
||||||
- **operators** — `d`/`c`/`y` + any motion · `dd`/`cc`/`yy`/`Y` · `x`/`X`/`s`/`S`/`C`/`D` ·
|
|
||||||
`o`/`O` open line · `r` replace char · `J`/`gJ` join · `p`/`P` put
|
|
||||||
- **text objects** — `iw`/`aw` · `iW`/`aW` · `i"`/`a"` · `i(`/`a(` · `i[`/`a[` · `i{`/`a{`
|
|
||||||
- **undo/repeat** — `u` and `Ctrl+R`, scoped to vim changes · `.` repeats the last edit
|
|
||||||
- **EX** — `:` dispatches real pi commands (`:model`, `:tree`) and shells out via
|
|
||||||
`:!git status`. pi exposes no command-dispatch API, so it re-submits the line as if
|
|
||||||
typed, snapshotting and restoring the prompt around it.
|
|
||||||
- cursor shape follows the mode (bar in INSERT, block elsewhere) via DECSCUSR.
|
|
||||||
|
|
||||||
Upstream implements no search (`/`, `?`, `n`, `N`), no macros (`q`/`@`) and no visual-block.
|
|
||||||
The fork had none of those either, so nothing regressed; `pi-vimmode` has them but is
|
|
||||||
rougher (5★/18 open issues vs 75★/2 at the time of choosing).
|
|
||||||
|
|
||||||
### Config (`settings.json > piVim`)
|
|
||||||
|
|
||||||
- `modeColors` — theme tokens, **ported from the fork**: `insert: success` (green),
|
|
||||||
`normal: accent` (mauve), `visual: warning`. ⚠️ the fork's header comment claimed visual
|
|
||||||
was "peach", but `warning` resolves to **yellow** `#f9e2af` in catppuccin-mocha — the port
|
|
||||||
keeps what actually rendered and hands the *peach* (`bashMode`) to the new EX mode.
|
|
||||||
- `borderSync` all `host`, `labelSync` all `mode` — also the upstream defaults, but pinned
|
|
||||||
explicitly so an upstream default change can't start repainting the input border. Matches
|
|
||||||
the fork: only the label is tinted, the border is left to the host.
|
|
||||||
- `clipboardMirror: "yank"` — only an explicit `y` reaches the **OS** clipboard. Upstream's
|
|
||||||
default `"all"` mirrors deletes too, so every `dd`/`x` would clobber the Wayland clipboard;
|
|
||||||
`"never"` is exact fork parity (pi's kill-ring only). Writes go through
|
|
||||||
`@mariozechner/clipboard` in a spawned helper, not a direct `wl-copy`.
|
|
||||||
- `exCommand.piDispatch: true` — the `:` bridge above. `copyInputToClipboard: false`: it
|
|
||||||
copies the composed prompt out, an exfiltration path, so upstream only honors it from the
|
|
||||||
user-global file (never project settings) — leave it off.
|
|
||||||
- `modeChange` (unset) — would run a shell command on every INSERT/NORMAL transition.
|
|
||||||
|
|
||||||
`/vim` still toggles the editor, now from **`extensions/vim-toggle.ts`** (pi-vim registers no
|
|
||||||
commands of its own): off drops the editor component for the rest of the session, on calls
|
|
||||||
`ctx.reload()` — the same flow as `/reload` — which re-runs discovery and reinstalls it.
|
|
||||||
No clash with `statusbar.ts`: pi-vim draws its label inside its own editor component, not
|
|
||||||
through `setFooter()`.
|
|
||||||
|
|
||||||
## Compaction tuning (small local windows)
|
|
||||||
|
|
||||||
`settings.json > compaction` is set for the small local context windows (24k–128k;
|
|
||||||
several presets are only 24k). `reserveTokens: 6144` (headroom for the
|
|
||||||
response; auto-compaction fires at `contextTokens > contextWindow − reserveTokens`)
|
|
||||||
and `keepRecentTokens: 6000` (kept verbatim, not summarized) — both well below pi's
|
|
||||||
16384/20000 defaults so short windows aren't dominated by the reserve or thrash into
|
|
||||||
repeated compaction. These are **global** (pi has no per-model compaction): on the
|
|
||||||
128k model you could raise `keepRecentTokens` for richer retained context; if you
|
|
||||||
see responses truncate near a full window, raise `reserveTokens` toward the models'
|
|
||||||
`maxTokens` (8192). All model `contextWindow`s are verified to match the server
|
|
||||||
`ctx-size` in `fl/.config/llamacpp/config.ini`.
|
|
||||||
|
|
||||||
## Providers and models
|
|
||||||
|
|
||||||
Two OpenAI-compatible providers are configured, both serving the same catalog of small local/self-hosted models (Qwen3-Coder-30B, Gemma 4, GLM-4.7-Flash, etc.). All have `cost: 0`:
|
|
||||||
|
|
||||||
- **`duskadiy`** — remote, `https://llm.duskadiy.com/api/v1`, key `$DUSKADIY_API_KEY`.
|
|
||||||
- **`localcpp`** — LAN llama.cpp server at `http://192.168.0.204:11343/v1`, no key.
|
|
||||||
|
|
||||||
⚠️ **`duskadiy` is parked out of the model picker** (2026-08-05): `settings.json > enabledModels` lists only `localcpp/*`. pi resolves `auth.json`'s `$DUSKADIY_API_KEY` from the environment, and this host has no `~/.config/fish/conf.d/secrets.fish`, so the credential resolves empty, pi drops the whole provider, and the `duskadiy/*` glob then matched nothing — printing `Warning: No models match pattern "duskadiy/*"` on every single start. Its box is down independently of the missing key: `llm.duskadiy.com` still resolves (`24.135.113.16`) but TCP 443 times out, as does the apex. Provider, catalog and auth entry are all left **intact** — re-add `"duskadiy/*"` to `enabledModels` once the key is back on the host and the box answers.
|
|
||||||
|
|
||||||
`defaultProvider` stays `duskadiy` on purpose. With the provider dropped pi falls back to the same model id under `localcpp` (the two catalogs are mirrors), so startup resolves fine today — and when duskadiy returns it is the provider that works **off**-LAN, unlike `localcpp`'s `192.168.0.204`. Don't "simplify" it to `localcpp` without weighing that.
|
|
||||||
|
|
||||||
When adding/editing a model, keep `id` exactly matching the server's model id (the section name in `fl/.config/llamacpp/config.ini`), and set `contextWindow` to match the `ctx-size` the model is actually loaded with server-side.
|
|
||||||
|
|
||||||
## Secrets
|
|
||||||
|
|
||||||
`$DUSKADIY_API_KEY` is the only real secret. It is defined in `.config/fish/conf.d/secrets.fish` (gitignored) and referenced — never inlined — in tracked config. Keep it that way: tracked files (`auth.json`, `models.json`) must contain `$DUSKADIY_API_KEY`, not the literal token.
|
|
||||||
|
|
||||||
`auth.json` is **no longer tracked** (2026-08-08, ahead of making the repo public): pi's interactive `/login` rewrites it with the **literal** key, so a tracked copy was one stray `/login` away from committing a real token. `auth.json.example` carries the same `$VAR` references. Every historical version of the old tracked file was audited and only ever contained references — nothing leaked. Installed-package and runtime artifacts (`.pi/agent/npm/`, `git/`, `trust.json`, `sessions/`) are gitignored.
|
|
||||||
|
|
||||||
## Running pi
|
|
||||||
|
|
||||||
- `pi` — interactive TUI. `pi -p "<prompt>"` — non-interactive, print and exit.
|
|
||||||
- `pi -c` / `pi -r` — continue / pick a session to resume.
|
|
||||||
- `pi config` — TUI to enable/disable discovered resources. `pi --list-models [search]` — list available models.
|
|
||||||
- `--provider` / `--model` / `--thinking` override the `settings.json` defaults per-run.
|
|
||||||
- pi also auto-discovers `CLAUDE.md`/`AGENTS.md` as context files (disable with `-nc`) — i.e. pi reads this very file too, not just Claude Code.
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# Global agent instructions
|
|
||||||
|
|
||||||
You are a coding assistant running on small, locally-hosted models. Be precise and economical with tokens.
|
|
||||||
|
|
||||||
## Working style
|
|
||||||
- Act with tools instead of describing what you would do. Keep prose short.
|
|
||||||
- Do the task that was asked. Don't add unrequested changes, refactors, or files.
|
|
||||||
- When done, stop. A one- or two-line summary is enough; no recaps of obvious steps.
|
|
||||||
|
|
||||||
## Files & edits
|
|
||||||
- Read a file before you edit it. Never guess at file paths, function names, or APIs — verify first.
|
|
||||||
- Use the edit tool for changes. Make minimal, targeted diffs; never paste an entire file back to the user.
|
|
||||||
- Match the surrounding code's style, naming, and imports.
|
|
||||||
|
|
||||||
## Shell
|
|
||||||
- Run one command at a time and check its output before the next.
|
|
||||||
- Prefer `rg` and `fd` for search. Use read-only commands when exploring.
|
|
||||||
- Never run destructive or system-changing commands (`rm -rf`, `sudo`, package installs, force-push) unless explicitly asked.
|
|
||||||
|
|
||||||
## Honesty
|
|
||||||
- If you're unsure, say so and check rather than inventing an answer.
|
|
||||||
- Report failures plainly with the actual error; don't claim success you didn't verify.
|
|
||||||
Reference in New Issue
Block a user