A decentralized music tracking and discovery platform built on AT Protocol 🎵
listenbrainz
spotify
atproto
lastfm
musicbrainz
scrobbling
1#!/usr/bin/env node
2
3import chalk from "chalk";
4import { albums } from "cmd/albums";
5import { artists } from "cmd/artists";
6import { createApiKey } from "cmd/create";
7import { mcp } from "cmd/mcp";
8import { nowplaying } from "cmd/nowplaying";
9import { scrobble } from "cmd/scrobble";
10import { scrobbles } from "cmd/scrobbles";
11import { search } from "cmd/search";
12import { stats } from "cmd/stats";
13import { tracks } from "cmd/tracks";
14import { whoami } from "cmd/whoami";
15import { Command } from "commander";
16import version from "../package.json" assert { type: "json" };
17import { login } from "./cmd/login";
18
19const program = new Command();
20
21program
22 .name("rocksky")
23 .description(
24 `Command-line interface for Rocksky (${chalk.underline(
25 "https://rocksky.app"
26 )}) – scrobble tracks, view stats, and manage your listening history.`
27 )
28 .version(version.version);
29
30program
31 .command("login")
32 .argument("<handle>", "your BlueSky handle (e.g., <username>.bsky.social)")
33 .description("login with your BlueSky account and get a session token.")
34 .action(login);
35
36program
37 .command("whoami")
38 .description("get the current logged-in user.")
39 .action(whoami);
40
41program
42 .command("nowplaying")
43 .argument(
44 "[did]",
45 "the DID or handle of the user to get the now playing track for."
46 )
47 .description("get the currently playing track.")
48 .action(nowplaying);
49
50program
51 .command("scrobbles")
52 .option("-s, --skip <number>", "number of scrobbles to skip")
53 .option("-l, --limit <number>", "number of scrobbles to limit")
54 .argument("[did]", "the DID or handle of the user to get the scrobbles for.")
55 .description("display recently played tracks.")
56 .action(scrobbles);
57
58program
59 .command("search")
60 .option("-a, --albums", "search for albums")
61 .option("-t, --tracks", "search for tracks")
62 .option("-u, --users", "search for users")
63 .option("-l, --limit <number>", "number of results to limit")
64 .argument(
65 "<query>",
66 "the search query, e.g., artist, album, title or account"
67 )
68 .description("search for tracks, albums, or accounts.")
69 .action(search);
70
71program
72 .command("stats")
73 .option("-l, --limit <number>", "number of results to limit")
74 .argument("[did]", "the DID or handle of the user to get stats for.")
75 .description("get the user's listening stats.")
76 .action(stats);
77
78program
79 .command("artists")
80 .option("-l, --limit <number>", "number of results to limit")
81 .argument("[did]", "the DID or handle of the user to get artists for.")
82 .description("get the user's top artists.")
83 .action(artists);
84
85program
86 .command("albums")
87 .option("-l, --limit <number>", "number of results to limit")
88 .argument("[did]", "the DID or handle of the user to get albums for.")
89 .description("get the user's top albums.")
90 .action(albums);
91
92program
93 .command("tracks")
94 .option("-l, --limit <number>", "number of results to limit")
95 .argument("[did]", "the DID or handle of the user to get tracks for.")
96 .description("get the user's top tracks.")
97 .action(tracks);
98
99program
100 .command("scrobble")
101 .argument("<track>", "the title of the track")
102 .argument("<artist>", "the artist of the track")
103 .option("-t, --timestamp <timestamp>", "the timestamp of the scrobble")
104 .description("scrobble a track to your profile.")
105 .action(scrobble);
106
107program
108 .command("create")
109 .description("create a new API key.")
110 .command("apikey")
111 .argument("<name>", "the name of the API key")
112 .option("-d, --description <description>", "the description of the API key")
113 .description("create a new API key.")
114 .action(createApiKey);
115
116program
117 .command("mcp")
118 .description("Starts an MCP server to use with Claude or other LLMs.")
119 .action(mcp);
120
121program.parse(process.argv);