SLOPSHOPPER

normal-swe

Engineering skills and specialist agents for Claude Code.

newrowsguardtoolmodel
A shopper browsing a rack in a slop shop
README

Normal SWE plugin

A Claude Code plugin with eight skills, four subagents, and an Editor tool for software development. It is based on agents and skills from Amp, pstack, HumanLayer, and Matt Pocock's skills. The repository also includes an optional, standalone main-agent system prompt, described under Extras.

Installation

Add the marketplace and install the plugin:

claude plugin marketplace add andreimaxim/claude-normal-swe-plugin
claude plugin install normal-swe@andreimaxim

How to use

Describe the task in natural language instead of invoking slash commands. The model decides which skills to load and which subagents to consult. One prompt can combine research, review, and document writing:

read the Jira ticket JIRA-123, see how it's implemented in Rails and run it by Oracle to see if the solution fits our shape then create a document for me to review

The sections below describe common workflows. For a summary of each skill and agent, see Skills and Agents.

Plan the work

Use shaping when work involves consequential choices about scope, behavior, or architecture. The skill is based on the shaping phase of 37signals' Shape Up.

  1. Bring a raw idea, such as a feature request, a difficult bug report, or an architectural problem.
  2. Agree on the specific problem and how much change and complexity you are willing to take on.
  3. Develop the main solution elements and resolve feasibility questions that would block the work. The Librarian subagent can research existing code, and the Oracle subagent can help examine difficult technical choices.
  4. Write a bounded plan, called a pitch in Shape Up, that explains the problem, solution, boundaries, and exclusions. The plan leaves implementation choices open instead of listing every task.

Implement an agreed plan

Use implementing to build the plan as small, end-to-end changes, verifying each one. The skill draws on 37signals' Shape Up building phase. It accepts a plan from shaping, from another workflow, or written by you. After a long shaping session, you can start a fresh conversation with the plan.

An independent reviewer must review the integrated result, and the Oracle subagent can provide that review. If no independent reviewer is available, the agent reports that the review is missing instead of treating self-review as a substitute.

These skills do not change Claude Code's native Plan mode or permissions.

Improve existing code

Use improving-code to improve the design of existing code. When the design needs substantial investigation, the Gardener subagent investigates it and proposes concrete improvements. The main agent chooses which improvements to make, implements and verifies them, and obtains an independent review. For substantial changes, the main agent can use implementing, with the Oracle subagent reviewing the integrated result.

Write technical documents

Use technical-writing for plans, documentation, ticket solutions, and PR descriptions. The skill starts from the reader's needs. When the Editor tool is available, it revises the draft's structure and wording, and the main agent decides which revisions to accept and applies them. You can also use the Editor tool for model instructions and other substantial drafts.

Other tasks

  • Use naming-things to choose names and terminology.
  • Use explaining-code to understand how code behaves and why it was designed that way.
  • Use writing-prompts to write clearer model instructions and subagent briefs.
  • Use building-skills to create custom skills for reusable capabilities.

Skills

  • shaping: produces a bounded plan that defines a solution's main elements and resolves major risks while leaving implementation choices open.
  • implementing: implements agreed plans in independently verifiable scopes defined during implementation, with architectural refinement and independent verification.
  • improving-code: improves existing code's design, maintainability, and testability. Uses the Gardener subagent for substantial design investigation, then implements and verifies the chosen changes.
  • naming-things: chooses names and terminology by clarifying meaning, behavior, and reader context.
  • explaining-code: explains how code and software systems work and investigates the reasons behind design decisions. Inspired by HumanLayer's show-me skill.
  • technical-writing: writes developer documentation and passes the completed draft to the Editor tool, when available, to improve clarity and flow.
  • building-skills: writes and revises skill prompts, including activation descriptions, instructions, and references.
  • writing-prompts: writes and revises model instructions in system prompts, skills, repository guidance, and tool descriptions.

Agents

The plugin's hooks module registers these agents and the Editor tool, so they need a Claude Code version that loads hooks modules. Without one, only the skills are available.

