Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ The PowerShell profile (`powershell/Microsoft.PowerShell_profile.ps1`) is a sing

1. **Phase 1 (blocking, must stay cheap)**: module-availability cache (single `PSModulePath` scan into `$global:ProfileModules`), PSReadLine with `PredictionSource History` only, keybindings, dot-source `Profile/Set-Prompt.ps1` (which defines but no longer *calls* `Initialize-AzTimer`), aliases, tool wrapper functions (`y` for yazi).
2. **Phase 2a (first idle)**: `Initialize-DeferredProfile` loads PSFzf and zoxide, upgrades PSReadLine to `HistoryAndPlugin`, sets the fd-backed `FZF_*_COMMAND` vars, defines the eza `ll`/`la`/`lt` helpers, and calls `Initialize-AzTimer` (Az context timer — `runspace.Open()` + first eventing call ~200ms, moved off the load path). Guarded by `$global:ProfileDeferredDone`. These are the interactive tools needed immediately after the prompt appears.
3. **Phase 2b (next idle)**: `Initialize-DeferredProfileSecondary` registers the az + zellij native tab-completers and loads git-completion (+ `g` alias completer) and WinGet CommandNotFound. Guarded by `$global:ProfileDeferredSecondaryDone`. Split from 2a so the first keypress isn't blocked by their import cost.
3. **Phase 2b (next idle)**: `Initialize-DeferredProfileSecondary` registers the az + zellij native tab-completers, defines the `prr`/`Invoke-AdoPrReview` ADO PR review helper (az-gated: REST-backed fzf PR picker — `ConvertFrom-AdoRemoteUrl` + a session-cached az bearer token (`Get-AdoAccessToken`), ~0.2s warm vs ~2-3s for `az repos pr list` — → detached `review` worktree → `nvim "+AdoPrReview <id>"`, pairing with ado-pr.nvim), and loads git-completion (+ `g` alias completer) and WinGet CommandNotFound. Guarded by `$global:ProfileDeferredSecondaryDone`. Split from 2a so the first keypress isn't blocked by their import cost.
4. **Phase 3 (async runspace)**: Az context refresh on a 60s timer. The timer is created in Phase 2a (`Initialize-AzTimer`); the refresh itself runs in a background runspace wired up in `Set-Prompt.ps1`.

