SLOPSHOPPER

rho

Moves Rho's instructions from the first user message into the system prompt, in place of the Claude Code sections that contradict them.

newprompt
★ 1v0.1.0MPL-2.0updated 2026-10-14maan2003/rho/agent-host/rho-claude/plugin
A shopper browsing a rack in a slop shop
Source 1 files
hooks/register.ts 48 lines
1import type { On, PromptComposeSection } from 'claude-code'
2
3/**
4 * Of `lean_body`, only its `IMPORTANT:` paragraphs: its intro and its
5 * `# Harness` list describe a terminal agent with file tools, and Rho's
6 * own prompt says who the agent is and how it acts.
7 */
8function important(section: PromptComposeSection): PromptComposeSection[] {
9  const text = section.text
10    .split(/\n{2,}/)
11    .filter(paragraph => paragraph.trimStart().startsWith('IMPORTANT:'))
12    .join('\n\n')
13  return text === '' ? [] : [{ ...section, text }]
14}
15
16/**
17 * Rho mounts the agent's prompt as the account's `CLAUDE.md`, which Claude
18 * Code sends in the first user message. Here the same file becomes the
19 * system prompt and the user message loses it; where this module does not
20 * load, the agent still reads the prompt there.
21 */
22export function register(on: On): void {
23  on('prompt.compose', async ($, e, next) => {
24    const { sections } = await next(e)
25    const home = await $.env.get('CLAUDE_CONFIG_DIR')
26    const text = await $.fs.read(`${home}/CLAUDE.md`)
27    // Rho's prompt covers what Claude Code's other sections say, or
28    // contradicts it. `lean_body` is shared, so it goes ahead of the rest.
29    const kept = sections.flatMap(section =>
30      section.id === 'lean_body' ? important(section) : [],
31    )
32    return { sections: [...kept, { id: 'rho:instructions', text, scope: 'session' }] }
33  })
34
35  // Claude Code's environment note names its own working directory, which
36  // can differ from the agent's, and the token count never changes.
37  on('prompt.attachment', { type: 'environment' }, () => ({ text: null }))
38  on('prompt.attachment', { type: 'total_tokens_reminder' }, () => ({ text: null }))
39
40  on('prompt.context', async ($, e, next) => {
41    const { blocks } = await next(e)
42    return {
43      blocks: blocks.filter(block => block.name !== 'claudeMd' && block.name !== 'userEmail'),
44      instructionFiles: [],
45    }
46  })
47}
48