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

hooks/register.ts 48 lines1import 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