Discord OMP plugin blueprint

Session ID: 019ea972-29da-7000-9f3b-327723e8c7ca
Date: 2026-06-08 22:54:51

Associated Memory Note: 019ea972-29da-7000-9f3b-327723e8c7ca

Telemetry Stats

MetricValue
User Prompt CWD/home/dev
Duration91.15s
Total Message Events13
Input / Output Tokens60192 / 3971
Cache Read / Write104773 / 0
Estimated Cost$0.000000

Chat Transcript

πŸ‘€ User

https://llm-wiki.0rk.de/topics/discord-omp-plugin/ Create a cheat-sheet style focused, dense blueprint: laying out entire stack, architecture, ui/ux, data model, routes, integration, and rollout phases. https://llm-wiki.0rk.de/topics/discord-omp-plugin-bp/

πŸ€– Assistant

Thinking Process

The user wants me to create a new page at https://llm-wiki.0rk.de/topics/discord-omp-plugin-bp/ that serves as a dense blueprint companion to the existing cheat sheet at /topics/discord-omp-plugin/. This blueprint should cover:

  • Entire stack
  • Architecture
  • UI/UX
  • Data model
  • Routes
  • Integration
  • Rollout phases

Let me first read the existing cheat sheet to understand what’s already covered, then create the blueprint page.

Let me start by reading the existing content and understanding the site structure.

πŸ€– Toolresult

Remaining items (8):

  • Read existing cheat sheet content [in_progress] (Research)
  • Read llm-wiki site config and structure [pending] (Research)
  • Read topics _index.md for nav patterns [pending] (Research)
  • Draft discord-omp-plugin-bp blueprint page [pending] (Author)
  • Write file to /home/dv/hugo-llm-wiki/content/topics/ [pending] (Author)
  • Fix ownership and rebuild Hugo site [pending] (Publish)
  • Verify live page returns 200 OK [pending] (Publish)
  • Add blueprint link to topics index [pending] (Polish)
    Phase 1/4 β€œResearch” β€” 0/3 tasks complete
    Research:
    β†’ Read existing cheat sheet content
    β—‹ Read llm-wiki site config and structure
    β—‹ Read topics _index.md for nav patterns
    Author:
    β—‹ Draft discord-omp-plugin-bp blueprint page
    β—‹ Write file to /home/dv/hugo-llm-wiki/content/topics/
    Publish:
    β—‹ Fix ownership and rebuild Hugo site
    β—‹ Verify live page returns 200 OK
    Polish:
    β—‹ Add blueprint link to topics index

πŸ€– Assistant

πŸ€– Toolresult

