new skills

This commit is contained in:
ilya-bov
2026-02-25 16:52:53 +03:00
parent 0963dee7e8
commit 3da703c01b
32 changed files with 3342 additions and 2199 deletions

View File

@@ -1,70 +0,0 @@
---
name: 1password
description: Set up and use 1Password CLI (op). Use when installing the CLI, enabling desktop app integration, signing in (single or multi-account), or reading/injecting/running secrets via op.
homepage: https://developer.1password.com/docs/cli/get-started/
metadata:
{
"eggent":
{
"emoji": "🔐",
"requires": { "bins": ["op"] },
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "1password-cli",
"bins": ["op"],
"label": "Install 1Password CLI (brew)",
},
],
},
}
---
# 1Password CLI
Follow the official CLI get-started steps. Don't guess install commands.
## References
- `references/get-started.md` (install + app integration + sign-in flow)
- `references/cli-examples.md` (real `op` examples)
## Workflow
1. Check OS + shell.
2. Verify CLI present: `op --version`.
3. Confirm desktop app integration is enabled (per get-started) and the app is unlocked.
4. REQUIRED: create a fresh tmux session for all `op` commands (no direct `op` calls outside tmux).
5. Sign in / authorize inside tmux: `op signin` (expect app prompt).
6. Verify access inside tmux: `op whoami` (must succeed before any secret read).
7. If multiple accounts: use `--account` or `OP_ACCOUNT`.
## REQUIRED tmux session (T-Max)
The shell tool uses a fresh TTY per command. To avoid re-prompts and failures, always run `op` inside a dedicated tmux session with a fresh socket/session name.
Example (see `tmux` skill for socket conventions, do not reuse old session names):
```bash
SOCKET_DIR="${eggent_TMUX_SOCKET_DIR:-${CLAWDBOT_TMUX_SOCKET_DIR:-${TMPDIR:-/tmp}/eggent-tmux-sockets}}"
mkdir -p "$SOCKET_DIR"
SOCKET="$SOCKET_DIR/eggent-op.sock"
SESSION="op-auth-$(date +%Y%m%d-%H%M%S)"
tmux -S "$SOCKET" new -d -s "$SESSION" -n shell
tmux -S "$SOCKET" send-keys -t "$SESSION":0.0 -- "op signin --account my.1password.com" Enter
tmux -S "$SOCKET" send-keys -t "$SESSION":0.0 -- "op whoami" Enter
tmux -S "$SOCKET" send-keys -t "$SESSION":0.0 -- "op vault list" Enter
tmux -S "$SOCKET" capture-pane -p -J -t "$SESSION":0.0 -S -200
tmux -S "$SOCKET" kill-session -t "$SESSION"
```
## Guardrails
- Never paste secrets into logs, chat, or code.
- Prefer `op run` / `op inject` over writing secrets to disk.
- If sign-in without app integration is needed, use `op account add`.
- If a command returns "account is not signed in", re-run `op signin` inside tmux and authorize in the app.
- Do not run `op` outside tmux; stop and ask if tmux is unavailable.

View File

@@ -1,29 +0,0 @@
# op CLI examples (from op help)
## Sign in
- `op signin`
- `op signin --account <shorthand|signin-address|account-id|user-id>`
## Read
- `op read op://app-prod/db/password`
- `op read "op://app-prod/db/one-time password?attribute=otp"`
- `op read "op://app-prod/ssh key/private key?ssh-format=openssh"`
- `op read --out-file ./key.pem op://app-prod/server/ssh/key.pem`
## Run
- `export DB_PASSWORD="op://app-prod/db/password"`
- `op run --no-masking -- printenv DB_PASSWORD`
- `op run --env-file="./.env" -- printenv DB_PASSWORD`
## Inject
- `echo "db_password: {{ op://app-prod/db/password }}" | op inject`
- `op inject -i config.yml.tpl -o config.yml`
## Whoami / accounts
- `op whoami`
- `op account list`

View File

@@ -1,17 +0,0 @@
# 1Password CLI get-started (summary)
- Works on macOS, Windows, and Linux.
- macOS/Linux shells: bash, zsh, sh, fish.
- Windows shell: PowerShell.
- Requires a 1Password subscription and the desktop app to use app integration.
- macOS requirement: Big Sur 11.0.0 or later.
- Linux app integration requires PolKit + an auth agent.
- Install the CLI per the official doc for your OS.
- Enable desktop app integration in the 1Password app:
- Open and unlock the app, then select your account/collection.
- macOS: Settings > Developer > Integrate with 1Password CLI (Touch ID optional).
- Windows: turn on Windows Hello, then Settings > Developer > Integrate.
- Linux: Settings > Security > Unlock using system authentication, then Settings > Developer > Integrate.
- After integration, run any command to sign in (example in docs: `op vault list`).
- If multiple accounts: use `op signin` to pick one, or `--account` / `OP_ACCOUNT`.
- For non-integration auth, use `op account add`.

View File

@@ -1,77 +0,0 @@
---
name: apple-notes
description: Manage Apple Notes via the `memo` CLI on macOS (create, view, edit, delete, search, move, and export notes). Use when a user asks eggent to add a note, list notes, search notes, or manage note folders.
homepage: https://github.com/antoniorodr/memo
metadata:
{
"eggent":
{
"emoji": "📝",
"os": ["darwin"],
"requires": { "bins": ["memo"] },
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "antoniorodr/memo/memo",
"bins": ["memo"],
"label": "Install memo via Homebrew",
},
],
},
}
---
# Apple Notes CLI
Use `memo notes` to manage Apple Notes directly from the terminal. Create, view, edit, delete, search, move notes between folders, and export to HTML/Markdown.
Setup
- Install (Homebrew): `brew tap antoniorodr/memo && brew install antoniorodr/memo/memo`
- Manual (pip): `pip install .` (after cloning the repo)
- macOS-only; if prompted, grant Automation access to Notes.app.
View Notes
- List all notes: `memo notes`
- Filter by folder: `memo notes -f "Folder Name"`
- Search notes (fuzzy): `memo notes -s "query"`
Create Notes
- Add a new note: `memo notes -a`
- Opens an interactive editor to compose the note.
- Quick add with title: `memo notes -a "Note Title"`
Edit Notes
- Edit existing note: `memo notes -e`
- Interactive selection of note to edit.
Delete Notes
- Delete a note: `memo notes -d`
- Interactive selection of note to delete.
Move Notes
- Move note to folder: `memo notes -m`
- Interactive selection of note and destination folder.
Export Notes
- Export to HTML/Markdown: `memo notes -ex`
- Exports selected note; uses Mistune for markdown processing.
Limitations
- Cannot edit notes containing images or attachments.
- Interactive prompts may require terminal access.
Notes
- macOS-only.
- Requires Apple Notes.app to be accessible.
- For automation, grant permissions in System Settings > Privacy & Security > Automation.

View File

@@ -1,118 +0,0 @@
---
name: apple-reminders
description: Manage Apple Reminders via remindctl CLI (list, add, edit, complete, delete). Supports lists, date filters, and JSON/plain output.
homepage: https://github.com/steipete/remindctl
metadata:
{
"eggent":
{
"emoji": "⏰",
"os": ["darwin"],
"requires": { "bins": ["remindctl"] },
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "steipete/tap/remindctl",
"bins": ["remindctl"],
"label": "Install remindctl via Homebrew",
},
],
},
}
---
# Apple Reminders CLI (remindctl)
Use `remindctl` to manage Apple Reminders directly from the terminal.
## When to Use
**USE this skill when:**
- User explicitly mentions "reminder" or "Reminders app"
- Creating personal to-dos with due dates that sync to iOS
- Managing Apple Reminders lists
- User wants tasks to appear in their iPhone/iPad Reminders app
## When NOT to Use
**DON'T use this skill when:**
- Scheduling Clawdbot tasks or alerts → use `cron` tool with systemEvent instead
- Calendar events or appointments → use Apple Calendar
- Project/work task management → use Notion, GitHub Issues, or task queue
- One-time notifications → use `cron` tool for timed alerts
- User says "remind me" but means a Clawdbot alert → clarify first
## Setup
- Install: `brew install steipete/tap/remindctl`
- macOS-only; grant Reminders permission when prompted
- Check status: `remindctl status`
- Request access: `remindctl authorize`
## Common Commands
### View Reminders
```bash
remindctl # Today's reminders
remindctl today # Today
remindctl tomorrow # Tomorrow
remindctl week # This week
remindctl overdue # Past due
remindctl all # Everything
remindctl 2026-01-04 # Specific date
```
### Manage Lists
```bash
remindctl list # List all lists
remindctl list Work # Show specific list
remindctl list Projects --create # Create list
remindctl list Work --delete # Delete list
```
### Create Reminders
```bash
remindctl add "Buy milk"
remindctl add --title "Call mom" --list Personal --due tomorrow
remindctl add --title "Meeting prep" --due "2026-02-15 09:00"
```
### Complete/Delete
```bash
remindctl complete 1 2 3 # Complete by ID
remindctl delete 4A83 --force # Delete by ID
```
### Output Formats
```bash
remindctl today --json # JSON for scripting
remindctl today --plain # TSV format
remindctl today --quiet # Counts only
```
## Date Formats
Accepted by `--due` and date filters:
- `today`, `tomorrow`, `yesterday`
- `YYYY-MM-DD`
- `YYYY-MM-DD HH:mm`
- ISO 8601 (`2026-01-04T12:34:56Z`)
## Example: Clarifying User Intent
User: "Remind me to check on the deploy in 2 hours"
**Ask:** "Do you want this in Apple Reminders (syncs to your phone) or as a Clawdbot alert (I'll message you here)?"
- Apple Reminders → use this skill
- Clawdbot alert → use `cron` tool with systemEvent

View File

@@ -1,69 +0,0 @@
---
name: blogwatcher
description: Monitor blogs and RSS/Atom feeds for updates using the blogwatcher CLI.
homepage: https://github.com/Hyaxia/blogwatcher
metadata:
{
"eggent":
{
"emoji": "📰",
"requires": { "bins": ["blogwatcher"] },
"install":
[
{
"id": "go",
"kind": "go",
"module": "github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest",
"bins": ["blogwatcher"],
"label": "Install blogwatcher (go)",
},
],
},
}
---
# blogwatcher
Track blog and RSS/Atom feed updates with the `blogwatcher` CLI.
Install
- Go: `go install github.com/Hyaxia/blogwatcher/cmd/blogwatcher@latest`
Quick start
- `blogwatcher --help`
Common commands
- Add a blog: `blogwatcher add "My Blog" https://example.com`
- List blogs: `blogwatcher blogs`
- Scan for updates: `blogwatcher scan`
- List articles: `blogwatcher articles`
- Mark an article read: `blogwatcher read 1`
- Mark all articles read: `blogwatcher read-all`
- Remove a blog: `blogwatcher remove "My Blog"`
Example output
```
$ blogwatcher blogs
Tracked blogs (1):
xkcd
URL: https://xkcd.com
```
```
$ blogwatcher scan
Scanning 1 blog(s)...
xkcd
Source: RSS | Found: 4 | New: 4
Found 4 new article(s) total!
```
Notes
- Use `blogwatcher <command> --help` to discover flags and options.

View File

@@ -1,47 +0,0 @@
---
name: blucli
description: BluOS CLI (blu) for discovery, playback, grouping, and volume.
homepage: https://blucli.sh
metadata:
{
"eggent":
{
"emoji": "🫐",
"requires": { "bins": ["blu"] },
"install":
[
{
"id": "go",
"kind": "go",
"module": "github.com/steipete/blucli/cmd/blu@latest",
"bins": ["blu"],
"label": "Install blucli (go)",
},
],
},
}
---
# blucli (blu)
Use `blu` to control Bluesound/NAD players.
Quick start
- `blu devices` (pick target)
- `blu --device <id> status`
- `blu play|pause|stop`
- `blu volume set 15`
Target selection (in priority order)
- `--device <id|name|alias>`
- `BLU_DEVICE`
- config default (if set)
Common tasks
- Grouping: `blu group status|add|remove`
- TuneIn search/play: `blu tunein search "query"`, `blu tunein play "query"`
Prefer `--json` for scripts. Confirm the target device before changing playback.

View File

@@ -1,77 +0,0 @@
---
name: clawhub
description: Use the ClawHub CLI to search, install, update, and publish agent skills from clawhub.com. Use when you need to fetch new skills on the fly, sync installed skills to latest or a specific version, or publish new/updated skill folders with the npm-installed clawhub CLI.
metadata:
{
"eggent":
{
"requires": { "bins": ["clawhub"] },
"install":
[
{
"id": "node",
"kind": "node",
"package": "clawhub",
"bins": ["clawhub"],
"label": "Install ClawHub CLI (npm)",
},
],
},
}
---
# ClawHub CLI
Install
```bash
npm i -g clawhub
```
Auth (publish)
```bash
clawhub login
clawhub whoami
```
Search
```bash
clawhub search "postgres backups"
```
Install
```bash
clawhub install my-skill
clawhub install my-skill --version 1.2.3
```
Update (hash-based match + upgrade)
```bash
clawhub update my-skill
clawhub update my-skill --version 1.2.3
clawhub update --all
clawhub update my-skill --force
clawhub update --all --no-input --force
```
List
```bash
clawhub list
```
Publish
```bash
clawhub publish ./my-skill --slug my-skill --name "My Skill" --version 1.2.0 --changelog "Fixes + docs"
```
Notes
- Default registry: https://clawhub.com (override with CLAWHUB_REGISTRY or --registry)
- Default workdir: cwd (falls back to eggent workspace); install dir: ./skills (override with --workdir / --dir / CLAWHUB_WORKDIR)
- Update command hashes local files, resolves matching version, and upgrades to latest unless --version is set

View File