When adding to the profile, classify by cost first — anything that loads .NET assemblies or scans the filesystem belongs in Phase 2, not Phase 1. Use the `$global:ProfileModules` cache rather than calling `Get-Module -ListAvailable` again.
Expand Down Expand Up @@ -137,7 +137,7 @@ User-scope Claude Code subagents live as flat `.md` files (frontmatter + system-

Modular Lua config targeting Neovim 0.12+. Uses `vim.pack` (Neovim 0.12 built-in package manager) — no external plugin manager. LSP is configured via the native `vim.lsp.config` / `vim.lsp.enable` API (not the lspconfig Lua framework).

Load order: `performance` → `user` → `plugins` → `options` → `keymaps` → `autocmds` → `treesitter` → `orgmode` → `lsp` → `dap` → `gitsigns` → `ui`
Load order: `performance` → `user` → `plugins` → `options` → `keymaps` → `autocmds` → `treesitter` → `orgmode` → `lsp` → `dap` → `gitsigns` → `diffview` → `ui`

The `vim/` directory (Vimscript setup) is the older Vim config — kept as the Linux fallback, not the active Neovim config. `vim/vimrc` is the vimrc; Vim finds it automatically at `~/.vim/vimrc` (Linux) or `~/vimfiles/vimrc` (Windows).

Expand All @@ -155,6 +155,7 @@ The `vim/` directory (Vimscript setup) is the older Vim config — kept as the L
- **C# / .NET**: `roslyn.nvim` (seblyng) is the C# LSP — not OmniSharp. Configured in `lsp.lua` (`require('roslyn').setup({})` + `vim.lsp.config('roslyn', …)`), gated on `dotnet` being on PATH (no `user_config` field). Analysis scope is pinned to `openFiles` (not `fullSolution`) — a deliberate perf choice. `*.cs` is a built-in filetype, so no autocmd is needed. Testing/run/build are buffer-local keymaps (`<leader>nt/nr/nb`) in `after/ftplugin/cs.lua` that shell out to the `dotnet` CLI — no `easy-dotnet`/plugin, kept minimal on purpose. Inlay hints are off by default (`<leader>th` toggles). Prerequisite global tool: `dotnet tool install -g roslyn-language-server --prerelease` (auto-detected on PATH).
- **PowerShell debugging**: `nvim-dap` (client only) + PowerShell Editor Services as the debug server — PSES *is* the DAP server VS Code drives, so no separate debugger. Wired in `lua/config/dap.lua` (loaded after `lsp`), gated on the same `pwsh_bundle_path` as the LSP; buffer-local keymaps in `after/ftplugin/ps1.lua` (`<F5>`/`<F10>`/`<F11>`/`<S-F11>`/`<S-F5>`, `<leader>b`/`<leader>B`, `<leader>nr`, `<leader>e`). State inspection uses nvim-dap's **built-in** `dap.ui.widgets.hover()` (`<leader>e`) — deliberately no `nvim-dap-ui`/`nvim-nio`/virtual-text; the built-in float + REPL are the effective minimum. The `ps1` adapter starts its **own** PSES process in `-DebugServiceOnly -EnableConsoleRepl` inside a `jobstart({ term = true })` terminal (the PowerShell Extension Terminal the debuggee runs in), then connects nvim-dap over the `debugServicePipeName` named pipe from PSES's session-details JSON (`type = 'pipe'`). **Deliberately independent of the `powershell_es` LSP** — separate PSES process, so the hand-tuned LSP block in `lsp.lua` is untouched; the two never share the session file (`powershell_es.dap_session.json` vs the LSP's stdio transport). This is a from-scratch port of the DAP handshake in `TheLeoP/powershell.nvim` (the `event_powerShell/sendKeyPress` listener and 60×500ms session-file poll included) **without** taking the plugin as a dependency — keep it in sync if that upstream changes. Do not re-fold this into the LSP config or add the plugin.
- **oil git status**: `refractalize/oil-git-status.nvim` shows per-file git status in oil's two sign columns (left = index, right = working tree). Requires oil's `win_options = { signcolumn = 'yes:2' }` (set in `ui.lua`) and `require('oil-git-status').setup()`.
- **ado-pr.nvim is loaded via rtp prepend, not vim.pack** (`plugins.lua`): it is a local checkout (`E:/Personal Projects/ado-pr.nvim`) with no git remote yet, and a vim.pack local-path spec would bake a machine-absolute `src` into `nvim-pack-lock.json`. The prepend is guarded on the directory existing, so other machines stay inert. `<leader>ga` (`diffview.lua`) opens its `:AdoPr` picker; the shell entry point is the profile's `prr`. Switch to a vim.pack spec once the plugin gets a remote.
- **Lockfile discipline**: `nvim/nvim-pack-lock.json` is vim.pack-managed — any `plugins.lua` change must be reflected there (launch nvim or `vim.pack.update()` to regenerate) and **committed together** with the plugin edit.
- **Offline / GitHub-blocked install**: `nvim/Install-PluginsOffline.ps1` provisions plugins and Treesitter parsers without cloning from `github.com`, for machines where it is blocked but `codeload.github.com` (the ZIP host) is reachable. **⚠ Its parser step is `master`-specific and is currently BROKEN for the `main` migration** — it drives nvim-treesitter's old local-path compile (no `tree-sitter` CLI, no curl/tar) and stages parser sources master's way; `main` compiles via `tree-sitter build`. The plugin-staging step still works; the parser + org-grammar steps need reworking before this script is usable on `main`. The rest of this bullet describes the still-master design: It reads `nvim-pack-lock.json`, downloads each plugin as a ZIP of its pinned rev into `<data>/site/pack/core/opt/<name>` — `vim.pack` loads any dir matching a valid lockfile entry via `:packadd` with no git calls, so a plain ZIP extract works. For parsers, it stages each `ensure_installed` grammar's source from codeload (repo from nvim-treesitter's parser config, rev from its `lockfile.json`) and lets nvim-treesitter compile from the **local** path (no network), using `zig` as the compiler. Every `nvim` call runs without the user config (`-u NONE` / minimal `packadd`) so `vim.pack.add` never fires a blocked clone. Rev-aware idempotent (per-dir `.codeload-rev` markers + a central parser-rev file) — re-run after a lockfile change to update. The parser step needs a C compiler (`winget install zig.zig`) **and** the `git` executable present (nvim-treesitter requires it, though no network is used). A third step stages **orgmode's own `tree-sitter-org` grammar**, which the parser step does *not* cover: nvim-orgmode bypasses nvim-treesitter entirely and, on first `setup()`, clones the grammar from `github.com` into `stdpath('cache')` and compiles it into its own plugin dir. The script stages that source from codeload at the version pinned in orgmode's `lua/orgmode/utils/treesitter/install.lua` (`required_version`, read not hardcoded) and lets orgmode compile it from the local path by overriding its `get_path` — orgmode *has* a local-path branch but hardcodes the github URL inside `run()`, so it's unreachable from config. This keeps orgmode's own compiler args, install path and lock format as the single source of truth. It must run **after** the plugin step: `parser/org.so` and `.org-ts-lock.json` both live *inside* the plugin dir, so a re-extract wipes them — which conveniently makes orgmode's own lock file a self-correcting marker (hence no `.codeload-rev` for it). Needs only the compiler, not `git` (orgmode shells out to git purely to clone, which is what codeload staging replaces). The script recreates `<plugin>/parser/` before invoking orgmode: orgmode ships that dir but never creates it, so deleting `org.so` to force a rebuild would otherwise fail with an opaque "cannot find the path specified" from its move.
- **No per-machine variants yet**: single `init.lua` only — no `init.WORK-PC.lua`. Apply the `.<HOSTNAME>` suffix convention when machine-specific behaviour is needed.
Expand Down
18 changes: 13 additions & 5 deletions claude/skills/review-ado-pr/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: review-ado-pr
description: "Review an Azure DevOps pull request locally, end-to-end. Use when the user says 'review this ADO PR', 'review PR <dev.azure.com url>', 'review azure devops PR <id>', or asks to look over an ADO pull request. Fetches the PR into the dedicated review worktree, runs IaC gates (Bicep), reads the diff, does a cross-model review, and — only on approval — posts inline threads / a vote back. Read-only by default. NOT for GitHub PRs (use the review skill) or the local working diff (use /code-review)."
description: "Review an Azure DevOps pull request locally, end-to-end. Use when the user says 'review this ADO PR', 'review PR <dev.azure.com url>', 'review azure devops PR <id>', 'review my pending ADO PRs' (no id needed — it discovers), or asks to look over an ADO pull request. Fetches the PR into the dedicated review worktree, runs IaC gates (Bicep), reads the diff, does a cross-model review, and — only on approval — posts inline threads / a vote back. Read-only by default. NOT for GitHub PRs (use the review skill) or the local working diff (use /code-review)."
metadata:
author: justin
version: "1.0.0"
Expand All @@ -15,7 +15,8 @@ read it, review it across models, and report. Posting anything back to the PR (t
## When to use

- The user gives an ADO PR URL or id and wants it reviewed.
- Trigger phrases: "review this ADO PR", "review PR <url>", "review azure devops PR <id>".
- Trigger phrases: "review this ADO PR", "review PR <url>", "review azure devops PR <id>",
"review my pending ADO PRs" (no id — discover via step 1).
- NOT GitHub (use the `review` skill), NOT the uncommitted working diff (use `/code-review`).

## Preconditions
Expand All @@ -27,7 +28,12 @@ read it, review it across models, and report. Posting anything back to the PR (t

## Steps

1. **Resolve the PR.** `az repos pr show --id <N> --organization <org> --query "{status:status, sourceRefName:sourceRefName, targetRefName:targetRefName, title:title, mergeStatus:mergeStatus}"`.
1. **Resolve the PR.** No id/URL given ("review my pending PRs")? Discover first — run inside the
repo (org/project/repo auto-detect from the remote):
`az repos pr list --status active --query "[].{id:pullRequestId, title:title, author:createdBy.displayName}" -o table`
and let the user pick. (From a shell, the `prr` profile helper is the same discovery: fzf-pick →
review worktree → nvim.) Then:
`az repos pr show --id <N> --organization <org> --query "{status:status, sourceRefName:sourceRefName, targetRefName:targetRefName, title:title, mergeStatus:mergeStatus}"`.
- Confirm `status` is `active`. A `lastMergeCommit` in the full payload is a **merge preview**, not
proof it merged — trust `status`, not the presence of a merge commit.
- Note the target branch; the review diff is always three-dot against it.
Expand Down Expand Up @@ -91,5 +97,7 @@ read it, review it across models, and report. Posting anything back to the PR (t
## Related

- `bicep-tdd` — the IaC gate for step 4. · `deep-review` / `codex-review` — the review passes in step 5.
- The `local-pr-review` brain initiative and `ado-pr.nvim` (a Neovim ADO PR plugin being built to fold
steps 2–3 and 7 into the editor).
- `prr` (PowerShell profile helper) — collapses discovery + steps 2–3 from a shell: fzf PR picker →
review worktree → `nvim "+AdoPrReview <id>"`.
- The `local-pr-review` brain initiative and `ado-pr.nvim` (a Neovim ADO PR plugin folding steps 2–3
and 7 into the editor: `:AdoPr` pick, `:AdoPrComment`, `:AdoPrVote`).
7 changes: 7 additions & 0 deletions nvim/lua/config/diffview.lua
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,10 @@ map('n', '<leader>gm', function()
end, { desc = 'Diffview: review branch vs default' })
map('n', '<leader>gh', '<cmd>DiffviewFileHistory<CR>', { desc = 'Diffview: repo history' })
map('n', '<leader>gH', '<cmd>DiffviewFileHistory %<CR>', { desc = 'Diffview: current file history' })

-- ado-pr.nvim (loaded in plugins.lua when the local checkout exists) layers ADO PR
-- pick/checkout/comment/vote on top of diffview. In the diff: <Tab>/<S-Tab> cycle files,
-- ]c/[c jump hunks (diffview defaults — the stepping keys for a PR review).
if pcall(require, 'ado-pr') then
map('n', '<leader>ga', '<cmd>AdoPr<CR>', { desc = 'ado-pr: pick an active PR to review' })
end
9 changes: 9 additions & 0 deletions nvim/lua/config/plugins.lua
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,12 @@ local specs = {
-- config modules (treesitter, lsp, ui, gitsigns) can use the plugins.
-- confirm=false: installs silently on a fresh machine.
vim.pack.add(specs, { load = true, confirm = false })

-- ado-pr.nvim (local checkout, no git remote yet): loaded via rtp prepend instead of a
-- vim.pack spec so the machine-absolute path never enters nvim-pack-lock.json; its
-- plugin/ file is sourced at startup like any rtp plugin, and the guard makes it inert
-- on machines without the checkout. Switch to vim.pack once it has a remote.
local ado_pr = 'E:/Personal Projects/ado-pr.nvim'
if vim.fn.isdirectory(ado_pr) == 1 then
vim.opt.rtp:prepend(ado_pr)
end
Loading