Distill recurring mistakes into numbered, human-approved Learned Rules. Runs the learner agent, then presents each candidate for approve/edit/reject. Supports --dry-run and --retire.
Scanned 9/5/2026
Install to Claude Code
npx -y skills add OrodruinLabs/nazgul --skill learn --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Learn?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/orodruinlabs-learn)More formats (shields.io, HTML) on the badges page.
---
name: nazgul:learn
description: Distill recurring mistakes into numbered, human-approved Learned Rules. Runs the learner agent, then presents each candidate for approve/edit/reject. Supports --dry-run and --retire.
context: fork
argument-hint: "[--dry-run] [--retire]"
allowed-tools: Read, Write, Edit, Bash, Glob, Grep, Agent, ToolSearch
metadata:
author: Jose Mejia
---
# Nazgul Learn
## Examples
- `/nazgul:learn` — distill mistakes, review candidates interactively
- `/nazgul:learn --dry-run` — show candidate rules, write nothing
- `/nazgul:learn --retire` — review un-cited rules for retirement
## Arguments
$ARGUMENTS
## Current State
- Config: !`cat nazgul/config.json 2>/dev/null | head -3 || echo "NOT_INITIALIZED"`
- Learning enabled: !`jq -r '.learning.enabled // false' nazgul/config.json 2>/dev/null || echo "n/a"`
- Active rules: !`grep -c '^## LR-' nazgul/learning/learned-rules.md 2>/dev/null || echo 0`
## Instructions
### Pre-flight
0. Load the `AskUserQuestion` tool (deferred by default): run `ToolSearch` with query `select:AskUserQuestion`. Do this BEFORE any step that uses `AskUserQuestion` — the interactive approve/edit/reject prompts (Step 2) and the retirement prompt (Step R) both require it.
1. If `nazgul/config.json` is missing: "Nazgul not initialized. Run `/nazgul:init` first." and STOP.
2. If `.learning.enabled` is not `true`: "Learning is disabled (`learning.enabled=false`). Enable it in nazgul/config.json to use this." and STOP.
3. Parse `$ARGUMENTS` for flags:
- Backstop: if the `## Arguments` block above is *exactly* the literal token `$ARGUMENTS` (not substituted), STOP and report "Skill argument substitution failed — plugin bug, do not proceed."
- `--dry-run` → distill and display candidates only; write nothing to the registry or declined log, and do NOT let the learner update `.last-run`.
- `--retire` → run retirement-review mode (Step R) INSTEAD of distillation.
4. Ensure `nazgul/learning/` exists (`mkdir -p`).
### Display Banner
Output per `${CLAUDE_PLUGIN_ROOT}/references/ui-brand.md`:
```
─── ◈ NAZGUL ▸ LEARNING ────────────────────────────────
```
### Step R: Retirement review (only if --retire)
1. Read the registry (`learning.rules_doc`). List ACTIVE rules whose **Hits** is 0,
plus — if active-rule count exceeds `learning.max_active_rules` — the lowest-Hits rules.
2. For each, show LR-NNN + title + Hits + Added, then use `AskUserQuestion` (header: "Retire?", question: "Retire LR-NNN — <title>? (Hits: <n>)") with options:
- "Keep" — "Leave this rule active"
- "Retire" — "Mark it retired (kept in the file, never deleted or renumbered)"
Batch up to 4 rules per `AskUserQuestion` call (one question each) to avoid one prompt per rule.
3. On yes: edit that rule's `- **Status**:` line to `retired` (keep the rule in the
file; never delete, never renumber).
4. Summarize how many were retired. STOP (do not distill in --retire mode).
### Step 1: Distill
Resolve `ROOT` once (`git rev-parse --show-toplevel` — the directory holding `nazgul/config.json`) and
open the prompt with these two lines verbatim, `$ROOT` expanded; the learner's input contract
(RULES.md §21) STOPs without them:
```text
Dispatch brief: <main_worktree_path> = /abs/path/to/project. Nazgul config: /abs/path/to/project/nazgul/config.json.
Address every runtime-state path under that root, absolute and verbatim — your cwd is not it.
```
Dispatch the learner agent with the Agent tool, `subagent_type: "nazgul:learner"`.
It reads mistake artifacts and writes `$ROOT/nazgul/learning/proposed-rules.md`.
(For `--dry-run`, tell the learner in the prompt NOT to update `.last-run`.)
### Step 2: Interactive approval
Read `nazgul/learning/proposed-rules.md`. If it has no `## CANDIDATE` sections,
say "No recurring mistakes met the threshold — nothing to propose." and STOP.
For EACH candidate, one at a time:
1. Display: title, Scope-Agents, Scope-Globs, Confidence, Evidence, Dedup, and the body.
2. Use `AskUserQuestion` (header: "LR candidate", question: "Approve this Learned Rule — <title>?") with options:
- "Approve" — "Add this rule to the registry as-is"
- "Edit" — "Revise the title/scope/body, then add it"
- "Reject" — "Skip it and record the decision so it isn't re-proposed"
The built-in "Other" free-text response is treated as **edit** guidance (the user's revision instructions). Then act on the choice:
- **approve**:
- Get the next id: `${CLAUDE_PLUGIN_ROOT}/scripts/lib/learned-rules.sh next-id` (uses the default
registry path, or pass `--doc <rules_doc>` if config overrides it).
- Append the rule to the registry file (`learning.rules_doc`) as a
`## LR-NNN: <title>` block with metadata lines in this exact order:
Status (active), Scope-Agents, Scope-Globs, Hits (0), Added
(`date -u +%Y-%m-%d`), Evidence — then the body. (For dry-run: skip writing.)
- **edit**: let the user revise title/scope/body, then approve as above.
- **reject** (skip writing for dry-run): append one JSON line to
`nazgul/learning/declined.jsonl`:
`{"fingerprint":"<id>","reason":"<reason>","ts":"<iso8601>"}` where `<id>` is
`${CLAUDE_PLUGIN_ROOT}/scripts/lib/learned-rules.sh fingerprint "$(printf '%s\n%s' "<candidate title>" "<candidate body>")"` —
title, newline, body — identical to how the learner computes it to skip declined candidates.
3. After the last candidate, delete `proposed-rules.md` (it is transient).
### Step 3: Complete
Show a Next Up block per ui-brand.md summarizing: N approved (with the new LR
numbers), N rejected, N skipped. For dry-run, label clearly "DRY RUN — nothing written."
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!