@@ -1,50 +0,0 @@
---
name: eightctl
description: Control Eight Sleep pods (status, temperature, alarms, schedules).
homepage: https://eightctl.sh
metadata:
{
"eggent":
{
"emoji": "🎛️",
"requires": { "bins": ["eightctl"] },
"install":
[
{
"id": "go",
"kind": "go",
"module": "github.com/steipete/eightctl/cmd/eightctl@latest",
"bins": ["eightctl"],
"label": "Install eightctl (go)",
},
],
},
}
---
# eightctl
Use `eightctl` for Eight Sleep pod control. Requires auth.
Auth
- Config: `~/.config/eightctl/config.yaml`
- Env: `EIGHTCTL_EMAIL`, `EIGHTCTL_PASSWORD`
Quick start
- `eightctl status`
- `eightctl on|off`
- `eightctl temp 20`
Common tasks
- Alarms: `eightctl alarm list|create|dismiss`
- Schedules: `eightctl schedule list|create|update`
- Audio: `eightctl audio state|play|pause`
- Base: `eightctl base info|angle`
Notes
- API is unofficial and rate-limited; avoid repeated logins.
- Confirm before changing temperature or alarms.

View File

@@ -0,0 +1,248 @@
---
name: excalidraw
description: Create and edit Excalidraw diagrams programmatically. Use when the user asks to create diagrams, flowcharts, architecture diagrams, wireframes, or any visual drawings in Excalidraw format (.excalidraw files).
---
# Excalidraw Diagram Creation
This skill enables you to create beautiful, hand-drawn style diagrams in the Excalidraw JSON format.
## File Format Overview
Excalidraw files (`.excalidraw`) are JSON with this structure:
```json
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [],
"appState": {
"viewBackgroundColor": "#ffffff",
"gridSize": null
},
"files": {}
}
```
## Quick Start - Creating Elements
Every element needs at minimum: `type`, `id`, `x`, `y`, and styling properties.
### Minimal Rectangle
```json
{
"type": "rectangle",
"id": "rect-1",
"x": 100,
"y": 100,
"width": 200,
"height": 100,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"seed": 1234,
"version": 1,
"versionNonce": 1234,
"isDeleted": false,
"groupIds": [],
"boundElements": null,
"link": null,
"locked": false,
"roundness": { "type": 3 }
}
```
### Text Element
```json
{
"type": "text",
"id": "text-1",
"x": 120,
"y": 130,
"width": 160,
"height": 25,
"text": "Hello World",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "center",
"verticalAlign": "middle",
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"seed": 5678,
"version": 1,
"versionNonce": 5678,
"isDeleted": false,
"groupIds": [],
"boundElements": null,
"link": null,
"locked": false,
"containerId": null,
"originalText": "Hello World",
"autoResize": true,
"lineHeight": 1.25
}
```
### Arrow Element
```json
{
"type": "arrow",
"id": "arrow-1",
"x": 300,
"y": 150,
"width": 100,
"height": 0,
"points": [[0, 0], [100, 0]],
"startArrowhead": null,
"endArrowhead": "arrow",
"startBinding": null,
"endBinding": null,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"seed": 9012,
"version": 1,
"versionNonce": 9012,
"isDeleted": false,
"groupIds": [],
"boundElements": null,
"link": null,
"locked": false
}
```
## Element Types
| Type | Use For |
|------|---------|
| `rectangle` | Boxes, containers, cards |
| `ellipse` | Circles, ovals, nodes |
| `diamond` | Decision points, conditions |
| `text` | Labels, descriptions |
| `arrow` | Connections, flow direction |
| `line` | Connectors without arrowheads |
| `freedraw` | Hand-drawn paths |
| `image` | Embedded images |
| `frame` | Grouping/framing elements |
## Color Palette (Recommended)
### Backgrounds
- `#a5d8ff` - Light blue
- `#b2f2bb` - Light green
- `#ffec99` - Light yellow
- `#ffc9c9` - Light red/pink
- `#e9ecef` - Light gray
- `#d0bfff` - Light purple
- `#99e9f2` - Light cyan
### Strokes
- `#1e1e1e` - Default black
- `#1971c2` - Blue
- `#2f9e44` - Green
- `#f08c00` - Orange
- `#e03131` - Red
- `#9c36b5` - Purple
## Styling Properties
| Property | Values | Description |
|----------|--------|-------------|
| `fillStyle` | `"solid"`, `"hachure"`, `"cross-hatch"` | Fill pattern |
| `strokeWidth` | `1`, `2`, `4` | Line thickness |
| `strokeStyle` | `"solid"`, `"dashed"`, `"dotted"` | Line style |
| `roughness` | `0`, `1`, `2` | Hand-drawn feel (0=smooth, 2=rough) |
| `opacity` | `0-100` | Transparency |
| `roundness` | `{ "type": 3 }` | Rounded corners |
## Connecting Elements with Arrows
To connect shapes with arrows, use bindings:
```json
{
"type": "arrow",
"id": "arrow-1",
"startBinding": {
"elementId": "rect-1",
"focus": 0,
"gap": 5
},
"endBinding": {
"elementId": "rect-2",
"focus": 0,
"gap": 5
},
"points": [[0, 0], [200, 0]]
}
```
Also add `boundElements` to the connected shapes:
```json
{
"type": "rectangle",
"id": "rect-1",
"boundElements": [{ "id": "arrow-1", "type": "arrow" }]
}
```
## Font Families
| Value | Font |
|-------|------|
| `1` | Hand-drawn (Virgil) |
| `2` | Normal (Helvetica) |
| `3` | Code (Cascadia) |
## Best Practices
1. **Generate unique IDs** - Use descriptive prefixes like `rect-1`, `arrow-2`, `text-header`
2. **Use random seeds** - Each element needs a unique `seed` for consistent rendering
3. **Space elements well** - Leave 50-100px between elements
4. **Align elements** - Use consistent x/y coordinates for visual alignment
5. **Group related items** - Use matching `groupIds` arrays
6. **Use the color palette** - Stick to Excalidraw's built-in colors for consistency
## Diagram Types You Can Create
| Type | Description |
|------|-------------|
| **Flowcharts** | Process flows, algorithms, decision trees |
| **Sequence Diagrams** | API calls, user interactions, message flows |
| **Architecture Diagrams** | System design, microservices, infrastructure |
| **Mind Maps** | Brainstorming, concept mapping, idea organization |
| **Data Flow Diagrams** | Data movement, system analysis |
| **ERD** | Database schemas, entity relationships |
| **Wireframes** | UI mockups, layout sketches |
## Reference Documentation
- **[diagram-patterns.md](diagram-patterns.md)** - Professional patterns for each diagram type (flowcharts, sequence diagrams, mind maps, architecture, etc.)
- **[examples.md](examples.md)** - Complete working JSON examples you can use as templates
- **[element-reference.md](element-reference.md)** - Full property reference for all element types
- **[best-practices.md](best-practices.md)** - Design tips, color theory, typography guidelines
## Quick Tips for Beautiful Diagrams
1. **Use `roughness: 0`** for formal/technical diagrams, `roughness: 1` for friendly sketches
2. **Use `fontFamily: 2`** (Helvetica) for professional look, `fontFamily: 1` (Virgil) only when casual look is requested
3. **Consistent spacing** - 100px between major elements, 50px for related items
4. **Color semantics** - Blue for info/input, Green for success/data, Yellow for decisions, Red for errors
5. **Arrow conventions** - Solid for actions, Dashed for responses/async

View File

