diff --git a/README.md b/README.md index cef57e2..1ec8daf 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ dotfiles/ ### Extras +- secret-tool (Debian only) - fzf - tree-sitter-cli - lesspipe @@ -124,7 +125,7 @@ sudo apt update && sudo apt install terraform ```bash # System packages -sudo apt install alacritty zsh tmux stow zsh-syntax-highlighting less python3-libtmux cbonsai shfmt shellcheck lua-check +sudo apt install alacritty zsh tmux stow zsh-syntax-highlighting less python3-libtmux cbonsai shfmt shellcheck lua-check secret-tool # Homebrew packages brew install neovim stylua lua-language-server bash-language-server prettier terraform-ls tflint ruff markdownlint-cli2 fzf tree-sitter-cli diff --git a/nvim/.config/nvim/lua/plugins/ai/init.lua b/nvim/.config/nvim/lua/plugins/ai/init.lua index 7f9c5e4..6aa579b 100644 --- a/nvim/.config/nvim/lua/plugins/ai/init.lua +++ b/nvim/.config/nvim/lua/plugins/ai/init.lua @@ -8,47 +8,52 @@ vim.pack.add({ }) local codecompanion = require("codecompanion") --- Dynamically choose the adapter based on the environment variable -local active_adapter = os.getenv("GEMINI_API_KEY") and "gemini" or "kiro" +local has_gemini = os.getenv("GEMINI_API_KEY") codecompanion.setup({ - strategies = { - chat = { adapter = active_adapter }, - inline = { adapter = active_adapter }, - agent = { adapter = active_adapter }, + 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 = { - gemini = function() - return require("codecompanion.adapters").extend("gemini", { - env = { - -- We read the API key from the environment variable. - -- You can also use "cmd:pass show gemini/api_key" or "cmd:cat ~/.gemini_api_key" - api_key = os.getenv("GEMINI_API_KEY") or "cmd:cat ~/.gemini_api_key", - }, - schema = { - model = { - default = "gemini-3.1-pro-preview", + http = { + gemini = function() + return require("codecompanion.adapters").extend("gemini", { + env = { + api_key = os.getenv("GEMINI_API_KEY") or "cmd:cat ~/.gemini_api_key", }, - }, - }) - end, - -- If 'kiro' is a custom CLI or local LLM, you must define it here. - -- This is a generic custom adapter stub for CodeCompanion: - kiro = function() - return require("codecompanion.adapters").extend("openai_compatible", { - name = "kiro", - env = { - url = "http://localhost:11434", -- Example endpoint - -- api_key = "...", - }, - }) - end, + schema = { + model = { + default = "gemini-3.1-pro-preview", + }, + }, + }) + end, + }, + acp = { + kiro = function() + return require("codecompanion.adapters").extend("kiro", { + defaults = { + model = "kiro_default", + }, + }) + end, + }, }, }) -- Optional: Keymaps for CodeCompanion vim.keymap.set({ "n", "v" }, "a", "CodeCompanionChat Toggle", { noremap = true, silent = true }) -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 @@ -65,6 +70,7 @@ blink.setup({ completion = { menu = { border = "rounded" }, documentation = { auto_show = true, window = { border = "rounded" } }, + ghost_text = { enabled = true }, -- Enables ghost text in insert mode }, cmdline = { @@ -92,6 +98,8 @@ blink.setup({ 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 }, }, }, diff --git a/nvim/.config/nvim/lua/plugins/basic/init.lua b/nvim/.config/nvim/lua/plugins/basic/init.lua index bd091e6..711bdbe 100644 --- a/nvim/.config/nvim/lua/plugins/basic/init.lua +++ b/nvim/.config/nvim/lua/plugins/basic/init.lua @@ -45,12 +45,42 @@ local function get_project_root() return tf_root or nvim_root or git_root or lang_root or vim.fn.getcwd() end +local function get_submodule_excludes() + local git_root = vim.fs.root(0, ".git") + if not git_root then + return {} + end + local handle = + io.popen("git -C " .. vim.fn.shellescape(git_root) .. " submodule --quiet foreach 'echo $sm_path' 2>/dev/null") + if not handle then + return {} + end + local excludes = {} + for line in handle:lines() do + table.insert(excludes, line) + end + handle:close() + return excludes +end + vim.keymap.set("n", "pf", function() - require("fzf-lua").files({ cwd = get_project_root() }) + local fzf = require("fzf-lua") + local defaults = fzf.defaults.files + local fd_opts = defaults.fd_opts + for _, sub in ipairs(get_submodule_excludes()) do + fd_opts = fd_opts .. " --exclude " .. sub + end + fzf.files({ cwd = get_project_root(), fd_opts = fd_opts }) end, { desc = "Fuzzy find project files" }) vim.keymap.set("n", "ps", function() - require("fzf-lua").live_grep({ cwd = get_project_root() }) + local fzf = require("fzf-lua") + local defaults = fzf.defaults.grep + local rg_opts = defaults.rg_opts + for _, sub in ipairs(get_submodule_excludes()) do + rg_opts = rg_opts .. " -g '!" .. sub .. "'" + end + fzf.live_grep({ cwd = get_project_root(), rg_opts = rg_opts }) end, { desc = "Live grep project text" }) vim.keymap.set("n", "", function() @@ -58,7 +88,12 @@ vim.keymap.set("n", "", function() end, { desc = "Fuzzy find open files" }) vim.keymap.set("n", "gf", function() - require("fzf-lua").git_files() + local excludes = get_submodule_excludes() + local pathspecs = {} + for _, sub in ipairs(excludes) do + table.insert(pathspecs, ":(exclude)" .. sub) + end + require("fzf-lua").git_files({ cmd = "git ls-files --exclude-standard -- . " .. table.concat(pathspecs, " ") }) end, { desc = "Fuzzy find tracked Git files" }) vim.api.nvim_create_autocmd("BufEnter", { diff --git a/nvim/.config/nvim/lua/plugins/formatting/init.lua b/nvim/.config/nvim/lua/plugins/formatting/init.lua index c5fef55..f6614c5 100644 --- a/nvim/.config/nvim/lua/plugins/formatting/init.lua +++ b/nvim/.config/nvim/lua/plugins/formatting/init.lua @@ -19,6 +19,12 @@ require("nvim-treesitter").install({ "yaml", }) +vim.api.nvim_create_autocmd("FileType", { + callback = function() + pcall(vim.treesitter.start) + end, +}) + -- Default to treesitter folding vim.opt.foldexpr = "v:lua.vim.treesitter.foldexpr()" vim.opt.foldmethod = "expr" diff --git a/zsh/.zsh_aliases b/zsh/.zsh_aliases index 6ccd81f..2421319 100644 --- a/zsh/.zsh_aliases +++ b/zsh/.zsh_aliases @@ -17,12 +17,84 @@ for index ({1..9}) alias "c$index"="cd +${index}"; unset index # shfmt:ignore:end alias kpget="keepassxc-cli show -a Password ${KEEPASS_DB}" +alias kptotp="keepassxc-cli show -t ${KEEPASS_DB}" +# --- KeePassXC cached session (password stored in OS keychain) --- +# macOS: security (Keychain) +# Linux: secret-tool (GNOME Keyring via libsecret-tools) +_KP_KEYCHAIN_SVC="keepassxc-cli-cache" +_KP_KEYCHAIN_ACCT="master-password" + +_kp_pw_store() { + case "$(uname -s)" in + Darwin) + security add-generic-password -U \ + -s "$_KP_KEYCHAIN_SVC" -a "$_KP_KEYCHAIN_ACCT" -w "$1" + ;; + Linux) + echo -n "$1" | secret-tool store --label="$_KP_KEYCHAIN_SVC" \ + service "$_KP_KEYCHAIN_SVC" account "$_KP_KEYCHAIN_ACCT" + ;; + esac +} + +_kp_pw_get() { + case "$(uname -s)" in + Darwin) + security find-generic-password \ + -s "$_KP_KEYCHAIN_SVC" -a "$_KP_KEYCHAIN_ACCT" -w 2>/dev/null + ;; + Linux) + secret-tool lookup \ + service "$_KP_KEYCHAIN_SVC" account "$_KP_KEYCHAIN_ACCT" 2>/dev/null + ;; + esac +} + +_kp_pw_clear() { + case "$(uname -s)" in + Darwin) + security delete-generic-password \ + -s "$_KP_KEYCHAIN_SVC" -a "$_KP_KEYCHAIN_ACCT" &>/dev/null + ;; + Linux) + secret-tool clear \ + service "$_KP_KEYCHAIN_SVC" account "$_KP_KEYCHAIN_ACCT" 2>/dev/null + ;; + esac +} + +_kp_run() { + local pw stderr_redir="/dev/null" + [[ -n "$KP_DEBUG" ]] && stderr_redir="/dev/stderr" + pw=$(_kp_pw_get) + if [[ -z "$pw" ]]; then + read -rs "pw?KeePassXC master password: " "$stderr_redir" +} + +kpclose() { _kp_pw_clear && echo "KeePassXC session cleared."; } + +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() { - export GEMINI_API_KEY=$(kpget "Gemini API Key") + export GEMINI_API_KEY=$(kpgets "Gemini API Key") echo "Gemini API Key loaded into environment!" } +function totp() { + case "$1" in + hcf) kptotps "personal/Dev/AWS Console" ;; + aws) kptotps "work/Own/AWS console" ;; + pci) kptotps "work/Own/PCI/AWS Workspaces" ;; + *) echo "Usage: totp {hcf|aws|pci}" >&2; return 1 ;; + esac +} + _get_aws_config_path() { local config_path="${AWS_CONFIG_FILE:-$HOME/.aws/config}" echo "$config_path"