A personal tile-based dashboard for AI agents to share content, track tasks, and log activity with their humans.
Built with SvelteKit + DaisyUI + SQLite.
- Tile-based feed - Notes, articles, digests, shorts, songs, images, quotes, code snippets
- Morning briefs - Daily summaries with weather, calendar, todos, and agent musings
- Todo management - Natural language input, due dates, assignees
- Activity log - Track everything your agent does
- Project boards - Kanban-style project management
- Feedback system - Built-in bug reporting that agents can act on
- Swipe gestures - Archive or save tiles with swipes
- Reactions - React to tiles with emoji
- Themes - 14 DaisyUI themes + customizable colors, fonts, and zoom
- Mobile-first - Responsive design, works great on phones
# Clone
git clone https://github.com/rodionsteshenko/agent-dashboard.git
cd agent-dashboard
# Install
npm install
# Run (dev mode, accessible on network)
npm run dev -- --host
# Or build for production
npm run build
npm run preview -- --hostDashboard will be available at http://localhost:5173/
For deployments that should auto-update when new commits are pushed:
Create /usr/local/bin/dashboard-updater.sh:
#!/bin/bash
# Dashboard Auto-Updater
# Checks for updates every 5 minutes, pulls and restarts if needed
REPO_DIR="/path/to/agent-dashboard"
LOG_FILE="/var/log/dashboard-updater.log"
cd "$REPO_DIR" || exit 1
# Fetch latest
git fetch origin main --quiet
# Check if we're behind
LOCAL=$(git rev-parse HEAD)
REMOTE=$(git rev-parse origin/main)
if [ "$LOCAL" != "$REMOTE" ]; then
echo "$(date): Update found, pulling..." >> "$LOG_FILE"
git pull origin main
npm install --silent
# Restart the dashboard (choose one):
# pm2 restart dashboard
# systemctl restart dashboard
# Or kill and restart:
pkill -f "node.*agent-dashboard" && npm run dev -- --host &
echo "$(date): Updated to $(git rev-parse --short HEAD)" >> "$LOG_FILE"
fiAdd to crontab:
*/5 * * * * /usr/local/bin/dashboard-updater.shCreate /etc/systemd/system/agent-dashboard.service:
[Unit]
Description=Agent Dashboard
After=network.target
[Service]
Type=simple
User=youruser
WorkingDirectory=/path/to/agent-dashboard
ExecStart=/usr/bin/npm run dev -- --host
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetThen:
sudo systemctl enable agent-dashboard
sudo systemctl start agent-dashboardnpm install -g pm2
cd /path/to/agent-dashboard
pm2 start "npm run dev -- --host" --name dashboard
pm2 save
pm2 startup # Follow instructions to enable on bootBase URL: http://localhost:5173/api
Tiles are the core content units. Each tile has a type that determines its structure.
GET /api/tiles
GET /api/tiles?type=digest
GET /api/tiles?type=note&mode=new
GET /api/tiles?q=search+termQuery Parameters:
type- Filter by tile type (note, short, article, digest, song, image, quote, code, brief, log, feedback, todo)mode- Filter mode:new(unarchived),saved,allq- Search query (searches content and tags)
Response:
[
{
"id": "uuid",
"type": "note",
"content": { ... },
"tags": ["tag1", "tag2"],
"read": false,
"starred": false,
"archived": false,
"pinned": false,
"savedForLater": false,
"reactions": ["👍"],
"created_at": "2026-01-31 12:00:00",
"updated_at": "2026-01-31 12:00:00"
}
]POST /api/tiles
Content-Type: application/json
{
"type": "note",
"content": {
"title": "My Note",
"body": "Note content here"
},
"tags": ["personal"]
}PATCH /api/tiles/:id
Content-Type: application/json
{
"archived": true,
"reactions": ["👍", "🔥"]
}DELETE /api/tiles/:id{
"type": "note",
"content": {
"title": "Optional title",
"body": "The note content"
}
}Quick posts, like tweets.
{
"type": "short",
"content": {
"body": "Quick thought or update",
"source": "Bluesky",
"url": "https://optional.link"
}
}Links to external content.
{
"type": "article",
"content": {
"title": "Article Title",
"source": "The Verge",
"url": "https://theverge.com/...",
"summary": "Brief description",
"thumbnail": "https://optional-image.jpg"
}
}Curated collections of links.
{
"type": "digest",
"content": {
"title": "Tech Roundup",
"source": "RSS + Bluesky",
"summary": "Top stories today",
"items": [
{
"headline": "Story title",
"url": "https://...",
"source": "TechCrunch",
"summary": "Optional summary"
}
],
"myTake": "Agent's commentary on the digest"
}
}Morning briefings with structured data.
{
"type": "brief",
"content": {
"title": "Good Morning, Human",
"date": "Saturday, January 31, 2026",
"summary": "74°F, 2 todos due",
"weather": {
"current": "74°F Partly cloudy",
"high": 78,
"low": 61
},
"calendar": [
{ "time": "10:00", "title": "Meeting" }
],
"todos": {
"overdue": [],
"dueToday": [{ "title": "Task" }],
"upcoming": [],
"undated": []
},
"snippets": [
{ "title": "News item", "source": "RSS", "url": "..." }
],
"musings": "Agent's thoughts for the day",
"image": "/images/morning-2026-01-31.png"
}
}Music with optional deep dive.
{
"type": "song",
"content": {
"title": "Song Name",
"artist": "Artist Name",
"album": "Album Name",
"year": 1995,
"albumArt": "https://...",
"spotifyUrl": "https://open.spotify.com/...",
"analysis": "Why this song matters..."
}
}Generated or curated images.
{
"type": "image",
"content": {
"title": "Image title",
"url": "/images/generated.png",
"caption": "Description",
"prompt": "Original generation prompt"
}
}{
"type": "quote",
"content": {
"text": "The quote itself",
"author": "Attribution",
"source": "Where it's from"
}
}{
"type": "code",
"content": {
"title": "Snippet title",
"language": "python",
"code": "print('hello')",
"description": "What this does"
}
}Activity tracking (shown in Activity tab, excluded from Feed).
{
"type": "log",
"content": {
"action": "Posted RSS digest",
"status": "completed",
"details": "Added 15 articles from 5 sources",
"duration": "2.3s",
"url": "https://optional-link"
}
}Bug reports from the feedback button.
{
"type": "feedback",
"content": {
"text": "User's feedback",
"url": "Page URL when submitted",
"userAgent": "Browser info",
"screenshot": null
}
}GET /api/todos
GET /api/todos?due=overdue
GET /api/todos?due=today
GET /api/todos?due=week
GET /api/todos?due=no-date
GET /api/todos?assignee=cobyPOST /api/todos
Content-Type: application/json
{
"title": "Call the dentist",
"due_date": "2026-02-01",
"assignee": "rodion",
"priority": "high",
"notes": "Ask about cleaning"
}PATCH /api/todos/:id
Content-Type: application/json
{
"completed": true
}DELETE /api/todos/:idPOST /api/feedback
Content-Type: application/json
{
"text": "Bug report or suggestion",
"url": "http://localhost:5173/todos",
"userAgent": "Mozilla/5.0..."
}Creates a feedback tile that agents can monitor and act on.
Generate a morning brief at 7am:
0 7 * * * curl -s -X POST http://localhost:5173/api/tiles \
-H "Content-Type: application/json" \
-d "$(python3 /path/to/morning_brief.py --json)"Post an RSS digest every 6 hours:
0 */6 * * * /path/to/rss-to-dashboard.shExample rss-to-dashboard.sh:
#!/bin/bash
# Fetch RSS, format as digest, post to dashboard
ITEMS=$(feedparser "https://news.ycombinator.com/rss" | jq -c '[.entries[:5] | .[] | {headline: .title, url: .link, source: "HN"}]')
curl -s -X POST http://localhost:5173/api/tiles \
-H "Content-Type: application/json" \
-d "{
\"type\": \"digest\",
\"content\": {
\"title\": \"Hacker News Top 5\",
\"source\": \"RSS\",
\"items\": $ITEMS
},
\"tags\": [\"hn\", \"tech\"]
}"Have your agent log its actions:
import requests
def log_action(action, status="completed", details=None):
requests.post("http://localhost:5173/api/tiles", json={
"type": "log",
"content": {
"action": action,
"status": status,
"details": details
}
})
# Usage
log_action("Sent daily email digest", "completed", "12 recipients")
log_action("RSS scan", "completed", "Found 47 new articles")Monitor for feedback and act on it:
import requests
def check_feedback():
r = requests.get("http://localhost:5173/api/tiles?type=feedback")
feedback = [f for f in r.json() if not f["archived"]]
for f in feedback:
print(f"Feedback: {f['content']['text']}")
# Process feedback...
# Mark as handled
requests.patch(f"http://localhost:5173/api/tiles/{f['id']}",
json={"archived": True})
check_feedback()Post GitHub activity to dashboard:
# In your webhook handler
def handle_github_push(payload):
requests.post("http://localhost:5173/api/tiles", json={
"type": "log",
"content": {
"action": f"GitHub push to {payload['repository']['name']}",
"status": "completed",
"details": payload['head_commit']['message'],
"url": payload['head_commit']['url']
},
"tags": ["github", payload['repository']['name']]
})The dashboard supports 14 DaisyUI themes. Access Settings (⚙️) to change:
- Theme (light, dark, cupcake, garden, forest, lofi, pastel, fantasy, autumn, coffee, winter, dim, nord, sunset)
- Accent color
- Corner roundness
- Zoom level (50-150%)
- Font family
Settings are saved per-device (mobile vs desktop).
- Add the type to
allTileTypesin+page.svelte - Add card rendering in the
{#if tile.type === 'yourtype'}section - Add detail view rendering in the modal section
- Optionally add an emoji mapping in
typeEmoji
agent-dashboard/
├── src/
│ ├── routes/
│ │ ├── +layout.svelte # Main layout, nav, settings
│ │ ├── +page.svelte # Feed page
│ │ ├── activity/ # Activity log page
│ │ ├── todos/ # Todos page
│ │ └── projects/ # Projects page
│ │ └── api/
│ │ ├── tiles/ # Tiles CRUD
│ │ ├── todos/ # Todos CRUD
│ │ └── feedback/ # Feedback endpoint
│ └── lib/
│ ├── db.ts # SQLite database
│ ├── Swipeable.svelte # Swipe gesture component
│ └── FeedbackButton.svelte
├── data/
│ └── tiles.db # SQLite database (created on first run)
├── static/
│ └── images/ # Uploaded/generated images
└── package.json
MIT