Oracle (Fable high) reviews code, investigates difficult bugs, and advises on consequential architecture decisions. It examines the relevant code, callers, and tests and reports findings or recommendations with supporting evidence. It is read-only and does not implement changes. Inspired by Amp's Oracle.

Librarian (Sonnet high) researches local and external code, including behavior, architecture, dependencies, and commit history, across multiple investigation steps. It returns concise, self-contained answers that explain the relevant code and cite sources. It leaves the working checkout unchanged and does not execute the code it researches. Inspired by Amp's Librarian.

Gardener (Sonnet high) investigates existing code and proposes concrete improvements based on design analysis, refactoring techniques, and available tools. It may run disposable experiments to test assumptions or compare designs. The main agent evaluates and implements the proposals.

Tools

Editor (Opus low) improves a draft's clarity and flow while preserving its meaning. It can rebuild sentences, reorder paragraphs, and remove AI writing patterns. Inspired by Amp's plugin tools.

Extras

extra/SYSTEM.md is an optional, standalone replacement for Claude Code's main-agent system prompt. The plugin neither loads nor requires it. Using it replaces the entire default prompt, including built-in tool guidance and safety instructions. See the extra README for its intended behavior, loading instructions, companion settings, and limitations.

Source 5 files
hooks/register.ts 42 lines
1import type { Register } from 'claude-code'
2import { gardener } from './agents/gardener'
3import { librarian } from './agents/librarian'
4import { oracle } from './agents/oracle'
5import { drawEditorRow, editor, hideEditorResult, reviseDraft } from './tools/editor'
6
7export const AGENT_NOT_REGISTERED = 'normal-swe could not register the agent'
8
9const AGENTS = [oracle, librarian, gardener]
10
11export const register: Register = (on) => {
12  on('session.start', async ($, e, next) => {
13    await $.tool.register(editor)
14
15    // A hook that throws is skipped whole, so one failed agent must not cost the others.
16    for (const agent of AGENTS) {
17      try {
18        await $.agent.register({
19          ...agent,
20          prompt: await $.fs.read(`${$.plugin.root}/prompts/${agent.name}.md`),
21        })
22      } catch (error) {
23        $.ui.log(`${AGENT_NOT_REGISTERED} ${agent.name}: ${error}`)
24      }
25    }
26
27    return next(e)
28  })
29
30  on('tool.call', { tool: 'mcp__normal-swe__editor' }, reviseDraft)
31  on(
32    'ui.render',
33    { component: 'ToolUse', props: { tool: 'mcp__normal-swe__editor' } },
34    drawEditorRow,
35  )
36  on(
37    'ui.render',
38    { component: 'ToolResult', props: { tool: 'mcp__normal-swe__editor' } },
39    hideEditorResult,
40  )
41}
42
hooks/agents/gardener.ts 12 lines
1import type { AgentSpec } from 'claude-code'
2
3// The system prompt is prompts/gardener.md.
4export const gardener = {
5  name: 'gardener',
6  description:
7    'Investigates existing code and proposes concrete improvements to its design, maintainability, and testability. Use for substantial structural or maintainability analysis.',
8  model: 'sonnet',
9  effort: 'high',
10  tools: ['Read', 'Glob', 'Grep', 'Edit', 'Write', 'Bash', 'WebSearch', 'WebFetch'],
11} satisfies Omit<AgentSpec, 'prompt'>
12
hooks/agents/librarian.ts 12 lines
1import type { AgentSpec } from 'claude-code'
2
3// The system prompt is prompts/librarian.md.
4export const librarian = {
5  name: 'librarian',
6  description:
7    'Researches code in local workspaces and external repositories. Use for multi-step code discovery, behavior and architecture questions, dependency research, and commit history. Use direct reads or rg for known paths and exact symbols.',
8  model: 'sonnet',
9  effort: 'high',
10  tools: ['Read', 'Glob', 'Grep', 'Bash', 'WebSearch', 'WebFetch'],
11} satisfies Omit<AgentSpec, 'prompt'>
12
hooks/agents/oracle.ts 12 lines
1import type { AgentSpec } from 'claude-code'
2
3// The system prompt is prompts/oracle.md.
4export const oracle = {
5  name: 'oracle',
6  description:
7    'Read-only expert advisor for focused code reviews, difficult debugging, and consequential architecture questions. Use when explicitly requested or when direct investigation leaves a specific high-impact question unresolved.',
8  model: 'fable',
9  effort: 'high',
10  tools: ['Read', 'Glob', 'Grep', 'Bash'],
11} satisfies Omit<AgentSpec, 'prompt'>
12
hooks/tools/editor.ts 72 lines
1import type { MatchedHook, ToolSpec } from 'claude-code'
2
3export const MISSING_TASK =
4  'The editor tool needs a `task` containing the full draft text to revise, plus the intended audience, tone, and constraints. Editor cannot read files, URLs, or this conversation, so paste the contents instead of a path or reference.'
5
6export const editor = {
7  name: 'editor',
8  description:
9    'Revise a supplied draft with Editor, using Opus at low effort. It improves sentence and paragraph structure and removes AI writing patterns while preserving meaning. Supply the draft and a self-contained brief. Editor has no tools, file access, or conversation history. It returns the revised text, followed by any notes after a line containing `--- Editor notes ---`. The caller reads files and applies edits.',
10  inputSchema: {
11    type: 'object',
12    properties: {
13      task: {
14        type: 'string',
15        minLength: 1,
16        description:
17          'The draft text, intended audience, requested tone, constraints, and any supporting context or source excerpts. Include the contents to revise, not just file paths or URLs.',
18      },
19    },
20    required: ['task'],
21    additionalProperties: false,
22  },
23} satisfies ToolSpec
24
25// Answers calls to the editor tool, with prompts/editor.md as the system prompt.
26export const reviseDraft: MatchedHook<'tool.call', { tool: 'mcp__normal-swe__editor' }> = async (
27  $,
28  e,
29) => {
30  if (typeof e.task !== 'string' || !e.task.trim()) {
31    return { deny: MISSING_TASK }
32  }
33
34  const reply = await $.model.complete({
35    model: 'opus',
36    effort: 'low',
37    maxTokens: 32000,
38    system: await $.fs.read(`${$.plugin.root}/prompts/editor.md`),
39    prompt: e.task,
40  })
41
42  return reply.isAnswered ? { result: reply.text } : { deny: `Editor failed: ${reply.reason}` }
43}
44
45type EditorRow = { isRunning: boolean; isErrored: boolean; isInterrupted: boolean }
46
47// Draws the editor's transcript row as one dim line, spaced and indented like the engine's folded
48// tool rows, so the draft never fills the screen. The transcript still stores the call whole.
49export const drawEditorRow: MatchedHook<
50  'ui.render',
51  { component: 'ToolUse'; props: { tool: 'mcp__normal-swe__editor' } }
52> = ($, e) => ({
53  type: 'Box',
54  props: { marginTop: 1, marginLeft: 2 },
55  children: [{ type: 'Text', props: { dimColor: true }, children: [describeEditorRow(e.props)] }],
56})
57
58// Draws nothing under the editor's row: the caller applies the revision, so the transcript need
59// not show it. The model still reads the full result. A failure's reason is drawn as the engine
60// draws any tool's, since nothing else on the screen says why the Editor did not answer.
61export const hideEditorResult: MatchedHook<
62  'ui.render',
63  { component: 'ToolResult'; props: { tool: 'mcp__normal-swe__editor' } }
64> = ($, e, next) => (e.props.isErrored ? next(e) : { type: 'Box', props: { display: 'none' } })
65
66export function describeEditorRow({ isRunning, isErrored, isInterrupted }: EditorRow): string {
67  if (isInterrupted) return 'The Editor was interrupted.'
68  if (isErrored) return 'The Editor could not revise the draft.'
69  if (isRunning) return 'Asking the Editor to revise a draft…'
70  return 'Asked the Editor to revise a draft.'
71}
72