@@ -0,0 +1,199 @@
# Excalidraw Design Best Practices
Guidelines for creating beautiful, professional-looking diagrams.
## Layout Principles
### Alignment
- Align elements on a grid (mentally or using coordinates)
- Keep consistent horizontal/vertical spacing between related elements
- Use multiples of 50px for spacing: 50, 100, 150, 200
### Hierarchy
- Larger elements = more important
- Use color to show relationships
- Group related items visually and with `groupIds`
### Flow Direction
- Left-to-right for processes/time
- Top-to-bottom for hierarchies
- Consistent arrow direction within a diagram
## Color Usage
### The 60-30-10 Rule
- **60%** - Background/white space
- **30%** - Primary accent color (main boxes)
- **10%** - Secondary accent (highlights, important items)
### Semantic Colors
| Color | Meaning | Hex Background | Hex Stroke |
|-------|---------|----------------|------------|
| Blue | Information, input, user | `#a5d8ff` | `#1971c2` |
| Green | Success, output, database | `#b2f2bb` | `#2f9e44` |
| Yellow | Warning, decision, process | `#ffec99` | `#f08c00` |
| Red | Error, danger, stop | `#ffc9c9` | `#e03131` |
| Purple | External, storage, special | `#d0bfff` | `#9c36b5` |
| Gray | Neutral, disabled, notes | `#e9ecef` | `#868e96` |
### Monochromatic Schemes
For minimal, professional looks:
- All shapes: `#e9ecef` background, `#1e1e1e` stroke
- Important items: `#a5d8ff` background
- Keep most elements neutral
## Typography
### Font Size Guidelines
| Use Case | Size |
|----------|------|
| Main titles | 28-36 |
| Section headers | 24 |
| Box labels | 20 |
| Descriptions | 16 |
| Notes/annotations | 14 |
| Fine print | 12 |
### Font Family Choices
- **Virgil (1)** - Default hand-drawn, casual, friendly
- **Helvetica (2)** - Clean, professional, formal
- **Cascadia (3)** - Code, technical specs, monospace
Do not use Virgil unless asked for a casual look.
### Text in Containers
- Center text horizontally and vertically
- Keep text concise (2-4 words per line)
- Use `\n` for multi-line text
## Shape Selection
### When to Use Each Shape
| Shape | Best For |
|-------|----------|
| **Rectangle** | Processes, services, containers, generic boxes |
| **Rounded Rectangle** | Buttons, cards, user-friendly elements |
| **Ellipse** | Start/end points, databases, data stores |
| **Diamond** | Decisions, conditions, branching logic |
### Shape Sizing
- Minimum: 80x60 for shapes with text
- Standard: 120x80 for process boxes
- Large: 160x100+ for containers with descriptions
## Arrow Best Practices
### Arrow Types by Usage
| Style | Use |
|-------|-----|
| Solid arrow | Primary flow, main sequence |
| Dashed arrow | Response, return, optional |
| Dotted arrow | Reference, dependency |
| No arrowhead (line) | Association, grouping |
### Arrow Routing
- Prefer horizontal or vertical arrows
- Avoid diagonal arrows when possible
- Use elbow connectors (L-shaped points) for complex routing
- Keep arrows from crossing when possible
### Binding Arrows
Always bind arrows to shapes when possible:
```json
"startBinding": { "elementId": "shape-id", "focus": 0, "gap": 5 }
```
This makes diagrams update automatically when shapes move.
## Diagram-Specific Tips
### Flowcharts
1. Start with ellipse (Start)
2. Use rectangles for processes
3. Use diamonds for decisions
4. Label arrows from decisions (Yes/No)
5. End with ellipse (End)
6. Flow top-to-bottom or left-to-right
### Architecture Diagrams
1. Group related services
2. Use color to show tiers (frontend, backend, data)
3. Label connections with protocols (HTTP, gRPC, SQL)
4. Include a legend if using many colors
5. Show boundaries with frames or large rectangles
### Sequence Diagrams
1. Actors at top as rectangles
2. Vertical dashed lines (lifelines)
3. Horizontal arrows for messages
4. Number messages in order
5. Dashed arrows for responses
### Entity Relationship Diagrams
1. Rectangles for entities
2. Include key fields as text
3. Lines for relationships
4. Label cardinality (1:N, M:N)
## Roughness Settings
| Value | Effect | Use When |
|-------|--------|----------|
| `0` | Clean, precise lines | Formal docs, technical specs |
| `1` | Slightly hand-drawn | Default, balanced look |
| `2` | Very sketchy | Brainstorming, informal |
Tip: Use `roughness: 0` for all technical documentation.
## Common Mistakes to Avoid
1. **Too many colors** - Stick to 2-3 accent colors max
2. **Inconsistent spacing** - Use the same gap everywhere
3. **Tiny text** - Minimum 14px for readability
4. **Unlabeled arrows** - Add labels for complex flows
5. **Crowded layouts** - Leave whitespace, spread out
6. **Missing legend** - Add one if using color/shape coding
7. **Crossed arrows** - Reroute to avoid crossings
8. **Inconsistent shapes** - Same concept = same shape type
## Quick Reference: Professional Template
For a clean, professional look:
```json
{
"strokeColor": "#1e1e1e",
"backgroundColor": "#e9ecef",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"fontFamily": 2,
"fontSize": 16
}
```
For highlighted/important elements:
```json
{
"backgroundColor": "#a5d8ff",
"strokeColor": "#1971c2"
}
```
## Responsive Sizing
When creating diagrams that might be viewed at different sizes:
- Use relative positioning (elements spaced proportionally)
- Minimum canvas size: 600x400
- Standard canvas: 800x600
- Large/complex: 1200x800+
## Testing Your Diagrams
1. Paste JSON into [excalidraw.com](https://excalidraw.com)
2. Check for visual issues
3. Verify all text is readable
4. Ensure arrows point correctly
5. Test zooming in/out

View File

@@ -0,0 +1,344 @@
# Professional Diagram Patterns
Patterns and techniques for creating beautiful, professional diagrams based on Excalidraw best practices.
## Flowcharts
### Standard Flowchart Symbols
| Shape | Meaning | Excalidraw Type |
|-------|---------|-----------------|
| Rounded rectangle | Start/End (Terminal) | `rectangle` with `roundness` |
| Rectangle | Process/Action | `rectangle` |
| Diamond | Decision | `diamond` |
| Parallelogram | Input/Output | `rectangle` rotated or use lines |
| Circle | Connector/Reference | `ellipse` |
### Swimlane Flowcharts
Organize by responsibility using frames or colored regions:
```
┌─────────────────┬─────────────────┬─────────────────┐
│ User │ System │ Database │
├─────────────────┼─────────────────┼─────────────────┤
│ ┌─────────┐ │ │ │
│ │ Request │────┼──►┌─────────┐ │ │
│ └─────────┘ │ │ Process │───┼──►┌─────────┐ │
│ │ └─────────┘ │ │ Query │ │
│ │ │ └─────────┘ │
└─────────────────┴─────────────────┴─────────────────┘
```
**Implementation tips:**
- Use large rectangles with `backgroundColor: "transparent"` and `strokeStyle: "dashed"` for lanes
- Add lane headers at top with bold text
- Keep elements within their lanes
- Horizontal arrows cross lane boundaries
### Color Coding for Flowcharts
- Start/End: `#b2f2bb` (green)
- Process: `#a5d8ff` (blue)
- Decision: `#ffec99` (yellow)
- Error/Exception: `#ffc9c9` (red)
---
## Sequence Diagrams
### Core Components
**1. Actors/Participants (top row)**
```json
{
"type": "rectangle",
"backgroundColor": "#a5d8ff",
"width": 100,
"height": 50,
"roundness": { "type": 3 }
}
```
**2. Lifelines (vertical dashed lines)**
```json
{
"type": "line",
"strokeStyle": "dashed",
"strokeWidth": 1,
"roughness": 0,
"points": [[0, 0], [0, 400]]
}
```
**3. Synchronous Messages (solid arrows)**
```json
{
"type": "arrow",
"strokeStyle": "solid",
"endArrowhead": "arrow"
}
```
**4. Asynchronous Messages (dashed arrows)**
```json
{
"type": "arrow",
"strokeStyle": "dashed",
"endArrowhead": "arrow"
}
```
**5. Return Messages (dashed, pointing back)**
```json
{
"type": "arrow",
"strokeStyle": "dashed",
"strokeColor": "#868e96",
"endArrowhead": "arrow"
}
```
**6. Activation Boxes (thin rectangles on lifelines)**
```json
{
"type": "rectangle",
"width": 16,
"height": 80,
"backgroundColor": "#e9ecef",
"strokeWidth": 1
}
```
### Sequence Diagram Layout
```
User Auth Database
│ │ │
│───Login────►│ │
│ │──Query User──►│
│ │◄──User Data───│
│◄──Token─────│ │
│ │ │
```
- Spacing: 200px between participants
- Message vertical spacing: 50-80px
- Label messages above arrows
- Number messages for complex flows: "1. Login", "2. Verify", etc.
---
## Mind Maps
### Radial Layout Pattern
Central idea with branches radiating outward:
```
┌─────────┐
│ Topic A │
└────┬────┘
┌────────────────┼────────────────┐
│ │ │
┌────┴────┐ ┌─────┴─────┐ ┌────┴────┐
│ Sub 1 │ │ CENTRAL │ │ Sub 2 │
└─────────┘ │ IDEA │ └─────────┘
└─────┬─────┘
┌────┴────┐
│ Topic B │
└─────────┘
```
### Mind Map Color Strategy
- Center: Bold color (`#228be6` blue or `#7950f2` purple)
- Level 1 branches: Distinct colors for each
- Level 2+: Lighter shades of parent color
### Branch Styling
- Use curved lines (multiple points) for organic feel
- Thicker lines closer to center (`strokeWidth: 4``2``1`)
- Branches spread at 45-60° angles
### Mind Map Element Sizes
| Level | Font Size | Shape Size | Stroke Width |
|-------|-----------|------------|--------------|
| Center | 28-36 | 180x100 | 4 |
| Level 1 | 20-24 | 140x70 | 2 |
| Level 2 | 16-18 | 100x50 | 2 |
| Level 3 | 14-16 | 80x40 | 1 |
---
## Software Architecture Diagrams
### Layered Architecture
Stack components vertically with clear boundaries:
```
┌─────────────────────────────────────────┐
│ Presentation Layer │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Web │ │ Mobile │ │ API │ │
│ └─────────┘ └─────────┘ └─────────┘ │
├─────────────────────────────────────────┤
│ Business Layer │
│ ┌─────────────────────────────────┐ │
│ │ Service Logic │ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────────┤
│ Data Layer │
│ ┌─────────┐ ┌─────────┐ │
│ │ DB │ │ Cache │ │
│ └─────────┘ └─────────┘ │
└─────────────────────────────────────────┘
```
**Layer colors (top to bottom):**
- Presentation: `#a5d8ff` (blue)
- Business: `#b2f2bb` (green)
- Data: `#d0bfff` (purple)
### Microservices Pattern
Independent services with API gateway:
```
┌─────────────┐
│ Clients │
└──────┬──────┘
┌──────┴──────┐
│ API Gateway │
└──────┬──────┘
┌───────────────┼───────────────┐
│ │ │
┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐
│ Service A │ │ Service B │ │ Service C │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐
│ DB A │ │ DB B │ │ DB C │
└─────────────┘ └─────────────┘ └─────────────┘
```
### Component Shapes by Type
| Component | Shape | Color |
|-----------|-------|-------|
| User/Client | Ellipse or stick figure | `#a5d8ff` |
| Service/API | Rectangle | `#b2f2bb` |
| Database | Ellipse (cylinder look) | `#d0bfff` |
| Queue/Message | Rectangle with wavy side | `#ffec99` |
| External System | Rectangle with double border | `#e9ecef` |
| Load Balancer | Diamond | `#99e9f2` |
---
## Data Flow Diagrams (DFD)
### Yourdon-Coad Notation
| Symbol | Meaning | Excalidraw |
|--------|---------|------------|
| Circle | Process | `ellipse` |
| Rectangle | External Entity | `rectangle` |
| Open Rectangle | Data Store | `rectangle` without one side (use lines) |
| Arrow | Data Flow | `arrow` |
### Gane-Sarson Notation
| Symbol | Meaning | Excalidraw |
|--------|---------|------------|
| Rounded Rectangle | Process | `rectangle` with `roundness` |
| Rectangle | External Entity | `rectangle` |
| Open Rectangle | Data Store | Same as above |
| Arrow | Data Flow | `arrow` |
### DFD Levels
- **Context (Level 0)**: Single process, external entities only
- **Level 1**: Main processes, data stores appear
- **Level 2+**: Detailed sub-processes
### DFD Layout Tips
- External entities at edges
- Processes in center
- Data stores between related processes
- Label every arrow with data name
---
## Entity Relationship Diagrams
### ER Notation
```
┌─────────────┐ ┌─────────────┐
│ ENTITY │ │ ENTITY │
├─────────────┤ ├─────────────┤
│ attribute1 │─────────│ attribute1 │
│ attribute2 │ 1:N │ attribute2 │
│ *key* │ │ *key* │
└─────────────┘ └─────────────┘
```
### Cardinality Notation
Use text labels or crow's foot:
- `1` - One
- `N` or `*` - Many
- `0..1` - Zero or one
- `1..*` - One or more
---
## Common Professional Patterns
### Consistent Styling Rules
1. **All diagrams**: `roughness: 1`, `strokeWidth: 2`
2. **Formal/technical**: `roughness: 0`, `fontFamily: 2` (Helvetica)
3. **Casual/brainstorm**: `roughness: 2`, `fontFamily: 1` (Virgil)
### Visual Hierarchy
```
Title (36px, bold stroke)
├── Primary Elements (20px, colored backgrounds)
│ │
│ └── Secondary Elements (16px, lighter colors)
│ │
│ └── Details (14px, gray text)
└── Annotations (12px, dashed borders)
```
### Arrow Conventions
| Style | Meaning |
|-------|---------|
| Solid + arrow | Primary flow, action |
| Dashed + arrow | Response, return, async |
| Solid + no head | Association, relationship |
| Dotted + arrow | Optional, reference |
| Thick solid | Main/critical path |
### Professional Color Combinations
**Corporate/Formal:**
- Primary: `#228be6` (blue)
- Secondary: `#868e96` (gray)
- Background: `#f8f9fa`
**Friendly/Startup:**
- Primary: `#7950f2` (purple)
- Secondary: `#20c997` (teal)
- Accent: `#ff922b` (orange)
**Technical/Engineering:**
- Primary: `#1e1e1e` (black)
- Background: `#e9ecef` (light gray)
- Accent: `#228be6` (blue)
---
## Quick Reference: Diagram Types
| Diagram Type | Key Shapes | Flow Direction | Best For |
|--------------|------------|----------------|----------|
| Flowchart | Rectangle, Diamond | Top→Bottom | Processes, algorithms |
| Sequence | Rectangle, Lines, Arrows | Left→Right time | Interactions, APIs |
| Mind Map | Ellipse, Lines | Center→Out | Brainstorming, concepts |
| Architecture | Rectangles, Layers | Top→Bottom | Systems, infrastructure |
| DFD | Circles, Rectangles | Various | Data movement |
| ERD | Rectangles, Lines | N/A | Database design |
| Wireframe | Rectangles, Text | N/A | UI/UX design |

View File

@@ -0,0 +1,399 @@
# Excalidraw Element Reference
Complete property reference for all Excalidraw element types.
## Common Properties (All Elements)
Every element must include these properties:
```json
{
"id": "unique-id",
"type": "rectangle",
"x": 0,
"y": 0,
"width": 100,
"height": 100,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"seed": 12345,
"version": 1,
"versionNonce": 12345,
"isDeleted": false,
"groupIds": [],
"boundElements": null,
"link": null,
"locked": false
}
```
### Property Details
| Property | Type | Description |
|----------|------|-------------|
| `id` | string | Unique identifier. Use descriptive names like `"header-box"` |
| `type` | string | Element type (see types below) |
| `x` | number | X coordinate (left edge) |
| `y` | number | Y coordinate (top edge) |
| `width` | number | Width in pixels |
| `height` | number | Height in pixels |
| `angle` | number | Rotation in radians (0 = no rotation) |
| `strokeColor` | string | Border/line color (hex) |
| `backgroundColor` | string | Fill color (hex or `"transparent"`) |
| `fillStyle` | string | `"solid"`, `"hachure"`, `"cross-hatch"` |
| `strokeWidth` | number | `1` (thin), `2` (medium), `4` (bold) |
| `strokeStyle` | string | `"solid"`, `"dashed"`, `"dotted"` |
| `roughness` | number | `0` (architect), `1` (artist), `2` (cartoonist) |
| `opacity` | number | 0-100 (100 = fully opaque) |
| `seed` | number | Random seed for hand-drawn rendering |
| `version` | number | Element version (start at 1) |
| `versionNonce` | number | Random nonce for version |
| `isDeleted` | boolean | Soft delete flag (use `false`) |
| `groupIds` | array | IDs of groups this element belongs to |
| `boundElements` | array/null | Elements bound to this (arrows, text) |
| `link` | string/null | URL link attached to element |
| `locked` | boolean | Prevent editing |
---
## Rectangle
```json
{
"type": "rectangle",
"id": "rect-1",
"x": 100,
"y": 100,
"width": 200,
"height": 100,
"roundness": { "type": 3 },
... common properties
}
```
### Rectangle-Specific Properties
| Property | Type | Description |
|----------|------|-------------|
| `roundness` | object/null | `{ "type": 3 }` for rounded corners, `null` for sharp |
---
## Ellipse
```json
{
"type": "ellipse",
"id": "ellipse-1",
"x": 100,
"y": 100,
"width": 150,
"height": 150,
... common properties
}
```
Note: For a perfect circle, set `width` equal to `height`.
---
## Diamond
```json
{
"type": "diamond",
"id": "diamond-1",
"x": 100,
"y": 100,
"width": 120,
"height": 120,
... common properties
}
```
Diamonds are rotated squares - useful for decision nodes in flowcharts.
---
## Text
```json
{
"type": "text",
"id": "text-1",
"x": 100,
"y": 100,
"width": 200,
"height": 50,
"text": "Your text here",
"fontSize": 20,
"fontFamily": 1,
"textAlign": "left",
"verticalAlign": "top",
"containerId": null,
"originalText": "Your text here",
"autoResize": true,
"lineHeight": 1.25,
... common properties
}
```
### Text-Specific Properties
| Property | Type | Values |
|----------|------|--------|
| `text` | string | The displayed text (supports `\n` for newlines) |
| `fontSize` | number | Common: `16`, `20`, `28`, `36` |
| `fontFamily` | number | `1` (Virgil/hand), `2` (Helvetica), `3` (Cascadia/code) |
| `textAlign` | string | `"left"`, `"center"`, `"right"` |
| `verticalAlign` | string | `"top"`, `"middle"` |
| `containerId` | string/null | ID of container element (for bound text) |
| `originalText` | string | Original text (same as `text` unless edited) |
| `autoResize` | boolean | Auto-resize container to fit |
| `lineHeight` | number | Line spacing multiplier (default: 1.25) |
---
## Arrow
```json
{
"type": "arrow",
"id": "arrow-1",
"x": 100,
"y": 100,
"width": 200,
"height": 0,
"points": [[0, 0], [200, 0]],
"startArrowhead": null,
"endArrowhead": "arrow",
"startBinding": null,
"endBinding": null,
... common properties
}
```
### Arrow-Specific Properties
| Property | Type | Description |
|----------|------|-------------|
| `points` | array | Array of [x, y] points relative to element origin |
| `startArrowhead` | string/null | `null`, `"arrow"`, `"bar"`, `"dot"`, `"triangle"` |
| `endArrowhead` | string/null | `null`, `"arrow"`, `"bar"`, `"dot"`, `"triangle"` |
| `startBinding` | object/null | Binding to start element |
| `endBinding` | object/null | Binding to end element |
### Arrow Points
Points are relative to the arrow's x, y position:
- First point is always `[0, 0]`
- Subsequent points define the path
- Width/height should match the bounding box of points
```json
// Straight horizontal arrow (200px long)
"points": [[0, 0], [200, 0]]
// Straight vertical arrow (150px down)
"points": [[0, 0], [0, 150]]
// L-shaped arrow
"points": [[0, 0], [100, 0], [100, 100]]
// Curved arrow (3+ points creates curves)
"points": [[0, 0], [50, -30], [100, 0]]
```
### Binding Objects
```json
{
"startBinding": {
"elementId": "rect-1",
"focus": 0,
"gap": 5
},
"endBinding": {
"elementId": "rect-2",
"focus": 0,
"gap": 5
}
}
```
| Property | Type | Description |
|----------|------|-------------|
| `elementId` | string | ID of the bound element |
| `focus` | number | -1 to 1, position along the edge (0 = center) |
| `gap` | number | Pixels gap between arrow and shape |
---
## Line
Same as arrow but without arrowheads by default:
```json
{
"type": "line",
"id": "line-1",
"x": 100,
"y": 100,
"width": 200,
"height": 0,
"points": [[0, 0], [200, 0]],
"startArrowhead": null,
"endArrowhead": null,
... common properties
}
```
---
## Freedraw
Hand-drawn paths:
```json
{
"type": "freedraw",
"id": "freedraw-1",
"x": 100,
"y": 100,
"width": 150,
"height": 80,
"points": [[0, 0], [10, 5], [20, 3], [30, 8], ...],
"pressures": [0.5, 0.6, 0.7, ...],
"simulatePressure": true,
... common properties
}
```
### Freedraw-Specific Properties
| Property | Type | Description |
|----------|------|-------------|
| `points` | array | Dense array of [x, y] points |
| `pressures` | array | Pressure values 0-1 for each point |
| `simulatePressure` | boolean | Simulate pen pressure |
---
## Frame
Groups elements visually:
```json
{
"type": "frame",
"id": "frame-1",
"x": 50,
"y": 50,
"width": 500,
"height": 400,
"name": "My Frame",
... common properties
}
```
Child elements should have `frameId` set to the frame's ID.
---
## Image
```json
{
"type": "image",
"id": "image-1",
"x": 100,
"y": 100,
"width": 300,
"height": 200,
"fileId": "file-abc123",
"status": "saved",
"scale": [1, 1],
... common properties
}
```
The actual image data goes in the top-level `files` object:
```json
{
"files": {
"file-abc123": {
"id": "file-abc123",
"mimeType": "image/png",
"dataURL": "data:image/png;base64,..."
}
}
}
```
---
## Bound Elements
When text is inside a shape, or arrows connect shapes:
### Shape with bound text:
```json
{
"type": "rectangle",
"id": "rect-1",
"boundElements": [
{ "id": "text-1", "type": "text" }
]
}
```
### Text bound to shape:
```json
{
"type": "text",
"id": "text-1",
"containerId": "rect-1"
}
```
### Shape with bound arrow:
```json
{
"type": "rectangle",
"id": "rect-1",
"boundElements": [
{ "id": "arrow-1", "type": "arrow" }
]
}
```
---
## Groups
To group elements, give them matching `groupIds`:
```json
{
"type": "rectangle",
"id": "rect-1",
"groupIds": ["group-1"]
}
{
"type": "text",
"id": "text-1",
"groupIds": ["group-1"]
}
```
Nested groups use multiple IDs (innermost first):
```json
"groupIds": ["inner-group", "outer-group"]
```

File diff suppressed because it is too large Load Diff

View File

@@ -1,48 +0,0 @@
---
name: food-order
description: Reorder Foodora orders + track ETA/status with ordercli. Never confirm without explicit user approval. Triggers: order food, reorder, track ETA.
homepage: https://ordercli.sh
metadata: {"eggent":{"emoji":"🥡","requires":{"bins":["ordercli"]},"install":[{"id":"go","kind":"go","module":"github.com/steipete/ordercli/cmd/ordercli@latest","bins":["ordercli"],"label":"Install ordercli (go)"}]}}
---
# Food order (Foodora via ordercli)
Goal: reorder a previous Foodora order safely (preview first; confirm only on explicit user “yes/confirm/place the order”).
Hard safety rules
- Never run `ordercli foodora reorder ... --confirm` unless user explicitly confirms placing the order.
- Prefer preview-only steps first; show what will happen; ask for confirmation.
- If user is unsure: stop at preview and ask questions.
Setup (once)
- Country: `ordercli foodora countries``ordercli foodora config set --country AT`
- Login (password): `ordercli foodora login --email you@example.com --password-stdin`
- Login (no password, preferred): `ordercli foodora session chrome --url https://www.foodora.at/ --profile "Default"`
Find what to reorder
- Recent list: `ordercli foodora history --limit 10`
- Details: `ordercli foodora history show <orderCode>`
- If needed (machine-readable): `ordercli foodora history show <orderCode> --json`
Preview reorder (no cart changes)
- `ordercli foodora reorder <orderCode>`
Place reorder (cart change; explicit confirmation required)
- Confirm first, then run: `ordercli foodora reorder <orderCode> --confirm`
- Multiple addresses? Ask user for the right `--address-id` (take from their Foodora account / prior order data) and run:
- `ordercli foodora reorder <orderCode> --confirm --address-id <id>`
Track the order
- ETA/status (active list): `ordercli foodora orders`
- Live updates: `ordercli foodora orders --watch`
- Single order detail: `ordercli foodora order <orderCode>`
Debug / safe testing
- Use a throwaway config: `ordercli --config /tmp/ordercli.json ...`

View File

@@ -1,116 +0,0 @@
---
name: gog
description: Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.
homepage: https://gogcli.sh
metadata:
{
"eggent":
{
"emoji": "🎮",
"requires": { "bins": ["gog"] },
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "steipete/tap/gogcli",
"bins": ["gog"],
"label": "Install gog (brew)",
},
],
},
}
---
# gog
Use `gog` for Gmail/Calendar/Drive/Contacts/Sheets/Docs. Requires OAuth setup.
Setup (once)
- `gog auth credentials /path/to/client_secret.json`
- `gog auth add you@gmail.com --services gmail,calendar,drive,contacts,docs,sheets`
- `gog auth list`
Common commands
- Gmail search: `gog gmail search 'newer_than:7d' --max 10`
- Gmail messages search (per email, ignores threading): `gog gmail messages search "in:inbox from:ryanair.com" --max 20 --account you@example.com`
- Gmail send (plain): `gog gmail send --to a@b.com --subject "Hi" --body "Hello"`
- Gmail send (multi-line): `gog gmail send --to a@b.com --subject "Hi" --body-file ./message.txt`
- Gmail send (stdin): `gog gmail send --to a@b.com --subject "Hi" --body-file -`
- Gmail send (HTML): `gog gmail send --to a@b.com --subject "Hi" --body-html "<p>Hello</p>"`
- Gmail draft: `gog gmail drafts create --to a@b.com --subject "Hi" --body-file ./message.txt`
- Gmail send draft: `gog gmail drafts send <draftId>`
- Gmail reply: `gog gmail send --to a@b.com --subject "Re: Hi" --body "Reply" --reply-to-message-id <msgId>`
- Calendar list events: `gog calendar events <calendarId> --from <iso> --to <iso>`
- Calendar create event: `gog calendar create <calendarId> --summary "Title" --from <iso> --to <iso>`
- Calendar create with color: `gog calendar create <calendarId> --summary "Title" --from <iso> --to <iso> --event-color 7`
- Calendar update event: `gog calendar update <calendarId> <eventId> --summary "New Title" --event-color 4`
- Calendar show colors: `gog calendar colors`
- Drive search: `gog drive search "query" --max 10`
- Contacts: `gog contacts list --max 20`
- Sheets get: `gog sheets get <sheetId> "Tab!A1:D10" --json`
- Sheets update: `gog sheets update <sheetId> "Tab!A1:B2" --values-json '[["A","B"],["1","2"]]' --input USER_ENTERED`
- Sheets append: `gog sheets append <sheetId> "Tab!A:C" --values-json '[["x","y","z"]]' --insert INSERT_ROWS`
- Sheets clear: `gog sheets clear <sheetId> "Tab!A2:Z"`
- Sheets metadata: `gog sheets metadata <sheetId> --json`
- Docs export: `gog docs export <docId> --format txt --out /tmp/doc.txt`
- Docs cat: `gog docs cat <docId>`
Calendar Colors
- Use `gog calendar colors` to see all available event colors (IDs 1-11)
- Add colors to events with `--event-color <id>` flag
- Event color IDs (from `gog calendar colors` output):
- 1: #a4bdfc
- 2: #7ae7bf
- 3: #dbadff
- 4: #ff887c
- 5: #fbd75b
- 6: #ffb878
- 7: #46d6db
- 8: #e1e1e1
- 9: #5484ed
- 10: #51b749
- 11: #dc2127
Email Formatting
- Prefer plain text. Use `--body-file` for multi-paragraph messages (or `--body-file -` for stdin).
- Same `--body-file` pattern works for drafts and replies.
- `--body` does not unescape `\n`. If you need inline newlines, use a heredoc or `$'Line 1\n\nLine 2'`.
- Use `--body-html` only when you need rich formatting.
- HTML tags: `<p>` for paragraphs, `<br>` for line breaks, `<strong>` for bold, `<em>` for italic, `<a href="url">` for links, `<ul>`/`<li>` for lists.
- Example (plain text via stdin):
```bash
gog gmail send --to recipient@example.com \
--subject "Meeting Follow-up" \
--body-file - <<'EOF'
Hi Name,
Thanks for meeting today. Next steps:
- Item one
- Item two
Best regards,
Your Name
EOF
```
- Example (HTML list):
```bash
gog gmail send --to recipient@example.com \
--subject "Meeting Follow-up" \
--body-html "<p>Hi Name,</p><p>Thanks for meeting today. Here are the next steps:</p><ul><li>Item one</li><li>Item two</li></ul><p>Best regards,<br>Your Name</p>"
```
Notes
- Set `GOG_ACCOUNT=you@gmail.com` to avoid repeating `--account`.
- For scripting, prefer `--json` plus `--no-input`.
- Sheets values can be passed via `--values-json` (recommended) or as inline rows.
- Docs supports export/cat/copy. In-place edits require a Docs API client (not in gog).
- Confirm before sending mail or creating events.
- `gog gmail search` returns one row per thread; use `gog gmail messages search` when you need every individual email returned separately.