[/home/dv/hugo-llm-wiki/content/topics/_index.md#6003]
1:---
2:title: β€œTopics”
3:date: 2026-05-29
4:---
5:
6:Knowledge base topics auto-discovered and curated through research pipelines. Fed by JSON API.
7:
8:## Svelte 5 Topics (New)
9:
10:- Svelte 5 Best Practices β€” Runes, derived, $effect, snippets, {@attach}, performance patterns
11:- Svelte 5 Migration Guide — Codemod, incremental migration, slots→snippets, events, dispatch→callbacks
12:
13:## Core Topics
14:
15:- Hugo Documentation Automation β€” JSON feeds, zero-build dashboard, Svelte 5 islands, taxonomy gotchas
16:- LLM-Powered Knowledge Bases β€” Self-discovering docs, RAG, structured LLM output, Svelte 5 interactive layer
17:- Self-Discovering Documentation β€” Autonomous research loop, topic discovery, quality signals, Svelte 5 TopicExplorer
18:- AI Content Evolution β€” Scheduled scans, freshness tracking, incremental updates, Svelte 5 evolution dashboard

πŸ€– Toolresult

[/home/dv/hugo-llm-wiki/config.toml#0764]
1:baseURL = β€œhttps://llm-wiki.0rk.de”
2:languageCode = β€œen-us”
3:title = β€œLLM Wiki”
4:theme = β€œlotusdocs”
5:enableRobotsTXT = true
6:enableGitInfo = false
7:enableEmoji = true
8:
9:[params]
10: description = β€œSelf-discovering documentation + system reference. Hugo static site powered by AI research pipelines.”
11: version = β€œ0.6.0”
12: lastUpdated = true
13: displayUpdatedDate = true
14: showReadingTime = true
15: showWordCount = true
16: showTableOfContents = true
17: tocLevel = 3
18:
19:[menu]
20: menu.main
21: name = β€œHome”
22: weight = 10
23: url = ”/”
24: menu.main
25: name = β€œArchitecture”
26: weight = 12
27: url = β€œ/architecture/”
28: menu.main
29: name = β€œReference”
30: weight = 15
31: url = β€œ/llm-wiki-reference/”
32: menu.main
33: name = β€œTopics”
34: weight = 20
35: url = β€œ/topics/”
36: menu.main
37: name = β€œResearch”
38: weight = 30
39: url = β€œ/research/”
40: menu.main
41: name = β€œEvolving”
42: weight = 40
43: url = β€œ/evolving/”
44: menu.main
45: name = β€œSearch”
46: weight = 50
47: url = β€œ/search/”
48:
49:[taxonomies]
50: tag = β€œtags”
51: series = β€œseries”
52:
53:[outputs]
54: home = [β€œHTML”, β€œRSS”, β€œJSON”]
55: page = [β€œHTML”, β€œJSON”]
56: section = [β€œHTML”, β€œRSS”, β€œJSON”]
57:
58:[outputFormats.JSON]
59: mediaType = β€œapplication/json”
60: baseName = β€œindex”
61: isPlainText = true
62:
63:[outputFormats.RSS]
64: mediaType = β€œapplication/rss+xml”
65: baseName = β€œindex”
66:
67:[sitemap]
68: changefreq = β€œdaily”
69: priority = 0.7
70: filename = β€œsitemap.xml”
71:
72:[markup]
73: [markup.goldmark]
74: [markup.goldmark.renderer]
75: unsafe = true
76: [markup.highlight]
77: codeFences = true
78: guessSyntax = true
79: hl_Lines = ""
80: lineNoStart = 1
81: lineNos = true
82: lineNumbersInTable = true
83: noClasses = false
84: style = β€œgithub-dark”
85: tabWidth = 2
86: [markup.tableOfContents]
87: endLevel = 3
88: ordered = false
89: startLevel = 2

πŸ€– Toolresult

[/home/dv/hugo-llm-wiki/content/topics/discord-omp-plugin.md#FBEB]
1:---
2:title: β€πŸ€– Discord.py Γ— OMP Plugin Cheat Sheet”
3:date: 2026-06-08T00:00:00+00:00
4:draft: false
5:topic_type: β€œtopics”
6:tags: [β€œdiscord”, β€œomp”, β€œplugin”, β€œmcp”, β€œextension”, β€œdiscord.py”, β€œcheatsheet”]
7:---
8:
9:# πŸ€– Discord.py Γ— OMP Plugin Cheat Sheet
10:
11:> Bridge a discord.py bot (Python) with the OMP coding agent extension system (Bun/TS) β€” from zero to full bidirectional integration.
12:
13:---
14:
15:## 🧠 Core Concept
16:
17:OMP extensions = TypeScript/Bun. discord.py = Python. They’re separate runtimes. You bridge them via one of three tiers:
18:
19:| | πŸͺ Tier 1: Webhook | πŸ”§ Tier 2: MCP Server | πŸŒ‰ Tier 3: Extension + Bot |
20:|:---|:---|:---|:---|
21:| Direction | OMP β†’ Discord only | Both (LLM invokes tools) | Both (event-driven) |
22:| Needs Python? | ❌ No | βœ… Yes | βœ… Yes |
23:| Needs Bot Token? | ❌ No (webhook URL) | βœ… Yes | βœ… Yes |
24:| Discord β†’ OMP? | ❌ | ❌ | βœ… |
25:| Auto-notify events? | βœ… | ❌ | βœ… |
26:| LLM calls Discord? | ❌ | βœ… | βœ… |
27:| Setup time | ~5 min | ~15 min | ~30–60 min |
28:| Deps | None | discord.py, mcp | discord.py, aiohttp |
29:
30:---
31:
32:## ⚑ OMP Extension Contract (All Tiers)
33:
34:typescript 35:// ~/.omp/agent/extensions/my-ext/index.ts 36:import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent"; 37: 38:export default function myExt(pi: ExtensionAPI) { 39: // ── Registration Phase (sync) ── 40: pi.setLabel("My Extension"); 41: 42: pi.on("agent_end", async (event, ctx) => { /* ... */ }); 43: pi.on("session_start", async (event, ctx) => { /* ... */ }); 44: 45: pi.registerTool({ name: "my_tool", /* ... */ }); 46: pi.registerCommand("my-cmd", { /* ... */ }); 47: 48: // ⚠️ CANNOT call pi.sendMessage() during load β€” only in handlers 49:} 50:
51:
52:# πŸ“‘ Key Lifecycle Events
53:
54:text 55:session_start β†’ input β†’ agent_start β†’ turn_start 56: β†’ tool_call β†’ tool_execution_start β†’ tool_execution_end β†’ tool_result 57: β†’ turn_end β†’ agent_end β†’ session_shutdown 58:
59:
60:| Event | Use Case | Return |
61:|:---|:---|:---|
62:| agent_end | β€œDone” notification to Discord | β€” |
63:| tool_call | Audit / block dangerous ops | { block, reason } |
64:| tool_result | Redact secrets before logging | { content } |
65:| session_start | Init bot connection | β€” |
66:| session_shutdown | Cleanup / final flush | β€” |
67:
68:---
69:
70:## πŸͺ Tier 1 β€” Webhook (Zero-Dep, 5 min)
71:
72:No bot. No Python. Just fetch(). OMP β†’ Discord one-way.
73:
74:# πŸ“ ~/.omp/agent/extensions/discord-notify/index.ts
75:
76:typescript 77:import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent"; 78: 79:export default function discordNotify(pi: ExtensionAPI) { 80: const WEBHOOK = process.env.DISCORD_WEBHOOK_URL; 81: if (!WEBHOOK) return; 82: 83: const send = (content: string) => 84: fetch(WEBHOOK, { 85: method: "POST", 86: headers: { "Content-Type": "application/json" }, 87: body: JSON.stringify({ content }), 88: }).catch(() => {}); 89: 90: // πŸ”” Auto-notify when agent finishes 91: pi.on("agent_end", async () => { 92: await send(`βœ… **Agent finished** β€” ${new Date().toLocaleTimeString()}`); 93: }); 94: 95: // πŸ’¬ Slash command: /discord-ping <message> 96: pi.registerCommand("discord-ping", { 97: description: "Send a message to the Discord webhook", 98: handler: async (args, ctx) => { 99: await send(args.trim() || "Ping from OMP!"); 100: ctx.ui.notify("Sent to Discord", "info"); 101: }, 102: }); 103:} 104:
105:
106:# πŸ”‘ Setup
107:
108:bash 109:# 1. Create webhook: Discord β†’ Server Settings β†’ Integrations β†’ Webhooks β†’ New 110:# 2. Export URL 111:export DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/123/abc..." 112: 113:# 3. Drop file, restart OMP. Done. 114:
115:
116:> ⚠️ Rate limit: Discord enforces 5 requests / 5 seconds per webhook.
117:
118:---
119:
120:## πŸ”§ Tier 2 β€” MCP Server (Bot as Tool Provider)
121:
122:discord.py bot runs as a stdio MCP server. OMP launches it, the LLM calls its tools.
123:
124:text 125:OMP Agent ◀──stdio JSON-RPC──▢ Python MCP Server ◀──gateway──▢ Discord 126:
127:
128:# πŸ“ ~/.omp/tools/discord-mcp/server.py
129:
130:python 131:#!/usr/bin/env python3 132:"""Discord bot exposed as MCP tool server.""" 133:import asyncio, os 134:import discord 135:from discord.ext import commands 136:from mcp.server import Server 137:from mcp.server.stdio import stdio_server 138:import mcp.types as types 139: 140:# ── Bot ── 141:intents = discord.Intents.default() 142:intents.message_content = True 143:bot = commands.Bot(command_prefix="!", intents=intents) 144:ready = asyncio.Event() 145: 146:@bot.event 147:async def on_ready(): 148: ready.set() 149: 150:# ── MCP Server ── 151:app = Server("discord-mcp") 152: 153:@app.list_tools() 154:async def list_tools() -> list[types.Tool]: 155: return [ 156: types.Tool( 157: name="discord_send", 158: description="Send a message to a Discord channel", 159: inputSchema={ 160: "type": "object", 161: "properties": { 162: "channel_id": {"type": "string"}, 163: "content": {"type": "string"}, 164: }, 165: "required": ["channel_id", "content"], 166: }, 167: ), 168: types.Tool( 169: name="discord_read", 170: description="Read recent messages from a Discord channel", 171: inputSchema={ 172: "type": "object", 173: "properties": { 174: "channel_id": {"type": "string"}, 175: "limit": {"type": "integer", "default": 10}, 176: }, 177: "required": ["channel_id"], 178: }, 179: ), 180: types.Tool( 181: name="discord_guilds", 182: description="List all guilds the bot is in", 183: inputSchema={"type": "object", "properties": {}}, 184: ), 185: ] 186: 187:@app.call_tool() 188:async def call_tool(name: str, arguments: dict) -> list[types.TextContent]: 189: await ready.wait() 190: 191: if name == "discord_send": 192: ch = bot.get_channel(int(arguments["channel_id"])) 193: if not ch: 194: return [types.TextContent(type="text", text="Channel not found")] 195: await ch.send(arguments["content"]) 196: return [types.TextContent(type="text", text=f"Sent to #{ch.name}")] 197: 198: if name == "discord_read": 199: ch = bot.get_channel(int(arguments["channel_id"])) 200: if not ch: 201: return [types.TextContent(type="text", text="Channel not found")] 202: msgs = [] 203: async for m in ch.history(limit=arguments.get("limit", 10)): 204: msgs.append(f"[{m.created_at:%H:%M}] {m.author.display_name}: {m.content}") 205: return [types.TextContent(type="text", text="\n".join(reversed(msgs)) or "(empty)")] 206: 207: if name == "discord_guilds": 208: lines = [f"{g.name} (id={g.id})" for g in bot.guilds] 209: return [types.TextContent(type="text", text="\n".join(lines) or "(none)")] 210: 211: return [types.TextContent(type="text", text=f"Unknown tool: {name}")] 212: 213:async def main(): 214: token = os.environ["DISCORD_TOKEN"] 215: async with bot: 216: bot_task = asyncio.create_task(bot.start(token)) 217: async with stdio_server() as (r, w): 218: await app.run(r, w, app.create_initialization_options()) 219: bot_task.cancel() 220: 221:if __name__ == "__main__": 222: asyncio.run(main()) 223:
224:
225:# πŸ“ ~/.omp/agent/mcp.json
226:
227:json 228:{ 229: "mcpServers": { 230: "discord": { 231: "command": "python3", 232: "args": ["/home/dev/.omp/tools/discord-mcp/server.py"], 233: "env": { "DISCORD_TOKEN": "DISCORD_TOKEN" } 234: } 235: } 236:} 237:
238:
239:# πŸ“¦ Install deps
240:
241:bash 242:pip install discord.py mcp 243:# env value "DISCORD_TOKEN": "DISCORD_TOKEN" = copy from shell env 244:export DISCORD_TOKEN="your-bot-token" 245:# Restart OMP β€” it auto-discovers mcp.json and launches the server 246:
247:
248:---
249:
250:## πŸŒ‰ Tier 3 β€” Full Extension + Sidecar Bot
251:
252:Most powerful. OMP extension (TS) ↔ discord.py bot (Python) over localhost HTTP. Full bidirectional event-driven bridge.
253:
254:text 255:β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” 256:β”‚ OMP Extension (Bun/TS) β”‚ 257:β”‚ β”œ on(agent_end) β†’ POST /notify β”‚ 258:β”‚ β”œ registerTool(discord_notify) β”‚ 259:β”‚ β”” registerCommand(/discord) β”‚ 260:β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ 261: β”‚ HTTP :8901 262:β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” 263:β”‚ discord.py Bot + aiohttp (Py) β”‚ 264:β”‚ β”œ POST /notify β†’ channel.send() β”‚ 265:β”‚ β”œ GET /status β†’ bot info β”‚ 266:β”‚ β”œ GET /channels β†’ list all β”‚ 267:β”‚ β”” !ask-omp <prompt> β†’ omp CLI β”‚ 268:β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ 269: β”‚ Discord Gateway 270:β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” 271:β”‚ Discord β”‚ 272:β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ 273:
274:
275:# πŸ“ Python Sidecar: discord_omp_bot.py
276:
277:python 278:#!/usr/bin/env python3 279:"""discord.py sidecar bot with HTTP API for OMP.""" 280:import asyncio, logging, os, subprocess 281:import discord 282:from discord import app_commands 283:from discord.ext import commands 284:from aiohttp import web 285: 286:log = logging.getLogger("discord-omp") 287:intents = discord.Intents.default() 288:intents.message_content = True 289:bot = commands.Bot(command_prefix="!", intents=intents) 290: 291:NOTIFY_CH = int(os.environ.get("DISCORD_NOTIFY_CHANNEL", "0")) 292:HTTP_PORT = int(os.environ.get("DISCORD_HTTP_PORT", "8901")) 293: 294:# ── HTTP endpoints (called by OMP extension) ── 295:async def handle_notify(req: web.Request) -> web.Response: 296: data = await req.json() 297: ch = bot.get_channel(NOTIFY_CH) 298: if not ch: 299: return web.json_response({"error": "channel not found"}, status=404) 300: … 364: asyncio.run(bot.start(os.environ["DISCORD_TOKEN"])) 365:
366:
…
458:
…
642:Last updated: 2026-06-08 Β· Grounded in OMP extension docs, pi-langfuse reference impl, and discord.py v2.x API.

[Showing lines 1-300 of 643. Use :301 to continue]

πŸ€– Assistant

πŸ€– Toolresult

Extensions

Primary guide for authoring runtime extensions in packages/coding-agent.

This document covers the current extension runtime in:

  • src/extensibility/extensions/types.ts
  • src/extensibility/extensions/runner.ts
  • src/extensibility/extensions/wrapper.ts
  • src/extensibility/extensions/index.ts
  • src/modes/controllers/extension-ui-controller.ts

For discovery paths and filesystem loading rules, see extension-loading.md.

What an extension is

An extension is a TS/JS module exporting a default factory:

import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
 
export default function myExtension(pi: ExtensionAPI) {
  // register handlers/tools/commands/renderers
}

Extensions can combine all of the following in one module:

  • event handlers (pi.on(...))
  • LLM-callable tools (pi.registerTool(...))
  • slash commands (pi.registerCommand(...))
  • keyboard shortcuts and flags
  • custom message rendering
  • session/message injection APIs (sendMessage, sendUserMessage, appendEntry)

Runtime model

  1. Extensions are imported and their factory functions run.
  2. During that load phase, registration methods are valid; runtime action methods are not yet initialized.
  3. ExtensionRunner.initialize(...) wires live actions/contexts for the active mode.
  4. Session/agent/tool lifecycle events are emitted to handlers.
  5. Every tool execution is wrapped with extension interception (tool_call / tool_result).
Extension lifecycle (simplified)
 
load paths
   β”‚
   β–Ό
import module + run factory (registration only)
   β”‚
   β–Ό
ExtensionRunner.initialize(mode/session/tool registry)
   β”‚
   β”œβ”€ emit session/agent events to handlers
   β”œβ”€ wrap tool execution (tool_call/tool_result)
   └─ expose runtime actions (sendMessage, setActiveTools, ...)

Important constraint from loader.ts:

  • calling action methods like pi.sendMessage() during extension load throws ExtensionRuntimeNotInitializedError
  • register first; perform runtime behavior from events/commands/tools

Quick start

import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent";
 
export default function (pi: ExtensionAPI) {
  const { z } = pi.zod;
 
  pi.setLabel("Safety + Utilities");
 
  pi.on("session_start", async (_event, ctx) => {
    ctx.ui.notify(`Extension loaded in ${ctx.cwd}`, "info");
  });
 
  pi.on("tool_call", async (event) => {
    if (event.toolName === "bash" && event.input.command?.includes("rm -rf")) {
      return { block: true, reason: "Blocked by extension policy" };
    }
  });
 
  pi.registerTool({
    name: "hello_extension",
    label: "Hello Extension",
    description: "Return a greeting",
    parameters: z.object({ name: z.string() }),
    async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
      return {
        content: [{ type: "text", text: `Hello, ${params.name}` }],
        details: { greeted: params.name },
      };
    },
  });
 
  pi.registerCommand("hello-ext", {
    description: "Show queue state",
    handler: async (_args, ctx) => {
      ctx.ui.notify(`pending=${ctx.hasPendingMessages()}`, "info");
    },
  });
}

Extension API surfaces

1) Registration and actions (ExtensionAPI)

Core methods:

  • on(event, handler)
  • registerTool, registerCommand, registerShortcut, registerFlag
  • registerMessageRenderer, registerAssistantThinkingRenderer
  • setLabel, getFlag
  • sendMessage, sendUserMessage, appendEntry, exec
  • getActiveTools, getAllTools, setActiveTools
  • getCommands
  • getSessionName, setSessionName
  • setModel, getThinkingLevel, setThinkingLevel
  • registerProvider
  • events (shared event bus)

In interactive mode, input handlers run before the built-in first-message auto-title check. Extensions that call await pi.setSessionName(...) from input can set the persisted session name and prevent the default auto-generated title from running for that session.

Also exposed:

  • pi.logger
  • pi.typebox (zod-backed compatibility shim for legacy TypeBox-style schemas)
  • pi.zod (injected zod/v4 module β€” canonical for tool parameter schemas)
  • pi.pi (package exports)

Message delivery semantics

pi.sendMessage(message, options) supports:

  • deliverAs: "steer" (default) β€” interrupts current run
  • deliverAs: "followUp" β€” queued to run after current run
  • deliverAs: "nextTurn" β€” stored and injected on the next user prompt
  • triggerTurn: true β€” starts a turn when idle (nextTurn ignores this)

pi.sendUserMessage(content, { deliverAs }) always goes through prompt flow; while streaming it queues as steer/follow-up.

2) Handler context (ExtensionContext)

