Contributing to fetchit
Thank you for considering contributing to fetchit. This guide covers everything you need to know — from setting up your development environment to shipping a release. Whether you are fixing a bug, adding a feature, improving documentation, or packaging for a new platform, you will find the relevant information here.
fetchit is MIT-licensed and maintained by Vedant Gupta. The source code is available at github.com/Vedant1521/fetchit. All contributions — code, docs, tests, bug reports — are welcome.
Prerequisites
Before you start contributing, make sure your environment meets these requirements:
- Node.js 18 or later — fetchit targets Node.js 18+. Verify with
node --version. We recommend the latest LTS release. - npm — ships with Node.js. Used for dependency management and running scripts.
- Git — for version control, forking, and branching.
- Bun (optional) — required only if you need to build standalone binaries via
scripts/build-binary.js. Install withcurl -fsSL https://bun.sh/install | bashorpowershell -c "irm bun.sh/install.ps1 | iex". - yt-dlp (optional) — fetchit bundles its own yt-dlp binary on first run. If you already have yt-dlp installed system-wide, it will use yours.
windows-build-tools for native module compilation. Install it with npm install --global windows-build-tools. On macOS, install Xcode Command Line Tools via xcode-select --install.Setup
Follow these steps to get the project running on your local machine.
1. Fork and clone the repository
Fork the repository on GitHub, then clone your fork:
git clone https://github.com/YOUR_USERNAME/fetchit.git cd fetchit
Add the original repository as an upstream remote to sync changes later:
git remote add upstream https://github.com/Vedant1521/fetchit.git
2. Install dependencies
npm install
This installs all Node.js dependencies — Ink, React, tsup, TypeScript, and development tools. Native addons (if any) are compiled during this step. If you encounter build errors, ensure your C/C++ build toolchain is installed (see prerequisites above).
3. Verify the setup
Run the following commands to confirm everything works:
npm run build npm test npm run typecheck
All three should pass without errors. If npm test fails, make sure you are using Node.js 18+ and have all dependencies installed.
Once the build succeeds, you can run fetchit locally:
node dist/cli.js https://youtu.be/dQw4w9WgXcQ
Or, if you want the fetchit command available globally during development:
npm link fetchit https://youtu.be/dQw4w9WgXcQ
Key technologies
fetchit is built on a modern, carefully chosen tech stack. Understanding these tools will help you navigate the codebase.
| Technology | Purpose | Learn more |
|---|---|---|
| TypeScript (strict) | Application language — all source code is typed | typescriptlang.org |
| React 19 | UI runtime used by the terminal interface via Ink | react.dev |
| Ink 7 | React renderer for the terminal — lets you build TUI components with JSX | github.com/vadimdemedes/ink |
| tsup | Bundler — compiles TypeScript to a single ESM bundle at dist/cli.js (~93 KB) | tsup.etsy.com |
| yt-dlp | Download engine — probes URLs, extracts formats, performs downloads across 2,000+ sites | github.com/yt-dlp/yt-dlp |
| ffmpeg-static | Bundled FFmpeg binary for merging streams and MP3 transcoding | npmjs.com/package/ffmpeg-static |
| Bun | JavaScript runtime used to compile standalone binaries via build-binary.js | bun.sh |
| Node built-in test runner | Testing via node:test and node:assert — no external test framework | nodejs.org/api/test.html |
Development workflow
The development cycle is straightforward: make changes, let tsup rebuild, and test. Here are the commands you will use most often:
| Command | What it does |
|---|---|
| npm run dev | Watch mode — rebuilds dist/cli.js on every file change via tsup --watch |
| npm run build | Production build — bundles TypeScript into dist/cli.js with tsup |
| npm test | Runs all tests via tsx --test src/**/*.test.ts |
| npm run typecheck | TypeScript strict type-checking via tsc --noEmit |
| npm run build:binary | Builds a standalone binary via bun scripts/build-binary.js (requires Bun) |
| node dist/cli.js <url> | Runs fetchit locally after a build |
Watch mode
The most productive way to develop is to keep npm run dev running in one terminal window. Every time you save a file, tsup re-bundles the project in under 200 ms. You can then run node dist/cli.js <url> in another terminal to test your changes instantly.
npm run typecheck and npm test separately before committing.Build output
The build produces a single ESM bundle at dist/cli.js with a Node.js shebang (#!/usr/bin/env node). This file is the entry point for both local development and the npm package. Despite the .js extension, the file is JavaScript output compiled from TypeScript. The bundle is intentionally small (~93 KB) — dependencies are externalized in the tsup configuration.
Project structure
The repository is organized as a monorepo with two main areas: the CLI application in the root and the documentation site in site/. Here is a breakdown of every directory and its purpose:
Root directory
| Path | Purpose |
|---|---|
| src/cli.tsx | Entry point — argument parsing, Ink bootstrap, and TUI startup |
| src/app.tsx | Main TUI component — manages all application phases, keyboard input, state, and rendering |
| src/components/ | Ink/React TUI components — text input, multi-select, logo, fullscreen wrapper, progress bar, keyboard shortcuts display, panel |
| src/lib/ | Core utilities — yt-dlp integration (probe, download, chapters), argument parser, clipboard, platform detection, URL history, mouse event handling |
| src/theme.ts | Theme system — auto (follows terminal), light, and dark palettes, hot-swappable via Ctrl+T |
| scripts/build-binary.js | Bun SEA builder — compiles the CLI into a standalone binary for distribution |
| scripts/build-binary.ts | TypeScript source for the binary builder script |
| dist/ | Build output — contains cli.js after tsup build, and platform binaries after build-binary (gitignored) |
| docs/ | Legacy user-facing documentation in Markdown (being migrated to the site/ directory) |
| assets/ | Images, demo GIFs, and logo SVGs used in README and documentation |
| site/ | Next.js documentation website — separate package with its own dependencies |
| .github/ | GitHub Actions workflows (ci.yml, release.yml), issue templates, and community files |
Source code details
src/cli.tsx — Entry point
This is where fetchit starts. It parses command-line arguments using the custom argument parser in src/lib/args.ts, determines whether to run in interactive (TUI) or scriptable (non-interactive) mode, and boots the Ink application. If scriptable mode is detected (flags like --best, --mp3, or a direct quality argument), it skips the TUI entirely and downloads directly.
src/app.tsx — Main TUI component
The heart of the interactive experience. This component manages all application phases: URL input, probing, format selection, downloading, and error handling. It wires up keyboard and mouse event handlers, manages the URL history, handles clipboard detection, and controls the theme system. The component is structured as a state machine where each phase renders a different set of child components.
src/components/ — TUI components
Individual Ink components that compose the user interface. Each file exports one or more React components that render directly to the terminal via Ink:
text-input.tsx— URL input field with readline-style editing, history recall via arrow keys, and clipboard paste detectionmulti-select.tsx— Checkbox list component used for playlist item selectionlogo.tsx— Animated ASCII fetchit logo rendered at startupfullscreen.tsx— Full-screen terminal wrapper with centered layout and scrollback restoration on exitprogress-bar.tsx— Real-time download progress bar showing speed, ETA, and percentagepanel.tsx— Format panel displaying resolution, file size, and format metadata for user selectionshortcuts.tsx— Keyboard shortcut hints displayed in the footerframed-input.tsx— Styled input wrapper used within the TUI
src/lib/ — Utilities and integrations
The library directory contains all non-component logic. This is where the real work happens:
ytdlp.ts— The download engine. Probes URLs, extracts format metadata, initiates downloads, handles playlists, and manages chapter embedding. All communication with yt-dlp happens here via child process spawning.args.ts— Command-line argument parser. Handles flags (--best,--mp3,-o, etc.), positional arguments (URL, quality), and theme options.clipboard.ts— Cross-platform clipboard access (sync and async) for detecting copied URLs on launch.platforms.ts— Site detection and URL validation. Maps hostnames to display labels (YouTube, X/Twitter, Instagram, etc.).history.ts— URL history persistence. Stores the last 50 URLs in~/.config/fetchit/history.json.format.ts— Formatting utilities for file sizes, durations, and human-readable output.click-map.ts— Frame capture system for mouse hit-testing. Maps terminal coordinates to clickable regions.use-mouse-click.ts— React hook for SGR mouse event handling. Listens forSGR 1006mouse sequences and dispatches click events.
src/theme.ts — Theme system
Defines three color palettes: auto (inherits terminal foreground/background),light, and dark. The theme can be toggled at runtime viaCtrl+T or set on startup with --theme. Colors are defined as CSS-like variables and consumed by Ink components through React context.
scripts/ directory
Contains build tooling for producing standalone binaries. build-binary.js uses Bun's --compile flag to bundle the entire CLI, the Node.js runtime, and all dependencies into a single executable (~100 MB). The TypeScript source atbuild-binary.ts is compiled to build-binary.js before execution.
site/ directory
The documentation website is a standalone Next.js project (version 16) located in thesite/ subdirectory. It has its own package.json, dependencies, and build pipeline. The site is deployed to Vercel automatically when changes are pushed tomain.
Making changes
Branch naming convention
Create a new branch for each change. Use a descriptive name that reflects the work:
fix/description— Bug fixes (e.g.,fix/playlist-concurrency)feat/description— New features (e.g.,feat/aria2-support)docs/description— Documentation updates (e.g.,docs/install-guide)refactor/description— Code refactoring without behavior changestest/description— Test additions or improvements
Always branch from main:
git checkout main git pull upstream main git checkout -b fix/your-bug-fix
Code style
fetchit follows a consistent code style enforced through conventions (not a formatter). Read through existing code before writing to match the style.
- No semicolons. The codebase does not use semicolons. Keep it consistent.
- TypeScript strict mode. The
tsconfig.jsonenablesstrict: true. Noanytypes, no@ts-ignorecomments, and noastype casts unless absolutely necessary (with a comment explaining why). - No commented-out code. Delete it. Git history preserves the original.
- No AI-generated contributions. Pull requests that appear to be written by an AI code assistant will be closed without review. Write your own code.
- No em dashes (—) in user-facing text. Use commas, colons, or new sentences instead.
- Keep the bundle small. The production bundle is ~93 KB. Think carefully before adding new dependencies.
- Import style. Use ESM imports (
import ... from "..."). Norequire()calls. - File extensions. Source files use
.tsxfor files containing JSX and.tsfor pure TypeScript. Test files use.test.ts. - Naming conventions. Files use kebab-case (e.g.,
text-input.tsx). Functions and variables use camelCase. Components use PascalCase.
Commit guidelines
- Write clear, descriptive commit messages in the imperative mood:
fix: handle empty playlist gracefullyrather thanfixed bug. - Keep commits focused. A commit should do one thing and include only related changes.
- Reference issues and pull requests in commit messages where applicable:
feat: add --concurrency flag (#42). - Run
npm run typecheck && npm test && npm run buildbefore every commit to make sure your changes do not break anything.
Testing
Running tests
fetchit uses Node.js built-in test runner (node:test and node:assert). There is no external test framework — just standard library APIs.
npm test
This runs tsx --test src/**/*.test.ts, which executes all test files matching the glob pattern. tsx compiles TypeScript on the fly, so tests run directly from source without a prior build step. Test files are colocated with their implementation files insrc/:
src/lib/args.test.ts— Tests for the argument parsersrc/lib/ytdlp.test.ts— Tests for the yt-dlp integrationsrc/components/panel.test.ts— Tests for the format panel component
Run a single test file to iterate faster during development:
npx tsx --test src/lib/args.test.ts
Writing tests
When adding new functionality, include tests that cover:
- Happy path — the feature works as expected with valid input
- Error handling — what happens with invalid input, missing data, or network failures
- Edge cases — empty inputs, boundary values, unexpected formats
Here is the pattern used across the test suite:
import { describe, it } from "node:test"
import assert from "node:assert/strict"
import { yourFunction } from "./your-module"
describe("yourFunction", () => {
it("handles the happy path", () => {
const result = yourFunction("valid input")
assert.equal(result, "expected output")
})
it("throws on invalid input", () => {
assert.throws(() => yourFunction("bad input"), /expected error/)
})
})Use node:assert/strict for strict equality checks. Prefer assert.equal,assert.deepEqual, and assert.throws over loose comparisons. Use assert.ok for boolean assertions.
render function that works in test environments without a real terminal.Building a binary (for testing)
If you need to test fetchit as a standalone binary — for example, to verify platform-specific behavior or to test the installer script — you can build a binary locally using Bun:
bun scripts/build-binary.js
This compiles the CLI into a standalone executable using Bun's --compile flag. The output is placed in dist/:
| Platform | Output file |
|---|---|
| Windows | dist/fetchit.exe |
| macOS (Apple Silicon) | dist/fetchit-darwin-arm64 |
| macOS (Intel) | dist/fetchit-darwin-x64 |
| Linux (x86-64) | dist/fetchit-linux-x64 |
| Linux (ARM64) | dist/fetchit-linux-arm64 |
The binary embeds the Node.js runtime and all dependencies into a single file (~100 MB). It requires no external runtimes — you can copy it to any machine and run it directly:
dist/fetchit.exe https://youtu.be/dQw4w9WgXcQ
Note that the binary will download its own yt-dlp engine on first run, separate from any yt-dlp you may have installed system-wide.
node dist/cli.js — the behavior is identical, just without the standalone packaging.Documentation
The official fetchit documentation lives in the site/ directory, which is a Next.js 16 project. All documentation pages are written as React components insite/src/app/docs/ using the prose components from@/components/prose.
How docs are organized
Each documentation topic is a subdirectory under site/src/app/docs/ with apage.tsx file. The routing is automatic — site/src/app/docs/install/page.tsxbecomes /docs/install. The current documentation pages are:
getting-started/— First run and basic usageinstall/— All installation methodsinteractive-mode/— Full-screen TUI referencescriptable-mode/— Non-interactive usage and configurationplaylists/— Playlist downloadingtroubleshooting/— Common issues and fixescontributing/— This guide
Adding a new page
To add a new documentation page:
- Create a new directory under
site/src/app/docs/your-topic/. - Create a
page.tsxfile in that directory. Use the prose components to build the page content. - Add a sidebar entry in
site/src/components/sidebar.tsx. The sidebar defines the navigation sections and items. Add your page to the appropriate array (sidebarItems,guideItems, orsupportItems).
Running the docs site locally
To preview documentation changes, start the Next.js development server from thesite/ directory:
cd site npm install npm run dev
This starts the dev server on http://localhost:3000. The site auto-reloads when you edit page files. The site/ directory has its ownpackage.json and dependencies — make sure to run npm install there before starting the server.
Documentation conventions
- Use the prose components (
H1,H2,P,Code,Pre,Ul,Ol,Li,Table,Note,Link,Blockquote) for consistent styling. - Use
Prefor code blocks. Pass afilenameprop to show a filename header. - Use
Codefor inline code, flag names, and file paths. - Use
Notefor tips, warnings, and supplementary information. - Use
Tablefor structured data — command references, configuration options, supported platforms. - Escape apostrophes with
'in JSX text content. - Link to other docs pages with
Link href="/docs/topic". - Link to external resources (GitHub, npm, etc.) with standard
atags orLinkwith full URLs.
Pull request process
Follow these steps to submit a pull request that is easy to review and quick to merge.
Before opening a pull request
- Open an issue first for new features or non-trivial changes. This lets the maintainers and community discuss the approach before you invest time writing code. Bug fixes and documentation improvements can go straight to a PR.
- Search existing issues and PRs to make sure no one else is already working on the same thing.
- Keep it focused. One PR should do one thing. If you find yourself fixing multiple bugs or adding multiple features, split them into separate PRs.
Opening a pull request
- Fork the repository and create a branch from
mainfollowing the naming convention above. - Make your changes. Write or update tests as needed.
- Run the full verification suite:All three must pass. If any fail, fix the issues before pushing.NPM
npm run typecheck npm test npm run build
- Push your branch to your fork and open a pull request targeting
main. - Write a clear PR description that explains:
- What changed — a summary of the code changes
- Why it changed — the motivation, bug reference, or feature request link
- How you tested it — what test cases you added or ran manually
- Screenshots — for UI changes, include before/after terminal captures
What happens after you open a PR
- CI checks run automatically. GitHub Actions executes
npm run typecheck,npm test, andnpm run build. A green checkmark means your changes are safe. A red cross means something failed — click "Details" to see the logs and fix the issue. - Code review. A maintainer will review your changes. They may request changes, ask questions, or approve. Respond to feedback promptly — address each comment or explain your reasoning.
- Merge. Once approved, a maintainer will merge your PR. The squash-merge strategy is used to keep the commit history clean.
First-time contributor? Welcome. Do not worry about getting everything perfect on the first try. Maintainers are happy to help you navigate the process. Open a draft pull request early if you want feedback on your approach.
CI/CD pipeline
fetchit uses GitHub Actions for continuous integration and delivery. All workflow definitions are in .github/workflows/.
Continuous Integration — ci.yml
Trigger: Every push to main and every pull request targeting main.
The CI workflow runs a single check job on an Ubuntu runner:
npm ci— Installs dependencies frompackage-lock.json(exact versions, faster thannpm install)npm run typecheck— TypeScript strict mode check viatsc --noEmitnpm test— Runs all tests using Node's built-in test runnernpm run build— Bundles the project with tsup
Continuous Delivery — release.yml
Trigger: Pushing a tag matching v* (e.g., v0.5.0).
The CD workflow has three sequential jobs:
| Job | Runner | What it does |
|---|---|---|
| check | Ubuntu | Same as CI — typecheck, test, build. Cancels the release if this fails. |
| build-binary | Windows, macOS, Linux (parallel) | Builds a standalone binary on each OS using bun build --compile. Each binary is uploaded as a temporary artifact. |
| create-release | Ubuntu | Downloads all binaries, creates a GitHub Release, attaches binaries, and generates release notes automatically. |
The matrix strategy runs the build step on three operating systems simultaneously, producing one binary per platform:
| OS | Runner | Output file |
|---|---|---|
| Windows | windows-latest | fetchit-win-x64.exe |
| macOS | macos-latest | fetchit-darwin-arm64 |
| Linux | ubuntu-latest | fetchit-linux-x64 |
The entire workflow typically completes in approximately 2 minutes. Release binaries are published at https://github.com/Vedant1521/fetchit/releases/latest/download/fetchit-<os>-<arch>.
Release process
This section is for maintainers who have push access to the repository. A release is triggered by pushing a version tag.
Creating a release
Follow these steps to publish a new version:
- Ensure main is up to date and CI passes.BASH
git checkout main git pull
- Bump the version. Edit
package.jsonto increment the version number following semantic versioning:- Patch (0.5.0 → 0.5.1) — Bug fixes and minor changes
- Minor (0.5.0 → 0.6.0) — New features, backward compatible
- Major (0.5.0 → 1.0.0) — Breaking changes
npm installto syncpackage-lock.json. - Commit and tag.BASH
git commit -am "v0.x.x" git tag v0.x.x
- Push the tag. This triggers the release workflow in GitHub Actions.BASH
git push && git push origin v0.x.x
What happens during a release
The release workflow automates everything after the tag push:
- Check job — Runs typecheck, tests, and tsup build to verify the code is production-ready.
- Build-binary job — Spawns three parallel runners (Windows, macOS, Linux) that each execute
scripts/build-binary.jsto produce platform-specific standalone binaries. - Create-release job — Downloads all three binaries, creates a GitHub Release page with auto-generated release notes, attaches the binaries as download artifacts, and publishes the release.
npm publish
Publishing to npm is a manual step (not automated in CI). After the release workflow completes successfully:
npm run build npm publish
This publishes the @vedant1521/fetchit package to the npm registry. Thefiles field in package.json ensures only the dist/directory is included. The prepublishOnly script runs tests, typecheck, and build before publishing as a safety net.
Post-release
- Update the install scripts (
install.shandinstall.ps1) if the release changes the download URL format or adds new platforms. - Update the documentation site if the release introduces new features or changes existing behavior.
- Post a summary in the GitHub Discussion or on social media if the release includes significant changes.
What needs help
Not sure where to start? Here are the areas where contributions are most valuable:
- Bug reports with clear reproduction steps — open an issue with the
buglabel - Test coverage — the test suite is growing but could use more edge case and error path coverage
- Documentation — improvements to existing guides, new guides, troubleshooting entries, and code comments
- Platform packages — package fetchit for system package managers: Homebrew (macOS), Scoop/Winget (Windows), APT/RPM (Linux)
- Accessibility — improve keyboard navigation, screen reader support in the terminal, and color-blind friendly themes
- Performance — optimize probe caching, reduce binary size, improve download concurrency
Check the issues page for open bugs and feature requests. Issues labeled good first issue are especially suitable for new contributors.
Code of conduct
Be decent. Disagreement is fine — personal attacks are not. fetchit is a small open-source project run by one person. Treat it, its maintainers, and its users the way you would want someone to treat your own project. Harassment, trolling, and disrespectful behavior will not be tolerated.
Getting help
If you have questions about contributing, need guidance on your first PR, or want to discuss an idea before writing code:
- Open a GitHub Discussion
- Open an issue (search first to see if someone already asked)
- Read the documentation for detailed usage guides
We are glad you are here. Happy contributing.