mirror of
https://github.com/eggent-ai/eggent.git
synced 2026-03-07 01:53:08 +00:00
new skills
This commit is contained in:
@@ -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.
|
||||
@@ -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`
|
||||
@@ -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`.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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.
|
||||
248
bundled-skills/excalidraw/SKILL.md
Normal file
248
bundled-skills/excalidraw/SKILL.md
Normal 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
|
||||
199
bundled-skills/excalidraw/references/best-practices.md
Normal file
199
bundled-skills/excalidraw/references/best-practices.md
Normal 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
|
||||
344
bundled-skills/excalidraw/references/diagram-patterns.md
Normal file
344
bundled-skills/excalidraw/references/diagram-patterns.md
Normal 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 |
|
||||
399
bundled-skills/excalidraw/references/element-reference.md
Normal file
399
bundled-skills/excalidraw/references/element-reference.md
Normal 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"]
|
||||
```
|
||||
1365
bundled-skills/excalidraw/references/examples.md
Normal file
1365
bundled-skills/excalidraw/references/examples.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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 ...`
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
@@ -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"
|
||||
```
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
328
bundled-skills/remotion/SKILL.md
Normal file
328
bundled-skills/remotion/SKILL.md
Normal 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
|
||||
```
|
||||
217
bundled-skills/remotion/references/components.md
Normal file
217
bundled-skills/remotion/references/components.md
Normal 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>
|
||||
);
|
||||
};
|
||||
```
|
||||
144
bundled-skills/remotion/references/composition-patterns.md
Normal file
144
bundled-skills/remotion/references/composition-patterns.md
Normal 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.
|
||||
98
bundled-skills/remotion/references/remotion.sh
Normal file
98
bundled-skills/remotion/references/remotion.sh
Normal 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
|
||||
@@ -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`)
|
||||
@@ -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."`
|
||||
@@ -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);
|
||||
@@ -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.
|
||||
@@ -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`.
|
||||
@@ -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.
|
||||
@@ -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; it’s 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).
|
||||
Reference in New Issue
Block a user