Generic dev-workflow skills for git worktrees, GitHub issues, pull requests, Azure Key Vault dev secrets, and developer machine setup, plus a pinned…

Generic dev-workflow skills for Claude Code: git worktrees, GitHub issue tracking, pull requests, PR review triage, Azure Key Vault setup for local dev secrets, and a Windows developer machine setup script.
claude plugin install elite-dev@elitebusinesssolutions
| Skill | Invoke | Purpose |
|---|---|---|
create-pr | /elite-dev:create-pr | Open a PR: suggest a review, flag stale docs, apply body conventions and copy labels |
plan-issue | /elite-dev:plan-issue | Turn a GitHub issue into a reuse-checked implementation plan, inside plan mode |
review-fix-pr-comments | /elite-dev:review-fix-pr-comments | Triage and (on approval) fix unresolved PR review comments |
setup-keyvault | /elite-dev:setup-keyvault | Set up Azure Key Vault for local dev secrets in .NET, Next.js, and Aspire apps |
setup-script | /elite-dev:setup-script | Create or extend a Windows setup.ps1 that prepares a developer machine for the repo |
setup-worktree | /elite-dev:setup-worktree | Set up or clean up a git worktree for a parallel dev session |
start-issue | /elite-dev:start-issue | Mark a GitHub issue as started: assignee + project board status |
elite-dev also loads a mod that pins the context window usage (ctx 42% 84k/200k) under the prompt and refreshes it on session start, each prompt, after compaction, and each finished turn. It needs no setup beyond installing the plugin.
Requires the GitHub CLI (gh), authenticated (gh auth login) — the GitHub issue, PR, and worktree skills shell out to it. setup-keyvault instead needs the Azure CLI (az) on each developer machine that runs the configured app.
See the root README for install/update shared by every plugin in this repo, and the root CLAUDE.md for how this repo is developed.
hooks/context-status-line.ts 57 lines1import type { EngineInterface, Register } from "claude-code";
2
3import { formatContextStatus } from "./context-status-format";
4
5let latest = 0;
6
7/**
8 * Reads the session's context usage and writes it to the status line. A failed read leaves the
9 * last line in place, so the hook chain is never held up by it. A refresh that a newer one has
10 * overtaken drops its write, so the newest reading wins.
11 */
12async function refreshContextStatus($: EngineInterface) {
13 const ticket = ++latest;
14 try {
15 const { context } = await $.session.usage();
16 if (ticket === latest) {
17 await $.ui.status(formatContextStatus(context));
18 }
19 } catch {
20 // keep the previous line
21 }
22}
23
24/**
25 * Refreshes the context status line on `session.start`, `session.compact`, and `turn.complete`,
26 * each after the rest of the hook chain has run, and on `prompt.submit` without awaiting it.
27 */
28export const register: Register = (on) => {
29 on("session.start", async ($, e, next) => {
30 const result = await next(e);
31 await refreshContextStatus($);
32
33 return result;
34 });
35
36 on("prompt.submit", async ($, e, next) => {
37 // Not awaited, so the usage read never delays the prompt.
38 void refreshContextStatus($);
39
40 return next(e);
41 });
42
43 on("session.compact", async ($, e, next) => {
44 const result = await next(e);
45 await refreshContextStatus($);
46
47 return result;
48 });
49
50 on("turn.complete", async ($, e, next) => {
51 const result = await next(e);
52 await refreshContextStatus($);
53
54 return result;
55 });
56};
57hooks/context-status-format.ts 28 lines1const number = new Intl.NumberFormat("en", { notation: "compact", maximumFractionDigits: 1 });
2/** Formats a count in compact English notation with a lowercase suffix: `84000` is `84k`. */
3const compact = (n: number): string => number.format(n).replace("K", "k");
4
5/**
6 * `ctx 42% 84k/200k`. A missing percent is derived from tokens and window. A missing reading is
7 * left out, never shown as zero: `ctx 200k` with no usage, `ctx` with nothing known.
8 */
9export const formatContextStatus = (c: {
10 tokens?: number;
11 window: number;
12 percent?: number;
13}): string => {
14 const { tokens, window } = c;
15 const percent =
16 c.percent ?? (tokens !== undefined && window > 0 ? (tokens / window) * 100 : undefined);
17 const head = percent === undefined ? "ctx" : `ctx ${Math.round(percent)}%`;
18
19 if (!(window > 0)) {
20 return head;
21 }
22 if (tokens === undefined) {
23 return percent === undefined ? `${head} ${compact(window)}` : head;
24 }
25
26 return `${head} ${compact(tokens)}/${compact(window)}`;
27};
28