View File

@@ -1,52 +0,0 @@
---
name: goplaces
description: Query Google Places API (New) via the goplaces CLI for text search, place details, resolve, and reviews. Use for human-friendly place lookup or JSON output for scripts.
homepage: https://github.com/steipete/goplaces
metadata:
{
"eggent":
{
"emoji": "📍",
"requires": { "bins": ["goplaces"], "env": ["GOOGLE_PLACES_API_KEY"] },
"primaryEnv": "GOOGLE_PLACES_API_KEY",
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "steipete/tap/goplaces",
"bins": ["goplaces"],
"label": "Install goplaces (brew)",
},
],
},
}
---
# goplaces
Modern Google Places API (New) CLI. Human output by default, `--json` for scripts.
Install
- Homebrew: `brew install steipete/tap/goplaces`
Config
- `GOOGLE_PLACES_API_KEY` required.
- Optional: `GOOGLE_PLACES_BASE_URL` for testing/proxying.
Common commands
- Search: `goplaces search "coffee" --open-now --min-rating 4 --limit 5`
- Bias: `goplaces search "pizza" --lat 40.8 --lng -73.9 --radius-m 3000`
- Pagination: `goplaces search "pizza" --page-token "NEXT_PAGE_TOKEN"`
- Resolve: `goplaces resolve "Soho, London" --limit 5`
- Details: `goplaces details <place_id> --reviews`
- JSON: `goplaces search "sushi" --json`
Notes
- `--no-color` or `NO_COLOR` disables ANSI color.
- Price levels: 0..4 (free → very expensive).
- Type filter sends only the first `--type` value (API accepts one).

View File

