SLOPSHOPPER

reply-style

Styles replies and prompts on the desktop: verdict and action boxes, section titles, a prompt bubble

newrows
★ 2v0.2.0no licenseupdated 2026-10-11aren13/ae-cc/mods/reply-style
A shopper browsing a rack in a slop shop
README

Aren Claude Code Resources

A growing collection of custom Claude Code resources built for real workflows.

What's Inside

Commands (27 total) - Slash commands that expand into structured workflows

  • Meta-Prompting: Separate planning from execution with staged prompts
  • Todo Management: Capture context mid-work, resume later with full state
  • Thinking Models: Mental frameworks (first principles, inversion, 80/20, etc.)
  • Deep Analysis: Systematic debugging methodology with evidence and hypothesis testing

Skills (7 total) - Autonomous workflows that research, generate, and self-heal

  • Create Plans: Hierarchical project planning for solo developer + Claude workflows
  • Create Agent Skills: Build new skills by describing what you want
  • Create Meta-Prompts: Generate staged workflow prompts with dependency detection
  • Create Slash Commands: Build custom commands with proper structure
  • Create Subagents: Build specialized Claude instances for isolated contexts
  • Create Hooks: Build event-driven automation
  • Debug Like Expert: Systematic debugging with evidence gathering and hypothesis testing

Agents (3 total) - Specialized subagents for validation and quality

  • skill-auditor: Reviews skills for best practices compliance
  • slash-command-auditor: Reviews commands for proper structure
  • subagent-auditor: Reviews agent configurations for effectiveness

Installation

Option 1: Plugin Install (Recommended)

# Add the marketplace
claude plugin marketplace add aren13/ae-cc

# Install the plugin
claude plugin install ae-cc

Start a new Claude Code session to use the commands and skills.

Option 2: Manual Install

# Clone the repo
git clone https://github.com/aren13/ae-cc.git
cd ae-cc

