Command Palette

Search for a command to run...

Contributing

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 with curl -fsSL https://bun.sh/install | bash or powershell -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.
On Windows, you may need 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:

BASH
git clone https://github.com/YOUR_USERNAME/fetchit.git
cd fetchit

Add the original repository as an upstream remote to sync changes later:

BASH
git remote add upstream https://github.com/Vedant1521/fetchit.git

2. Install dependencies

NPM
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
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:

BASH
node dist/cli.js https://youtu.be/dQw4w9WgXcQ

Or, if you want the fetchit command available globally during development:

NPM
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.

TechnologyPurposeLearn more
TypeScript (strict)Application language — all source code is typedtypescriptlang.org
React 19UI runtime used by the terminal interface via Inkreact.dev
Ink 7React renderer for the terminal — lets you build TUI components with JSXgithub.com/vadimdemedes/ink
tsupBundler — compiles TypeScript to a single ESM bundle at dist/cli.js (~93 KB)tsup.etsy.com
yt-dlpDownload engine — probes URLs, extracts formats, performs downloads across 2,000+ sitesgithub.com/yt-dlp/yt-dlp
ffmpeg-staticBundled FFmpeg binary for merging streams and MP3 transcodingnpmjs.com/package/ffmpeg-static
BunJavaScript runtime used to compile standalone binaries via build-binary.jsbun.sh
Node built-in test runnerTesting via node:test and node:assert — no external test frameworknodejs.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:

CommandWhat it does
npm run devWatch mode — rebuilds dist/cli.js on every file change via tsup --watch
npm run buildProduction build — bundles TypeScript into dist/cli.js with tsup
npm testRuns all tests via tsx --test src/**/*.test.ts
npm run typecheckTypeScript strict type-checking via tsc --noEmit
npm run build:binaryBuilds 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.

The watch mode only rebuilds the bundle. It does not re-run type checking or tests. Run 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

PathPurpose
src/cli.tsxEntry point — argument parsing, Ink bootstrap, and TUI startup
src/app.tsxMain 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.tsTheme system — auto (follows terminal), light, and dark palettes, hot-swappable via Ctrl+T
scripts/build-binary.jsBun SEA builder — compiles the CLI into a standalone binary for distribution
scripts/build-binary.tsTypeScript 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 detection
  • multi-select.tsx — Checkbox list component used for playlist item selection
  • logo.tsx — Animated ASCII fetchit logo rendered at startup
  • fullscreen.tsx — Full-screen terminal wrapper with centered layout and scrollback restoration on exit
  • progress-bar.tsx — Real-time download progress bar showing speed, ETA, and percentage
  • panel.tsx — Format panel displaying resolution, file size, and format metadata for user selection
  • shortcuts.tsx — Keyboard shortcut hints displayed in the footer
  • framed-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 for SGR 1006 mouse 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 changes
  • test/description — Test additions or improvements

Always branch from main:

BASH
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.json enables strict: true. No any types, no @ts-ignore comments, and no as type 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 "..."). No require() calls.
  • File extensions. Source files use .tsx for files containing JSX and .ts for 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 gracefully rather than fixed 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 build before 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
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 parser
  • src/lib/ytdlp.test.ts — Tests for the yt-dlp integration
  • src/components/panel.test.ts — Tests for the format panel component

Run a single test file to iterate faster during development:

NPM
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:

JAVASCRIPTsrc/lib/example.test.ts
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.

Ink components render to a virtual terminal during tests. For component tests, you can import and render components directly — Ink provides a 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:

BASH
bun scripts/build-binary.js

This compiles the CLI into a standalone executable using Bun's --compile flag. The output is placed in dist/:

PlatformOutput file
Windowsdist/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:

BASH
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.

Building a binary requires Bun to be installed. If you do not have Bun, you can still test fetchit via 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 usage
  • install/ — All installation methods
  • interactive-mode/ — Full-screen TUI reference
  • scriptable-mode/ — Non-interactive usage and configuration
  • playlists/ — Playlist downloading
  • troubleshooting/ — Common issues and fixes
  • contributing/ — This guide

Adding a new page

To add a new documentation page:

  1. Create a new directory under site/src/app/docs/your-topic/.
  2. Create a page.tsx file in that directory. Use the prose components to build the page content.
  3. 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, or supportItems).

Running the docs site locally

To preview documentation changes, start the Next.js development server from thesite/ directory:

BASH
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 Pre for code blocks. Pass a filename prop to show a filename header.
  • Use Code for inline code, flag names, and file paths.
  • Use Note for tips, warnings, and supplementary information.
  • Use Table for structured data — command references, configuration options, supported platforms.
  • Escape apostrophes with &apos; in JSX text content.
  • Link to other docs pages with Link href="/docs/topic".
  • Link to external resources (GitHub, npm, etc.) with standard a tags or Link with 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

  1. 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.
  2. Search existing issues and PRs to make sure no one else is already working on the same thing.
  3. 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

  1. Fork the repository and create a branch from main following the naming convention above.
  2. Make your changes. Write or update tests as needed.
  3. Run the full verification suite:
    NPM
    npm run typecheck
    npm test
    npm run build
    All three must pass. If any fail, fix the issues before pushing.
  4. Push your branch to your fork and open a pull request targeting main.
  5. 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

  1. CI checks run automatically. GitHub Actions executes npm run typecheck, npm test, and npm 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.
  2. 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.
  3. 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:

  1. npm ci — Installs dependencies from package-lock.json (exact versions, faster than npm install)
  2. npm run typecheck — TypeScript strict mode check via tsc --noEmit
  3. npm test — Runs all tests using Node's built-in test runner
  4. npm 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:

JobRunnerWhat it does
checkUbuntuSame as CI — typecheck, test, build. Cancels the release if this fails.
build-binaryWindows, macOS, Linux (parallel)Builds a standalone binary on each OS using bun build --compile. Each binary is uploaded as a temporary artifact.
create-releaseUbuntuDownloads 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:

OSRunnerOutput file
Windowswindows-latestfetchit-win-x64.exe
macOSmacos-latestfetchit-darwin-arm64
Linuxubuntu-latestfetchit-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:

  1. Ensure main is up to date and CI passes.
    BASH
    git checkout main
    git pull
  2. Bump the version. Edit package.json to 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
    After editing, run npm install to sync package-lock.json.
  3. Commit and tag.
    BASH
    git commit -am "v0.x.x"
    git tag v0.x.x
  4. 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:

  1. Check job — Runs typecheck, tests, and tsup build to verify the code is production-ready.
  2. Build-binary job — Spawns three parallel runners (Windows, macOS, Linux) that each execute scripts/build-binary.js to produce platform-specific standalone binaries.
  3. 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
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.sh and install.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 bug label
  • 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:

We are glad you are here. Happy contributing.

Was this page helpful?