SLOPSHOPPER

events-demo

Model request/response and notification events, a subagent, and a provider offer

new
A shopper browsing a rack in a slop shop
README

events-demo

Sample mod for the roadmap E events and registrations. It:

  • adds an instruction to every model call with model.request (systemAppend),
  • counts model replies and the tools they ask for with model.response,
  • counts desktop notifications with Notification (Claude Code's hook name, mapped to notification),
  • counts tool runs with PostToolUse / PostToolUseFailure and shows the last subagent result with SubagentStop (mapped to tool.result, tool.error, subagent.stop),
  • registers the events-demo-doc-checker subagent from agents/doc-checker.md with $.agent.register,
  • offers a local Ollama provider with $.provider.register.

Not installed by default. Copy this folder to ~/.pawn/mods/events-demo and allow it in the review dialog. The dialog rates it High risk because it changes what is sent to the model and offers a provider. The provider appears under Settings → Mods → Providers offered by mods; it is only added, turned off, when you click Add.

Docs: docs/agent/MODS.md.

Source 1 files
hooks/register.js 86 lines
1// events-demo: the roadmap E events and registrations in one small mod.
2let calls = 0
3let lastTools = []
4let notes = 0
5let toolRuns = 0
6let toolFails = 0
7let lastSubagent = 'none'
8
9// $.slot calls are written out literally so the review dialog can list them.
10function draw($) {
11  const el = $.ui.resolve({})
12  $.slot.sidebar({
13    id: 'events',
14    title: 'Events demo',
15    tree: [
16      el.Text({ text: 'Model calls: ' + calls }),
17      el.Text({ text: 'Last tools: ' + (lastTools.join(', ') || 'none') }),
18      el.Text({ text: 'Notifications seen: ' + notes }),
19      el.Text({ text: 'Tool runs: ' + toolRuns + ' (failed ' + toolFails + ')' }),
20      el.Text({ text: 'Last subagent: ' + lastSubagent })
21    ]
22  })
23}
24
25export function register(on) {
26  on('session.start', async ($, e, next) => {
27    if (e.plugin !== $.plugin.name) return next(e)
28
29    // A subagent from a Claude Code agent file shipped with the mod.
30    const md = await $.fs.read($.plugin.root + '/agents/doc-checker.md')
31    $.agent.register({ name: 'doc-checker', markdown: md })
32
33    // A provider offer. Nothing changes until you click Add in Settings → Mods.
34    $.provider.register({
35      id: 'local-ollama',
36      name: 'Local Ollama',
37      apiFormat: 'openai',
38      baseUrl: 'http://localhost:11434/v1',
39      models: [{ modelId: 'qwen3:8b', label: 'Qwen3 8B', contextWindow: 32768, supportsTools: true }]
40    })
41
42    draw($)
43    return next(e)
44  })
45
46  // Before each model call: add one short instruction to the system prompt.
47  on('model.request', async ($, e, next) => {
48    return next({ ...e, systemAppend: 'When you finish a change, name the files you touched.' })
49  })
50
51  // After each model call: count it (observe only).
52  on('model.response', async ($, e, next) => {
53    calls += 1
54    lastTools = e.toolCalls.slice(0, 3)
55    draw($)
56    return next(e)
57  })
58
59  // Claude Code's Notification hook name works too.
60  on('Notification', async ($, e, next) => {
61    notes += 1
62    draw($)
63    return next(e)
64  })
65
66  // Claude Code's PostToolUse / PostToolUseFailure: after a tool really ran.
67  on('PostToolUse', async ($, e, next) => {
68    toolRuns += 1
69    draw($)
70    return next(e)
71  })
72  on('PostToolUseFailure', async ($, e, next) => {
73    toolRuns += 1
74    toolFails += 1
75    draw($)
76    return next(e)
77  })
78
79  // Claude Code's SubagentStop: when a subagent finishes (ok, error, or aborted).
80  on('SubagentStop', async ($, e, next) => {
81    lastSubagent = e.agent_type + ' ' + e.status + ' (' + e.rounds + ' rounds)'
82    draw($)
83    return next(e)
84  })
85}
86