Handlers and tool execute receive ctx with:

  • ui
  • hasUI
  • cwd
  • sessionManager (read-only)
  • modelRegistry, model
  • getContextUsage()
  • compact(...)
  • isIdle(), hasPendingMessages(), abort()
  • shutdown()
  • getSystemPrompt()

3) Command context (ExtensionCommandContext)

Command handlers additionally get:

  • waitForIdle()
  • newSession(...)
  • switchSession(...)
  • branch(entryId)
  • navigateTree(targetId, { summarize })
  • reload()

Use command context for session-control flows; these methods are intentionally separated from general event handlers.

Event surface (current names and behavior)

Canonical event unions and payload types are in types.ts.

Session lifecycle

  • session_start
  • session_before_switch / session_switch
  • session_before_branch / session_branch
  • session_before_compact / session.compacting / session_compact
  • session_before_tree / session_tree
  • session_shutdown

Cancelable pre-events:

  • session_before_switch β†’ { cancel?: boolean }
  • session_before_branch β†’ { cancel?: boolean; skipConversationRestore?: boolean }
  • session_before_compact β†’ { cancel?: boolean; compaction?: CompactionResult }
  • session_before_tree β†’ { cancel?: boolean; summary?: { summary: string; details?: unknown } }

Prompt and turn lifecycle

  • input
  • before_agent_start
  • before_provider_request (may replace provider request payload)
  • after_provider_response
  • context
  • agent_start / agent_end
  • turn_start / turn_end
  • message_start / message_update / message_end

