Tools for reviewing code quality

A Claude Code plugin marketplace package for software engineering disciplines.
In Claude Code:
claude plugin marketplace add robmadole/agent-tools
claude plugin install testing@agent-tools
claude plugin install develop@agent-tools
claude plugin install quality@agent-tools
claude plugin install humanity@agent-tools
Using open agent skills (for other Agents):
npx skills add robmadole/agent-tools
Testing tools for developing software.
| Skill | Description |
|---|---|
| browser-test | Orchestrate QA browser testing via Gherkin specs and Playwright MCP (up to 3 parallel instances). Run /browser-test in create mode (generate specs from a PR/feature) or run mode (execute existing specs). |
| bug-hunter | Time/count-boxed bug hunt against a feature branch, combining code reading with live browser verification, ending in a scored report. |
| jit-catch | Generate catching tests for Elixir code changes to surface unintended behavioral regressions before they land. |
Tools for writing code during development.
| Skill | Description |
|---|---|
| hobgoblin | Examine similar files for consistency violations and produce a list of differences. |
Tools for reviewing code quality.
| Skill | Description |
|---|---|
| deskcheck | Local, interactive PR review: splits a branch diff into sections and serves a web UI with resumable per-section "reviewed" state. |
Agent to human interaction, or dealing with the wetware simians who walk on two legs.
| Skill | Description |
|---|---|
| whoa-hang-on | Run /whoa-hang-on when you've lost the thread. The agent stops, backtracks from its last message, and catches you up as a ladder of short, self-contained rungs, one per message, checking you understand each rung before climbing to the next. |
Prerequisites: mise
mise install # install tooling (lefthook)
lefthook install # activate pre-commit hooks
The pre-commit hooks automatically:
skills/ (deploy-skills.sh)bump-plugin-versions.sh)hooks/register.tsx 84 lines1// deskcheck band: under the prompt, the running /deskcheck review's URL and
2// hunks reviewed (polled from server.py's /api/progress).
3import { atom, read, update } from 'claude-code'
4import type { EngineInterface, Register, Timer } from 'claude-code'
5
6import type { Progress } from '../types'
7
8const url = atom({ plugin: 'quality', key: 'url' } as const, null)
9const progress = atom({ plugin: 'quality', key: 'progress' } as const, null)
10
11// server.py prints this on startup; Claude reads it back in deskcheck step 4.
12const SERVING = /Serving on (http:\/\/127\.0\.0\.1:\d+)/g
13
14let timer: Timer | undefined
15
16async function poll($: EngineInterface, base: string) {
17 try {
18 const res = await $.http.fetch(`${base}/api/progress`)
19 if (!res.ok) throw new Error(String(res.status))
20 const p = JSON.parse(res.text) as Progress
21 await update($, progress, () => p)
22 } catch {
23 // ponytail: one failed poll = server gone; a relaunch re-arms us via its Serving line
24 timer?.cancel()
25 await update($, url, () => null)
26 await update($, progress, () => null)
27 }
28}
29
30function watch($: EngineInterface, base: string) {
31 timer?.cancel()
32 void poll($, base)
33 timer = $.clock.every(5000, () => void poll($, base))
34}
35
36export const register: Register = on => {
37 timer = undefined
38
39 on('session.start', async ($, e, next) => {
40 const saved = await read($, url)
41 if (saved) watch($, saved)
42 return next(e)
43 })
44
45 on('tool.call', async ($, e, next) => {
46 const ran = await next(e)
47 if (ran.deny !== undefined) return ran
48
49 const found = [...JSON.stringify(ran.result ?? '').matchAll(SERVING)].pop()?.[1]
50 if (found && found !== (await read($, url))) {
51 await update($, url, () => found)
52 await update($, progress, () => null)
53 watch($, found)
54 }
55 return ran
56 })
57
58 // Under the prompt: ours first, then the engine's hint line.
59 on('ui.render', { component: 'PromptHint' }, async ($, e, next) => {
60 const href = await read($, url)
61 if (!href) return next(e)
62
63 const hint = await next(e)
64 const p = await read($, progress)
65 const done = p !== null && p.total > 0 && p.reviewed === p.total
66 const { Box, Text, Link } = $.ui.resolve(e)
67
68 return (
69 <Box>
70 {/* ponytail: Unicode has no desk emoji; 🖥️ needs VS16 and drifts a column in some terminals */}
71 <Text>📋 </Text>
72 <Text color="magenta" bold>deskcheck </Text>
73 {/* desktop only links https: or http://localhost */}
74 <Link href={href.replace('127.0.0.1', 'localhost')} />
75 <Text color={p ? (done ? 'green' : 'yellow') : 'gray'}>
76 {p ? ` ${p.reviewed}/${p.total} hunks reviewed` : ' …'}
77 </Text>
78 <Text> </Text>
79 {hint}
80 </Box>
81 )
82 })
83}
84types/index.d.ts 8 lines1export type Progress = { reviewed: number; total: number }
2
3declare module 'claude-code' {
4 interface PluginState {
5 'quality': { url: string | null; progress: Progress | null }
6 }
7}
8