Pluck git repos from any forge — auto-detect, auto-install, done!
Note: Development and CI run on GitLab; this GitHub repo is a mirror.
- Quick Start
- How It Works
- Features
- Commands
- Flags
- Installation
- Configuration
- Project Structure
- Development
🚀 Quick Start
# Install from any git forge — one command, zero fuss
pluck install https://github.com/user/repo
pluck install https://gitlab.com/user/project
pluck install https://codeberg.org/user/repo
pluck install https://bitbucket.org/owner/repo
# That's it. Pluck detects the project type and installs it.
🔄 How It Works
┌─────────────────┐ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐
│ Any git URL │────▶│ Clone to │────▶│ Detect Method │────▶│ Install │
│ (any forge) │ │ Temp Dir │ │ (Auto-detect) │ │ (8 methods) │
└─────────────────┘ └──────────────┘ └─────────────────┘ └──────────────┘
│
┌─────────────────┐ ┌──────────────┐ │
│ Post-Install │◀────│ Register │◀───────────────────────────────┘
│ Hook (opt) │ │ App │
└─────────────────┘ └──────────────┘
│
▼
┌─────────────────┐
│ Summary + │
│ Cleanup │
└─────────────────┘
Install Methods
| Method | Detection | Action |
|---|---|---|
| 🔧 Script | install.sh |
Runs bash install.sh --yes |
| 🐍 Python | pyproject.toml, setup.py |
Creates venv, pip installs |
| 🟢 Node.js | package.json |
Copies source, runs npm install |
| 🔵 Go | go.mod, *.go |
Runs go build -o to install dir |
| 🦀 Rust | Cargo.toml |
Runs cargo build --release, copies binary |
| 📋 Makefile | Makefile |
Runs make install PREFIX=... |
| 📦 Binary | release/, bin/, *.AppImage, *.deb |
Copies to install dir |
| 📥 Download | Fallback | Copies entire directory |
✨ Features
Installation
- 🔗 Any git forge — Install from GitHub, GitLab, Codeberg, Bitbucket, SourceHut, Gitea, Gogs, Pagure, Forgejo, self-hosted, or any git URL
- 🔍 Auto-detection — Automatically detects project type and install method
- 📦 Batch install — Install multiple repositories in one command
- ⚡ Shallow clone —
--shallowfor faster downloads - 🏷️ Branch/tag support —
--refto install specific versions - 🎯 Force method —
--methodto override auto-detection - ⏱️ Timeout & retry —
--timeoutand--retriesfor flaky connections
Management
- 📋 List apps — See all installed applications with disk size
- 🔄 Update apps — Re-install from original URL
- 🗑️ Uninstall — Remove apps with safety guards
- ✅ Verify — Check installed apps integrity
- 🧹 Clean — Remove orphaned registry entries
- 📊 Stats — Installation statistics and method breakdown
- 🌐 Multi-forge search — Search GitHub, GitLab, or Codeberg with
--forge - 🖱️ Browser right-click — Optional extension + protocol handler to install from any page
- 📤 Export/Import — Migrate registry between machines
Configuration
- 📁 Custom directory —
--dirto override default install location - ⚙️ User config — Persistent settings via config file
- 🎨 JSON output —
--jsonfor machine-readable output - 🚫 No colors —
--no-colorfor clean terminal output - 🔇 Non-interactive —
--yesfor scripting - 🐳 Docker support — Containerized installation
- 📖 Man page —
man pluckfor offline docs - 🔧 Post-install hooks — Custom scripts after each install
🌐 Supported Forges
| Forge | Host | Built-in | Notes |
|---|---|---|---|
| GitHub | github.com |
✅ | Full support including gists |
| GitLab | gitlab.com |
✅ | HTTPS & SSH, self-hosted instances auto-detected |
| Codeberg | codeberg.org |
✅ | Powered by Forgejo |
| Bitbucket | bitbucket.org |
✅ | Both cloud & self-hosted |
| SourceHut | git.sr.ht |
✅ | Supports ~user prefix |
| Gitea | gitea.com |
✅ | Any Gitea instance works |
| Gogs | gogs.io |
✅ | Lightweight git service |
| Pagure | pagure.io |
✅ | Fedora's git hosting |
| Forgejo | forgejo.org |
✅ | Self-hosted friendly |
| Any other hosted git | Any domain | ✅ | Parses as generic type |
| Self-hosted | Any IP/domain | ✅ | SSH & HTTPS both supported |
Any git hosting platform that follows the standard host/owner/repo URL pattern works — no plugin or config needed.
📖 Commands
| Command | Description | Example |
|---|---|---|
install <url> |
Install from any git repo URL | pluck install https://gitlab.com/user/project |
update <name> |
Update an installed app | pluck update myapp |
info <name> |
Show app details | pluck info myapp |
list |
List installed apps | pluck list |
uninstall <name> |
Uninstall an app | pluck uninstall myapp |
remove <name> |
Alias for uninstall | pluck remove myapp |
verify |
Check apps validity | pluck verify |
clean |
Remove orphaned entries | pluck clean --force |
stats |
Show statistics | pluck stats |
doctor |
Check tool availability | pluck doctor |
config [key] [val] |
View/set config | pluck config install_dir ~/Apps |
search <query> [--forge <name>] [--all] [--output <file>] |
Search repos across forges | pluck search python installer --all --output results.txt |
pin <name> |
Pin an app to prevent updates | pluck pin myapp |
unpin <name> |
Unpin an app | pluck unpin myapp |
self-update |
Update pluck itself | pluck self-update |
cache prune |
Clear download cache | pluck cache prune |
export <file> |
Export registry | pluck export ~/backup.json |
import <file> |
Import registry | pluck import ~/backup.json |
completion <shell> |
Generate shell completion | pluck completion bash |
version |
Show version | pluck version |
help |
Show help | pluck help |
🏷️ Flags
| Flag | Description |
|---|---|
--dir <path> |
Install to a custom directory |
--dry-run |
Preview without making changes |
--force |
Skip confirmation prompts |
--shallow |
Use shallow clone (--depth 1) |
--ref <ref> |
Clone a specific branch or tag |
--method <method> |
Force install method |
--yes |
Non-interactive mode (alias for --force) |
--json |
Output in JSON format (for scripting) |
--no-color |
Disable colored output |
--timeout <secs> |
Timeout for git clone in seconds |
--retries <n> |
Number of retries for failed git clone |
--jobs <n> |
Number of parallel installs (default: 1) |
--release |
Install from pre-built release assets instead of cloning |
--verbose |
Show detailed git clone output |
📥 Installation
From Source
# Clone the repository (GitLab is canonical - see note above; this repo is a mirror)
git clone https://gitlab.com/mabodu/pluck.git
cd pluck
# Install via pip
pip install -e .
# Or run directly
./src/pluck.py install https://gitlab.com/user/project
Via pip
pip install pluck-cli
Via Docker
docker build -t pluck .
docker run pluck install https://gitlab.com/user/project
⚙️ Configuration
Default Paths
| Constant | macOS | Linux | Description |
|---|---|---|---|
DEFAULT_INSTALL_DIR |
~/Applications |
~/.local/opt |
Where apps are installed |
APP_REGISTRY_FILE |
~/.pluck-registry.json |
~/.pluck-registry.json |
App registry |
CONFIG_FILE |
~/.config/pluck/config.json |
~/.config/pluck/config.json |
User config |
User Config File
{
"install_dir": "/custom/path",
"method_priority": ["script", "python", "node", "go", "rust", "make", "binary", "download"]
}
Manage via CLI:
pluck config install_dir ~/Apps
pluck config method_priority '["python","node","binary","download"]'
Post-Install Hooks
Create ~/.config/pluck/hooks/post-install.sh to run custom scripts after each install.
Available environment variables:
$PLUCK_APP— Repository name$PLUCK_PATH— Installation path$PLUCK_METHOD— Install method used
🖱️ Browser Integration
Pluck includes a right-click context menu integration. Install a repo from any forge without leaving your browser.
Quick Setup
# 1. Register the pluck:// protocol handler on your OS
bash scripts/install-protocol-handler.sh
# 2. Load the browser extension (Chrome/Chromium/Brave/Edge)
# Open chrome://extensions → Developer mode → Load unpacked
# Select assets/browser-extension/
# 3. Right-click any git repo link → "Install with pluck"
Works with GitHub, GitLab, Codeberg, Bitbucket, SourceHut — any forge.
No Extension? Use the Bookmarklet
If you prefer not to install an extension, create a bookmark with this URL:
javascript:location.href='pluck://install?url='+encodeURIComponent(location.href)
When you're on a git repo page, click the bookmark to install it.
How It Works
The protocol handler is a small shell script (scripts/pluck-protocol-handler.sh)
that receives pluck://install?url=... calls from the browser, decodes the URL,
and runs pluck install <url>. The browser extension adds the right-click menu
item that triggers this protocol call.
📁 Project Structure
pluck/
├── src/
│ ├── pluck.py # Main application (~2400 lines)
│ └── gh_install.py # Backward-compat alias (imports pluck)
├── tests/
│ └── test_pluck.py # Test suite (132 tests)
├── assets/
│ ├── images/ # Logo and illustrations
│ └── browser-extension/ # Chrome/Chromium right-click extension
├── scripts/
│ ├── pluck-protocol-handler.sh # pluck:// URL handler
│ └── install-protocol-handler.sh # Protocol registration
├── docs/
│ └── IMPLEMENTATION.md # Implementation details
├── man/
│ ├── pluck.1 # Man page
│ └── gh-install.1 # Legacy man page (kept for backward compat)
├── .github/
│ └── workflows/
│ └── release.yml # Build wheel + binaries, publish to PyPI + GitHub Releases
├── README.md # This file
├── CHANGELOG.md # Version history
├── CONTRIBUTING.md # Developer guide
├── LICENSE # MIT License
├── Dockerfile # Container image
├── pyproject.toml # Package config
├── .pre-commit-config.yaml # Pre-commit hooks
├── .gitignore # Git ignore patterns
└── .dockerignore # Docker ignore patterns
🛠️ Development
# Install dev dependencies
pip install pytest ruff
# Run tests
python -m pytest tests/ -v
# Run linter
ruff check src/ tests/
# Install pre-commit hooks
pip install pre-commit
pre-commit install
Test Coverage
132 tests passing across 31 test classes:
├── TestParseRepoUrl (22 tests)
├── TestGistUrl (7 tests) — includes GitLab snippets
├── TestDetectInstallMethod (17 tests)
├── TestSharedPaths (2 tests)
├── TestValidMethods (2 tests)
├── TestSanitizeRepoName (4 tests)
├── TestIsExecutable (6 tests)
├── TestGetDiskSize (3 tests)
├── TestParseArgs (10 tests)
├── TestDryRun (1 test)
├── TestRegistryOperations (3 tests)
├── TestUpdateApp (2 tests)
├── TestInfoApp (2 tests)
├── TestDoctor (2 tests)
├── TestConfigCommand (2 tests)
├── TestExportImport (4 tests)
├── TestVerifyApps (3 tests)
├── TestStatsCommand (2 tests)
├── TestFormatBytes (5 tests)
├── TestExtractGlobalFlags (5 tests)
├── TestDownloadAndInstallMocked (4 tests)
├── TestParseArgsMissingFlagValue (6 tests) — regression: flags with missing values
├── TestRegistryAtomicWrite (5 tests) — regression: atomic writes + locking across all registry ops
├── TestInstallPythonSymlink (2 tests) — regression: symlink bug + directory-guard
├── TestSafeTarMembers (2 tests) — regression: CVE-2007-4559 + backslash bypass
├── TestProtocolHandlerUrlParsing (4 tests) — regression: multi-param URL parsing
├── TestReleaseInstallFallback (1 test) — regression: --release fallback to clone
└── TestInstallMakeFallback (1 test) — regression: uncaught CalledProcessError on make failure
📄 License
This project is licensed under the MIT License — see the LICENSE file for details.
🤝 Contributing
Contributions are welcome! Please read the Contributing Guide for details on our code of conduct and the process for submitting pull requests.
Made with ❤️ for non-technical users everywhere
Metadata
Release files for pluck-cli 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pluck_cli-0.6.0.tar.gz | 45.1 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pluck_cli-0.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 75.3 kB
Release files / pluck_cli-0.6.0.tar.gz
| Download URL | pluck_cli-0.6.0.tar.gz |
|---|---|
| Size | 45.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2dcc8f713c81174ce85b5beac76573fe8ef9a1fa38199eec38e3d06b7982f4b1
|
|
BLAKE2b-256 checksum How to use checksums |
92dcbb826d0af228a6270cc51a633c89ed8daa80a190e1aecc09edfcf1c58de4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 4, 2026.
Transparency logRelease files / pluck_cli-0.6.0-py3-none-any.whl
| Download URL | pluck_cli-0.6.0-py3-none-any.whl |
|---|---|
| Size | 30.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
874f46c78f29902a0d475c0b8af21eea988095b1ceda325ee15ade67d92fdba2
|
|
BLAKE2b-256 checksum How to use checksums |
9d96d78e7a8d860223552916d99049b26ea4458ccf4e2217a1cbb0ac88cfddf9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 4, 2026.
Transparency logSecurity for pluck-cli 0.6.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for pluck_cli-0.6.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 4, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.