mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-11 19:47:12 +03:00
feat: add lastfm skill — Last.fm music data API with discovery pipeline
A skill wrapping the lastfm-cli tool for the Last.fm music data API: user listening history, artist/album/track metadata, collaborative filtering-based similar music discovery, global/per-country charts, search, tags, and scrobble/love/now-playing write operations. Includes a Music Discovery Pipeline section for turning liked tracks into recommendations via Last.fm's collaborative filtering graph.
This commit is contained in:
@@ -63,7 +63,8 @@ When the user mentions these keywords, load the corresponding skill:
|
|||||||
|| "brand identity", "brand guidelines", "style guide", "brand card", "brand strategy", "visual identity", "brand documentation", "color palette", "brand book" | [brand-designer](brand-designer/SKILL.md) |
|
|| "brand identity", "brand guidelines", "style guide", "brand card", "brand strategy", "visual identity", "brand documentation", "color palette", "brand book" | [brand-designer](brand-designer/SKILL.md) |
|
||||||
|| "kanban", "WIP", "cycle time", "flow metrics", "Scrum to Kanban", "multi-portfolio", "throughput", "classes of service" | [kanban-guru](kanban-guru/SKILL.md) |
|
|| "kanban", "WIP", "cycle time", "flow metrics", "Scrum to Kanban", "multi-portfolio", "throughput", "classes of service" | [kanban-guru](kanban-guru/SKILL.md) |
|
||||||
|| "skill format", "how do I make a skill", "agentskills.io" | [agent-skills](agent-skills/SKILL.md) |
|
|| "skill format", "how do I make a skill", "agentskills.io" | [agent-skills](agent-skills/SKILL.md) |
|
||||||
|| "nous", "theia", "hermes brand", "brand identity", "style guide", "mascot", "anime style", "cyber-classical", "color palette reference" | [nous-branding](nous-branding/SKILL.md) |
|
||| "last.fm", "scrobble", "music discovery", "listening history", "similar artists", "lastfm", "weekly top artists", "genre charts" | [lastfm](lastfm/SKILL.md) |
|
||||||
|
||| "nous", "theia", "hermes brand", "brand identity", "style guide", "mascot", "anime style", "cyber-classical", "color palette reference" | [nous-branding](nous-branding/SKILL.md) |
|
||||||
|
|
||||||
## Best Practices
|
## Best Practices
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ Atlassian Jira from the terminal. Search issues with JQL, view details, create i
|
|||||||
|
|
||||||
Expert-level Jira Query Language reference covering all operators, functions (date/time, user, sprint/version, issue, custom field, JSM), history operators (WAS/CHANGED), relative dates, performance best practices, role-based ready queries, REST API usage, and troubleshooting. Three companion references: complete function catalog, role-specific query bank (dev, scrum master, PO, power user, admin), and gotchas/troubleshooting guide.
|
Expert-level Jira Query Language reference covering all operators, functions (date/time, user, sprint/version, issue, custom field, JSM), history operators (WAS/CHANGED), relative dates, performance best practices, role-based ready queries, REST API usage, and troubleshooting. Three companion references: complete function catalog, role-specific query bank (dev, scrum master, PO, power user, admin), and gotchas/troubleshooting guide.
|
||||||
|
|
||||||
|
### [lastfm](lastfm/SKILL.md)
|
||||||
|
|
||||||
|
Last.fm music data API from the terminal. Lookup user listening history, get artist/album/track metadata, discover similar music via collaborative filtering, explore global and per-country charts, search, manage tags, and scrobble listening events. API key from last.fm/api/account/create (free). Includes a music discovery pipeline for turning liked tracks into recommendations.
|
||||||
|
|
||||||
### [nous-branding](nous-branding/SKILL.md)
|
### [nous-branding](nous-branding/SKILL.md)
|
||||||
|
|
||||||
Generate images and content consistent with the Nous Research brand identity.
|
Generate images and content consistent with the Nous Research brand identity.
|
||||||
|
|||||||
+161
@@ -0,0 +1,161 @@
|
|||||||
|
---
|
||||||
|
name: lastfm
|
||||||
|
description: >-
|
||||||
|
Interact with the Last.fm music data API: lookup user listening history,
|
||||||
|
get artist/album/track metadata, discover similar music via collaborative
|
||||||
|
filtering, explore global and per-country charts, search by artist/album/track,
|
||||||
|
manage tags, and scrobble listening events. Use when the user asks about
|
||||||
|
music data, listening statistics, music recommendations, similar artists,
|
||||||
|
charts, or wants to scrobble or love tracks.
|
||||||
|
license: MIT
|
||||||
|
compatibility: Requires a Last.fm API key in the LASTFM_API_KEY env var.
|
||||||
|
For write operations (scrobble, love, now-playing), also needs
|
||||||
|
LASTFM_API_SECRET and LASTFM_SESSION_KEY. The `lastfm-cli` CLI tool
|
||||||
|
must be on PATH.
|
||||||
|
metadata:
|
||||||
|
tags: [music, lastfm, scrobbling, api-client, music-discovery]
|
||||||
|
source: https://www.last.fm/api
|
||||||
|
---
|
||||||
|
|
||||||
|
# Last.fm
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LASTFM_API_KEY="your_api_key_here"
|
||||||
|
# For write operations only:
|
||||||
|
export LASTFM_API_SECRET="your_api_secret"
|
||||||
|
export LASTFM_SESSION_KEY="your_session_key"
|
||||||
|
```
|
||||||
|
|
||||||
|
Get an API key at https://www.last.fm/api/account/create (free, requires a Last.fm account).
|
||||||
|
|
||||||
|
## Essential Commands
|
||||||
|
|
||||||
|
### User queries — "what am I listening to"
|
||||||
|
|
||||||
|
Configurable via `LASTFM_USERNAME` env var. Defaults to `<username>` if unset.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lastfm-cli user info <username> # Profile + stats
|
||||||
|
lastfm-cli user recent-tracks <username> --limit 10 # Recent listening
|
||||||
|
lastfm-cli user top-artists <username> --period 7day # Weekly top artists
|
||||||
|
lastfm-cli user top-tracks <username> --period overall # All-time faves
|
||||||
|
lastfm-cli user loved-tracks <username> # Loved tracks
|
||||||
|
lastfm-cli user friends <username> # Social graph
|
||||||
|
lastfm-cli user weekly-charts <username> # Available chart periods
|
||||||
|
```
|
||||||
|
|
||||||
|
### Music Discovery — "what's similar to X"
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lastfm-cli artist similar <artist> --limit 10 # Taste graph neighbors
|
||||||
|
lastfm-cli artist info <artist> # Bio, stats, tags
|
||||||
|
lastfm-cli artist top-tracks <artist> # Their most popular
|
||||||
|
lastfm-cli track similar <artist> "<track>" # Track-level similarity
|
||||||
|
lastfm-cli album info <artist> "<album>" # Tracklist, metadata
|
||||||
|
lastfm-cli tag top-artists <tag> # Genre browsing
|
||||||
|
```
|
||||||
|
|
||||||
|
### Charts — "what's popular"
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lastfm-cli chart top-artists --limit 20 # Global
|
||||||
|
lastfm-cli geo top-artists "Japan" --limit 20 # Per-country
|
||||||
|
lastfm-cli geo top-tracks "Germany" --limit 20
|
||||||
|
lastfm-cli tag top-tracks "electronic" --limit 15 # By genre tag
|
||||||
|
```
|
||||||
|
|
||||||
|
### Search — "find that thing"
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lastfm-cli artist search "Radiohead" # Find by name
|
||||||
|
lastfm-cli album search "OK Computer" # Find albums
|
||||||
|
lastfm-cli track search "Karma Police" # Find tracks
|
||||||
|
```
|
||||||
|
|
||||||
|
### Write operations (require session auth)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lastfm-cli track scrobble "Artist" "Song" --album "Album"
|
||||||
|
lastfm-cli track now-playing "Artist" "Song"
|
||||||
|
lastfm-cli track love "Artist" "Song"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Auth flow (one-time setup)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Get a token
|
||||||
|
lastfm-cli auth get-token
|
||||||
|
|
||||||
|
# 2. User visits: https://www.last.fm/api/auth/?api_key=KEY&token=TOKEN
|
||||||
|
|
||||||
|
# 3. Exchange for session key
|
||||||
|
lastfm-cli auth get-session <token>
|
||||||
|
# → Set export LASTFM_SESSION_KEY=<key>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reference Files
|
||||||
|
|
||||||
|
- `references/auth-flow.md` — Full walkthrough for setting up write operation auth (scrobble, love, now-playing). Read this before attempting write operations.
|
||||||
|
|
||||||
|
## Music Discovery Pipeline — "find me new stuff I'll like"
|
||||||
|
|
||||||
|
The core flow for turning liked tracks into recommendations:
|
||||||
|
|
||||||
|
1. **Get user's top artists/tracks** from Last.fm:
|
||||||
|
```bash
|
||||||
|
lastfm-cli user top-artists <username> --period 1month --limit 5 --json
|
||||||
|
lastfm-cli user top-tracks <username> --period 1month --limit 10 --json
|
||||||
|
lastfm-cli user loved-tracks <username> --json
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **For each artist, find similar artists** via Last.fm's collaborative filtering:
|
||||||
|
```bash
|
||||||
|
lastfm-cli artist similar "<artist>" --limit 5 --json
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **For each track, find similar tracks** for more granular recommendations:
|
||||||
|
```bash
|
||||||
|
lastfm-cli track similar "<artist>" "<track>" --limit 5 --json
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Cross-reference** against what they've already scrobbled (recent-tracks) to filter out already-heard material.
|
||||||
|
|
||||||
|
5. **Check against the user's own collection** (via Radarr/Sonarr/jellyfin-cli or Spotify library) to see what's already in the library vs genuinely new discovery.
|
||||||
|
|
||||||
|
## Using with --json for Machine Processing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Pipe to jq for structured queries
|
||||||
|
lastfm-cli artist similar "Radiohead" --limit 5 --json | jq '.similarartists.artist[] | {name, match}'
|
||||||
|
|
||||||
|
# Get a user's all-time top artists with playcounts
|
||||||
|
lastfm-cli user top-artists <username> --period overall --limit 3 --json | jq '.topartists.artist[] | {name, playcount}'
|
||||||
|
|
||||||
|
# Find most popular tracks by a genre
|
||||||
|
lastfm-cli tag top-tracks "electronic" --limit 10 --json | jq '.tracks.track[] | {name, artist: .artist.name, listeners}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Known Gotchas
|
||||||
|
|
||||||
|
- **API key required for EVERY request.** The key is free — get one at the Last.fm API account page.
|
||||||
|
- **Rate limit is ~5 req/sec sustained.** The API docs say "be reasonable" — if you're making several calls per second continuously, your account may be suspended. Add small delays in loops.
|
||||||
|
- **User-Agent header matters.** The CLI sends `lastfm-cli/1.0 (hermes-agent)`. Last.fm's docs explicitly ask for an identifiable User-Agent.
|
||||||
|
- **XML is the default response format.** The CLI requests `format=json`. Without it, responses come back as XML.
|
||||||
|
- **Artist names can be misspelled.** Use `--autocorrect` flag on artist commands to let Last.fm correct misspellings.
|
||||||
|
- **Write operations need auth setup.** scrobble, love, and now-playing require a full auth flow (token → session key). Read-only endpoints don't.
|
||||||
|
- **The collaborative filtering graph is NOT social.** `artist.getSimilar` returns algorithmic similarity based on aggregate listening patterns, not human-curated recommendations.
|
||||||
|
- **Timestamps for scrobbles** are UNIX epoch seconds. If omitted, uses current time.
|
||||||
|
- **`--from` and `--to` on recent-tracks** accept ISO 8601 date strings (e.g., `2026-05-27`).
|
||||||
|
- **Period values**: `overall`, `7day`, `1month`, `3month`, `6month`, `12month`.
|
||||||
|
|
||||||
|
## When to Reach for This Tool
|
||||||
|
|
||||||
|
- User asks "what's [person] listening to lately?"
|
||||||
|
- User wants music discovery: "find me artists similar to..."
|
||||||
|
- User wants listening statistics: top artists, tracks, albums by period
|
||||||
|
- User wants geographic music trends: "what's popular in [country]"
|
||||||
|
- User wants genre exploration via tags
|
||||||
|
- User wants to scrobble or love tracks programmatically
|
||||||
|
- Any question about music metadata, artist info, album tracklists
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Last.fm API Authentication Flow
|
||||||
|
|
||||||
|
Write operations (scrobble, love, now-playing, add/remove tags) require a **session key** obtained through a three-step web auth flow. Read operations (getInfo, getSimilar, search, chart, geo, tag) need only an API key.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
```
|
||||||
|
LASTFM_API_KEY # Required for ALL requests (read + write)
|
||||||
|
LASTFM_API_SECRET # Required only for signed (write) requests
|
||||||
|
```
|
||||||
|
|
||||||
|
Get both from https://www.last.fm/api/account/create — they come together when you register an API application.
|
||||||
|
|
||||||
|
## Step 1: Get a Token
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lastfm-cli auth get-token
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns a short-lived token (like a one-time use code). The token is valid for only a few minutes.
|
||||||
|
|
||||||
|
## Step 2: User Authorizes the Token
|
||||||
|
|
||||||
|
The user must visit this URL in a browser:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://www.last.fm/api/auth/?api_key=***&token=***
|
||||||
|
```
|
||||||
|
|
||||||
|
This page asks the user to authorize your application to scrobble/love on their behalf. They click "Allow" and the token becomes authorized.
|
||||||
|
|
||||||
|
**Important:** This step MUST be done by a human in a browser. There's no programmatic way to authorize — the whole point is that the user grants permission explicitly.
|
||||||
|
|
||||||
|
## Step 3: Exchange Token for Session Key
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lastfm-cli auth get-session <token>
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- `session.key` — the session key (store this, it doesn't expire)
|
||||||
|
- `session.name` — the Last.fm username that authorized
|
||||||
|
|
||||||
|
## Step 4: Store and Use
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LASTFM_SESSION_KEY="<the session key>"
|
||||||
|
```
|
||||||
|
|
||||||
|
Once set, write operations work:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
lastfm-cli track love "Radiohead" "Karma Police"
|
||||||
|
lastfm-cli track scrobble "Massive Attack" "Teardrop" --album "Mezzanine"
|
||||||
|
lastfm-cli track now-playing "Portishead" "Glory Box"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
- **The session key does not expire.** Set it once, use it forever (or until the user revokes your API app's access from their Last.fm account settings).
|
||||||
|
- **Each user needs their own session key.** If you're building something for multiple users, each one goes through steps 1-3 separately.
|
||||||
|
- **The API secret is NOT the session key.** The secret comes from your API app registration page. The session key comes from the auth flow. They are different values.
|
||||||
|
- **Signing:** Write requests are "signed" — the CLI sorts all parameters alphabetically, concatenates them with the API secret, MD5-hashes them, and sends the hash as `api_sig`. The CLI handles this automatically when you provide `LASTFM_API_SECRET` and `LASTFM_SESSION_KEY`.
|
||||||
|
- **The token is single-use.** If the auth page errors or times out, get a new token and try again.
|
||||||
Executable
+71
@@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# lastfm-cli test suite
|
||||||
|
set -euo pipefail
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
test() {
|
||||||
|
local desc="$1"
|
||||||
|
shift
|
||||||
|
if "$@" 2>&1; then
|
||||||
|
echo " PASS: $desc"
|
||||||
|
PASS=$((PASS + 1))
|
||||||
|
else
|
||||||
|
echo " FAIL: $desc"
|
||||||
|
FAIL=$((FAIL + 1))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_output() {
|
||||||
|
local desc="$1" expected="$2"
|
||||||
|
shift 2
|
||||||
|
local output
|
||||||
|
output="$("$@" 2>&1 || true)"
|
||||||
|
if echo "$output" | grep -qi "$expected"; then
|
||||||
|
echo " PASS: $desc"
|
||||||
|
PASS=$((PASS + 1))
|
||||||
|
else
|
||||||
|
echo " FAIL: $desc (expected to contain '$expected')"
|
||||||
|
echo " Got: $output"
|
||||||
|
FAIL=$((FAIL + 1))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# CLI path — adjust if not on PATH
|
||||||
|
CLI=lastfm-cli
|
||||||
|
|
||||||
|
echo "=== lastfm-cli Test Suite ===
|
||||||
|
"
|
||||||
|
|
||||||
|
# 1. Syntax check
|
||||||
|
test "Python syntax check" python3 -c "import py_compile; py_compile.compile('$CLI', doraise=True)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 2. --help on main
|
||||||
|
check_output "--help shows usage" "Examples" $CLI --help
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 3. --help on subcommands
|
||||||
|
check_output "user --help" "username" $CLI user info --help
|
||||||
|
check_output "artist --help" "similar" $CLI artist --help
|
||||||
|
check_output "track --help" "scrobble" $CLI track --help
|
||||||
|
check_output "chart --help" "top-artists" $CLI chart --help
|
||||||
|
check_output "geo --help" "country" $CLI geo top-artists --help
|
||||||
|
check_output "tag --help" "top-albums" $CLI tag --help
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 4. Missing required args → error with corrective info
|
||||||
|
check_output "user info missing username → error" "required" $CLI user info
|
||||||
|
check_output "artist info missing artist → error" "required" $CLI artist info
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 5. --dry-run works on read commands
|
||||||
|
check_output "user recent-tracks --dry-run" "dry" $CLI user recent-tracks testuser --dry-run
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 6. --json output with no api key → graceful error
|
||||||
|
check_output "Missing API key → graceful error" "LASTFM_API_KEY" $CLI user info testuser
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=== Results: $PASS passed, $FAIL failed ==="
|
||||||
|
[[ $FAIL -eq 0 ]] || exit 1
|
||||||
Reference in New Issue
Block a user