@@ -1,257 +0,0 @@
---
name: himalaya
description: "CLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language)."
homepage: https://github.com/pimalaya/himalaya
metadata:
{
"eggent":
{
"emoji": "📧",
"requires": { "bins": ["himalaya"] },
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "himalaya",
"bins": ["himalaya"],
"label": "Install Himalaya (brew)",
},
],
},
}
---
# Himalaya Email CLI
Himalaya is a CLI email client that lets you manage emails from the terminal using IMAP, SMTP, Notmuch, or Sendmail backends.
## References
- `references/configuration.md` (config file setup + IMAP/SMTP authentication)
- `references/message-composition.md` (MML syntax for composing emails)
## Prerequisites
1. Himalaya CLI installed (`himalaya --version` to verify)
2. A configuration file at `~/.config/himalaya/config.toml`
3. IMAP/SMTP credentials configured (password stored securely)
## Configuration Setup
Run the interactive wizard to set up an account:
```bash
himalaya account configure
```
Or create `~/.config/himalaya/config.toml` manually:
```toml
[accounts.personal]
email = "you@example.com"
display-name = "Your Name"
default = true
backend.type = "imap"
backend.host = "imap.example.com"
backend.port = 993
backend.encryption.type = "tls"
backend.login = "you@example.com"
backend.auth.type = "password"
backend.auth.cmd = "pass show email/imap" # or use keyring
message.send.backend.type = "smtp"
message.send.backend.host = "smtp.example.com"
message.send.backend.port = 587
message.send.backend.encryption.type = "start-tls"
message.send.backend.login = "you@example.com"
message.send.backend.auth.type = "password"
message.send.backend.auth.cmd = "pass show email/smtp"
```
## Common Operations
### List Folders
```bash
himalaya folder list
```
### List Emails
List emails in INBOX (default):
```bash
himalaya envelope list
```
List emails in a specific folder:
```bash
himalaya envelope list --folder "Sent"
```
List with pagination:
```bash
himalaya envelope list --page 1 --page-size 20
```
### Search Emails
```bash
himalaya envelope list from john@example.com subject meeting
```
### Read an Email
Read email by ID (shows plain text):
```bash
himalaya message read 42
```
Export raw MIME:
```bash
himalaya message export 42 --full
```
### Reply to an Email
Interactive reply (opens $EDITOR):
```bash
himalaya message reply 42
```
Reply-all:
```bash
himalaya message reply 42 --all
```
### Forward an Email
```bash
himalaya message forward 42
```
### Write a New Email
Interactive compose (opens $EDITOR):
```bash
himalaya message write
```
Send directly using template:
```bash
cat << 'EOF' | himalaya template send
From: you@example.com
To: recipient@example.com
Subject: Test Message
Hello from Himalaya!
EOF
```
Or with headers flag:
```bash
himalaya message write -H "To:recipient@example.com" -H "Subject:Test" "Message body here"
```
### Move/Copy Emails
Move to folder:
```bash
himalaya message move 42 "Archive"
```
Copy to folder:
```bash
himalaya message copy 42 "Important"
```
### Delete an Email
```bash
himalaya message delete 42
```
### Manage Flags
Add flag:
```bash
himalaya flag add 42 --flag seen
```
Remove flag:
```bash
himalaya flag remove 42 --flag seen
```
## Multiple Accounts
List accounts:
```bash
himalaya account list
```
Use a specific account:
```bash
himalaya --account work envelope list
```
## Attachments
Save attachments from a message:
```bash
himalaya attachment download 42
```
Save to specific directory:
```bash
himalaya attachment download 42 --dir ~/Downloads
```
## Output Formats
Most commands support `--output` for structured output:
```bash
himalaya envelope list --output json
himalaya envelope list --output plain
```
## Debugging
Enable debug logging:
```bash
RUST_LOG=debug himalaya envelope list
```
Full trace with backtrace:
```bash
RUST_LOG=trace RUST_BACKTRACE=1 himalaya envelope list
```
## Tips
- Use `himalaya --help` or `himalaya <command> --help` for detailed usage.
- Message IDs are relative to the current folder; re-list after folder changes.
- For composing rich emails with attachments, use MML syntax (see `references/message-composition.md`).
- Store passwords securely using `pass`, system keyring, or a command that outputs the password.

View File

@@ -1,184 +0,0 @@
# Himalaya Configuration Reference
Configuration file location: `~/.config/himalaya/config.toml`
## Minimal IMAP + SMTP Setup
```toml
[accounts.default]
email = "user@example.com"
display-name = "Your Name"
default = true
# IMAP backend for reading emails
backend.type = "imap"
backend.host = "imap.example.com"
backend.port = 993
backend.encryption.type = "tls"
backend.login = "user@example.com"
backend.auth.type = "password"
backend.auth.raw = "your-password"
# SMTP backend for sending emails
message.send.backend.type = "smtp"
message.send.backend.host = "smtp.example.com"
message.send.backend.port = 587
message.send.backend.encryption.type = "start-tls"
message.send.backend.login = "user@example.com"
message.send.backend.auth.type = "password"
message.send.backend.auth.raw = "your-password"
```
## Password Options
### Raw password (testing only, not recommended)
```toml
backend.auth.raw = "your-password"
```
### Password from command (recommended)
```toml
backend.auth.cmd = "pass show email/imap"
# backend.auth.cmd = "security find-generic-password -a user@example.com -s imap -w"
```
### System keyring (requires keyring feature)
```toml
backend.auth.keyring = "imap-example"
```
Then run `himalaya account configure <account>` to store the password.
## Gmail Configuration
```toml
[accounts.gmail]
email = "you@gmail.com"
display-name = "Your Name"
default = true
backend.type = "imap"
backend.host = "imap.gmail.com"
backend.port = 993
backend.encryption.type = "tls"
backend.login = "you@gmail.com"
backend.auth.type = "password"
backend.auth.cmd = "pass show google/app-password"
message.send.backend.type = "smtp"
message.send.backend.host = "smtp.gmail.com"
message.send.backend.port = 587
message.send.backend.encryption.type = "start-tls"
message.send.backend.login = "you@gmail.com"
message.send.backend.auth.type = "password"
message.send.backend.auth.cmd = "pass show google/app-password"
```
**Note:** Gmail requires an App Password if 2FA is enabled.
## iCloud Configuration
```toml
[accounts.icloud]
email = "you@icloud.com"
display-name = "Your Name"
backend.type = "imap"
backend.host = "imap.mail.me.com"
backend.port = 993
backend.encryption.type = "tls"
backend.login = "you@icloud.com"
backend.auth.type = "password"
backend.auth.cmd = "pass show icloud/app-password"
message.send.backend.type = "smtp"
message.send.backend.host = "smtp.mail.me.com"
message.send.backend.port = 587
message.send.backend.encryption.type = "start-tls"
message.send.backend.login = "you@icloud.com"
message.send.backend.auth.type = "password"
message.send.backend.auth.cmd = "pass show icloud/app-password"
```
**Note:** Generate an app-specific password at appleid.apple.com
## Folder Aliases
Map custom folder names:
```toml
[accounts.default.folder.alias]
inbox = "INBOX"
sent = "Sent"
drafts = "Drafts"
trash = "Trash"
```
## Multiple Accounts
```toml
[accounts.personal]
email = "personal@example.com"
default = true
# ... backend config ...
[accounts.work]
email = "work@company.com"
# ... backend config ...
```
Switch accounts with `--account`:
```bash
himalaya --account work envelope list
```
## Notmuch Backend (local mail)
```toml
[accounts.local]
email = "user@example.com"
backend.type = "notmuch"
backend.db-path = "~/.mail/.notmuch"
```
## OAuth2 Authentication (for providers that support it)
```toml
backend.auth.type = "oauth2"
backend.auth.client-id = "your-client-id"
backend.auth.client-secret.cmd = "pass show oauth/client-secret"
backend.auth.access-token.cmd = "pass show oauth/access-token"
backend.auth.refresh-token.cmd = "pass show oauth/refresh-token"
backend.auth.auth-url = "https://provider.com/oauth/authorize"
backend.auth.token-url = "https://provider.com/oauth/token"
```
## Additional Options
### Signature
```toml
[accounts.default]
signature = "Best regards,\nYour Name"
signature-delim = "-- \n"
```
### Downloads directory
```toml
[accounts.default]
downloads-dir = "~/Downloads/himalaya"
```
### Editor for composing
Set via environment variable:
```bash
export EDITOR="vim"
```

View File

@@ -1,199 +0,0 @@
# Message Composition with MML (MIME Meta Language)
Himalaya uses MML for composing emails. MML is a simple XML-based syntax that compiles to MIME messages.
## Basic Message Structure
An email message is a list of **headers** followed by a **body**, separated by a blank line:
```
From: sender@example.com
To: recipient@example.com
Subject: Hello World
This is the message body.
```
## Headers
Common headers:
- `From`: Sender address
- `To`: Primary recipient(s)
- `Cc`: Carbon copy recipients
- `Bcc`: Blind carbon copy recipients
- `Subject`: Message subject
- `Reply-To`: Address for replies (if different from From)
- `In-Reply-To`: Message ID being replied to
### Address Formats
```
To: user@example.com
To: John Doe <john@example.com>
To: "John Doe" <john@example.com>
To: user1@example.com, user2@example.com, "Jane" <jane@example.com>
```
## Plain Text Body
Simple plain text email:
```
From: alice@localhost
To: bob@localhost
Subject: Plain Text Example
Hello, this is a plain text email.
No special formatting needed.
Best,
Alice
```
## MML for Rich Emails
### Multipart Messages
Alternative text/html parts:
```
From: alice@localhost
To: bob@localhost
Subject: Multipart Example
<#multipart type=alternative>
This is the plain text version.
<#part type=text/html>
<html><body><h1>This is the HTML version</h1></body></html>
<#/multipart>
```
### Attachments
Attach a file:
```
From: alice@localhost
To: bob@localhost
Subject: With Attachment
Here is the document you requested.
<#part filename=/path/to/document.pdf><#/part>
```
Attachment with custom name:
```
<#part filename=/path/to/file.pdf name=report.pdf><#/part>
```
Multiple attachments:
```
<#part filename=/path/to/doc1.pdf><#/part>
<#part filename=/path/to/doc2.pdf><#/part>
```
### Inline Images
Embed an image inline:
```
From: alice@localhost
To: bob@localhost
Subject: Inline Image
<#multipart type=related>
<#part type=text/html>
<html><body>
<p>Check out this image:</p>
<img src="cid:image1">
</body></html>
<#part disposition=inline id=image1 filename=/path/to/image.png><#/part>
<#/multipart>
```
### Mixed Content (Text + Attachments)
```
From: alice@localhost
To: bob@localhost
Subject: Mixed Content
<#multipart type=mixed>
<#part type=text/plain>
Please find the attached files.
Best,
Alice
<#part filename=/path/to/file1.pdf><#/part>
<#part filename=/path/to/file2.zip><#/part>
<#/multipart>
```
## MML Tag Reference
### `<#multipart>`
Groups multiple parts together.
- `type=alternative`: Different representations of same content
- `type=mixed`: Independent parts (text + attachments)
- `type=related`: Parts that reference each other (HTML + images)
### `<#part>`
Defines a message part.
- `type=<mime-type>`: Content type (e.g., `text/html`, `application/pdf`)
- `filename=<path>`: File to attach
- `name=<name>`: Display name for attachment
- `disposition=inline`: Display inline instead of as attachment
- `id=<cid>`: Content ID for referencing in HTML
## Composing from CLI
### Interactive compose
Opens your `$EDITOR`:
```bash
himalaya message write
```
### Reply (opens editor with quoted message)
```bash
himalaya message reply 42
himalaya message reply 42 --all # reply-all
```
### Forward
```bash
himalaya message forward 42
```
### Send from stdin
```bash
cat message.txt | himalaya template send
```
### Prefill headers from CLI
```bash
himalaya message write \
-H "To:recipient@example.com" \
-H "Subject:Quick Message" \
"Message body here"
```
## Tips
- The editor opens with a template; fill in headers and body.
- Save and exit the editor to send; exit without saving to cancel.
- MML parts are compiled to proper MIME when sending.
- Use `himalaya message export --full` to inspect the raw MIME structure of received emails.

View File

@@ -1,190 +0,0 @@
---
name: peekaboo
description: Capture and automate macOS UI with the Peekaboo CLI.
homepage: https://peekaboo.boo
metadata:
{
"eggent":
{
"emoji": "👀",
"os": ["darwin"],
"requires": { "bins": ["peekaboo"] },
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "steipete/tap/peekaboo",
"bins": ["peekaboo"],
"label": "Install Peekaboo (brew)",
},
],
},
}
---
# Peekaboo
Peekaboo is a full macOS UI automation CLI: capture/inspect screens, target UI
elements, drive input, and manage apps/windows/menus. Commands share a snapshot
cache and support `--json`/`-j` for scripting. Run `peekaboo` or
`peekaboo <cmd> --help` for flags; `peekaboo --version` prints build metadata.
Tip: run via `polter peekaboo` to ensure fresh builds.
## Features (all CLI capabilities, excluding agent/MCP)
Core
- `bridge`: inspect Peekaboo Bridge host connectivity
- `capture`: live capture or video ingest + frame extraction
- `clean`: prune snapshot cache and temp files
- `config`: init/show/edit/validate, providers, models, credentials
- `image`: capture screenshots (screen/window/menu bar regions)
- `learn`: print the full agent guide + tool catalog
- `list`: apps, windows, screens, menubar, permissions
- `permissions`: check Screen Recording/Accessibility status
- `run`: execute `.peekaboo.json` scripts
- `sleep`: pause execution for a duration
- `tools`: list available tools with filtering/display options
Interaction
- `click`: target by ID/query/coords with smart waits
- `drag`: drag & drop across elements/coords/Dock
- `hotkey`: modifier combos like `cmd,shift,t`
- `move`: cursor positioning with optional smoothing
- `paste`: set clipboard -> paste -> restore
- `press`: special-key sequences with repeats
- `scroll`: directional scrolling (targeted + smooth)
- `swipe`: gesture-style drags between targets
- `type`: text + control keys (`--clear`, delays)
System
- `app`: launch/quit/relaunch/hide/unhide/switch/list apps
- `clipboard`: read/write clipboard (text/images/files)
- `dialog`: click/input/file/dismiss/list system dialogs
- `dock`: launch/right-click/hide/show/list Dock items
- `menu`: click/list application menus + menu extras
- `menubar`: list/click status bar items
- `open`: enhanced `open` with app targeting + JSON payloads
- `space`: list/switch/move-window (Spaces)
- `visualizer`: exercise Peekaboo visual feedback animations
- `window`: close/minimize/maximize/move/resize/focus/list
Vision
- `see`: annotated UI maps, snapshot IDs, optional analysis
Global runtime flags
- `--json`/`-j`, `--verbose`/`-v`, `--log-level <level>`
- `--no-remote`, `--bridge-socket <path>`
## Quickstart (happy path)
```bash
peekaboo permissions
peekaboo list apps --json
peekaboo see --annotate --path /tmp/peekaboo-see.png
peekaboo click --on B1
peekaboo type "Hello" --return
```
## Common targeting parameters (most interaction commands)
- App/window: `--app`, `--pid`, `--window-title`, `--window-id`, `--window-index`
- Snapshot targeting: `--snapshot` (ID from `see`; defaults to latest)
- Element/coords: `--on`/`--id` (element ID), `--coords x,y`
- Focus control: `--no-auto-focus`, `--space-switch`, `--bring-to-current-space`,
`--focus-timeout-seconds`, `--focus-retry-count`
## Common capture parameters
- Output: `--path`, `--format png|jpg`, `--retina`
- Targeting: `--mode screen|window|frontmost`, `--screen-index`,
`--window-title`, `--window-id`
- Analysis: `--analyze "prompt"`, `--annotate`
- Capture engine: `--capture-engine auto|classic|cg|modern|sckit`
## Common motion/typing parameters
- Timing: `--duration` (drag/swipe), `--steps`, `--delay` (type/scroll/press)
- Human-ish movement: `--profile human|linear`, `--wpm` (typing)
- Scroll: `--direction up|down|left|right`, `--amount <ticks>`, `--smooth`
## Examples
### See -> click -> type (most reliable flow)
```bash
peekaboo see --app Safari --window-title "Login" --annotate --path /tmp/see.png
peekaboo click --on B3 --app Safari
peekaboo type "user@example.com" --app Safari
peekaboo press tab --count 1 --app Safari
peekaboo type "supersecret" --app Safari --return
```
### Target by window id
```bash
peekaboo list windows --app "Visual Studio Code" --json
peekaboo click --window-id 12345 --coords 120,160
peekaboo type "Hello from Peekaboo" --window-id 12345
```
### Capture screenshots + analyze
```bash
peekaboo image --mode screen --screen-index 0 --retina --path /tmp/screen.png
peekaboo image --app Safari --window-title "Dashboard" --analyze "Summarize KPIs"
peekaboo see --mode screen --screen-index 0 --analyze "Summarize the dashboard"
```
### Live capture (motion-aware)
```bash
peekaboo capture live --mode region --region 100,100,800,600 --duration 30 \
--active-fps 8 --idle-fps 2 --highlight-changes --path /tmp/capture
```
### App + window management
```bash
peekaboo app launch "Safari" --open https://example.com
peekaboo window focus --app Safari --window-title "Example"
peekaboo window set-bounds --app Safari --x 50 --y 50 --width 1200 --height 800
peekaboo app quit --app Safari
```
### Menus, menubar, dock
```bash
peekaboo menu click --app Safari --item "New Window"
peekaboo menu click --app TextEdit --path "Format > Font > Show Fonts"
peekaboo menu click-extra --title "WiFi"
peekaboo dock launch Safari
peekaboo menubar list --json
```
### Mouse + gesture input
```bash
peekaboo move 500,300 --smooth
peekaboo drag --from B1 --to T2
peekaboo swipe --from-coords 100,500 --to-coords 100,200 --duration 800
peekaboo scroll --direction down --amount 6 --smooth
```
### Keyboard input
```bash
peekaboo hotkey --keys "cmd,shift,t"
peekaboo press escape
peekaboo type "Line 1\nLine 2" --delay 10
```
Notes
- Requires Screen Recording + Accessibility permissions.
- Use `peekaboo see --annotate` to identify targets before clicking.

