Creating a Plugin
Plugins add content processors and agent tools to EchOS without modifying core code. Each plugin is a separate workspace package inplugins/.
Steps
Write the processor
Create Security rules (non-negotiable):
plugins/my-plugin/src/processor.ts — the logic that fetches/transforms external content:- Always use
validateUrl()before fetching any URL - Always use
sanitizeHtml()on external content - Never use
eval(),Function(), orvm - Never log secrets
Create the agent tool
Create New in this example: AI-powered auto-categorization support using the
plugins/my-plugin/src/tool.ts — defines the tool the LLM agent can call:categorizeContent function from @echos/core. When autoCategorize=true, the plugin will automatically extract category, tags, and optionally gist/summary/key points from the content.PluginContext API
Every plugin receives aPluginContext with:
| Property | Type | Description |
|---|---|---|
sqlite | SqliteStorage | Metadata DB (upsert, query, FTS5 search) |
markdown | MarkdownStorage | Markdown file storage (save, read, delete) |
vectorDb | VectorStorage | Vector embeddings (upsert, search) |
generateEmbedding | (text: string) => Promise<number[]> | Generate embedding vectors |
logger | Logger (Pino) | Structured logger |
config | Record<string, unknown> | App config (API keys, etc.) |
AI Categorization
Plugins can use the built-in categorization service from@echos/core:
- Uses
streamSimple+parseStreamingJsonto stream the LLM response progressively - Fires
onProgressas fields resolve: category → tags → gist (full mode) - Handles errors with safe defaults (fallback to ‘uncategorized’)
- Respects content length limits (5000 chars for lightweight, 10000 for full)
- Is safe to call without
onProgress— callers that don’t need streaming omit the last argument
Existing plugins
| Plugin | Package | Description |
|---|---|---|
| YouTube | @echos/plugin-youtube | Transcript extraction + Whisper fallback |
| Article | @echos/plugin-article | Web article extraction via Readability + DOMPurify |
@echos/plugin-twitter | Tweet/thread extraction via FxTwitter API | |
| Image | @echos/plugin-image | Image storage with metadata extraction (Sharp) |
Twitter Plugin
The Twitter plugin (@echos/plugin-twitter) provides the save_tweet tool for saving tweets and threads from Twitter/X.
Features:
- Save individual tweets with full metadata (text, author, engagement stats, media URLs)
- Automatic thread unrolling — reply chains by the same author are merged into a clean article
- Quote tweet extraction
- Media URLs referenced as markdown links (images and videos)
- Optional AI categorization for automatic tagging
- No API key required — uses the free FxTwitter API (
api.fxtwitter.com)
twitter.com/<user>/status/<id>x.com/<user>/status/<id>mobile.twitter.com/<user>/status/<id>fxtwitter.com/<user>/status/<id>vxtwitter.com/<user>/status/<id>- All formats support query parameters (
?s=20,?t=...)
- Walks up the reply chain via
replying_to_statusto find earlier tweets by the same author - Stops when a different author is reached or the chain exceeds 25 tweets
- Merges thread tweets into a clean article format, stripping self-reply @mentions
- Single tweets (no thread) are saved in blockquote format with engagement stats
Image Plugin
The image plugin (@echos/plugin-image) provides the save_image tool for storing and organizing images in the knowledge base.
Features:
- Download images from URLs or accept base64 data
- Extract metadata: dimensions, format, file size, EXIF
- Store original files in
knowledge/image/{category}/ - Create searchable markdown notes with image references
- Optional AI categorization for automatic tagging
- JPEG, PNG, GIF, WebP, AVIF, TIFF, BMP
- Maximum size: 20MB
processImage):
- Validates image format and size
- Extracts metadata using Sharp library
- Generates content-based filename hash
- Returns structured metadata and buffer
- Original file:
knowledge/image/{category}/{hash}.{ext} - Markdown note:
knowledge/note/{category}/{date}-{slug}.md - Embedded reference:

- Automatic photo handler via
bot.on('message:photo') - Downloads from Telegram API
- Passes URL to save_image tool
- Supports captions for context