Tool lifecycle

  • tool_call (pre-exec, may block)
  • tool_result (post-exec, may patch content/details/isError)
  • tool_execution_start / tool_execution_update / tool_execution_end (observability)

tool_result is middleware-style: handlers run in extension order and each sees prior modifications.

Reliability/runtime signals

  • auto_compaction_start / auto_compaction_end
  • auto_retry_start / auto_retry_end
  • ttsr_triggered
  • todo_reminder
  • goal_updated
  • credential_disabled

User command interception

  • user_bash (override with { result })
  • user_python (override with { result })

resources_discover

resources_discover exists in extension types and ExtensionRunner.
Current runtime note: ExtensionRunner.emitResourcesDiscover(...) is implemented, but there are no AgentSession callsites invoking it in the current codebase.

Tool authoring details

registerTool uses ToolDefinition from types.ts.

Current execute signature:

execute(
	toolCallId,
	params,
	signal,
	onUpdate,
	ctx,
): Promise<AgentToolResult>

Template:

const { z } = pi.zod;
 
pi.registerTool({
  name: "my_tool",
  label: "My Tool",
  description: "...",
  parameters: z.object({}),
  hidden: false,
  defaultInactive: false,
  deferrable: false,
  async execute(_id, _params, signal, onUpdate, ctx) {
    if (signal?.aborted) {
      return { content: [{ type: "text", text: "Cancelled" }] };
    }
    onUpdate?.({ content: [{ type: "text", text: "Working..." }] });
    return { content: [{ type: "text", text: "Done" }], details: {} };
  },
  onSession(event, ctx) {
    // reason: start|switch|branch|tree|shutdown
  },
  renderCall(args, options, theme) {
    // optional TUI render
  },
  renderResult(result, options, theme, args) {
    // optional TUI render
  },
});