View File

@@ -0,0 +1,328 @@
---
name: remotion
description: AI video production workflow using Remotion. Use when creating videos, short films, commercials, or motion graphics. Triggers on requests to make promotional videos, product demos, social media videos, animated explainers, or any programmatic video content. Produces polished motion graphics, not slideshows.
---
# Video Generator (Remotion)
Create professional motion graphics videos programmatically with React and Remotion.
## Default Workflow (ALWAYS follow this)
1. **Scrape brand data** (if featuring a product) using Firecrawl
2. **Create the project** in `output/<project-name>/`
3. **Build all scenes** with proper motion graphics
4. **Install dependencies** with `npm install`
5. **Fix package.json scripts** to use `npx remotion` (not `bun`):
```json
"scripts": {
"dev": "npx remotion studio",
"build": "npx remotion bundle"
}
```
5. **Start Remotion Studio** as a background process:
```bash
cd output/<project-name> && npm run dev
```
Wait for "Server ready" on port 3000.
6. **Expose via Cloudflare tunnel** so user can access it:
```bash
bash skills/cloudflare-tunnel/scripts/tunnel.sh start 3000
```
7. **Send the user the public URL** (e.g. `https://xxx.trycloudflare.com`)
The user will preview in their browser, request changes, and you edit the source files. Remotion hot-reloads automatically.
### Rendering (only when user explicitly asks to export):
```bash
cd output/<project-name>
npx remotion render CompositionName out/video.mp4
```
## Quick Start
```bash
# Scaffold project
cd output && npx --yes create-video@latest my-video --template blank
cd my-video && npm install
# Add motion libraries
npm install lucide-react
# Fix scripts in package.json (replace any "bun" references with "npx remotion")
# Start dev server
npm run dev
# Expose publicly
bash skills/cloudflare-tunnel/scripts/tunnel.sh start 3000
```
## Fetching Brand Data with Firecrawl
**MANDATORY:** When a video mentions or features any product/company, use Firecrawl to scrape the product's website for brand data, colors, screenshots, and copy BEFORE designing the video. This ensures visual accuracy and brand consistency.
API Key: Set `FIRECRAWL_API_KEY` in `.env` (see TOOLS.md).
### Usage
```bash
bash scripts/firecrawl.sh "https://example.com"
```
Returns structured brand data: brandName, tagline, headline, description, features, logoUrl, faviconUrl, primaryColors, ctaText, socialLinks, plus screenshot URL and OG image URL.
### Download Assets After Scraping
```bash
mkdir -p public/images/brand
curl -s "https://example.com/favicon.svg" -o public/images/brand/logo.svg
curl -s "${OG_IMAGE_URL}" -o public/images/brand/og-image.png
curl -sL "${SCREENSHOT_URL}" -o public/images/brand/screenshot.png
```
## Core Architecture
### Scene Management
Use scene-based architecture with proper transitions:
```tsx
const SCENE_DURATIONS: Record<string, number> = {
intro: 3000, // 3s hook
problem: 4000, // 4s dramatic
solution: 3500, // 3.5s reveal
features: 5000, // 5s showcase
cta: 3000, // 3s close
};
```
### Video Structure Pattern
```tsx
import {
AbsoluteFill, Sequence, useCurrentFrame,
useVideoConfig, interpolate, spring,
Img, staticFile, Audio,
} from "remotion";
export const MyVideo = () => {
const frame = useCurrentFrame();
const { fps, durationInFrames } = useVideoConfig();
return (
<AbsoluteFill>
{/* Background music */}
<Audio src={staticFile("audio/bg-music.mp3")} volume={0.35} />
{/* Persistent background layer - OUTSIDE sequences */}
<AnimatedBackground frame={frame} />
{/* Scene sequences */}
<Sequence from={0} durationInFrames={90}>
<IntroScene />
</Sequence>
<Sequence from={90} durationInFrames={120}>
<FeatureScene />
</Sequence>
</AbsoluteFill>
);
};
```
## Motion Graphics Principles
### AVOID (Slideshow patterns)
- Fading to black between scenes
- Centered text on solid backgrounds
- Same transition for everything
- Linear/robotic animations
- Static screens
- `slideLeft`, `slideRight`, `crossDissolve`, `fadeBlur` presets
- Emoji icons — NEVER use emoji, always use Lucide React icons
### PURSUE (Motion graphics)
- Overlapping transitions (next starts BEFORE current ends)
- Layered compositions (background/midground/foreground)
- Spring physics for organic motion
- Varied timing (2-5s scenes, mixed rhythms)
- Continuous visual elements across scenes
- Custom transitions with clipPath, 3D transforms, morphs
- Lucide React for ALL icons (`npm install lucide-react`) — never emoji
## Transition Techniques
1. **Morph/Scale** - Element scales up to fill screen, becomes next scene's background
2. **Wipe** - Colored shape sweeps across, revealing next scene
3. **Zoom-through** - Camera pushes into element, emerges into new scene
4. **Clip-path reveal** - Circle/polygon grows from point to reveal
5. **Persistent anchor** - One element stays while surroundings change
6. **Directional flow** - Scene 1 exits right, Scene 2 enters from right
7. **Split/unfold** - Screen divides, panels slide apart
8. **Perspective flip** - Scene rotates on Y-axis in 3D
## Animation Timing Reference
```tsx
// Timing values (in seconds)
const timing = {
micro: 0.1-0.2, // Small shifts, subtle feedback
snappy: 0.2-0.4, // Element entrances, position changes
standard: 0.5-0.8, // Scene transitions, major reveals
dramatic: 1.0-1.5, // Hero moments, cinematic reveals
};
// Spring configs
const springs = {
snappy: { stiffness: 400, damping: 30 },
bouncy: { stiffness: 300, damping: 15 },
smooth: { stiffness: 120, damping: 25 },
};
```
## Visual Style Guidelines
### Typography
- One display font + one body font max
- Massive headlines, tight tracking
- Mix weights for hierarchy
- Keep text SHORT (viewers can't pause)
### Colors
- **Use brand colors from Firecrawl scrape** as the primary palette — match the product's actual look
- **Avoid purple/indigo gradients** unless the brand uses them or the user explicitly requests them
- Simple, clean backgrounds are generally best — a single dark tone or subtle gradient beats layered textures
- Intentional accent colors pulled from the brand
### Layout
- Use asymmetric layouts, off-center type
- Edge-aligned elements create visual tension
- Generous whitespace as design element
- Use depth sparingly — a subtle backdrop blur or single gradient, not stacked textures
## Remotion Essentials
### Interpolation
```tsx
const opacity = interpolate(frame, [0, 30], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp"
});
const scale = spring({
frame, fps,
from: 0.8, to: 1,
durationInFrames: 30,
config: { damping: 12 }
});
```
### Sequences with Overlap
```tsx
<Sequence from={0} durationInFrames={100}>
<Scene1 />
</Sequence>
<Sequence from={80} durationInFrames={100}>
<Scene2 />
</Sequence>
```
### Cross-Scene Continuity
Place persistent elements OUTSIDE Sequence blocks:
```tsx
const PersistentShape = ({ currentScene }: { currentScene: number }) => {
const positions = {
0: { x: 100, y: 100, scale: 1, opacity: 0.3 },
1: { x: 800, y: 200, scale: 2, opacity: 0.5 },
2: { x: 400, y: 600, scale: 0.5, opacity: 1 },
};
return (
<motion.div
animate={positions[currentScene]}
transition={{ duration: 0.8, ease: "easeInOut" }}
className="absolute w-32 h-32 rounded-full bg-gradient-to-r from-coral to-orange"
/>
);
};
```
## Quality Tests
Before delivering, verify:
- **Mute test:** Story follows visually without sound?
- **Squint test:** Hierarchy visible when squinting?
- **Timing test:** Motion feels natural, not robotic?
- **Consistency test:** Similar elements behave similarly?
- **Slideshow test:** Does NOT look like PowerPoint?
- **Loop test:** Video loops smoothly back to start?
## Implementation Steps
1. **Firecrawl brand scrape** — If featuring a product, scrape its site first
2. **Director's treatment** — Write vibe, camera style, emotional arc
3. **Visual direction** — Colors, fonts, brand feel, animation style
4. **Scene breakdown** — List every scene with description, duration, text, transitions
5. **Plan assets** — User assets + generated images/videos + brand scrape assets
9. **Define durations** — Vary pacing (2-3s punchy, 4-5s dramatic)
10. **Build persistent layer** — Animated background outside scenes
11. **Build scenes** — Each with enter/exit animations, 3-5 timed moments
12. **Open with hook** — High-impact first scene
13. **Develop narrative** — Content-driven middle scenes
14. **Strong ending** — Intentional, resolved close
15. **Start Remotion Studio** — `npm run dev` on port 3000
16. **Expose via tunnel** — `bash skills/cloudflare-tunnel/scripts/tunnel.sh start 3000`
17. **Send user the public URL** — They preview and request changes live
18. **Iterate** — Edit source, hot-reload, repeat
19. **Render** — Only when user says to export final video
## File Structure
```
my-video/
├── src/
│ ├── Root.tsx # Composition definitions
│ ├── index.ts # Entry point
│ ├── index.css # Global styles
│ ├── MyVideo.tsx # Main video component
│ └── scenes/ # Scene components (optional)
├── public/
│ ├── images/
│ │ └── brand/ # Firecrawl-scraped assets
│ └── audio/ # Background music
├── remotion.config.ts
└── package.json
```
## Common Components
See `references/components.md` for reusable:
- Animated backgrounds
- Terminal windows
- Feature cards
- Stats displays
- CTA buttons
- Text reveal animations
## Tunnel Management
```bash
# Start tunnel (exposes port 3000 publicly)
bash skills/cloudflare-tunnel/scripts/tunnel.sh start 3000
# Check status
bash skills/cloudflare-tunnel/scripts/tunnel.sh status 3000
# List all tunnels
bash skills/cloudflare-tunnel/scripts/tunnel.sh list
# Stop tunnel
bash skills/cloudflare-tunnel/scripts/tunnel.sh stop 3000
```

View File

@@ -0,0 +1,217 @@
# Reusable Components
## Animated Background
```tsx
import { useCurrentFrame, interpolate } from "remotion";
export const AnimatedBackground = ({ frame }: { frame: number }) => {
const hueShift = interpolate(frame, [0, 300], [0, 360]);
const gradientAngle = interpolate(frame, [0, 300], [0, 180]);
return (
<div
style={{
position: "absolute",
inset: 0,
background: `linear-gradient(${gradientAngle}deg,
hsl(${hueShift}, 70%, 15%),
hsl(${hueShift + 60}, 60%, 10%))`,
}}
/>
);
};
```
## Terminal Window
```tsx
export const TerminalWindow = ({
lines,
frame,
fps,
}: {
lines: string[];
frame: number;
fps: number;
}) => {
const visibleLines = Math.floor(frame / (fps * 0.3));
return (
<div className="bg-gray-900 rounded-xl p-6 font-mono text-sm shadow-2xl border border-gray-700">
<div className="flex gap-2 mb-4">
<div className="w-3 h-3 rounded-full bg-red-500" />
<div className="w-3 h-3 rounded-full bg-yellow-500" />
<div className="w-3 h-3 rounded-full bg-green-500" />
</div>
{lines.slice(0, visibleLines).map((line, i) => (
<div key={i} className="text-green-400 leading-relaxed">
<span className="text-gray-500">$ </span>{line}
</div>
))}
{visibleLines <= lines.length && (
<span className="inline-block w-2 h-5 bg-green-400 animate-pulse" />
)}
</div>
);
};
```
## Feature Card
```tsx
import { spring, useCurrentFrame, useVideoConfig, interpolate } from "remotion";
// icon should be a Lucide React component, NEVER an emoji string
export const FeatureCard = ({
icon: Icon,
title,
description,
delay = 0,
}: {
icon: React.FC<{ size?: number; color?: string }>;
title: string;
description: string;
delay?: number;
}) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const scale = spring({
frame: frame - delay,
fps,
config: { stiffness: 300, damping: 20 },
});
const opacity = interpolate(frame - delay, [0, 15], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
return (
<div
style={{ transform: `scale(${scale})`, opacity }}
className="bg-white/10 backdrop-blur-md rounded-2xl p-8 border border-white/20"
>
<div className="mb-4"><Icon size={40} color="white" /></div>
<h3 className="text-2xl font-bold text-white mb-2">{title}</h3>
<p className="text-gray-300">{description}</p>
</div>
);
};
```
## Stats Display
```tsx
import { interpolate } from "remotion";
export const StatsDisplay = ({
value,
label,
frame,
fps,
}: {
value: number;
label: string;
frame: number;
fps: number;
}) => {
const progress = interpolate(frame, [0, fps * 1.5], [0, 1], {
extrapolateRight: "clamp",
});
const displayValue = Math.round(value * progress);
return (
<div className="text-center">
<div className="text-7xl font-black text-white tracking-tight">
{displayValue.toLocaleString()}
</div>
<div className="text-lg text-gray-400 uppercase tracking-widest mt-2">
{label}
</div>
</div>
);
};
```
## CTA Button
```tsx
import { spring, useCurrentFrame, useVideoConfig, interpolate } from "remotion";
export const CTAButton = ({
text,
frame,
fps,
}: {
text: string;
frame: number;
fps: number;
}) => {
const scale = spring({
frame,
fps,
config: { stiffness: 200, damping: 15 },
});
const shimmer = interpolate(frame, [0, fps * 2], [-100, 200]);
return (
<div
style={{ transform: `scale(${scale})` }}
className="relative inline-block px-12 py-5 bg-gradient-to-r from-blue-500 to-purple-600 rounded-full text-white text-2xl font-bold overflow-hidden"
>
{text}
<div
className="absolute inset-0 bg-gradient-to-r from-transparent via-white/30 to-transparent"
style={{ transform: `translateX(${shimmer}%)` }}
/>
</div>
);
};
```
## Text Reveal
```tsx
import { interpolate } from "remotion";
export const TextReveal = ({
text,
frame,
fps,
charDelay = 2,
}: {
text: string;
frame: number;
fps: number;
charDelay?: number;
}) => {
return (
<div className="flex flex-wrap">
{text.split("").map((char, i) => {
const charFrame = frame - i * charDelay;
const opacity = interpolate(charFrame, [0, 8], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
const y = interpolate(charFrame, [0, 8], [20, 0], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
return (
<span
key={i}
style={{ opacity, transform: `translateY(${y}px)` }}
className="text-6xl font-bold text-white"
>
{char === " " ? "\u00A0" : char}
</span>
);
})}
</div>
);
};
```

View File

@@ -0,0 +1,144 @@
# Remotion Composition Patterns
## Project Structure
```
my-video/
├── src/
│ ├── index.ts # registerRoot entry point
│ ├── Root.tsx # <Composition> declarations
│ └── MyVideo/
│ ├── index.tsx # Main component
│ └── styles.ts # Optional styles
├── public/ # Static assets (images, fonts, audio)
├── remotion.config.ts # Remotion config
├── package.json
└── tsconfig.json
```
## Basic Composition (Root.tsx)
```tsx
import { Composition } from "remotion";
import { MyVideo } from "./MyVideo";
export const RemotionRoot = () => (
<>
<Composition
id="MyVideo"
component={MyVideo}
durationInFrames={300} // 10s at 30fps
fps={30}
width={1920}
height={1080}
defaultProps={{ title: "Hello World" }}
/>
</>
);
```
## Common Aspect Ratios
- **16:9 landscape (YouTube):** 1920x1080 or 1280x720
- **9:16 vertical (Reels/TikTok/Shorts):** 1080x1920
- **4:5 Instagram feed:** 1080x1350
- **1:1 square:** 1080x1080
## Key Remotion APIs
```tsx
import {
useCurrentFrame, // Current frame number
useVideoConfig, // { fps, width, height, durationInFrames }
interpolate, // Map frame ranges to values
spring, // Physics-based spring animation
Sequence, // Time-offset children
AbsoluteFill, // Full-frame container
Img, // Image component (preloads)
Audio, // Audio component
Video, // Video component
staticFile, // Reference files in public/
delayRender, // Hold render until async ready
continueRender, // Resume after delayRender
} from "remotion";
```
## Animation Example
```tsx
import { useCurrentFrame, interpolate, spring, useVideoConfig, AbsoluteFill } from "remotion";
export const FadeInText: React.FC<{ text: string }> = ({ text }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const opacity = interpolate(frame, [0, 30], [0, 1], { extrapolateRight: "clamp" });
const scale = spring({ frame, fps, config: { damping: 200 } });
return (
<AbsoluteFill className="items-center justify-center bg-black">
<h1
style={{ opacity, transform: `scale(${scale})` }}
className="text-white text-7xl font-bold"
>
{text}
</h1>
</AbsoluteFill>
);
};
```
## Sequences (Timing)
```tsx
<AbsoluteFill>
<Sequence from={0} durationInFrames={60}>
<Intro />
</Sequence>
<Sequence from={60} durationInFrames={120}>
<MainContent />
</Sequence>
<Sequence from={180}>
<Outro />
</Sequence>
</AbsoluteFill>
```
## Input Props (Dynamic Data)
Pass data via `--props` flag or `defaultProps`:
```tsx
// Component
export const MyVideo: React.FC<{ title: string; items: string[] }> = ({ title, items }) => { ... };
// Render with props
// npx remotion render MyVideo --props='{"title":"Demo","items":["a","b"]}'
```
## Audio
```tsx
import { Audio, staticFile, Sequence } from "remotion";
<Sequence from={0}>
<Audio src={staticFile("bgm.mp3")} volume={0.5} />
</Sequence>
```
## Fetching Data (delayRender)
```tsx
const [data, setData] = useState(null);
const [handle] = useState(() => delayRender());
useEffect(() => {
fetch("https://api.example.com/data")
.then((r) => r.json())
.then((d) => { setData(d); continueRender(handle); });
}, []);
```
## TailwindCSS
Remotion supports Tailwind out of the box when scaffolded with `--tailwind`. Use className as normal on any element.

View File

@@ -0,0 +1,98 @@
#!/usr/bin/env bash
# remotion.sh - Wrapper for common Remotion operations
# Usage: remotion.sh <command> [args...]
#
# Commands:
# init <project-name> - Scaffold a new Remotion project (blank + tailwind)
# render <project-dir> [comp] [output] [--props '{}'] [--width N] [--height N]
# still <project-dir> [comp] [output] [--props '{}'] [--frame N]
# preview <project-dir> - Start Remotion Studio dev server
# list <project-dir> - List available compositions
# upgrade <project-dir> - Upgrade Remotion packages to latest
set -euo pipefail
CMD="${1:-help}"
shift || true
case "$CMD" in
init)
PROJECT_NAME="${1:?Usage: remotion.sh init <project-name>}"
echo "Creating Remotion project: $PROJECT_NAME"
npx --yes create-video@latest --blank --tailwind --no-skills "$PROJECT_NAME" 2>&1 || {
# Fallback: manual scaffold if create-video doesn't support flags
echo "Trying interactive scaffold..."
echo -e "blank\ny\nn" | npx --yes create-video@latest "$PROJECT_NAME" 2>&1
}
echo "Project created at ./$PROJECT_NAME"
echo "Next: cd $PROJECT_NAME && npm install && npm run dev"
;;
render)
PROJECT_DIR="${1:?Usage: remotion.sh render <project-dir> [composition] [output] [flags...]}"
shift
COMP="${1:-}"
shift 2>/dev/null || true
OUTPUT="${1:-}"
shift 2>/dev/null || true
cd "$PROJECT_DIR"
ARGS=()
if [ -n "$COMP" ]; then ARGS+=("$COMP"); fi
if [ -n "$OUTPUT" ]; then ARGS+=("$OUTPUT"); fi
# Pass remaining flags through
ARGS+=("$@")
npx remotion render "${ARGS[@]}" 2>&1
;;
still)
PROJECT_DIR="${1:?Usage: remotion.sh still <project-dir> [composition] [output] [flags...]}"
shift
COMP="${1:-}"
shift 2>/dev/null || true
OUTPUT="${1:-}"
shift 2>/dev/null || true
cd "$PROJECT_DIR"
ARGS=()
if [ -n "$COMP" ]; then ARGS+=("$COMP"); fi
if [ -n "$OUTPUT" ]; then ARGS+=("$OUTPUT"); fi
ARGS+=("$@")
npx remotion still "${ARGS[@]}" 2>&1
;;
preview)
PROJECT_DIR="${1:?Usage: remotion.sh preview <project-dir>}"
cd "$PROJECT_DIR"
npm run dev 2>&1
;;
list)
PROJECT_DIR="${1:?Usage: remotion.sh list <project-dir>}"
cd "$PROJECT_DIR"
npx remotion compositions 2>&1
;;
upgrade)
PROJECT_DIR="${1:?Usage: remotion.sh upgrade <project-dir>}"
cd "$PROJECT_DIR"
npx remotion upgrade 2>&1
;;
help|*)
echo "remotion.sh - Remotion video toolkit"
echo ""
echo "Commands:"
echo " init <name> Scaffold new project"
echo " render <dir> [comp] [out] Render video (mp4/webm/gif)"
echo " still <dir> [comp] [out] Render single frame"
echo " preview <dir> Start dev server"
echo " list <dir> List compositions"
echo " upgrade <dir> Upgrade Remotion"
;;
esac

