Add s2-storage skill with reusable upload script

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Connor Rhodes 2026-05-22 16:53:14 -05:00
parent eb8da9611e
commit 5dcfd0413d
3 changed files with 92 additions and 2 deletions

View file

@ -232,10 +232,10 @@ description: Master index of all skills in your robot assistant system. Your ass
**Dependencies:** `yt-dlp` CLI, Python 3 **Dependencies:** `yt-dlp` CLI, Python 3
### S2 Storage ### S2 Storage
**Purpose:** Upload and manage files on S2, a self-hosted file storage service at s2.connorrhodes.com. Provides upload endpoint and auth details for persistent file storage. **Purpose:** Upload and manage files on S2, a self-hosted file storage service at s2.connorrhodes.com. Use `skills/s2-storage/scripts/s2_upload.py` for all uploads — do not rewrite the curl command inline in other skills.
**Triggers:** "upload to s2," "s2," "s2 storage," "upload a file," "store this file" **Triggers:** "upload to s2," "s2," "s2 storage," "upload a file," "store this file"
**File:** `skills/s2-storage/SKILL.md` **File:** `skills/s2-storage/SKILL.md`
**Dependencies:** `curl` CLI **Dependencies:** `uv`, Python `requests`
### SilverBullet Query ### SilverBullet Query
**Purpose:** Write Space Lua (SLIQ) queries and live filter widgets for SilverBullet notes. Covers filtering pages by folder or tag, listing tasks, showing recently modified notes, building dashboards, and embedding dynamic query expressions in any note. **Purpose:** Write Space Lua (SLIQ) queries and live filter widgets for SilverBullet notes. Covers filtering pages by folder or tag, listing tasks, showing recently modified notes, building dashboards, and embedding dynamic query expressions in any note.

35
s2-storage/SKILL.md Normal file
View file

@ -0,0 +1,35 @@
---
name: s2-storage
description: Upload and manage files on S2, a self-hosted file storage service at s2.connorrhodes.com. Use when the user says "upload to s2", "store this file", "put this on s2", or when any other skill needs to upload a file and get a persistent URL back.
version: 1.0.0
---
# S2 Storage
Upload files to `s2.connorrhodes.com` via the API and get back a persistent URL.
## Endpoint
- **URL:** `POST https://api.connorrhodes.com/agent/s2_upload`
- **Auth:** `x-api-key: LT6CXiLT5cEApfqtThz17bENr6OLP804FepOMqa1tZkfTGXiiCcSFlupl6gaYeX`
- **Response:** JSON string — the public URL of the uploaded file
## Script
Use `scripts/s2_upload.py` for all uploads. Do not rewrite the curl command inline.
```bash
# Upload and print the URL
uv run ~/notes/skills/s2-storage/scripts/s2_upload.py /path/to/file.pdf
# Upload and delete the local file afterwards
uv run ~/notes/skills/s2-storage/scripts/s2_upload.py /path/to/file.pdf --delete
```
The script prints the S2 URL to stdout on success and exits non-zero on failure.
## Rules
- Always delete the local file after upload unless the user explicitly wants to keep it. Pass `--delete` to the script.
- The returned URL is permanent and publicly accessible — treat it as the canonical reference going forward.
- When another skill needs to upload a file (e.g., log-work-expense, brag-sheet), use this script rather than reimplementing the curl call.

View file

@ -0,0 +1,55 @@
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.11"
# dependencies = ["requests"]
# ///
"""Upload a file to S2 storage at s2.connorrhodes.com.
Usage:
s2_upload.py <file_path> [--delete]
Options:
--delete Delete the local file after a successful upload.
Output:
Prints the S2 URL on stdout.
"""
import sys
import os
import requests
API_KEY = "LT6CXiLT5cEApfqtThz17bENr6OLP804FepOMqa1tZkfTGXiiCcSFlupl6gaYeX"
ENDPOINT = "https://api.connorrhodes.com/agent/s2_upload"
def main():
args = sys.argv[1:]
if not args or args[0] in ("-h", "--help"):
print(__doc__)
sys.exit(0 if args else 1)
file_path = args[0]
delete_after = "--delete" in args
if not os.path.isfile(file_path):
print(f"Error: file not found: {file_path}", file=sys.stderr)
sys.exit(1)
with open(file_path, "rb") as f:
resp = requests.post(
ENDPOINT,
headers={"x-api-key": API_KEY},
files={"file": (os.path.basename(file_path), f)},
)
resp.raise_for_status()
url = resp.json() if isinstance(resp.json(), str) else resp.text.strip().strip('"')
print(url)
if delete_after:
os.remove(file_path)
if __name__ == "__main__":
main()