agent-p 0.4.0

A fast podcast downloader with customizable ID3 tagging
agent-p-0.4.0 is not a library.

Why AgentP?

Interactive or automatedChoose episodes in the TUI or script repeatable downloads with the CLI. Library-ready metadataControl album, artist, title, artwork, filenames, and numeric prefixes.
Any RSS podcastAdd a feed manually or let AgentP prepopulate its channel metadata. Flexible selectionDownload specific episodes, the latest or oldest N, or the complete feed.
Fast command palettePress Ctrl+K to find actions without memorizing every shortcut. Runs everywhereOne Rust binary for macOS, Linux, and Windows with a built-in Dracula theme.

Installation

Method Install
Homebrew · macOS and Linux brew install ByteMagicInc/tap/agent-p
Scoop · Windows scoop bucket add bytemagicinc https://github.com/ByteMagicInc/scoop-bucketscoop install agent-p
Cargo · Rust 1.88+ cargo install agent-p --locked
Pre-built binary Download an archive from GitHub Releases and place agentp on your PATH.

[!TIP] Alpine and other minimal Linux distributions can use the fully static x86_64-unknown-linux-musl build.

Installing ffmpeg is recommended but optional (brew install ffmpeg, scoop install ffmpeg, or your distribution's package). AgentP needs it only for Convert To MP3, an off-by-default setting that re-encodes episodes a feed serves as M4A or other non-MP3 audio. Homebrew and Scoop mention ffmpeg after installing AgentP but do not install it. Without ffmpeg, everything else works and those episodes keep their original format.

Quick start

Interactive TUI

agentp

The first launch creates your configuration, seeds a set of demo podcasts, and points downloads at ~/Downloads/Podcasts so you can explore immediately:

  1. Choose a podcast and press Enter.
  2. Mark episodes with Space or select everything with a.
  3. Press Enter to download.

Press c to add your own feeds, change the download folder, and set the defaults for new podcasts.

AgentP comes with my favorite podcasts so you can see how different per-podcast configs are applied.

Scriptable CLI

agentp podcast add --feed-url <RSS_URL> --from-feed
agentp podcast list
agentp download --podcast <NAME_OR_INDEX> --latest 5

Podcast selectors accept either a 1-based index or a case-insensitive name. A subcommand always runs headlessly, and the mode flags do not change that. With no subcommand, agentp opens the TUI unless config.json sets "default_mode": "cli", and --tui or --cli (aliases --user-mode and --agent-mode) override that default for a single run, where --cli prints this help. --help works on every command and agentp --version prints the version.

From a coding agent

npx skills add ByteMagicInc/AgentP --skill agentp -g

This installs the agentp agent skill for Claude Code, Codex, Cursor and other coding agents, so you can ask one to download an episode, find one by topic, date or number, or add a podcast, and it drives the CLI for you. Leave out -g to install it into the current project only. It needs Node.js 22.20 or later. The skill tracks the master branch, so it can be ahead of the latest release: it offers to upgrade an older AgentP, and stops when no newer release is out yet.

Without Node, agentp skill (in releases after 0.3.0) prints the copy bundled with your installed version. Save it as SKILL.md in your agent's skill folder:

mkdir -p ~/.claude/skills/agentp && agentp skill > ~/.claude/skills/agentp/SKILL.md

On Windows, from PowerShell, go through cmd: Windows PowerShell 5.1's > writes UTF-16, which agents cannot read.

New-Item -ItemType Directory -Force "$HOME\.claude\skills\agentp" | Out-Null
cmd /c 'agentp skill > "%USERPROFILE%\.claude\skills\agentp\SKILL.md"'

See AgentP in action

Search episodes and jump to one by number

Add a podcast from its RSS feed

Find actions and update configuration

Screenshots

Podcast library Episode selection
AgentP podcast library Selecting episodes from Committing High Reason
Command palette Configuration
AgentP command palette filtered to folder actions AgentP configuration screen

Keyboard reference

Every screen lists its own keys in the bar at the bottom. Press ? to expand it, or Ctrl+K to search every action by name.

Global

Key Action
Ctrl+K Toggle the command palette
Ctrl+C Quit from anywhere, including during a download (asks first if the podcast editor has unsaved changes)
q Quit when not typing in a text field; ignored while a download is running
Ctrl+V Paste into the active text field or the palette filter
? or . Expand or collapse the key-hint bar

Any list

Available on the podcast list, episode select, podcast picker, config menu, and the podcast editor.

Key Action
Home / g Jump to the first row
End / G Jump to the last row
PageUp / PageDown Move ten rows

Podcast list

Key Action
j / ↓, k / ↑ Move selection
Enter Open episode list
Shift+↑ / Shift+↓ Reorder podcasts
r Refresh feeds
e Edit selected podcast
o Open selected podcast folder
c Config menu
D Delete selected podcast; confirm with y or D, cancel with Esc

Deleting is the one confirmation that does not take Enter. Enter opens a podcast on this screen, and removing one cannot be undone.

Episode select

Key Action
j / ↓, k / ↑ Move selection
Space Toggle episode
a Toggle all (only the episodes a search leaves showing)
s Sort (newest/oldest)
/ Search episodes by title, description, date, or date range
# or any digit Go to an episode by its number
o Open podcast folder
Enter Download selected
Esc Clear the search, or go back when none is active

Every row starts with its episode number, the same numbering --episodes uses: numbers count up from the feed's last item, so in a feed that lists newest first, 1 is the oldest episode and the highest number is the newest.

Search. Press / and type; the list narrows as you type, and the matching words are underlined in each title. Several terms must all match, in any order, ignoring case:

Term Matches Examples
A word The title, the description, or the date as the list shows it, including the full month name copilot, sep, september, 2025
A "phrase" Those words together, in that order; it narrows while you type it, before the closing quote "github copilot"
- before a term Episodes that do not match it -intro, -"live show", -11/2026
A date Episodes published that day. With slashes or dashes the month comes first (11/2/2026 is November 2); a four-digit year at the front reads year, month, day (ISO). Leading zeros are optional 11/2/2026, 11-02-2026, 2026-11-02
A month Episodes published that month, month first or year first 11/2026, 2026-11
A month and day That day in any year 9/10
# and a number That episode number, or an inclusive range #500, #100-120
since: / until: On or after / on or before a date, month, year, or relative day since:2026-01, until:2025, since:30d
after: / before: Strictly after / before the same after:9/10/2025, before:2026, before:1y

A relative day counts back from today: today, yesterday, or a number of days, weeks, months, or years such as 30d, 2w, 6m, 1y. A date written in a title, such as 9/11, still matches that title, and a hashtag such as #MacBookNeo is a plain word. While a range term is unfinished, such as after:20, it is left out and the search bar says what it needs. An episode whose feed date AgentP cannot read shows no date in the list; its words still match the date as the feed wrote it, but it never falls inside a range.

Enter keeps the filter and returns to the list; Esc puts back the filter the search opened with. On the list, Esc clears an active filter. Pasting into the search turns line breaks into spaces. Selections are kept while the list is narrowed, and Download fetches every selected episode, including any the search is currently hiding.

Go to a number. Type a number such as 128 (or press # first) and press Enter. If a search is hiding that episode, the search is cleared. A number the feed does not have leaves the prompt open with a message. A pasted number may start with #; anything else that is not a plain number, such as -2, is reported rather than changed.

Command palette

Key Action
Type Filter actions
↑ / ↓, Shift+Tab / Tab Move selection
Enter Run the selected action
Esc Close

Downloading

Key Action
Esc Back to the podcast list once the run has finished
q Ignored while the transfer runs, so a stray keypress cannot leave a partial file

Config menu

The menu has eleven rows: Add New Podcast, Download Folder, Default Mode, Banner Style, New Podcast Defaults, Edit Existing Podcasts, Open Config File, Open Podcasts Config File, Open Download Folder, About AgentP, and Report Issue.

Key Action
j / ↓, k / ↑ Move selection
Enter Activate the row (edit the folder path, toggle tui/cli, open an editor, or open a file or folder)
r on Download Folder Reset the download folder to the built-in default
Enter / Esc while editing the path Confirm or cancel the new folder path
y / Enter, Esc Answer a confirmation dialog
Esc Back to the podcast list

Add podcast wizard

Key Action
Enter on Feed URL Open the mode selector
1 / m Manual — fill every field yourself
2 / p / Enter Prepopulate from feed — fetches the channel title, album, and artist
Enter Next step (the last step saves the podcast)
Tab / ↓ / j on a yes/no or numeric step Next step
↑ / k / Backspace on a yes/no or numeric step Previous step
Backspace on an empty text step Previous step
Space Toggle a yes/no step
← / -, → / + Adjust the leading-zeros count
Ctrl+V Paste into a text step
Esc Cancel the wizard, or cancel an in-flight feed fetch

Edit podcast

Key Action
j / ↓, k / ↑ Move between fields
Enter Edit a text field or toggle a yes/no field
Enter / Esc while editing text Commit or cancel the edit
Space Toggle a yes/no field
← / -, → / + Adjust the leading-zeros count
r Restore the current field to its built-in default
R Reset every yes/no and numeric field to built-in defaults (template editor only)
s Save
Esc Back; asks s save / d discard / Esc cancel if there are unsaved changes
Ctrl+V Paste into the text field being edited

CLI reference

Commands that select a podcast accept either its 1-based index or a case-insensitive name. Run agentp <command> --help for the full flag list.

Podcasts

agentp podcast list
agentp podcast list --no-fetch
agentp podcast show --podcast "Darknet Diaries"
agentp podcast episodes --podcast 1 --limit 20
agentp podcast episodes --podcast 1 --search 'copilot since:30d -intro'
agentp podcast add --name "My Show" --feed-url https://example.com/feed.xml
agentp podcast add --feed-url https://example.com/feed.xml --from-feed
agentp podcast add --feed-url https://example.com/feed.xml --from-feed --artist "Jane Doe" --overwrite-file-name true
agentp podcast edit --podcast 2 --name "New Name" --feed-url https://example.com/new.xml
agentp podcast edit --podcast 2 --remove-images true
agentp podcast delete --podcast "Old Show"

podcast episodes --search takes the same terms as the episode-list search described above, and --limit then caps the matches. A range term that is not a date, such as since:soon, is an error.

New podcasts start from the default_podcast template in config.json. --from-feed fills in the name, album, and artist from the channel metadata; --name still wins if you pass both. Every per-podcast option in the table below is also a flag on podcast add and podcast edit, and yes/no flags take an explicit true or false.

Downloads

agentp download --podcast 1 --latest 5
agentp download --podcast 1 --oldest 5
agentp download --podcast 1 --episodes 3,7,12
agentp download --podcast 1 --all

--episodes, --latest, --oldest, and --all are mutually exclusive and exactly one is required. Indices follow the feed's item order: index 1 is the feed's last item and the highest index is its first. Most feeds list newest first, which makes 1 the oldest episode. --latest and --oldest take items from the top and bottom of the feed, and agentp podcast episodes prints the indices.

Configuration and folders

agentp config show
agentp config set --download-dir ~/Podcasts
agentp config set --default-mode cli
agentp config set --banner-style ascii
agentp config path

agentp open downloads
agentp open downloads --create
agentp open podcast --podcast 1 --create

agentp skill

open refuses to open a folder that does not exist yet; --create makes it first. agentp skill prints the agent skill for this version (see From a coding agent).

Configuration

Config lives under $HOME/.config/AgentP/ (or %USERPROFILE%\.config\AgentP\ on Windows). agentp config path prints the directory.

  • config.json — download folder, the default_podcast template, default_mode (tui or cli), and banner_style (joined or ascii)
  • podcasts.json — your podcast list, as { "podcasts": [ ... ] }

banner_style picks the TUI banner wordmark: joined (the default) draws the letters with box-drawing characters and needs a terminal that draws those itself, such as Windows Terminal or kitty; ascii is the plain fallback for terminals that leave gaps between the strokes, Warp among them.

Per-podcast options (podcasts.json)

Each entry in podcasts.json has the same shape as default_podcast plus its identity fields. The CLI flag column applies to podcast add and podcast edit.

Field CLI flag Purpose
name --name Display name in lists
feed_url --feed-url RSS feed URL
album_name --album-name Subfolder name and album tag; falls back to name when empty
artist --artist Artist and album-artist tag
user_agent --user-agent Custom User-Agent for this podcast's audio downloads; empty uses AgentP's default
overwrite_tags --overwrite-tags Master switch for writing text tags: ID3 on MP3, MP4 metadata on M4A
remove_existing_tags --remove-existing-tags Drop the source file's existing tag (ID3 or MP4 metadata) and write a fresh minimal one (only when overwrite_tags is on)
remove_images --remove-images Strip embedded cover art; works even when overwrite_tags is off
overwrite_album_name --overwrite-album-name Write the album tag
overwrite_artist --overwrite-artist Write the artist and album-artist tags
overwrite_title --overwrite-title Write the title tag
overwrite_file_name --overwrite-file-name Rename the file to the episode title
convert_to_mp3 --convert-to-mp3 Re-encode episodes served as M4A, AAC, Ogg, FLAC, or WAV to MP3 with ffmpeg; off by default, which keeps the feed's original format. Needs ffmpeg on PATH, and the re-encode is lossy
append_number_to_title --append-number-to-title Prefix the title tag with the episode number
leading_zeros_amount --leading-zeros-amount Zero-padding for that number; 3 pads to four digits (0001), 0 disables

[!NOTE] Older configs that use override_*, leading_zeros_to_title, remove_image, or create_new_tags still load. AgentP rewrites them with the current key names the next time it saves.

New-podcast defaults

The default_podcast block in config.json is copied into every podcast you create, whether through the TUI wizard or agentp podcast add. Edit it from New Podcast Defaults in the TUI config menu or directly in the file. Changing the template does not touch podcasts that already exist.

Download layout

Downloads are organized by album name, which falls back to the podcast name when empty:

<download_dir>/
└── <album_name>/
    ├── <first episode>.mp3
    └── <second episode>.mp3

By default an episode keeps the format the feed serves, so a feed that mixes formats, as Latent Space does, gives you some <episode>.m4a files. Turn on convert_to_mp3 and every episode lands as .mp3 once its conversion succeeds. It still keeps its original format when ffmpeg is missing, when the conversion fails, or when AgentP cannot identify the file's format.

Without overwrite_file_name, the filename is the last path segment of the episode's enclosure URL (hosts that wrap the real file URL inside a redirect link, such as Anchor, are unwrapped), falling back to the final response URL and then default.<ext>. With it, the file is renamed to <sanitized episode title>.<ext>. Downloading an episode again overwrites the existing file.

AgentP checks the first bytes of each download to find its real format rather than trusting the URL or the server's Content-Type. A URL's extension is kept only when it is an audio extension matching that format (mp3 for MP3; m4a, m4b, or mp4 for MP4 audio; aac for raw AAC; ogg, oga, or opus for Ogg; flac; wav). Otherwise <ext> is the format's default (mp3, m4a, aac, ogg, flac, or wav; mp3 for an unrecognised format), and a kept URL file name with any other extension gets that default appended, so a feed cannot make AgentP save an episode as, say, an .exe.

With convert_to_mp3 on, AgentP saves an M4A, AAC, Ogg, FLAC, or WAV download under that name, re-encodes it with ffmpeg to the same name ending in .mp3 (LAME VBR quality 4, about 130–165 kbps, keeping cover art and the feed's metadata; a video episode keeps only its audio), and then deletes the original. A download AgentP cannot identify as one of those formats is never handed to ffmpeg; it is saved as downloaded, with a notice. AgentP also tells ffmpeg the input format rather than letting it guess, and limits it to local files, so a feed cannot pass off a playlist as an episode to make ffmpeg fetch URLs or read other files. That narrows what an untrusted download can do but does not sandbox ffmpeg. A missing ffmpeg is reported once per download run and a failed conversion once per episode, both as warnings: the original file is kept and tagged as below, and agentp download still exits successfully. Turning the setting on in the TUI shows a warning beside it while ffmpeg is missing, and podcast add and podcast edit print one; neither stops you saving. agentp config show reports whether ffmpeg was found. Raw AAC or FLAC that starts with an ID3 tag is recognised as AAC or FLAC, not MP3, so it is converted too. A conversion still running after 20 minutes is stopped and the original kept. Conversion takes several seconds per episode; stopping AgentP mid-conversion can leave a <episode>.mp3.part file beside the original, which the next download of that episode replaces.

When overwrite_tags is on, AgentP writes the title, album, and artist plus album-artist tags that you have enabled and leaves every other tag alone. MP3 files, converted ones included, get ID3 frames. With convert_to_mp3 off, or when conversion could not run, M4A files get MP4 metadata atoms, and raw AAC and other formats are saved untagged, with a notice in the download log when tagging or image removal is on. Turn on remove_existing_tags to discard the source file's tag first, which helps when a host embeds large metadata blocks that confuse players.

[!NOTE] Episode indices are 1-based, counted up from the feed's last item, so in a feed that lists newest first, 1 is the oldest episode and the newest has the highest index. AgentP does not re-sort by publication date. This applies to --episodes and numeric title prefixes. With leading_zeros_amount: 3, episode 1 becomes 0001, episode 10 becomes 0010, and episode 100 becomes 0100.

Troubleshooting

  • ... (internet-filter/proxy) blocked URL ... while downloading — the host answered with an HTML or text page instead of audio. This usually means a proxy or content filter intercepted the request, or the host rejected AgentP's User-Agent. Check the filter, or set user_agent on that podcast (agentp podcast edit --podcast N --user-agent "...").
  • ... (internet-filter/proxy) blocked URL ... when adding a podcast or loading its episodes — the feed request was redirected to another host that answered with a web page instead of RSS, the signature of a content filter. Ask for the feed's host to be allowed; user_agent applies only to audio downloads and does not change feed requests.
  • A feed adds fine but every download fails — some hosts (Buzzsprout's CDN, for example) refuse anonymous clients. AgentP sends AgentP/<version> by default; a browser-style user_agent on the podcast usually resolves it.
  • ffmpeg was not found on PATH — Convert To MP3 is on for that podcast but ffmpeg is not installed, so episodes keep their original format. Install ffmpeg, or turn the setting off (agentp podcast edit --podcast N --convert-to-mp3 false) to stop the warning.
  • Episode index N is out of range — indices follow the feed's item order, counting up from its last item, so 1 is the oldest episode only when the feed lists newest first, as most do. Run agentp podcast episodes --podcast N to see the current numbering.
  • Start over — delete config.json and podcasts.json from the directory printed by agentp config path. The next launch recreates both, including the demo podcasts.

Building from source

Requires Rust 1.88+ (edition 2024). The binary lands at target/release/agentp.

git clone https://github.com/ByteMagicInc/AgentP.git
cd AgentP
cargo build --release --locked

CI runs automatically only when a release is tagged, so before opening a pull request run the same checks locally:

cargo fmt --all -- --check && cargo clippy --all-targets --locked -- -D warnings && cargo test --all --locked

Contributing

Issues and PRs welcome at https://github.com/ByteMagicInc/AgentP. Read CONTRIBUTING.md before you contribute and AGENTS.md for architecture details. Start commit messages with Fix, Add, Refactor, or Update so the changelog groups them correctly.

Maintainers: see the short release guide.

License

GPL-3.0-only © 2026 ByteMagic Inc.