diff --git a/.gitignore b/.gitignore index b171063..f5df47b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -lazy-lock.json .gitconfig.local k9s/.config/k9s/clusters diff --git a/README.md b/README.md index b597a06..ef477c3 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ dotfiles/ ├── curl/ # curl configuration ├── git/ # Git configuration ├── nvim/ # Neovim configuration +├── nvidia-power-tray/ # GPU power-state tray indicator (Debian/GNOME only) ├── scripts/ # Utility scripts ├── tmux/ # tmux configuration └── zsh/ # ZSH shell configuration @@ -54,6 +55,8 @@ dotfiles/ prevent conflicting Python environments. - gdb - reattach-to-user-namespace (macOS only) +- [Claude Code](https://claude.com/claude-code) CLI, plus the + `@agentclientprotocol/claude-agent-acp` bridge used by agentic.nvim ### Language Servers & Linters @@ -76,6 +79,10 @@ dotfiles/ - tree-sitter-cli - lesspipe - cbonsai +- `nvidia-power-tray` (Debian/GNOME only): `python3-gi`, + `gir1.2-gtk-3.0`, `gir1.2-ayatanaappindicator3-0.1` and the + [AppIndicator support](https://extensions.gnome.org/extension/615/appindicator-support/) + GNOME extension. See `nvidia-power-tray/README.md`. ## Installation @@ -94,13 +101,15 @@ rustup component add rust-analyzer /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ``` -1. **Install Cursor agent binary** +1. **Install the Claude ACP bridge** -Install the [Cursor](https://www.cursor.com/) `agent` binary to -`~/.local/bin/agent` (used by avante.nvim for ACP). Then authenticate: +agentic.nvim talks to Claude over ACP and does not manage the binary itself. +Install it globally, then authenticate once with the Claude Code CLI — no API +key is needed: ```bash -~/.local/bin/agent login +npm i -g @agentclientprotocol/claude-agent-acp +claude /login ``` 1. **Install tmux-plugin-manager** @@ -164,6 +173,11 @@ cd ~/dotfiles ```bash stow zsh tmux git nvim curl alacritty wallpaper + +# Debian/GNOME only, then enable the user service +stow nvidia-power-tray +systemctl --user daemon-reload +systemctl --user enable --now nvidia-power-tray.service ``` 1. Change the default shell diff --git a/alacritty/.config/alacritty/colors/rose-pine-dawn.toml b/alacritty/.config/alacritty/colors/rose-pine-dawn.toml index 0e0444a..76f9a83 100644 --- a/alacritty/.config/alacritty/colors/rose-pine-dawn.toml +++ b/alacritty/.config/alacritty/colors/rose-pine-dawn.toml @@ -7,13 +7,15 @@ background = "#faf4ed" dim_foreground = "#797593" bright_foreground = "#575279" +# Upstream rose-pine-dawn uses #cecacd here, which is barely distinguishable +# from base. Use text/base instead so the cursor is legible. [colors.cursor] -text = "#575279" -cursor = "#cecacd" +text = "#faf4ed" +cursor = "#575279" [colors.vi_mode_cursor] -text = "#575279" -cursor = "#cecacd" +text = "#faf4ed" +cursor = "#d7827e" [colors.search.matches] foreground = "#797593" diff --git a/alacritty/.config/alacritty/colors/rose-pine.toml b/alacritty/.config/alacritty/colors/rose-pine.toml index ff757b2..d277f79 100644 --- a/alacritty/.config/alacritty/colors/rose-pine.toml +++ b/alacritty/.config/alacritty/colors/rose-pine.toml @@ -7,13 +7,15 @@ background = "#191724" dim_foreground = "#908caa" bright_foreground = "#e0def4" +# Upstream rose-pine uses highlight_high (#524f67) here, which is barely +# distinguishable from base. Use text/base instead so the cursor is legible. [colors.cursor] -text = "#e0def4" -cursor = "#524f67" +text = "#191724" +cursor = "#e0def4" [colors.vi_mode_cursor] -text = "#e0def4" -cursor = "#524f67" +text = "#191724" +cursor = "#ebbcba" [colors.search.matches] foreground = "#908caa" diff --git a/nvidia-power-tray/.config/systemd/user/nvidia-power-tray.service b/nvidia-power-tray/.config/systemd/user/nvidia-power-tray.service new file mode 100644 index 0000000..bbda651 --- /dev/null +++ b/nvidia-power-tray/.config/systemd/user/nvidia-power-tray.service @@ -0,0 +1,14 @@ +[Unit] +Description=NVIDIA GPU power state tray indicator +Documentation=file:%h/dotfiles/nvidia-power-tray/README.md +PartOf=graphical-session.target +After=graphical-session.target + +[Service] +Type=simple +ExecStart=%h/.local/bin/nvidia-power-tray +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=graphical-session.target diff --git a/nvidia-power-tray/.local/bin/nvidia-power-tray b/nvidia-power-tray/.local/bin/nvidia-power-tray new file mode 100755 index 0000000..2a2ddea --- /dev/null +++ b/nvidia-power-tray/.local/bin/nvidia-power-tray @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 +"""Expose an NVIDIA GPU's PCI power state as an AppIndicator tray icon. + +The state comes from /sys/bus/pci/devices/
/power_state, which is a +plain sysfs read and (unlike nvidia-smi) never wakes a suspended GPU. +""" + +import argparse +import os +import sys +from pathlib import Path + +PCI_DEVICES = Path("/sys/bus/pci/devices") +PCI_IDS = Path("/usr/share/misc/pci.ids") +NVIDIA_VENDOR = "0x10de" +APP_ID = "nvidia-power-tray" + +# Adwaita symbolic icons, themed automatically by the shell. +ICONS = { + "D0": "power-profile-performance-symbolic", + "D1": "power-profile-balanced-symbolic", + "D2": "power-profile-balanced-symbolic", + "D3hot": "power-profile-power-saver-symbolic", + "D3cold": "power-profile-power-saver-symbolic", +} +UNKNOWN_ICON = "power-profile-balanced-symbolic" + + +def is_display_controller(device: Path) -> bool: + """PCI class 0x03xxxx covers VGA, XGA, 3D and other display controllers.""" + try: + return device.joinpath("class").read_text().strip().startswith("0x03") + except OSError: + return False + + +def find_gpu() -> str: + """Return the address of the first NVIDIA display controller found.""" + candidates = [] + for device in sorted(PCI_DEVICES.iterdir()): + try: + vendor = device.joinpath("vendor").read_text().strip() + except OSError: + continue + if vendor.lower() != NVIDIA_VENDOR or not is_display_controller(device): + continue + # A bound nvidia/nouveau driver is the stronger signal; keep it first. + driver = read_driver(device.name) + candidates.append((driver is None, device.name)) + if not candidates: + sys.exit("no NVIDIA display controller found under /sys/bus/pci/devices") + candidates.sort() + return candidates[0][1] + + +def read_attr(address: str, *parts: str) -> str: + try: + return PCI_DEVICES.joinpath(address, *parts).read_text().strip() + except OSError: + return "unavailable" + + +def read_driver(address: str): + try: + return os.path.basename(os.readlink(PCI_DEVICES / address / "driver")) + except OSError: + return None + + +def describe(address: str) -> str: + """Human-readable device name, falling back to the raw PCI IDs. + + Deliberately looks the IDs up in pci.ids rather than shelling out to + lspci: lspci reads the device's config space, which wakes a GPU that had + reached D3cold — the exact thing this indicator exists to observe. + """ + vendor = read_attr(address, "vendor").removeprefix("0x") + device = read_attr(address, "device").removeprefix("0x") + name = lookup_pci_ids(vendor, device) + return name or f"{vendor}:{device}" + + +def lookup_pci_ids(vendor: str, device: str): + """Resolve "10de", "2d19" to a device name using the pci.ids database.""" + try: + with PCI_IDS.open(encoding="utf-8", errors="replace") as handle: + in_vendor = False + for line in handle: + if line.startswith("#") or not line.strip(): + continue + if not line.startswith("\t"): + # Vendor lines are "10de NVIDIA Corporation". + if in_vendor: + return None # left our vendor block without a match + in_vendor = line.split(None, 1)[0].lower() == vendor + elif in_vendor and not line.startswith("\t\t"): + # Device lines are "\t2d19 GB206M [GeForce RTX 5060 ...]". + code, _, name = line.strip().partition(" ") + if code.lower() == device: + return name.strip() + except OSError: + pass + return None + + +class Tray: + def __init__(self, address: str, interval: int): + self.address = address + self.interval = interval + self.state = None + + from gi.repository import GLib, Gtk + + self.GLib = GLib + self.Gtk = Gtk + self.indicator = self.make_indicator() + self.details = {} + self.indicator.set_menu(self.make_menu()) + self.refresh() + GLib.timeout_add_seconds(interval, self.refresh) + + def make_indicator(self): + import gi + + for namespace in ("AyatanaAppIndicator3", "AppIndicator3"): + try: + gi.require_version(namespace, "0.1") + module = __import__("gi.repository", fromlist=[namespace]) + appindicator = getattr(module, namespace) + break + except (ValueError, ImportError): + continue + else: + sys.exit( + "no AppIndicator GIR found; install gir1.2-ayatanaappindicator3-0.1" + ) + + indicator = appindicator.Indicator.new( + APP_ID, UNKNOWN_ICON, appindicator.IndicatorCategory.HARDWARE + ) + indicator.set_status(appindicator.IndicatorStatus.ACTIVE) + return indicator + + def make_menu(self): + menu = self.Gtk.Menu() + for key, label in ( + ("device", "Device"), + ("address", "Address"), + ("driver", "Driver"), + ("power_state", "Power state"), + ("runtime_status", "Runtime status"), + ("runtime_pm", "Runtime PM"), + ): + item = self.Gtk.MenuItem(label=f"{label}: …") + item.set_sensitive(False) + item.show() + menu.append(item) + self.details[key] = (label, item) + + separator = self.Gtk.SeparatorMenuItem() + separator.show() + menu.append(separator) + + refresh = self.Gtk.MenuItem(label="Refresh now") + refresh.connect("activate", lambda _item: self.refresh()) + refresh.show() + menu.append(refresh) + + quit_item = self.Gtk.MenuItem(label="Quit") + quit_item.connect("activate", lambda _item: self.Gtk.main_quit()) + quit_item.show() + menu.append(quit_item) + return menu + + def set_detail(self, key: str, value: str) -> None: + label, item = self.details[key] + item.set_label(f"{label}: {value}") + + def refresh(self) -> bool: + state = read_attr(self.address, "power_state") + runtime = read_attr(self.address, "power", "runtime_status") + control = read_attr(self.address, "power", "control") + driver = read_driver(self.address) or "none" + + if state != self.state: + self.state = state + self.indicator.set_icon_full( + ICONS.get(state, UNKNOWN_ICON), f"GPU power state {state}" + ) + # The second argument is a sizing hint, not a fallback value. + self.indicator.set_label(state, "D3cold") + self.indicator.set_title(f"GPU {self.address}: {state}") + + self.set_detail("address", self.address) + self.set_detail("driver", driver) + self.set_detail("power_state", state) + self.set_detail("runtime_status", runtime) + self.set_detail("runtime_pm", control) + return True # keep the GLib timeout alive + + def run(self) -> None: + self.set_detail("device", describe(self.address)) + self.Gtk.main() + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "-a", + "--address", + default=os.environ.get("NVIDIA_POWER_TRAY_ADDRESS"), + help="PCI address (e.g. 0000:01:00.0); autodetected when omitted", + ) + parser.add_argument( + "-i", + "--interval", + type=int, + default=int(os.environ.get("NVIDIA_POWER_TRAY_INTERVAL", "5")), + help="seconds between sysfs polls (default: 5)", + ) + parser.add_argument( + "-p", + "--print", + action="store_true", + dest="print_once", + help="print the current state and exit, without starting the tray", + ) + args = parser.parse_args() + + address = args.address or find_gpu() + if not PCI_DEVICES.joinpath(address, "power_state").exists(): + sys.exit(f"no power_state attribute for PCI device {address}") + + if args.print_once: + print(f"{address} {read_attr(address, 'power_state')}") + return + + import gi + + gi.require_version("Gtk", "3.0") + Tray(address, max(1, args.interval)).run() + + +if __name__ == "__main__": + main() diff --git a/nvidia-power-tray/README.md b/nvidia-power-tray/README.md new file mode 100644 index 0000000..466ef08 --- /dev/null +++ b/nvidia-power-tray/README.md @@ -0,0 +1,75 @@ +# nvidia-power-tray + +A tray indicator showing the PCI power state of the NVIDIA GPU — `D0` when the +card is awake, `D3cold` when the driver has runtime-suspended it. Useful on +hybrid-graphics laptops to see at a glance whether something is holding the +discrete GPU on. + +The state is read from `/sys/bus/pci/devices/
/power_state`. That is a +plain sysfs read and does not wake a suspended card — unlike `nvidia-smi` or +`lspci`, both of which touch the device and pull it back into `D0`. For the +same reason the device name in the menu is resolved from `/usr/share/misc/pci.ids` +instead of by shelling out to `lspci`. + +## Contents + +```text +.local/bin/nvidia-power-tray # the indicator +.config/systemd/user/nvidia-power-tray.service # runs it in the GNOME session +``` + +## Dependencies + +- GNOME with the [AppIndicator support extension](https://extensions.gnome.org/extension/615/appindicator-support/) + (`appindicatorsupport@rgcjonas.gmail.com`) — GNOME Shell has no tray of its own +- `gir1.2-ayatanaappindicator3-0.1`, `gir1.2-gtk-3.0`, `python3-gi` +- `pciutils` for `/usr/share/misc/pci.ids` (already present on Debian) + +```bash +sudo apt install gir1.2-ayatanaappindicator3-0.1 gir1.2-gtk-3.0 python3-gi +``` + +The script also accepts the older `AppIndicator3` namespace if that is what the +system ships. + +## Install + +```bash +cd ~/dotfiles +stow nvidia-power-tray +systemctl --user daemon-reload +systemctl --user enable --now nvidia-power-tray.service +``` + +The unit is `WantedBy=graphical-session.target`, so it starts with the GNOME +session and stops with it. + +## Usage + +The icon reflects the state (`power-profile-performance-symbolic` for `D0`, +`power-profile-power-saver-symbolic` for `D3hot`/`D3cold`) and the label next to +it is the raw state string. The menu shows the device, PCI address, bound +driver, power state, runtime-PM status and runtime-PM control setting, plus +`Refresh now` and `Quit`. + +The PCI address is autodetected: the first NVIDIA (vendor `0x10de`) display +controller, preferring one with a driver bound. Override it, or the 5 second +poll interval, with flags or environment variables: + +```bash +nvidia-power-tray --address 0000:01:00.0 --interval 10 +NVIDIA_POWER_TRAY_ADDRESS=0000:01:00.0 NVIDIA_POWER_TRAY_INTERVAL=10 nvidia-power-tray +``` + +`nvidia-power-tray --print` prints `
` once and exits without +touching GTK — handy for testing or for a shell prompt/status bar. + +## Troubleshooting + +```bash +systemctl --user status nvidia-power-tray.service +journalctl --user -u nvidia-power-tray.service -f +``` + +If the service runs but no icon appears, the AppIndicator extension is probably +disabled: `gnome-extensions list --enabled | grep appindicator`. diff --git a/nvim/.config/nvim/README.md b/nvim/.config/nvim/README.md index a28effd..3cef4f0 100644 --- a/nvim/.config/nvim/README.md +++ b/nvim/.config/nvim/README.md @@ -9,8 +9,8 @@ linting, formatting, and AI features. Leader key is ``. Local leader is ` | ------------ | ------ | ---------------------------------------- | | `sn` | Normal | Save without formatting | | `w` | Normal | Toggle line wrapping | -| `tt` | Normal | Alternate light/dark theme (Themery) | -| `x` | Nor/Vis| Remove char under cursor without yank | +| `tt` | Normal | Alternate light/dark background | +| `x` | Normal | Remove char under cursor without yank | | `p` | Visual | Paste without yanking underlying text | | `<` / `>` | Visual | Indent left / right | | `gl` | Normal | Jump to last change | @@ -22,7 +22,7 @@ linting, formatting, and AI features. Leader key is ``. Local leader is ` | ------------ | ------ | ---------------------------------------- | | `` | Normal | Switch to next buffer | | `` | Normal | Switch to previous buffer | -| `x` | Normal | Close buffer | +| `xb` | Normal | Close buffer | | `b` | Normal | Open new buffer | ## Splits & Navigation (Tmux Integrated) @@ -65,7 +65,7 @@ linting, formatting, and AI features. Leader key is ``. Local leader is ` | `Tab` | Command | Show and accept cmdline completion | Completion is handled natively via Blink and Neovim 0.11+ built-in -`vim.snippet`. CodeCompanion and LSP provide the completion sources. +`vim.snippet`. Sources are LSP, path, snippets and buffer. ## LSP & Navigation @@ -114,13 +114,38 @@ Supports C, C++, Rust (via gdb/rust-gdb) and Go (via delve). | `gdl` | Normal | Re-run last debug session | | `gdt` | Normal | Terminate session | -## AI (CodeCompanion) +## AI (agentic.nvim) + +Chat interface driven over ACP. The provider is detected at startup from what is +installed: `claude-agent-acp` if present, otherwise `kiro-cli` (for workstations +where Kiro is the only sanctioned agent). Neither needs an API key — +authentication is inherited from that agent's own CLI login (`claude /login` or +`kiro-cli login`). `\s` switches provider at runtime when both are installed. | Keybind | Mode | Description | | ------------ | ------------- | --------------------------------- | -| `a` | Normal/Visual | Toggle CodeCompanion Chat | -| `ca` | Visual | Add visual selection to Chat | -| `ci` | Normal/Visual | Open inline prompt | -| `cp` | Normal/Visual | Open Action Palette | +| `a` | Normal/Visual | Toggle Agentic chat | +| `ca` | Normal/Visual | Add selection or file to context | +| `cn` | Normal | Start a new session | -Active provider is automatically selected: **Gemini** (if `GEMINI_API_KEY` is set) or **Kiro** (local model). +Inside the chat widget (local leader is `\`): + +| Keybind | Mode | Description | +| ------------- | ------ | --------------------------------------- | +| `` | Normal | Submit prompt | +| `` | N/I/V | Submit prompt | +| `` | N/I/V | Switch agent mode | +| `@` | Insert | Trigger file picker | +| `/` | Insert | Trigger slash commands | +| `` | Insert | Paste image from clipboard | +| `q` | Normal | Hide the widget | +| `\s` | Normal | Switch ACP provider | +| `\m` | Normal | Switch model | +| `\t` | Normal | Select thought level | +| `\o` | Normal | Open options modal | +| `\l` | Normal | Pick a live session | +| `\[` / `\]` | Normal | Previous / next session | +| `\D` | Normal | Destroy the current session | + +Sessions are shared with the CLI: start one in the terminal and resume it here, +or the reverse. Run `:checkhealth agentic` if the provider misbehaves. diff --git a/nvim/.config/nvim/lua/plugins/ai/init.lua b/nvim/.config/nvim/lua/plugins/ai/init.lua index 6aa579b..08c2f4a 100644 --- a/nvim/.config/nvim/lua/plugins/ai/init.lua +++ b/nvim/.config/nvim/lua/plugins/ai/init.lua @@ -1,66 +1,49 @@ vim.pack.add({ - { src = "https://github.com/olimorris/codecompanion.nvim" }, - { src = "https://github.com/nvim-lua/plenary.nvim" }, + { src = "https://github.com/carlos-algms/agentic.nvim" }, { src = "https://github.com/saghen/blink.cmp", version = vim.version.range("^1"), }, }) -local codecompanion = require("codecompanion") -local has_gemini = os.getenv("GEMINI_API_KEY") +-- Chat interface over ACP. This config is shared across workstations that allow +-- different agents, so pick whichever bridge is actually on PATH, in order: +-- claude-agent-acp -- npm i -g @agentclientprotocol/claude-agent-acp +-- kiro-cli -- curl -fsSL https://cli.kiro.dev/install | bash +-- Neither takes an API key; auth is inherited from that agent's own CLI login +-- (`claude /login` / `kiro-cli login`). +local acp_bridges = { + { provider = "claude-agent-acp", command = "claude-agent-acp" }, + { provider = "kiro-acp", command = "kiro-cli" }, +} -codecompanion.setup({ - interactions = { - chat = { - adapter = has_gemini and "gemini" or "kiro", - tools = { - ["web_search"] = { - opts = { require_approval_before = false }, - }, - ["fetch_webpage"] = { - opts = { require_approval_before = false }, - }, - }, - }, - inline = { adapter = has_gemini and "gemini" or nil }, - }, - adapters = { - http = { - gemini = function() - return require("codecompanion.adapters").extend("gemini", { - env = { - api_key = os.getenv("GEMINI_API_KEY") or "cmd:cat ~/.gemini_api_key", - }, - schema = { - model = { - default = "gemini-3.1-pro-preview", - }, - }, - }) - end, - }, - acp = { - kiro = function() - return require("codecompanion.adapters").extend("kiro", { - defaults = { - model = "kiro_default", - }, - }) - end, - }, - }, +local function detect_provider() + for _, bridge in ipairs(acp_bridges) do + if vim.fn.executable(bridge.command) == 1 then + return bridge.provider + end + end + -- none installed: agentic shows its own "not installed" warning on first use + return acp_bridges[1].provider +end + +require("agentic").setup({ + provider = detect_provider(), + -- only offer the bridges that are installed, not all thirteen + provider_switcher = { hide_unhealthy_providers = true }, }) --- Optional: Keymaps for CodeCompanion -vim.keymap.set({ "n", "v" }, "a", "CodeCompanionChat Toggle", { noremap = true, silent = true }) --- Inline prompt for buffer modifications (Generates diffs) -vim.keymap.set({ "n", "v" }, "ci", "CodeCompanion ", { noremap = true, silent = true }) --- Add visual selection to chat -vim.keymap.set("v", "ca", "CodeCompanionChat Add", { noremap = true, silent = true }) --- Action palette (Use this to Accept/Reject inline diffs) -vim.keymap.set({ "n", "v" }, "cp", "CodeCompanionActions", { noremap = true, silent = true }) -vim.cmd([[cab cc CodeCompanion]]) +vim.keymap.set({ "n", "v" }, "a", function() + require("agentic").toggle() +end, { desc = "Toggle Agentic chat" }) + +vim.keymap.set({ "n", "v" }, "ca", function() + require("agentic").add_selection_or_file_to_context() +end, { desc = "Add selection or file to Agentic context" }) + +vim.keymap.set("n", "cn", function() + require("agentic").new_session() +end, { desc = "Start a new Agentic session" }) vim.cmd("packadd blink.cmp") local blink = require("blink.cmp") @@ -82,8 +65,7 @@ blink.setup({ -- defaults to Neovim 0.11+ built-in `vim.snippet` API. sources = { - -- Added codecompanion, removed emoji - default = { "lsp", "path", "snippets", "buffer", "codecompanion" }, + default = { "lsp", "path", "snippets", "buffer" }, providers = { cmdline = { min_keyword_length = function(ctx) @@ -93,14 +75,6 @@ blink.setup({ return 0 end, }, - -- Wire up CodeCompanion to Blink - codecompanion = { - name = "CodeCompanion", - module = "codecompanion.providers.completion.blink", - enabled = true, - score_offset = 100, -- Boosts priority so AI suggestions appear first in ghost text - async = true, -- Ensures AI fetching doesn't block the UI - }, }, }, fuzzy = { implementation = "prefer_rust_with_warning" }, diff --git a/nvim/.config/nvim/lua/plugins/ui/init.lua b/nvim/.config/nvim/lua/plugins/ui/init.lua index 1830896..2c1b6d4 100644 --- a/nvim/.config/nvim/lua/plugins/ui/init.lua +++ b/nvim/.config/nvim/lua/plugins/ui/init.lua @@ -19,44 +19,35 @@ end vim.cmd.colorscheme("rose-pine") +-- Cursor: the default 'guicursor' names no highlight group, so every mode +-- inherits the terminal's cursor colour and the thin insert bar gets lost +-- between characters. Name a group per mode and let Neovim drive the colour +-- (OSC 12, passed through by tmux via the Cs/Cr capabilities). +vim.opt.guicursor = { + "n-v-c-sm:block-Cursor", + "i-ci-ve:ver25-CursorInsert-blinkwait300-blinkon500-blinkoff400", + "r-cr-o:hor20-CursorReplace", +} + +local function set_cursor_highlights() + local palette = require("rose-pine.palette") + vim.api.nvim_set_hl(0, "Cursor", { fg = palette.base, bg = palette.text }) + vim.api.nvim_set_hl(0, "CursorInsert", { fg = palette.base, bg = palette.rose }) + vim.api.nvim_set_hl(0, "CursorReplace", { fg = palette.base, bg = palette.love }) +end + +-- Re-applied on ColorScheme so the colours follow the light/dark toggle below. +vim.api.nvim_create_autocmd("ColorScheme", { + pattern = "rose-pine", + callback = set_cursor_highlights, + desc = "Keep the cursor legible against the background in every mode", +}) +set_cursor_highlights() + vim.keymap.set("n", "tt", function() vim.o.background = vim.o.background == "dark" and "light" or "dark" end, { desc = "Alternate between light and dark mode" }) --- Insert-mode cursor: extmark overlay for full fg+bg control (terminal ignores fg on cursor hl groups) -local function set_icursor_hl() - local palette = require("rose-pine.palette") - vim.api.nvim_set_hl(0, "iCursorChar", { fg = palette.surface, bg = palette.love }) -end - -set_icursor_hl() -vim.api.nvim_create_autocmd({ "ColorScheme", "OptionSet" }, { - pattern = { "*", "background" }, - callback = set_icursor_hl, -}) - -local icursor_ns = vim.api.nvim_create_namespace("icursor") - -local function update_icursor() - vim.api.nvim_buf_clear_namespace(0, icursor_ns, 0, -1) - local r, c = unpack(vim.api.nvim_win_get_cursor(0)) - local line = vim.api.nvim_get_current_line() - local char = c < #line and line:sub(c + 1, c + 1) or " " - vim.api.nvim_buf_set_extmark(0, icursor_ns, r - 1, c, { - virt_text = { { char, "iCursorChar" } }, - virt_text_pos = "overlay", - }) -end - -vim.api.nvim_create_autocmd("ModeChanged", { pattern = "*:i*", callback = update_icursor }) -vim.api.nvim_create_autocmd("CursorMovedI", { callback = update_icursor }) -vim.api.nvim_create_autocmd("ModeChanged", { - pattern = "i*:*", - callback = function() - vim.api.nvim_buf_clear_namespace(0, icursor_ns, 0, -1) - end, -}) - require("nvim-highlight-colors").setup() vim.pack.add({ @@ -71,7 +62,7 @@ require("lualine").setup({ component_separators = "", theme = "rose-pine", }, - extensions = { "fugitive", "lazy" }, + extensions = { "fugitive" }, }) require("ibl").setup() diff --git a/nvim/.config/nvim/nvim-pack-lock.json b/nvim/.config/nvim/nvim-pack-lock.json index 95ceaeb..c22bb26 100644 --- a/nvim/.config/nvim/nvim-pack-lock.json +++ b/nvim/.config/nvim/nvim-pack-lock.json @@ -1,14 +1,14 @@ { "plugins": { + "agentic.nvim": { + "rev": "267b86224f9931bde47225852a58b21f9d153a9f", + "src": "https://github.com/carlos-algms/agentic.nvim" + }, "blink.cmp": { "rev": "78336bc89ee5365633bcf754d93df01678b5c08f", "src": "https://github.com/saghen/blink.cmp", "version": "1.0.0 - 2.0.0" }, - "codecompanion.nvim": { - "rev": "7d7957c26d33a97085d3a0c82eeb0147a0f51314", - "src": "https://github.com/olimorris/codecompanion.nvim" - }, "conform.nvim": { "rev": "086a40dc7ed8242c03be9f47fbcee68699cc2395", "src": "https://github.com/stevearc/conform.nvim" @@ -83,10 +83,6 @@ "rev": "0fcc83805ad11cf714a949c98c605ed717e0b83e", "src": "https://github.com/stevearc/oil.nvim" }, - "plenary.nvim": { - "rev": "b9fd5226c2f76c951fc8ed5923d85e4de065e509", - "src": "https://github.com/nvim-lua/plenary.nvim" - }, "snacks.nvim": { "rev": "ad9ede6a9cddf16cedbd31b8932d6dcdee9b716e", "src": "https://github.com/folke/snacks.nvim" diff --git a/zsh/.zsh_aliases b/zsh/.zsh_aliases index 96cdc7a..112d86c 100644 --- a/zsh/.zsh_aliases +++ b/zsh/.zsh_aliases @@ -76,17 +76,10 @@ _kp_run() { kpclose() { _kp_pw_clear && echo "KeePassXC session cleared."; } +# run with KP_DEBUG=1 to troubleshoot if needed kpgets() { _kp_run show -sa Password "$KEEPASS_DB" "$1"; } kptotps() { _kp_run show -st "$KEEPASS_DB" "$1"; } -# run with KP_DEBUG=1 to troubleshoot if needed -function load_gemini() { - if [[ -z "${MY_VARIABLE+set}" ]]; then - export GEMINI_API_KEY=$(kpgets "Gemini API Key") - echo "Gemini API Key loaded into environment!" - fi -} - function totp() { local clip=false [[ "$1" == "-c" ]] && clip=true && shift