diff --git a/website/src/pages/docs.tsx b/website/src/pages/docs.tsx index 83f696f..0e602fe 100644 --- a/website/src/pages/docs.tsx +++ b/website/src/pages/docs.tsx @@ -203,9 +203,16 @@ export const Docs = () => {
soulseek-rs skills install writes a SKILL.md into
every coding agent on the machine — Claude Code and opencode today,{' '}
- --dir for anything else. It carries what --help{' '}
- cannot: the JSON keys of every record, what each exit code means, and the
- idioms worth reaching for. Run it again after an upgrade to update it, and{' '}
+ --dir for anything else. After it lands you describe the
+ outcome instead of the pipeline: “mirror that peer’s FLAC
+ folder”, “watch this room for an hour and save anything
+ mentioning the label”.
+
+ It carries what --help cannot: the JSON keys of every record,
+ that exit 4 means widen the query rather than retry it, that remote paths go
+ back byte-identical, and that --follow never returns so an agent
+ passes --duration. Run it again after an upgrade to update it;{' '}
skills uninstall takes it back out.
--json. The skill ships inside the binary, so there is nothing to
- clone and nothing to configure — it finds Claude Code and opencode on its own.
+ Run it once and asking beats scripting: your agent knows the record shapes, the
+ exit codes, and the filters, so “find the 1998 pressing, lossless only”
+ becomes a pipeline it writes itself. No MCP server, no wrapper — the skill ships
+ inside the binary and finds Claude Code and opencode on its own.
>
),
},
@@ -257,6 +257,53 @@ export const Home = () => {
+ skills install closes that gap in one command.
+
+ --help lists flags. It cannot say which JSON keys a record
+ carries, that exit 4 means widen the query rather than retry it, that a
+ remote path is backslash-separated and has to go back byte-identical, or
+ that --follow never returns and an agent wants{' '}
+ --duration instead. An agent without that guesses, and guessing
+ against someone else’s file server is how you get a hung job.
+
+ The skill is that missing half. Once it lands, the interesting requests stop + being pipelines you write: mirror a peer’s FLAC folder overnight, watch + a room and save anything that mentions a label, check whether a user is worth + queueing behind before committing to a 2 GB transfer. +
+
+ It stays a CLI — no daemon, no MCP server, no API key. Upgrading the binary
+ upgrades the skill; run skills install again to take it.{' '}
+
+ See the commands
+
+
portmap ask
+ your router.
+ >
+ ),
+ },
+ {
+ key: 'disable_listener',
+ fallback: 'false',
+ body: (
+ <>
+ true refuses incoming peer connections. You can still download from
+ reachable peers, but firewalled ones can no longer reach you.
+ >
+ ),
+ },
+ {
+ key: 'download_dir',
+ fallback: 'the Downloads path above',
+ body: <>Where finished transfers land.>,
+ },
+ {
+ key: 'shared_dirs',
+ fallback: 'the download folder',
+ body: (
+ <>
+ Folders offered to the network, as a list. An explicitly empty list shares
+ nothing. shared_dir is the older single-folder spelling and still
+ reads; shares add writes the list form.
+ >
+ ),
+ },
+ {
+ key: 'max_concurrent_downloads',
+ fallback: '5',
+ body: <>Transfers allowed to run at once. The rest queue.>,
+ },
+ {
+ key: 'search_timeout',
+ fallback: '10',
+ body: (
+ <>
+ Seconds a search collects replies before reporting. Results trickle in from
+ separate peers, so a short window finds less.
+ >
+ ),
+ },
+ {
+ key: 'password_cmd',
+ fallback: 'unset',
+ body: (
+ <>
+ A shell command whose stdout is the password, used when the keychain has none.
+ The password itself never belongs in this file.
+ >
+ ),
+ },
+]
+
+const CONFIG_EXAMPLE: TermLine[] = [
+ 'username = "your-name"',
+ 'download_dir = "~/Music/Soulseek"',
+ 'shared_dirs = ["~/Music/Soulseek", "~/Music/Library"]',
+ 'max_concurrent_downloads = 3',
+ 'search_timeout = 15',
+ 'listener_port = 2234',
+ '',
+ '# never store the password itself; shell out for it instead',
+ 'password_cmd = "pass show soulseek"',
+].map((text) => ({ t: 'code' as const, text }))
+
interface StepProps {
n: number
title: string
@@ -308,6 +400,48 @@ export const Install = () => {
+ config list prints the effective value of each.
+
+ {setting.key}
+
+
+ {setting.fallback}
+
+ + {setting.body} +
+
+ config path prints the file in use, config get{' '}
+ reads one setting and config set writes one. Unknown keys are
+ rejected rather than silently ignored, so a typo fails loudly instead of
+ quietly doing nothing. --no-config ignores the file entirely
+ and --config FILE points at another one.
+