tool_call/tool_result intercept all tools once the registry is wrapped in sdk.ts, including built-ins and extension/custom tools. ToolDefinition also supports optional hidden, defaultInactive, deferrable, mcpServerName, mcpToolName, renderCall, and renderResult fields.

UI integration points

ctx.ui implements the ExtensionUIContext interface. Support differs by mode.

Interactive mode (extension-ui-controller.ts)

Supported:

  • dialogs: select, confirm, input, editor
  • input editing: setEditorText, getEditorText, pasteToEditor, editor
  • terminal title and working message (setTitle, setWorkingMessage)
  • notifications/status/editor text/terminal input/custom overlays
  • theme listing/loading by name (setTheme supports string names)
  • tools expanded toggle

Current no-op methods in this controller:

  • setFooter
  • setHeader
  • setEditorComponent

Also note: setWidget currently routes to status-line text via setHookWidget(...).

RPC mode (rpc-mode.ts)

ctx.ui is backed by RPC extension_ui_request events:

  • dialog methods (select, confirm, input, editor) round-trip to client responses
  • fire-and-forget methods emit requests (notify, setStatus, setWidget for string arrays, setTitle, setEditorText)

Unsupported/no-op in RPC implementation:

  • onTerminalInput
  • custom
  • setFooter, setHeader, setEditorComponent
  • setWorkingMessage
  • theme switching/loading (setTheme returns failure)
  • tool expansion controls are inert

Print/headless/subagent paths

When no UI context is supplied to runner init, ctx.hasUI is false and methods are no-op/default-returning.

Background interactive mode

Background mode installs a non-interactive UI context object. In current implementation, ctx.hasUI may still be true while interactive dialogs return defaults/no-op behavior.

Session and state patterns

For durable extension state:

  1. Persist with pi.appendEntry(customType, data).
  2. Rebuild state from ctx.sessionManager.getBranch() on session_start, session_branch, session_tree.
  3. Keep tool result details structured when state should be visible/reconstructible from tool result history.

Example reconstruction pattern:

pi.on("session_start", async (_event, ctx) => {
  let latest;
  for (const entry of ctx.sessionManager.getBranch()) {
    if (entry.type === "custom" && entry.customType === "my-state") {
      latest = entry.data;
    }
  }
  // restore from latest
});

Rendering extension points

Custom message renderer

pi.registerMessageRenderer("my-type", (message, { expanded }, theme) => {
  // return pi-tui Component
});

Used by interactive rendering when custom messages are displayed.

Assistant thinking renderer

import { Container, Text } from "@oh-my-pi/pi-tui";
 
pi.registerAssistantThinkingRenderer((context, theme) => {
  const container = new Container();
  container.addChild(new Text(theme.fg("dim", `thinking chars: ${context.text.length}`), 1, 0));
  return container;
});

Used by interactive rendering to add display-only supplemental UI below each visible assistant thinking block. The renderer receives the already-visible thinking text, content/thinking indexes, theme, and a requestRender() callback for async renderers. All registered renderers that return a component are appended in registration order. Renderers must not mutate messages; the original thinking block remains the provider/session source of truth.

Tool call/result renderer

Provide renderCall / renderResult on registerTool definitions for custom tool visualization in TUI.

Constraints and pitfalls

  • Runtime actions are unavailable during extension load.
  • tool_call errors block execution (fail-closed).
  • Command name conflicts with built-ins are skipped with diagnostics.
  • Reserved shortcuts are ignored (ctrl+c, ctrl+d, ctrl+z, ctrl+k, ctrl+p, ctrl+l, ctrl+o, ctrl+t, ctrl+g, ctrl+q, alt+m, shift+tab, shift+ctrl+p, alt+enter, escape, enter).
  • Treat ctx.reload() as terminal for the current command handler frame.

Extensions vs hooks vs custom-tools

