From 29623a175582aedc6c005953f6fb570df7c99846 Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Mon, 6 Apr 2026 13:26:57 -0400 Subject: [PATCH 1/7] Add globs-scoped coding rules and ADR skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coding rules (rules/) auto-activate per file type via Claude Code native globs: frontmatter. Each complements existing hooks — rules guide how to write, hooks catch what you missed. Covers Go, TypeScript, Python, Rust, and Shell. ADR skill (skills/adr/) generates Architecture Decision Records in docs/adr/ for capturing the why behind technical choices. --- rules/go.md | 17 +++++++++++++ rules/python.md | 17 +++++++++++++ rules/rust.md | 17 +++++++++++++ rules/shell.md | 17 +++++++++++++ rules/typescript.md | 18 +++++++++++++ skills/adr/SKILL.md | 62 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+) create mode 100644 rules/go.md create mode 100644 rules/python.md create mode 100644 rules/rust.md create mode 100644 rules/shell.md create mode 100644 rules/typescript.md create mode 100644 skills/adr/SKILL.md diff --git a/rules/go.md b/rules/go.md new file mode 100644 index 0000000..694eda3 --- /dev/null +++ b/rules/go.md @@ -0,0 +1,17 @@ +--- +globs: "*.go" +--- + +# Go Rules + +Hooks already catch: error-path result access, concurrent map without mutex, filepath traversal, nil-error returns, security patterns. These rules guide *how to write* — hooks catch *what you missed*. + +- Wrap errors: `fmt.Errorf("doing X: %w", err)` — bare `return err` loses context. +- `context.Context` first param for I/O or blocking calls. +- `defer mu.Unlock()` immediately after `mu.Lock()`. +- Table-driven tests with `t.Run`. Use `t.Helper()` in test helpers. +- One package per directory. `main.go` stays thin: flags, wiring, `run()`. +- Imports: stdlib, blank, external, blank, internal. +- `defer` in loops defers until function exit — use closure or extract. +- `json.Unmarshal` into `interface{}` gives `float64` for numbers — use concrete types. +- `range` vars reused pre-1.22 — capture in closure for goroutines. diff --git a/rules/python.md b/rules/python.md new file mode 100644 index 0000000..0fd37ca --- /dev/null +++ b/rules/python.md @@ -0,0 +1,17 @@ +--- +globs: "*.py" +--- + +# Python Rules + +Hooks already catch: bare except, pass-in-except, mutable defaults, security patterns (eval, exec, pickle, os.system, yaml.load). These rules guide *how to write*. + +- `raise ... from err` to preserve exception chains. +- Type hints on function signatures. `X | None` over `Optional[X]`. +- `TypedDict` for dict shapes passed around, not bare `dict`. +- `dataclass` or `NamedTuple` over plain tuples/dicts for structured data. +- f-strings over `.format()`. `pathlib` over `os.path.join`. +- `with` for resource cleanup — files, locks, connections. +- Comprehensions over `map/filter` with lambdas. `enumerate()` over manual index. +- `is` only for `None`/`True`/`False`. `==` for value comparison. +- `ruff` for formatting and linting. diff --git a/rules/rust.md b/rules/rust.md new file mode 100644 index 0000000..6e78768 --- /dev/null +++ b/rules/rust.md @@ -0,0 +1,17 @@ +--- +globs: "*.rs" +--- + +# Rust Rules + +Hooks already catch: unwrap-after-error, `let _ =` discard, unwrap on Option/Result in non-test code. These rules guide *how to write*. + +- `?` for propagation. `thiserror` for libraries, `anyhow` for applications. +- Borrow (`&T`) over clone. `&str` in params, not `String`, unless ownership needed. +- `if let` / `let else` over `match` with one interesting arm. +- Iterators over manual loops. `enum` over boolean flags. +- Newtype for type safety: `struct UserId(u64)` not bare `u64`. +- `Debug` on all structs. `Clone`/`PartialEq` only when needed. +- `to_owned()` for `&str` → `String`. `to_string()` for Display types. +- Don't fight the borrow checker with `Rc>` unless genuinely needed. +- `cargo clippy` warnings are errors. `cargo fmt` for formatting. diff --git a/rules/shell.md b/rules/shell.md new file mode 100644 index 0000000..1b1a24b --- /dev/null +++ b/rules/shell.md @@ -0,0 +1,17 @@ +--- +globs: "*.sh" +--- + +# Shell Rules + +Hooks already catch: macOS portability (grep -P, sed -i, readlink -f, stat --format, date -d, timeout, xargs -d). These rules guide *how to write*. + +- `set -euo pipefail` always. No exceptions. +- Quote everything: `"$var"` not `$var`. `[[ ]]` not `[ ]`. +- `$(command)` not backticks. `printf` over `echo`. +- `local` for function vars. `readonly` for constants. +- `mktemp` + `trap 'rm -f "$tmpfile"' EXIT` for temp files. +- `command -v` not `which`. `${var:-default}` for defaults. +- `cd` changes dir permanently — use subshell: `(cd dir && command)`. +- Pipes create subshells — variables set in `while read | pipe` are lost. +- macOS: `sed -i ''`, `grep -E`, no `readlink -f`/`timeout`/`xargs -d`. diff --git a/rules/typescript.md b/rules/typescript.md new file mode 100644 index 0000000..e81b57c --- /dev/null +++ b/rules/typescript.md @@ -0,0 +1,18 @@ +--- +globs: "*.{ts,tsx,js,jsx,mjs,cjs}" +--- + +# TypeScript / JavaScript Rules + +Hooks already catch: empty catch blocks, unhandled promises, `any` usage, security patterns (eval, innerHTML, etc). These rules guide *how to write*. + +- `unknown` not `any`. Narrow with type guards. +- Discriminated unions over optional fields when exactly one variant applies. +- `catch (e: unknown)` and narrow — never `catch (e: any)`. +- `try/catch` at boundaries (API route, handler), not deep in logic. +- `const` over `let`. Never `var`. Named exports over default. +- `async/await` over `.then()` chains. +- Options object when 3+ params: `fn({ name, age, role })`. +- Early returns to flatten nesting. `??` over `||` when 0/"" are valid. +- `===` always. `JSON.parse` returns `any` — type immediately or validate (zod/valibot). +- `Array.sort()` mutates and sorts lexicographically — always pass comparator. diff --git a/skills/adr/SKILL.md b/skills/adr/SKILL.md new file mode 100644 index 0000000..d0482eb --- /dev/null +++ b/skills/adr/SKILL.md @@ -0,0 +1,62 @@ +--- +name: adr +description: Generate Architecture Decision Records — use when asked to document a decision, create an ADR, record why we chose X, or capture architectural rationale. +--- + +# ADR — Architecture Decision Record + +Capture the *why* behind architectural decisions so future-you (and future-agents) don't reverse them without context. + +## Step 1: Gather Context + +Identify: +- What decision was made (or needs to be made) +- What alternatives were considered +- What constraints drove the choice +- What the consequences are + +If the user hasn't specified, ask one question: "What decision are you documenting?" + +## Step 2: Check Existing ADRs + +```bash +ls docs/adr/ 2>/dev/null | tail -5 +``` + +Determine the next sequence number. If no `docs/adr/` exists, start at `0001`. + +## Step 3: Write the ADR + +Create `docs/adr/NNNN-short-title.md`: + +```markdown +# NNNN. Short Decision Title + +**Date:** YYYY-MM-DD +**Status:** accepted | proposed | deprecated | superseded by [NNNN] + +## Context + +What is the issue? What forces are at play? 2-4 sentences max. + +## Decision + +What did we decide? State it directly. 1-3 sentences. + +## Alternatives Considered + +- **Alternative A** — why rejected (1 line) +- **Alternative B** — why rejected (1 line) + +## Consequences + +What follows from this decision? Both positive and negative. Bullet list. +``` + +## Rules + +- Keep it short. An ADR is a reference, not an essay. Under 200 words total. +- One decision per ADR. If there are two decisions, write two ADRs. +- Use plain language. No jargon that requires context to parse. +- Status is usually `accepted`. Use `proposed` only if the user hasn't decided yet. +- Never modify existing ADRs — create a new one that supersedes. From a33be72315092bd725f5fd1882e411e3cd5d9a01 Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Mon, 6 Apr 2026 13:33:16 -0400 Subject: [PATCH 2/7] Move coding rules to ~/.claude/rules/ (user-level) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plugins don't support a rules/ directory — only commands/, agents/, skills/, hooks/. Rules need to live in ~/.claude/rules/ with paths: frontmatter (not globs:) per Claude Code docs. Moved there directly since this is personal tooling, not distributed. --- rules/go.md | 17 ----------------- rules/python.md | 17 ----------------- rules/rust.md | 17 ----------------- rules/shell.md | 17 ----------------- rules/typescript.md | 18 ------------------ 5 files changed, 86 deletions(-) delete mode 100644 rules/go.md delete mode 100644 rules/python.md delete mode 100644 rules/rust.md delete mode 100644 rules/shell.md delete mode 100644 rules/typescript.md diff --git a/rules/go.md b/rules/go.md deleted file mode 100644 index 694eda3..0000000 --- a/rules/go.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -globs: "*.go" ---- - -# Go Rules - -Hooks already catch: error-path result access, concurrent map without mutex, filepath traversal, nil-error returns, security patterns. These rules guide *how to write* — hooks catch *what you missed*. - -- Wrap errors: `fmt.Errorf("doing X: %w", err)` — bare `return err` loses context. -- `context.Context` first param for I/O or blocking calls. -- `defer mu.Unlock()` immediately after `mu.Lock()`. -- Table-driven tests with `t.Run`. Use `t.Helper()` in test helpers. -- One package per directory. `main.go` stays thin: flags, wiring, `run()`. -- Imports: stdlib, blank, external, blank, internal. -- `defer` in loops defers until function exit — use closure or extract. -- `json.Unmarshal` into `interface{}` gives `float64` for numbers — use concrete types. -- `range` vars reused pre-1.22 — capture in closure for goroutines. diff --git a/rules/python.md b/rules/python.md deleted file mode 100644 index 0fd37ca..0000000 --- a/rules/python.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -globs: "*.py" ---- - -# Python Rules - -Hooks already catch: bare except, pass-in-except, mutable defaults, security patterns (eval, exec, pickle, os.system, yaml.load). These rules guide *how to write*. - -- `raise ... from err` to preserve exception chains. -- Type hints on function signatures. `X | None` over `Optional[X]`. -- `TypedDict` for dict shapes passed around, not bare `dict`. -- `dataclass` or `NamedTuple` over plain tuples/dicts for structured data. -- f-strings over `.format()`. `pathlib` over `os.path.join`. -- `with` for resource cleanup — files, locks, connections. -- Comprehensions over `map/filter` with lambdas. `enumerate()` over manual index. -- `is` only for `None`/`True`/`False`. `==` for value comparison. -- `ruff` for formatting and linting. diff --git a/rules/rust.md b/rules/rust.md deleted file mode 100644 index 6e78768..0000000 --- a/rules/rust.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -globs: "*.rs" ---- - -# Rust Rules - -Hooks already catch: unwrap-after-error, `let _ =` discard, unwrap on Option/Result in non-test code. These rules guide *how to write*. - -- `?` for propagation. `thiserror` for libraries, `anyhow` for applications. -- Borrow (`&T`) over clone. `&str` in params, not `String`, unless ownership needed. -- `if let` / `let else` over `match` with one interesting arm. -- Iterators over manual loops. `enum` over boolean flags. -- Newtype for type safety: `struct UserId(u64)` not bare `u64`. -- `Debug` on all structs. `Clone`/`PartialEq` only when needed. -- `to_owned()` for `&str` → `String`. `to_string()` for Display types. -- Don't fight the borrow checker with `Rc>` unless genuinely needed. -- `cargo clippy` warnings are errors. `cargo fmt` for formatting. diff --git a/rules/shell.md b/rules/shell.md deleted file mode 100644 index 1b1a24b..0000000 --- a/rules/shell.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -globs: "*.sh" ---- - -# Shell Rules - -Hooks already catch: macOS portability (grep -P, sed -i, readlink -f, stat --format, date -d, timeout, xargs -d). These rules guide *how to write*. - -- `set -euo pipefail` always. No exceptions. -- Quote everything: `"$var"` not `$var`. `[[ ]]` not `[ ]`. -- `$(command)` not backticks. `printf` over `echo`. -- `local` for function vars. `readonly` for constants. -- `mktemp` + `trap 'rm -f "$tmpfile"' EXIT` for temp files. -- `command -v` not `which`. `${var:-default}` for defaults. -- `cd` changes dir permanently — use subshell: `(cd dir && command)`. -- Pipes create subshells — variables set in `while read | pipe` are lost. -- macOS: `sed -i ''`, `grep -E`, no `readlink -f`/`timeout`/`xargs -d`. diff --git a/rules/typescript.md b/rules/typescript.md deleted file mode 100644 index e81b57c..0000000 --- a/rules/typescript.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -globs: "*.{ts,tsx,js,jsx,mjs,cjs}" ---- - -# TypeScript / JavaScript Rules - -Hooks already catch: empty catch blocks, unhandled promises, `any` usage, security patterns (eval, innerHTML, etc). These rules guide *how to write*. - -- `unknown` not `any`. Narrow with type guards. -- Discriminated unions over optional fields when exactly one variant applies. -- `catch (e: unknown)` and narrow — never `catch (e: any)`. -- `try/catch` at boundaries (API route, handler), not deep in logic. -- `const` over `let`. Never `var`. Named exports over default. -- `async/await` over `.then()` chains. -- Options object when 3+ params: `fn({ name, age, role })`. -- Early returns to flatten nesting. `??` over `||` when 0/"" are valid. -- `===` always. `JSON.parse` returns `any` — type immediately or validate (zod/valibot). -- `Array.sort()` mutates and sorts lexicographically — always pass comparator. From 73ad72ce1e9282330cf5d7873e964b5b15c423b1 Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Mon, 6 Apr 2026 13:33:36 -0400 Subject: [PATCH 3/7] Fix ADR skill: mkdir before ls, add supersede guidance Address review findings: ensure docs/adr/ exists before listing, and clarify how to update old ADR status when superseding. --- skills/adr/SKILL.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/skills/adr/SKILL.md b/skills/adr/SKILL.md index d0482eb..c0e7359 100644 --- a/skills/adr/SKILL.md +++ b/skills/adr/SKILL.md @@ -20,10 +20,10 @@ If the user hasn't specified, ask one question: "What decision are you documenti ## Step 2: Check Existing ADRs ```bash -ls docs/adr/ 2>/dev/null | tail -5 +mkdir -p docs/adr && ls docs/adr/ 2>/dev/null | tail -5 ``` -Determine the next sequence number. If no `docs/adr/` exists, start at `0001`. +Determine the next sequence number. If no ADRs exist, start at `0001`. ## Step 3: Write the ADR @@ -59,4 +59,4 @@ What follows from this decision? Both positive and negative. Bullet list. - One decision per ADR. If there are two decisions, write two ADRs. - Use plain language. No jargon that requires context to parse. - Status is usually `accepted`. Use `proposed` only if the user hasn't decided yet. -- Never modify existing ADRs — create a new one that supersedes. +- Never modify existing ADRs — create a new one that supersedes. When superseding, update the old ADR's status line to `superseded by [MMMM]`. From 44bdbbbefdd3cd6113996cee1b67da9a8248d7a8 Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Mon, 6 Apr 2026 13:34:30 -0400 Subject: [PATCH 4/7] Fix ADR numbering: use grep + sort for deterministic sequence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address tri-review consensus finding — ls | tail is fragile for determining next ADR number. Now extracts numeric prefixes and sorts numerically. --- skills/adr/SKILL.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/skills/adr/SKILL.md b/skills/adr/SKILL.md index c0e7359..c4edc39 100644 --- a/skills/adr/SKILL.md +++ b/skills/adr/SKILL.md @@ -20,10 +20,11 @@ If the user hasn't specified, ask one question: "What decision are you documenti ## Step 2: Check Existing ADRs ```bash -mkdir -p docs/adr && ls docs/adr/ 2>/dev/null | tail -5 +mkdir -p docs/adr +ls docs/adr/ 2>/dev/null | grep -oE '^[0-9]+' | sort -n | tail -1 ``` -Determine the next sequence number. If no ADRs exist, start at `0001`. +Add 1 to the highest number found, zero-padded to 4 digits. If none exist, start at `0001`. ## Step 3: Write the ADR From a5cb29a78d62153ca2c5586e4b7515c38a44f856 Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Mon, 6 Apr 2026 13:36:48 -0400 Subject: [PATCH 5/7] Fix ADR supersede contradiction and clarify empty-dir case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rule said "never modify" then immediately said "update the status line" — contradiction. Now explicitly scopes the no-modify rule to substantive content only. Also clarifies the empty-output signal for the numbering command. --- skills/adr/SKILL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skills/adr/SKILL.md b/skills/adr/SKILL.md index c4edc39..806781b 100644 --- a/skills/adr/SKILL.md +++ b/skills/adr/SKILL.md @@ -24,7 +24,7 @@ mkdir -p docs/adr ls docs/adr/ 2>/dev/null | grep -oE '^[0-9]+' | sort -n | tail -1 ``` -Add 1 to the highest number found, zero-padded to 4 digits. If none exist, start at `0001`. +Add 1 to the highest number found, zero-padded to 4 digits. If no output, the directory is empty — start at `0001`. ## Step 3: Write the ADR @@ -60,4 +60,4 @@ What follows from this decision? Both positive and negative. Bullet list. - One decision per ADR. If there are two decisions, write two ADRs. - Use plain language. No jargon that requires context to parse. - Status is usually `accepted`. Use `proposed` only if the user hasn't decided yet. -- Never modify existing ADRs — create a new one that supersedes. When superseding, update the old ADR's status line to `superseded by [MMMM]`. +- Never modify the substantive content of existing ADRs (Context, Decision, Alternatives, Consequences). When superseding, only update the old ADR's Status line to `superseded by [MMMM]`. From 2dc2e298d862f2b0de6e211771b9486050e8657c Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Mon, 6 Apr 2026 13:39:55 -0400 Subject: [PATCH 6/7] Update skill counts and add ADR to README listings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI validate-counts requires README and ROADMAP to reflect actual skill count (17 → 18 after adding ADR skill). --- README.md | 6 ++++-- ROADMAP.md | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8bf12fc..26fb1d1 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ These replace slash commands. Ask naturally and the skill fires: | `devkit:research` | "research X", "compare approaches for", "investigate options" | | `devkit:deep-research` | "deep research", "validate this", "make sure this is right", "rigorous analysis" | | `devkit:scrape` | "scrape this URL", "fetch content from", "extract from this page" | +| `devkit:adr` | "create an ADR", "document this decision", "record why we chose X" | ### Coding Principles @@ -326,7 +327,7 @@ devkit/ │ ├── audit.md # Project health audit │ ├── repo-map.md # AST-based symbol index │ └── status.md # Health check -├── skills/ # 17 context-activated skills +├── skills/ # 18 context-activated skills │ ├── executing/SKILL.md # Principle: methodical execution │ ├── clean-code/SKILL.md # Principle: readability │ ├── dry/SKILL.md # Principle: don't repeat yourself @@ -342,7 +343,8 @@ devkit/ │ ├��─ research/SKILL.md # Auto: "research X" │ ├── deep-research/SKILL.md # Auto: "deep research", ACH pipeline │ ├── scratchpad/SKILL.md # Iteration memory protocol -│ └── scrape/SKILL.md # Auto: "scrape this URL" +│ ├── scrape/SKILL.md # Auto: "scrape this URL" +│ └── adr/SKILL.md # Auto: "create an ADR", decision records ├── agents/ # 6 agents │ ├── reviewer.md # Opus, worktree isolation │ ├── researcher.md # Sonnet, worktree isolation diff --git a/ROADMAP.md b/ROADMAP.md index cd9e9b1..4c6a28e 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -34,6 +34,6 @@ Items below were on the roadmap but determined to be unnecessary — either alre | Stop hook redesign | Still fires every turn, but exits early with `approve` when no files are changed — near-instant on clean trees, so the performance concern is moot. Revisit only if it causes measurable latency. | | Cost event hooks | Budget enforcement already exists in the Go engine via `overBudget()` + `addCost()` callbacks with hard limits | | Execution registry | Step tracking already handled by SQLite via `lib.DB` with status, cost, and timing per step | -| Preset library | The 15 YAML workflows and 17 skills already serve this purpose | +| Preset library | The 15 YAML workflows and 18 skills already serve this purpose | | Framework-specific review checklists | `lang-review.sh` covers language-level patterns; framework-specific rules are better added per-project via hookify | | Conditional hook firing | Hooks already self-filter internally (extension checks, changed-file checks); a generic condition system adds complexity for no current need | From ed15ecd74514b3ac03cc33a626f275f1c287a2a5 Mon Sep 17 00:00:00 2001 From: Tym Rabchuk Date: Mon, 6 Apr 2026 13:42:00 -0400 Subject: [PATCH 7/7] =?UTF-8?q?Fix=20ROADMAP=20skill=20count=20header=20(1?= =?UTF-8?q?7=20=E2=86=92=2018)=20and=20add=20adr=20to=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bold header line also needed updating, not just the table row. --- ROADMAP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index 4c6a28e..c1cd853 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -3,7 +3,7 @@ ## Implemented - **23 slash commands** — Lifecycle workflows, self-improvement loops, multi-agent dispatch, project health audit, post-PR monitoring, AST repo mapping, autoresearch-inspired self-audit, autoloop -- **17 context-activated skills** — 8 auto-trigger workflows (test-gen, doc-gen, changelog, onboard, research, deep-research, scrape, autoloop) + 6 coding principles (executing, clean-code, DRY, YAGNI, dont-reinvent, stuck) + 2 tools (gcli, creating-workflows) + 1 iteration memory (scratchpad) +- **18 context-activated skills** — 9 auto-trigger workflows (test-gen, doc-gen, changelog, onboard, research, deep-research, scrape, autoloop, adr) + 6 coding principles (executing, clean-code, DRY, YAGNI, dont-reinvent, stuck) + 2 tools (gcli, creating-workflows) + 1 iteration memory (scratchpad) - **6 agents** — Scoped tool access, worktree isolation, model assignment - **10 hooks** — Safety (destructive command blocking, edit-time security patterns, PR gate), observability (audit trail, slop detection, post-validation, subagent verification, language-aware code review), optimization (RTK token compression) - **Graceful degradation** — tri:* commands work with 1-3 agents depending on installed CLIs