View File

@@ -1,87 +0,0 @@
---
name: sag
description: ElevenLabs text-to-speech with mac-style say UX.
homepage: https://sag.sh
metadata:
{
"eggent":
{
"emoji": "🗣️",
"requires": { "bins": ["sag"], "env": ["ELEVENLABS_API_KEY"] },
"primaryEnv": "ELEVENLABS_API_KEY",
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "steipete/tap/sag",
"bins": ["sag"],
"label": "Install sag (brew)",
},
],
},
}
---
# sag
Use `sag` for ElevenLabs TTS with local playback.
API key (required)
- `ELEVENLABS_API_KEY` (preferred)
- `SAG_API_KEY` also supported by the CLI
Quick start
- `sag "Hello there"`
- `sag speak -v "Roger" "Hello"`
- `sag voices`
- `sag prompting` (model-specific tips)
Model notes
- Default: `eleven_v3` (expressive)
- Stable: `eleven_multilingual_v2`
- Fast: `eleven_flash_v2_5`
Pronunciation + delivery rules
- First fix: respell (e.g. "key-note"), add hyphens, adjust casing.
- Numbers/units/URLs: `--normalize auto` (or `off` if it harms names).
- Language bias: `--lang en|de|fr|...` to guide normalization.
- v3: SSML `<break>` not supported; use `[pause]`, `[short pause]`, `[long pause]`.
- v2/v2.5: SSML `<break time="1.5s" />` supported; `<phoneme>` not exposed in `sag`.
v3 audio tags (put at the entrance of a line)
- `[whispers]`, `[shouts]`, `[sings]`
- `[laughs]`, `[starts laughing]`, `[sighs]`, `[exhales]`
- `[sarcastic]`, `[curious]`, `[excited]`, `[crying]`, `[mischievously]`
- Example: `sag "[whispers] keep this quiet. [short pause] ok?"`
Voice defaults
- `ELEVENLABS_VOICE_ID` or `SAG_VOICE_ID`
Confirm voice + speaker before long output.
## Chat voice responses
When Peter asks for a "voice" reply (e.g., "crazy scientist voice", "explain in voice"), generate audio and send it:
```bash
# Generate audio file
sag -v Clawd -o /tmp/voice-reply.mp3 "Your message here"
# Then include in reply:
# MEDIA:/tmp/voice-reply.mp3
```
Voice character tips:
- Crazy scientist: Use `[excited]` tags, dramatic pauses `[short pause]`, vary intensity
- Calm: Use `[whispers]` or slower pacing
- Dramatic: Use `[sings]` or `[shouts]` sparingly
Default voice for Clawd: `lj2rcrvANS3gaWWnczSX` (or just `-v Clawd`)

View File