Use the right surface:

  • Extensions (src/extensibility/extensions/*): unified system (events + tools + commands + renderers + provider registration).
  • Hooks (src/extensibility/hooks/*): separate legacy event API.
  • Custom-tools (src/extensibility/custom-tools/*): tool-focused modules; when loaded alongside extensions they are adapted and still pass through extension interception wrappers.

If you need one package that owns policy, tools, command UX, and rendering together, use extensions.

πŸ€– Toolresult

[/home/dv/hugo-llm-wiki/content/topics/discord-omp-plugin.md#FBEB]
9:# πŸ€– Discord.py Γ— OMP Plugin Cheat Sheet
…
250:## πŸŒ‰ Tier 3 β€” Full Extension + Sidecar Bot
…
275:# πŸ“ Python Sidecar: discord_omp_bot.py
…
277:python 278:#!/usr/bin/env python3 … 300: 301: embed_data = data.get("embed") 302: if embed_data: 303: embed = discord.Embed( 304: title=embed_data.get("title", ""), 305: description=embed_data.get("description", ""), 306: color=embed_data.get("color", 0x5865F2), 307: ) 308: for f in embed_data.get("fields", []): 309: embed.add_field( 310: name=f["name"], value=f["value"], inline=f.get("inline", False) 311: ) 312: await ch.send(content=data.get("content"), embed=embed) 313: else: 314: await ch.send(data.get("content", "πŸ“¨ (empty)")) 315: return web.json_response({"ok": True}) 316: 317:async def handle_status(_: web.Request) -> web.Response: 318: return web.json_response({ 319: "bot": str(bot.user), "guilds": len(bot.guilds), 320: "latency_ms": round(bot.latency * 1000, 1), "ready": bot.is_ready(), 321: }) 322: 323:async def handle_channels(_: web.Request) -> web.Response: 324: return web.json_response([ 325: {"id": str(c.id), "name": c.name, "guild": g.name} 326: for g in bot.guilds for c in g.text_channels 327: ]) 328: 329:async def start_http(): 330: app = web.Application() 331: app.router.add_post("/notify", handle_notify) 332: app.router.add_get("/status", handle_status) 333: app.router.add_get("/channels", handle_channels) 334: runner = web.AppRunner(app) 335: await runner.setup() 336: await web.TCPSite(runner, "127.0.0.1", HTTP_PORT).start() 337: log.info(f"HTTP API β†’ 127.0.0.1:{HTTP_PORT}") 338: 339:# ── Discord commands ── 340:@bot.event 341:async def on_ready(): 342: await start_http() 343: await bot.tree.sync() 344: log.info(f"Ready as {bot.user}") 345: 346:@bot.hybrid_command(name="ask-omp", description="Ask the OMP coding agent") 347:@app_commands.describe(prompt="Your question") 348:async def ask_omp(ctx: commands.Context, *, prompt: str): 349: await ctx.defer() 350: try: 351: r = subprocess.run( 352: ["omp", "-p", prompt, "--no-input"], 353: capture_output=True, text=True, timeout=120, cwd="/home/dev", 354: ) 355: out = r.stdout.strip()[:1900] or "(no output)" 356: await ctx.send(f"\n{out}\n") 357: except subprocess.TimeoutExpired: 358: await ctx.send("⏱️ Timed out (120s)") 359: except Exception as e: 360: await ctx.send(f"❌ {e}") 361: 362:if __name__ == "__main__": 363: logging.basicConfig(level=logging.INFO) 364: asyncio.run(bot.start(os.environ["DISCORD_TOKEN"])) 365:
366:
367:# πŸ“ OMP Extension: ~/.omp/agent/extensions/discord-bridge/index.ts
368:
369:typescript 370:import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent"; 371: 372:const BOT = process.env.DISCORD_BOT_URL ?? "http://127.0.0.1:8901"; 373: 374:export default function discordBridge(pi: ExtensionAPI) { 375: const { z } = pi.zod; 376: pi.setLabel("Discord Bridge"); 377: 378: // πŸ”” Auto-notify on agent completion 379: pi.on("agent_end", async (_ev, ctx) => { 380: const tokens = ctx.getContextUsage?.()?.tokens ?? "?"; 381: await fetch(`${BOT}/notify`, { 382: method: "POST", 383: headers: { "Content-Type": "application/json" }, 384: body: JSON.stringify({ 385: embed: { 386: title: "βœ… Agent Run Complete", 387: description: `Session: ${pi.getSessionName?.() ?? "β€”"}`, 388: color: 0x00ff00, 389: fields: [{ name: "Tokens", value: `${tokens}`, inline: true }], 390: }, 391: }), 392: }).catch(() => {}); 393: }); 394: 395: // πŸ› οΈ LLM-callable tool 396: pi.registerTool({ 397: name: "discord_notify", 398: label: "Discord Notify", 399: description: "Send a notification to the configured Discord channel", 400: parameters: z.object({ 401: message: z.string().describe("Message to send"), 402: }), 403: async execute(_id, params) { 404: const res = await fetch(`${BOT}/notify`, { 405: method: "POST", 406: headers: { "Content-Type": "application/json" }, 407: body: JSON.stringify({ content: params.message }), 408: }); 409: const data = (await res.json()) as { ok?: boolean; error?: string }; 410: return data.ok 411: ? { content: [{ type: "text", text: "βœ… Sent to Discord" }] } 412: : { content: [{ type: "text", text: `❌ ${data.error}` }], isError: true }; 413: }, 414: }); 415: 416: // ⌨️ OMP slash command: /discord 417: pi.registerCommand("discord", { 418: description: "Check Discord bot status", 419: handler: async (_args, ctx) => { 420: try { 421: const s = (await (await fetch(`${BOT}/status`)).json()) as Record<string, unknown>; 422: ctx.ui.notify(`πŸ€– ${s.bot} | ${s.guilds} guilds | ${s.latency_ms}ms`, "info"); 423: } catch { 424: ctx.ui.notify("Bot unreachable", "warning"); 425: } 426: }, 427: }); 428:} 429:
430:
431:# πŸš€ Deploy the Sidecar as systemd
432:
433:ini 434:# ~/.config/systemd/user/discord-omp-bot.service 435:[Unit] 436:Description=Discord OMP Bridge Bot 437:After=network.target 438: 439:[Service] 440:ExecStart=/usr/bin/python3 /home/dev/discord_omp_bot.py 441:Environment=DISCORD_TOKEN=your-token 442:Environment=DISCORD_NOTIFY_CHANNEL=123456789 443:Environment=DISCORD_HTTP_PORT=8901 444:Restart=on-failure 445:RestartSec=5 446: 447:[Install] 448:WantedBy=default.target 449:
450:
451:bash 452:systemctl --user daemon-reload 453:systemctl --user enable --now discord-omp-bot 454:journalctl --user -u discord-omp-bot -f 455:
456:
457:---
458:
459:## πŸ“¦ Packaging as Distributable OMP Plugin
460:
461:# πŸ“ File Structure
462:
463:text 464:omp-discord-bridge/ 465:β”œβ”€β”€ index.ts ← source 466:β”œβ”€β”€ package.json 467:└── dist/ 468: └── index.js ← bundled (generated) 469:
470:
471:# πŸ“ package.json
472:
473:json 474:{ 475: "name": "omp-discord-bridge", 476: "version": "1.0.0", 477: "type": "module", 478: "main": "dist/index.js", 479: "omp": { "extensions": ["./dist/index.js"] }, 480: "peerDependencies": { 481: "@oh-my-pi/pi-coding-agent": "*" 482: }, 483: "scripts": { 484: "build": "bun build ./index.ts --outdir ./dist --target node --external @oh-my-pi/pi-coding-agent" 485: } 486:} 487:
488:
489:# πŸ”¨ Build & Install
490:
491:bash 492:# Bundle (CRITICAL β€” OMP legacy loader copies to /tmp, breaking relative imports) 493:bun build ./index.ts --outdir ./dist --target node \ 494: --external @oh-my-pi/pi-coding-agent 495: 496:# Install as plugin 497:omp plugin link /path/to/omp-discord-bridge 498: 499:# Verify 500:omp plugin list 501:
502:
503:> 🚨 NEVER skip bundling. OMP mirrors extensions to /tmp/omp-legacy-pi-file/ β€” unbundled relative require() calls break silently. Always --external @oh-my-pi/pi-coding-agent.
504:
505:---
506:
507:## 🐍 discord.py Essentials
508:
509:# πŸ”‘ Bot Token Setup
510:
511:text 512:1. discord.com/developers/applications β†’ New Application 513:2. Bot tab β†’ Reset Token β†’ πŸ“‹ copy 514:3. Bot tab β†’ βœ… MESSAGE CONTENT Intent 515:4. OAuth2 β†’ URL Generator β†’ bot + applications.commands 516: β†’ Send Messages, Read History, Embed Links, Use Slash Commands 517: β†’ Copy invite URL β†’ open in browser β†’ select server 518:
519:
520:# 🧩 Intents Quick-Ref
521:
522:python 523:intents = discord.Intents.default() 524:intents.message_content = True # ⚠️ Required for prefix commands 525:intents.guilds = True # Guild join/leave events 526:intents.members = True # ⚠️ Privileged β€” needs portal toggle 527:
528:
529:# πŸ”€ Hybrid Commands (Slash + Prefix)
530:
531:python 532:@bot.hybrid_command(name="ping", description="Pong!") 533:async def ping(ctx: commands.Context): 534: await ctx.send(f"πŸ“ {bot.latency*1000:.0f}ms") 535: 536:# Don't forget: 537:@bot.event 538:async def on_ready(): 539: await bot.tree.sync() # ← registers slash commands with Discord 540:
541:
542:# 🧱 Cog Pattern
543:
544:python 545:class MyCog(commands.Cog): 546: def __init__(self, bot): self.bot = bot 547: async def cog_load(self): ... # setup 548: async def cog_unload(self): ... # teardown 549: 550: @commands.hybrid_command() 551: async def hello(self, ctx): await ctx.send("Hi!") 552: 553:async def setup(bot): # ← entry point 554: await bot.add_cog(MyCog(bot)) 555:
556:
557:# ⏰ Background Tasks
558:
559:python 560:from discord.ext import tasks 561: 562:@tasks.loop(minutes=5) 563:async def heartbeat(): 564: ... # periodic work 565: 566:@heartbeat.before_loop 567:async def wait_ready(): 568: await bot.wait_until_ready() 569: 570:heartbeat.start() # call in on_ready or cog_load 571:
572:
573:# πŸ–±οΈ Views (Buttons)
574:
575:python 576:class Confirm(discord.ui.View): 577: value: bool | None = None 578: 579: @discord.ui.button(label="βœ… Yes", style=discord.ButtonStyle.green) 580: async def yes(self, interaction, button): 581: self.value = True 582: await interaction.response.edit_message(content="Confirmed!", view=None) 583: self.stop() 584: 585: @discord.ui.button(label="❌ No", style=discord.ButtonStyle.red) 586: async def no(self, interaction, button): 587: self.value = False 588: await interaction.response.edit_message(content="Cancelled.", view=None) 589: self.stop() 590: 591:# Usage: 592:view = Confirm(timeout=60) 593:await ctx.send("Proceed?", view=view) 594:await view.wait() 595:
596:
597:---
598:
599:## πŸ” Security Checklist
600:
601:| Rule | Why |
602:|:---|:---|
603:| ❌ Never commit DISCORD_TOKEN or webhook URLs | They’re bearer credentials |
604:| πŸ”’ Bind HTTP API to 127.0.0.1 only | No external exposure |
605:| ⏳ Rate-limit webhook calls (5/5s) | Discord enforces this |
606:| πŸ›‘οΈ Validate all Discord input | Before passing to OMP tools |
607:| 🎯 Use least-privilege bot permissions | Only what you actually need |
608:| πŸ”„ Store tokens in env vars or systemd Environment= | Not in code or config files |
609:
610:---
611:
612:## πŸ—ΊοΈ Quick Decision Flowchart
613:
614:text 615:Need Discord integration? 616:β”‚ 617:β”œβ”€ Just notifications? ──────────── πŸͺ Tier 1 (Webhook) 618:β”‚ └─ Drop index.ts, set env var, done. 619:β”‚ 620:β”œβ”€ LLM needs to call Discord? ──── πŸ”§ Tier 2 (MCP) 621:β”‚ └─ Write Python MCP server, add mcp.json, done. 622:β”‚ 623:└─ Full two-way bridge? ─────────── πŸŒ‰ Tier 3 (Extension + Bot) 624: └─ OMP ext (.ts) + sidecar bot (.py) + systemd service. 625:
626:
627:---
628:
629:## πŸ“š Reference Links
630:
631:| Resource | URL |
632:|:---|:---|
633:| discord.py Docs | https://discordpy.readthedocs.io/ |
634:| MCP Spec | https://spec.modelcontextprotocol.io/ |
635:| MCP Python SDK | pip install mcp |
636:| OMP Extension Docs | omp://extensions.md |
637:| OMP MCP Config | omp://mcp-config.md |
638:| OMP Plugin System | omp://plugin-manager-installer-plumbing.md |
639:
640:---
641:
642:Last updated: 2026-06-08 Β· Grounded in OMP extension docs, pi-langfuse reference impl, and discord.py v2.x API.
643:

πŸ€– Assistant

πŸ€– Toolresult

Remaining items (5):

  • Draft discord-omp-plugin-bp blueprint page [in_progress] (Author)
  • Write file to /home/dv/hugo-llm-wiki/content/topics/ [pending] (Author)
  • Fix ownership and rebuild Hugo site [pending] (Publish)
  • Verify live page returns 200 OK [pending] (Publish)
  • Add blueprint link to topics index [pending] (Polish)
    Phase 2/4 β€œAuthor” β€” 0/2 tasks complete
    Research:
    βœ“ Read existing cheat sheet content
    βœ“ Read llm-wiki site config and structure
    βœ“ Read topics _index.md for nav patterns
    Author:
    β†’ Draft discord-omp-plugin-bp blueprint page
    β—‹ Write file to /home/dv/hugo-llm-wiki/content/topics/
    Publish:
    β—‹ Fix ownership and rebuild Hugo site
    β—‹ Verify live page returns 200 OK
    Polish:
    β—‹ Add blueprint link to topics index

πŸ€– Assistant

πŸ€– Assistant

πŸ€– Assistant

πŸ€– Assistant

πŸ‘€ User

Embark on an ultra deep yet holistic reflective reasoning, analyzing your last output meticulously and comprehensively yet precisely, listing and brainstorming EVERYTHING that you deem improvable: make it absolute masterfully genius 100% perfect in every way, shape and form! Consider EVERYTHING established so far, the users request(s) and latest best practices, preparing for the final perfect enhanced version. Masterfully proceed as proposed most genius and efficiently while always considering everything established so far.

πŸ€– Assistant

Thinking Process

The user wants me to deeply analyze the existing cheat sheet at /topics/discord-omp-plugin/ and then create a companion blueprint page at /topics/discord-omp-plugin-bp/. The blueprint should be a dense, focused document covering:

  • Entire stack
  • Architecture
  • UI/UX
  • Data model
  • Routes
  • Integration
  • Rollout phases

Let me think deeply about what the existing cheat sheet covers and what the blueprint should add:

Existing cheat sheet covers:

  • Three-tier architecture overview (Webhook / MCP / Extension+Sidecar)
  • Code snippets for each tier
  • OMP extension contract
  • discord.py essentials (intents, hybrid commands, cogs, background tasks, views)
  • Security checklist
  • Decision flowchart
  • Packaging as distributable plugin

What the blueprint should cover (that the cheat sheet doesn’t):

The blueprint needs to go deeper into the full stack specificationβ€”every technology, version, runtime, and dependency from discord.py to SQLite for state management to systemd for process control to nginx for reverse proxying. It should include proper system architecture diagrams showing process topology, what runs where, and how data flows between components. Beyond that, I need to detail the data model with all entities and relationships, define every API route and webhook endpoint, specify the UI/UX patterns for the Discord interface, and lay out a phased rollout strategy with migration paths. HTTP API endpoints with full specifications, WebSocket protocol details if needed, MCP tool schemas, and the OMP command registry. Then I’m mapping out how everything integratesβ€”environment variables, service dependencies, health checks, monitoring, logging, and error propagation across the system. Finally, I’m laying out a phased rollout from basic notifications through to production hardening and advanced features.

Now I’m thinking about what would really elevate thisβ€”detailed ASCII architecture diagrams showing process boundaries and data flow, sequence diagrams tracing a Discord command through the entire stack, actual data models with SQL and TypeScript definitions, comprehensive configuration matrices for every environment variable, error handling strategies for each failure point, observability setup with logging and metrics, Discord UX patterns with embed templates and button layouts, the security model with auth flows and token management, and performance considerations around rate limits and caching.

I want to structure this as a dense, blueprint-style document with tables and diagrams, cross-referenced back to the cheat sheet for implementation details, where each section stands alone but connects to the others. The focus should be on what needs to be built and why, leaving the how to the cheat sheet itself.

Now I’m thinking through the data model more carefully β€” what tables and interfaces would a production Discord Γ— OMP bridge actually need, like sessions to track OMP workflows triggered from Discord, message logs, and other state management.

I’m also sketching out the HTTP API endpoints for notifications, health checks, channel management, and async task handling, plus the OMP extension state and Discord embed templates for different notification types like agent lifecycle events and task progress.

Now thinking through the UI/UX patterns β€” Discord side uses color-coded embeds for severity levels, interactive buttons for approvals and retries, dedicated threads per OMP session, modal forms for configuration, select menus for workspace and model selection, autocomplete on slash commands, typing indicators during agent execution, and error messages with clear next steps. On the OMP side, I’m adding a Discord connection status indicator in the TUI, toast notifications when commands arrive, and slash command integration.

Mapping out the rollout strategy across four phases: starting with project setup and bot configuration, moving into one-way webhook notifications for agent lifecycle events, then adding the MCP tool layer so the LLM can interact with Discord directly, and finally building the full bidirectional bridge with HTTP API, OMP extension hooks, and thread management in Discord.

Then I’m planning Phase 4 for production hardening with systemd services, health checks, rate limiting, and monitoring, followed by Phase 5 for advanced features like interactive buttons, modals, multi-guild support, and a live stats dashboard.

Before I write the blueprint, I need to ensure the front matter follows the site’s conventions with YAML delimiters and proper metadata, and I want to make this genuinely useful by cross-referencing the cheat sheet for implementation details, documenting the architectural decisions behind each choice, and calling out anti-patterns to avoid.

I’m also thinking through what visual aids would help mostβ€”a process topology diagram showing all the connections, a sequence diagram for how Discord commands flow through the system, a state machine for connection states, a data flow visualization, and a deployment topology showing where everything runs.

Now I’m mapping out the complete project directory structure with the bot entry point, cogs for different command types, an API server layer, and database models.

I’ll deeply analyze the existing cheat sheet, cross-reference with the OMP extension API surface I’ve loaded, and identify every gap that the blueprint must fill. Then I’ll write and publish it.

Analysis of existing cheat sheet vs. blueprint requirements:

DimensionCheat Sheet HasBlueprint Needs
StackImplicit (discord.py, Bun/TS)Explicit version matrix, every dep + runtime
Architecture3 ASCII boxes per tierProcess topology, sequence flows, failure modes, state machines
UI/UXBasic embed + one View exampleFull embed templates, button matrices, thread strategy, modal forms, OMP TUI indicators
Data ModelNoneSQLite schema, Pydantic models, TS interfaces, API payloads
Routes3 endpoints mentionedFull REST spec with methods, bodies, responses, status codes
IntegrationEnv vars scattered in snippetsConfiguration matrix, service dependency graph, health checks
Security6-row checklistAuth flow, permission scoping, token rotation, rate-limit strategy
RolloutNonePhased plan with acceptance criteria per phase
ObservabilityNoneLogging strategy, structured log schema, alerts
TestingNoneTest strategy per layer
Anti-patterns1 bundling warningDecision records with rationale + escape hatches
File structureFlat snippetsComplete project tree

Now drafting and publishing:

πŸ€– Toolresult

Remaining items (5):

  • Draft discord-omp-plugin-bp blueprint page [in_progress] (Author)
  • Write file to /home/dv/hugo-llm-wiki/content/topics/ [pending] (Author)
  • Fix ownership and rebuild Hugo site [pending] (Publish)
  • Verify live page returns 200 OK [pending] (Publish)
  • Add blueprint link to topics index [pending] (Polish)
    Phase 2/4 β€œAuthor” β€” 0/2 tasks complete
    Research:
    βœ“ Read existing cheat sheet content
    βœ“ Read llm-wiki site config and structure
    βœ“ Read topics _index.md for nav patterns
    Author:
    β†’ Draft discord-omp-plugin-bp blueprint page
    β—‹ Write file to /home/dv/hugo-llm-wiki/content/topics/
    Publish:
    β—‹ Fix ownership and rebuild Hugo site
    β—‹ Verify live page returns 200 OK
    Polish:
    β—‹ Add blueprint link to topics index

πŸ€– Assistant

πŸ€– Assistant

πŸ€– Assistant

πŸ€– Assistant