# Install commands
cp -r commands/* ~/.claude/commands/

# Install skills
cp -r skills/* ~/.claude/skills/

Commands install globally to ~/.claude/commands/. Skills install to ~/.claude/skills/. Project-specific data (prompts, todos) lives in each project's working directory.

Commands

Meta-Prompting

Separate analysis from execution. Describe what you want in natural language, Claude generates a rigorous prompt, then runs it in a fresh sub-agent context.

Todo Management

Capture ideas mid-conversation without derailing current work. Resume later with full context intact.

Context Handoff

Create structured handoff documents to continue work in a fresh context. Reference with @./ae-cc/whats-next.md to resume seamlessly.

  • /whats-next - Create handoff document for fresh context

Create Extensions

Wrapper commands that invoke the skills below.

Audit Extensions

Invoke auditor subagents.

Self-Improvement

Thinking Models

Apply mental frameworks to decisions and problems.

Deep Analysis

Systematic debugging with methodical investigation.

  • /debug - Apply expert debugging methodology to investigate issues

Agents

Specialized subagents used by the audit commands.

Skills

Create Plans

Hierarchical project planning optimized for solo developer + Claude. Create executable plans that Claude runs, not enterprise documentation that sits unused.

PLAN.md IS the prompt - not documentation that gets transformed later. Brief → Roadmap → Research (if needed) → PLAN.md → Execute → SUMMARY.md.

Domain-aware: Optionally loads framework-specific expertise from ~/.claude/skills/expertise/ (e.g., macos-apps, iphone-apps) to make plans concrete instead of generic. Domain expertise skills are created with create-agent-skills - exhaustive knowledge bases (5k-10k+ lines) that make task specifications framework-appropriate.

Quality controls: Research includes verification checklists, blind spots review, critical claims audits, and streaming writes to prevent gaps and token limit failures.

Context management: Auto-handoff at 10% tokens remaining. Git versioning commits outcomes, not process.

Commands: /create-plan (invoke skill), /run-plan <path> (execute PLAN.md with intelligent segmentation)

See create-plans README for full documentation.

Create Agent Skills

Build skills by describing what you want. Asks clarifying questions, researches APIs if needed, and generates properly structured skill files.

Two types of skills:

  1. Task-execution skills - Regular skills that perform specific operations
  2. Domain expertise skills - Exhaustive knowledge bases (5k-10k+ lines) that live in ~/.claude/skills/expertise/ and provide framework-specific context to other skills like create-plans

Context-aware: Detects if you're in a skill directory and presents relevant options. Progressive disclosure guides you through complex choices.

When things don't work perfectly, /heal-skill analyzes what went wrong and updates the skill based on what actually worked.

Commands: /create-agent-skill, /heal-skill, /audit-skill

Create Meta-Prompts

The skill-based evolution of the meta-prompting system. Builds prompts with structured outputs (research.md, plan.md) that subsequent prompts can parse. Adds automatic dependency detection to chain research → plan → implement workflows.

Note: For end-to-end project building, consider create-plans - it's the more structured evolution of this approach with full lifecycle management (brief → roadmap → execution → handoffs). Use create-meta-prompts for abstract workflows and Claude→Claude pipelines. Use create-plans for actually building projects.

Commands: /create-meta-prompt

Create Slash Commands

Build commands that expand into full prompts when invoked. Describe the command you want, get proper YAML configuration with arguments, tool restrictions, and dynamic context loading.

Commands: /create-slash-command, /audit-slash-command

Create Subagents

Build specialized Claude instances that run in isolated contexts. Describe the agent's purpose, get optimized system prompts with the right tool access and orchestration patterns.

Commands: /create-subagent, /audit-subagent

Create Hooks

Build event-driven automation that triggers on tool calls, session events, or prompt submissions. Describe what you want to automate, get working hook configurations.

Commands: /create-hook

Debug Like Expert

Deep analysis debugging mode for complex issues. Activates methodical investigation protocol with evidence gathering, hypothesis testing, and rigorous verification. Use when standard troubleshooting fails or when issues require systematic root cause analysis.

Commands: /debug


Recommended Workflow

For building projects: Use /create-plan to invoke the create-plans skill. After planning, use /run-plan <path-to-PLAN.md> to execute phases with intelligent segmentation. This provides hierarchical planning (BRIEF.md → ROADMAP.md → phases/PLAN.md), domain-aware task generation, context management with handoffs, and git versioning.

For domain expertise: Use create-agent-skills to create exhaustive knowledge bases in ~/.claude/skills/expertise/. These skills are automatically loaded by create-plans to make task specifications framework-specific instead of generic.

Other tools: The create-meta-prompts skill and /create-prompt + /run-prompt commands are available for custom Claude→Claude pipelines that don't fit the project planning structure.


More resources coming soon.


PS: Original repo and work -> TÂCHES's https://github.com/glittercowboy/taches-cc-resources/ repo. Please use the original repo if you are starting this journey. Community Ports: OpenCode

—Aren

Source 3 files
hooks/register.tsx 110 lines
1import { atom, memberOf, read, update } from 'claude-code'
2import type { Register } from 'claude-code'
3
4import { collapsePrompt, parseReply, plain } from './parse'
5
6// Muted green read off the person's reference screenshot (approximate).
7const BUBBLE_GREEN = '#3B6637'
8
9// Past this many characters the bubble stops growing and wraps at 70% of the row.
10const BUBBLE_WRAP_AT = 60
11
12// Which long prompts the person opened, one member per transcript row.
13const isPromptOpen = atom({ plugin: 'reply-style', key: 'isPromptOpen' } as const, false)
14
15// The desktop app stamps the person's prompts as `sdk`; the terminal as `composer`.
16const PROMPT_KINDS = new Set(['composer', 'sdk', 'bridge'])
17
18export const register: Register = on => {
19  on('ui.render', { component: 'AssistantMessage' }, ($, e, next) => {
20    if (e.surface !== 'desktop' || e.props.isSummary) {
21      return next(e)
22    }
23
24    const blocks = parseReply(e.props.text)
25    if (!blocks.some(b => b.kind !== 'markdown')) {
26      return next(e)
27    }
28
29    const { Box, Text, Markdown } = $.ui.resolve(e)
30
31    return (
32      <Box flexDirection="column" gap={1}>
33        {blocks.map((b, i) => {
34          switch (b.kind) {
35            case 'verdict':
36              return (
37                <Box key={`b${i}`} borderStyle="bold" borderColor={b.label === 'Decision' ? 'permission' : 'claude'} paddingX={1}>
38                  <Text>
39                    <Text bold color={b.label === 'Decision' ? 'permission' : 'claude'}>
40                      {b.label.toUpperCase()}
41                    </Text>
42                    {`  ${plain(b.text)}`}
43                  </Text>
44                </Box>
45              )
46            case 'section':
47              return (
48                <Box key={`b${i}`} flexDirection="column">
49                  <Text bold color="claude">
50                    {b.label}
51                  </Text>
52                  {b.body.trim() === '' ? null : <Markdown text={b.body} />}
53                </Box>
54              )
55            case 'action':
56              return (
57                <Box key={`b${i}`} flexDirection="column" borderStyle="bold" borderColor="warning" paddingX={1}>
58                  <Text bold color="warning">
59                    ACTION NEEDED
60                  </Text>
61                  {b.body.trim() === '' ? null : <Markdown text={b.body} />}
62                </Box>
63              )
64            case 'markdown':
65              return <Markdown key={`b${i}`} text={b.text} />
66          }
67        })}
68      </Box>
69    )
70  })
71
72  on('ui.render', { component: 'UserMessage' }, async ($, e, next) => {
73    if (e.surface !== 'desktop' || !PROMPT_KINDS.has(e.props.origin.kind)) {
74      return next(e)
75    }
76
77    const { Box, Button, Text } = $.ui.resolve(e)
78    const open = memberOf(isPromptOpen, e)
79    const isOpen = await read($, open)
80    const collapsed = collapsePrompt(e.props.text)
81    const text = collapsed && !isOpen ? collapsed.head : e.props.text
82
83    return (
84      <Box flexDirection="row" justifyContent="flex-end" width="100%">
85        <Box
86          width={e.props.text.length > BUBBLE_WRAP_AT ? '70%' : undefined}
87          flexShrink={1}
88          backgroundColor={BUBBLE_GREEN}
89          borderStyle="round"
90          borderColor={BUBBLE_GREEN}
91          paddingX={2}
92          paddingY={1}
93        >
94          <Box flexDirection="column" gap={1}>
95            <Text color="#FFFFFF">{text}</Text>
96            {collapsed === null ? null : (
97              <Button
98                key="toggle"
99                label={isOpen ? 'Show less' : `Show all (${collapsed.hiddenLines} more lines)`}
100                onPress={() => update($, open, v => !v)}
101              />
102            )}
103          </Box>
104        </Box>
105      </Box>
106    )
107  })
108
109}
110
hooks/parse.ts 105 lines
1export type Block =
2  | { kind: 'verdict'; label: string; text: string }
3  | { kind: 'section'; label: string; body: string }
4  | { kind: 'action'; body: string }
5  | { kind: 'markdown'; text: string }
6
7const VERDICT = /^\*\*(Verdict|Decision):\*\*\s*/
8const LEAD_IN = /^\*\*([^*\n]{1,40})\*\*\s*$/
9const ACTION = /^\*\*Action needed:\*\*\s*/
10
11// Splits markdown into paragraphs, keeping fenced code (even unclosed while streaming) whole.
12export function paragraphs(text: string): string[] {
13  const out: string[] = []
14  let current: string[] = []
15  let inFence = false
16
17  for (const line of text.split('\n')) {
18    if (/^\s*(```|~~~)/.test(line)) {
19      inFence = !inFence
20    }
21    if (!inFence && line.trim() === '') {
22      if (current.length > 0) {
23        out.push(current.join('\n'))
24        current = []
25      }
26      continue
27    }
28    current.push(line)
29  }
30  if (current.length > 0) {
31    out.push(current.join('\n'))
32  }
33
34  return out
35}
36
37// Strips inline markdown marks for text drawn on a filled background.
38export function plain(text: string): string {
39  return text.replace(/\*\*([^*]+)\*\*/g, '$1').replace(/`([^`]+)`/g, '$1')
40}
41
42// Classifies a reply into styled blocks; plain paragraphs are merged so lists and tables stay intact.
43export function parseReply(text: string): Block[] {
44  const blocks: Block[] = []
45  const paras = paragraphs(text)
46
47  const pushMarkdown = (md: string) => {
48    const last = blocks[blocks.length - 1]
49    if (last?.kind === 'markdown') {
50      last.text += '\n\n' + md
51    } else {
52      blocks.push({ kind: 'markdown', text: md })
53    }
54  }
55
56  for (let i = 0; i < paras.length; i++) {
57    const p = paras[i]!
58
59    if (p.trim() === '---' && ACTION.test(paras[i + 1] ?? '')) {
60      continue
61    }
62    if (ACTION.test(p)) {
63      blocks.push({ kind: 'action', body: [p.replace(ACTION, ''), ...paras.slice(i + 1)].filter(s => s.trim() !== '').join('\n\n') })
64      break
65    }
66
67    const verdict = blocks.length === 0 ? VERDICT.exec(p) : null
68    if (verdict) {
69      blocks.push({ kind: 'verdict', label: verdict[1]!, text: p.slice(verdict[0].length) })
70      continue
71    }
72
73    const [first = '', ...rest] = p.split('\n')
74    const lead = LEAD_IN.exec(first)
75    if (lead) {
76      blocks.push({ kind: 'section', label: lead[1]!, body: rest.join('\n') })
77      continue
78    }
79
80    pushMarkdown(p)
81  }
82
83  return blocks
84}
85
86const PROMPT_MAX_LINES = 8
87const PROMPT_HEAD_LINES = 6
88const PROMPT_HEAD_CHARS = 600
89
90// The head of a long prompt and how many lines it hides, or null when it is short.
91export function collapsePrompt(text: string): { head: string; hiddenLines: number } | null {
92  const lines = text.split('\n')
93  if (lines.length <= PROMPT_MAX_LINES && text.length <= PROMPT_HEAD_CHARS) {
94    return null
95  }
96
97  let head = lines.slice(0, PROMPT_HEAD_LINES).join('\n')
98  if (head.length > PROMPT_HEAD_CHARS) {
99    head = head.slice(0, PROMPT_HEAD_CHARS)
100  }
101  const shownLines = head.split('\n').length
102
103  return { head: head + '…', hiddenLines: Math.max(1, lines.length - shownLines) }
104}
105
types/index.d.ts 8 lines
1export type PromptOpen = boolean
2
3declare module 'claude-code' {
4  interface PluginState {
5    'reply-style': { isPromptOpen: StateFamily<PromptOpen> }
6  }
7}
8