@@ -1,103 +0,0 @@
---
name: sherpa-onnx-tts
description: Local text-to-speech via sherpa-onnx (offline, no cloud)
metadata:
{
"eggent":
{
"emoji": "🗣️",
"os": ["darwin", "linux", "win32"],
"requires": { "env": ["SHERPA_ONNX_RUNTIME_DIR", "SHERPA_ONNX_MODEL_DIR"] },
"install":
[
{
"id": "download-runtime-macos",
"kind": "download",
"os": ["darwin"],
"url": "https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-osx-universal2-shared.tar.bz2",
"archive": "tar.bz2",
"extract": true,
"stripComponents": 1,
"targetDir": "runtime",
"label": "Download sherpa-onnx runtime (macOS)",
},
{
"id": "download-runtime-linux-x64",
"kind": "download",
"os": ["linux"],
"url": "https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-linux-x64-shared.tar.bz2",
"archive": "tar.bz2",
"extract": true,
"stripComponents": 1,
"targetDir": "runtime",
"label": "Download sherpa-onnx runtime (Linux x64)",
},
{
"id": "download-runtime-win-x64",
"kind": "download",
"os": ["win32"],
"url": "https://github.com/k2-fsa/sherpa-onnx/releases/download/v1.12.23/sherpa-onnx-v1.12.23-win-x64-shared.tar.bz2",
"archive": "tar.bz2",
"extract": true,
"stripComponents": 1,
"targetDir": "runtime",
"label": "Download sherpa-onnx runtime (Windows x64)",
},
{
"id": "download-model-lessac",
"kind": "download",
"url": "https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-en_US-lessac-high.tar.bz2",
"archive": "tar.bz2",
"extract": true,
"targetDir": "models",
"label": "Download Piper en_US lessac (high)",
},
],
},
}
---
# sherpa-onnx-tts
Local TTS using the sherpa-onnx offline CLI.
## Install
1. Download the runtime for your OS (extracts into `~/.eggent/tools/sherpa-onnx-tts/runtime`)
2. Download a voice model (extracts into `~/.eggent/tools/sherpa-onnx-tts/models`)
Update `~/.eggent/eggent.json`:
```json5
{
skills: {
entries: {
"sherpa-onnx-tts": {
env: {
SHERPA_ONNX_RUNTIME_DIR: "~/.eggent/tools/sherpa-onnx-tts/runtime",
SHERPA_ONNX_MODEL_DIR: "~/.eggent/tools/sherpa-onnx-tts/models/vits-piper-en_US-lessac-high",
},
},
},
},
}
```
The wrapper lives in this skill folder. Run it directly, or add the wrapper to PATH:
```bash
export PATH="{baseDir}/bin:$PATH"
```
## Usage
```bash
{baseDir}/bin/sherpa-onnx-tts -o ./tts.wav "Hello from local TTS."
```
Notes:
- Pick a different model from the sherpa-onnx `tts-models` release if you want another voice.
- If the model dir has multiple `.onnx` files, set `SHERPA_ONNX_MODEL_FILE` or pass `--model-file`.
- You can also pass `--tokens-file` or `--data-dir` to override the defaults.
- Windows: run `node {baseDir}\\bin\\sherpa-onnx-tts -o tts.wav "Hello from local TTS."`

View File

@@ -1,178 +0,0 @@
#!/usr/bin/env node
const fs = require("node:fs");
const path = require("node:path");
const { spawnSync } = require("node:child_process");
function usage(message) {
if (message) {
console.error(message);
}
console.error(
"\nUsage: sherpa-onnx-tts [--runtime-dir <dir>] [--model-dir <dir>] [--model-file <file>] [--tokens-file <file>] [--data-dir <dir>] [--output <file>] \"text\"",
);
console.error("\nRequired env (or flags):\n SHERPA_ONNX_RUNTIME_DIR\n SHERPA_ONNX_MODEL_DIR");
process.exit(1);
}
function resolveRuntimeDir(explicit) {
const value = explicit || process.env.SHERPA_ONNX_RUNTIME_DIR || "";
return value.trim();
}
function resolveModelDir(explicit) {
const value = explicit || process.env.SHERPA_ONNX_MODEL_DIR || "";
return value.trim();
}
function resolveModelFile(modelDir, explicitFlag) {
const explicit = (explicitFlag || process.env.SHERPA_ONNX_MODEL_FILE || "").trim();
if (explicit) return explicit;
try {
const candidates = fs
.readdirSync(modelDir)
.filter((entry) => entry.endsWith(".onnx"))
.map((entry) => path.join(modelDir, entry));
if (candidates.length === 1) return candidates[0];
} catch {
return "";
}
return "";
}
function resolveTokensFile(modelDir, explicitFlag) {
const explicit = (explicitFlag || process.env.SHERPA_ONNX_TOKENS_FILE || "").trim();
if (explicit) return explicit;
const candidate = path.join(modelDir, "tokens.txt");
return fs.existsSync(candidate) ? candidate : "";
}
function resolveDataDir(modelDir, explicitFlag) {
const explicit = (explicitFlag || process.env.SHERPA_ONNX_DATA_DIR || "").trim();
if (explicit) return explicit;
const candidate = path.join(modelDir, "espeak-ng-data");
return fs.existsSync(candidate) ? candidate : "";
}
function resolveBinary(runtimeDir) {
const binName = process.platform === "win32" ? "sherpa-onnx-offline-tts.exe" : "sherpa-onnx-offline-tts";
return path.join(runtimeDir, "bin", binName);
}
function prependEnvPath(current, next) {
if (!next) return current;
if (!current) return next;
return `${next}${path.delimiter}${current}`;
}
const args = process.argv.slice(2);
let runtimeDir = "";
let modelDir = "";
let modelFile = "";
let tokensFile = "";
let dataDir = "";
let output = "tts.wav";
const textParts = [];
for (let i = 0; i < args.length; i += 1) {
const arg = args[i];
if (arg === "--runtime-dir") {
runtimeDir = args[i + 1] || "";
i += 1;
continue;
}
if (arg === "--model-dir") {
modelDir = args[i + 1] || "";
i += 1;
continue;
}
if (arg === "--model-file") {
modelFile = args[i + 1] || "";
i += 1;
continue;
}
if (arg === "--tokens-file") {
tokensFile = args[i + 1] || "";
i += 1;
continue;
}
if (arg === "--data-dir") {
dataDir = args[i + 1] || "";
i += 1;
continue;
}
if (arg === "-o" || arg === "--output") {
output = args[i + 1] || output;
i += 1;
continue;
}
if (arg === "--text") {
textParts.push(args[i + 1] || "");
i += 1;
continue;
}
textParts.push(arg);
}
runtimeDir = resolveRuntimeDir(runtimeDir);
modelDir = resolveModelDir(modelDir);
if (!runtimeDir || !modelDir) {
usage("Missing runtime/model directory.");
}
modelFile = resolveModelFile(modelDir, modelFile);
tokensFile = resolveTokensFile(modelDir, tokensFile);
dataDir = resolveDataDir(modelDir, dataDir);
if (!modelFile || !tokensFile || !dataDir) {
usage(
"Model directory is missing required files. Set SHERPA_ONNX_MODEL_FILE, SHERPA_ONNX_TOKENS_FILE, SHERPA_ONNX_DATA_DIR or pass --model-file/--tokens-file/--data-dir.",
);
}
const text = textParts.join(" ").trim();
if (!text) {
usage("Missing text.");
}
const bin = resolveBinary(runtimeDir);
if (!fs.existsSync(bin)) {
usage(`TTS binary not found: ${bin}`);
}
const env = { ...process.env };
const libDir = path.join(runtimeDir, "lib");
if (process.platform === "darwin") {
env.DYLD_LIBRARY_PATH = prependEnvPath(env.DYLD_LIBRARY_PATH || "", libDir);
} else if (process.platform === "win32") {
env.PATH = prependEnvPath(env.PATH || "", [path.join(runtimeDir, "bin"), libDir].join(path.delimiter));
} else {
env.LD_LIBRARY_PATH = prependEnvPath(env.LD_LIBRARY_PATH || "", libDir);
}
const outputPath = path.isAbsolute(output) ? output : path.join(process.cwd(), output);
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
const child = spawnSync(
bin,
[
`--vits-model=${modelFile}`,
`--vits-tokens=${tokensFile}`,
`--vits-data-dir=${dataDir}`,
`--output-filename=${outputPath}`,
text,
],
{
stdio: "inherit",
env,
},
);
if (typeof child.status === "number") {
process.exit(child.status);
}
if (child.error) {
console.error(child.error.message || String(child.error));
}
process.exit(1);

View File

@@ -1,49 +0,0 @@
---
name: songsee
description: Generate spectrograms and feature-panel visualizations from audio with the songsee CLI.
homepage: https://github.com/steipete/songsee
metadata:
{
"eggent":
{
"emoji": "🌊",
"requires": { "bins": ["songsee"] },
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "steipete/tap/songsee",
"bins": ["songsee"],
"label": "Install songsee (brew)",
},
],
},
}
---
# songsee
Generate spectrograms + feature panels from audio.
Quick start
- Spectrogram: `songsee track.mp3`
- Multi-panel: `songsee track.mp3 --viz spectrogram,mel,chroma,hpss,selfsim,loudness,tempogram,mfcc,flux`
- Time slice: `songsee track.mp3 --start 12.5 --duration 8 -o slice.jpg`
- Stdin: `cat track.mp3 | songsee - --format png -o out.png`
Common flags
- `--viz` list (repeatable or comma-separated)
- `--style` palette (classic, magma, inferno, viridis, gray)
- `--width` / `--height` output size
- `--window` / `--hop` FFT settings
- `--min-freq` / `--max-freq` frequency range
- `--start` / `--duration` time slice
- `--format` jpg|png
Notes
- WAV/MP3 decode native; other formats use ffmpeg if available.
- Multiple `--viz` renders a grid.

View File

@@ -1,46 +0,0 @@
---
name: sonoscli
description: Control Sonos speakers (discover/status/play/volume/group).
homepage: https://sonoscli.sh
metadata:
{
"eggent":
{
"emoji": "🔊",
"requires": { "bins": ["sonos"] },
"install":
[
{
"id": "go",
"kind": "go",
"module": "github.com/steipete/sonoscli/cmd/sonos@latest",
"bins": ["sonos"],
"label": "Install sonoscli (go)",
},
],
},
}
---
# Sonos CLI
Use `sonos` to control Sonos speakers on the local network.
Quick start
- `sonos discover`
- `sonos status --name "Kitchen"`
- `sonos play|pause|stop --name "Kitchen"`
- `sonos volume set 15 --name "Kitchen"`
Common tasks
- Grouping: `sonos group status|join|unjoin|party|solo`
- Favorites: `sonos favorites list|open`
- Queue: `sonos queue list|play|clear`
- Spotify search (via SMAPI): `sonos smapi search --service "Spotify" --category tracks "query"`
Notes
- If SSDP fails, specify `--ip <speaker-ip>`.
- Spotify Web API search is optional and requires `SPOTIFY_CLIENT_ID/SECRET`.

View File

@@ -1,64 +0,0 @@
---
name: spotify-player
description: Terminal Spotify playback/search via spogo (preferred) or spotify_player.
homepage: https://www.spotify.com
metadata:
{
"eggent":
{
"emoji": "🎵",
"requires": { "anyBins": ["spogo", "spotify_player"] },
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "spogo",
"tap": "steipete/tap",
"bins": ["spogo"],
"label": "Install spogo (brew)",
},
{
"id": "brew",
"kind": "brew",
"formula": "spotify_player",
"bins": ["spotify_player"],
"label": "Install spotify_player (brew)",
},
],
},
}
---
# spogo / spotify_player
Use `spogo` **(preferred)** for Spotify playback/search. Fall back to `spotify_player` if needed.
Requirements
- Spotify Premium account.
- Either `spogo` or `spotify_player` installed.
spogo setup
- Import cookies: `spogo auth import --browser chrome`
Common CLI commands
- Search: `spogo search track "query"`
- Playback: `spogo play|pause|next|prev`
- Devices: `spogo device list`, `spogo device set "<name|id>"`
- Status: `spogo status`
spotify_player commands (fallback)
- Search: `spotify_player search "query"`
- Playback: `spotify_player playback play|pause|next|previous`
- Connect device: `spotify_player connect`
- Like track: `spotify_player like`
Notes
- Config folder: `~/.config/spotify-player` (e.g., `app.toml`).
- For Spotify Connect integration, set a user `client_id` in config.
- TUI shortcuts are available via `?` in the app.

View File

@@ -1,72 +0,0 @@
---
name: wacli
description: Send WhatsApp messages to other people or search/sync WhatsApp history via the wacli CLI (not for normal user chats).
homepage: https://wacli.sh
metadata:
{
"eggent":
{
"emoji": "📱",
"requires": { "bins": ["wacli"] },
"install":
[
{
"id": "brew",
"kind": "brew",
"formula": "steipete/tap/wacli",
"bins": ["wacli"],
"label": "Install wacli (brew)",
},
{
"id": "go",
"kind": "go",
"module": "github.com/steipete/wacli/cmd/wacli@latest",
"bins": ["wacli"],
"label": "Install wacli (go)",
},
],
},
}
---
# wacli
Use `wacli` only when the user explicitly asks you to message someone else on WhatsApp or when they ask to sync/search WhatsApp history.
Do NOT use `wacli` for normal user chats; eggent routes WhatsApp conversations automatically.
If the user is chatting with you on WhatsApp, you should not reach for this tool unless they ask you to contact a third party.
Safety
- Require explicit recipient + message text.
- Confirm recipient + message before sending.
- If anything is ambiguous, ask a clarifying question.
Auth + sync
- `wacli auth` (QR login + initial sync)
- `wacli sync --follow` (continuous sync)
- `wacli doctor`
Find chats + messages
- `wacli chats list --limit 20 --query "name or number"`
- `wacli messages search "query" --limit 20 --chat <jid>`
- `wacli messages search "invoice" --after 2025-01-01 --before 2025-12-31`
History backfill
- `wacli history backfill --chat <jid> --requests 2 --count 50`
Send
- Text: `wacli send text --to "+14155551212" --message "Hello! Are you free at 3pm?"`
- Group: `wacli send text --to "1234567890-123456789@g.us" --message "Running 5 min late."`
- File: `wacli send file --to "+14155551212" --file /path/agenda.pdf --caption "Agenda"`
Notes
- Store dir: `~/.wacli` (override with `--store`).
- Use `--json` for machine-readable output when parsing.
- Backfill requires your phone online; results are best-effort.
- WhatsApp CLI is not needed for routine user chats; its for messaging other people.
- JIDs: direct chats look like `<number>@s.whatsapp.net`; groups look like `<id>@g.us` (use `wacli chats list` to find).