Skip to content
Back to skills

Add Tool

ASecurity

Add a new tool to the dotfiles repository. Use when asked to add a new CLI tool, application, or package as a managed dotfiles tool.

  • 24 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 3, 2026
ai-agentsgoshellbashgit

Works with

  • claude code
  • cli

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 4, 2026

npx -y skills add ooloth/dotfiles --skill add-tool --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Add Tool?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Add Tool
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/ooloth-add-tool/badge)](https://www.skillsdirectory.com/skills/ooloth-add-tool)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: add-tool
description: Add a new tool to the dotfiles repository. Use when asked to add a new CLI tool, application, or package as a managed dotfiles tool.
---

## Context

- Existing tools: !`ls "${DOTFILES}/tools" | grep -v ^@`
- Template: `tools/@new/` — reference for file structure and patterns

## Your task

Add a new tool to this dotfiles repository. Follow these steps:

### 1. Research the tool

Before writing any files:

- **How is it installed?** Check its README/docs for the recommended installer. Common options: `brew install`, `npm install -g`, `bun install -g`, `uv tool install`, `cargo install`, or a custom install script.
- **What does `{command} --version` output?** Run it (or check the docs) to know the exact format — you'll need this to write `parse_version`.
- **Are there zsh completions?** Check the docs or the brew caveats after install. If completions require explicit shell setup (e.g. `eval "$(tool completion zsh)"`), note it — you'll need `shell.zsh`.
- **Does it have dotfiles config to manage?** If it writes config to `~/.config/{tool}/` or similar, you'll need `link.bash` and a `config/` directory.

### 2. Classify the tool

**Simple tool** (no dotfiles config to symlink): needs only the 4 core files.
Examples: `eza`, `sd`, `logcli`

**Tool with config** (has config files to symlink): also needs `link.bash` and a `config/` directory.
Examples: `claude`, `neovim`, `git`

### 3. Create `tools/{tool}/utils.bash`

```bash
#!/usr/bin/env bash
set -euo pipefail

export TOOL_LOWER="{tool}"
export TOOL_UPPER="{Tool}"
export TOOL_COMMAND="{tool}"
export TOOL_PACKAGE="{package-name}"
export TOOL_EMOJI="🔧"
export TOOL_CONFIG_DIR="${HOME}/.config/${TOOL_LOWER}"  # omit if no config

parse_version() {
  local raw_version="${1}"
  # Adapt to match the actual output of the version command (arg 6 in install/update):
  printf "${raw_version}"
}
```

Key variables:
- `TOOL_LOWER`: lowercase name used in paths and log messages
- `TOOL_UPPER`: display name (often same as `TOOL_LOWER`, but e.g. `"Claude Code"`)
- `TOOL_COMMAND`: the binary name (`which` resolves this)
- `TOOL_PACKAGE`: the package name passed to the installer
- `TOOL_EMOJI`: used in log output
- `TOOL_CONFIG_DIR`: only needed for tools with config; omit the export if unused

**`parse_version` by installer** — receives the raw output of the version command (arg 6) and must return a clean semver string:

| Installer | Version command (arg 6) | Typical raw output | `parse_version` logic |
|---|---|---|---|
| `brew` | `brew list --version {pkg}` | `sd 1.0.0` | strip `"sd "` prefix |
| `npm` / `bun` | `{command} --version` | `1.10.2` | print as-is |
| `npm` (some) | `{command} --version` | `1.0.0 (Claude Code)` | strip suffix |
| `uv tool` | `{pkg} --version` | `ty, version 0.0.1` | strip `"ty, version "` prefix |
| `cargo` | `{command} --version` | `sd 1.0.0` | strip `"sd "` prefix |

### 4. Create `tools/{tool}/install.bash`

Structure is always the same — only the install command (arg 5) and version command (arg 6) change by installer:

**brew:**
```bash
#!/usr/bin/env bash
set -euo pipefail

source "${DOTFILES}/features/install/utils.bash"
source "${DOTFILES}/tools/{tool}/utils.bash" # source last to avoid env var overrides

install_and_symlink \
  "${TOOL_LOWER}" \
  "${TOOL_UPPER}" \
  "${TOOL_COMMAND}" \
  "${TOOL_EMOJI}" \
  "brew install --formula ${TOOL_PACKAGE}" \
  "brew list --version ${TOOL_PACKAGE}" \
  "parse_version"
```

**npm:**
```bash
  "npm install --global ${TOOL_PACKAGE}@latest" \
  "${TOOL_COMMAND} --version" \
```

**bun:**
```bash
  "bun install --global ${TOOL_PACKAGE}" \
  "${TOOL_COMMAND} --version" \
```

**uv:**
```bash
  "uv tool install ${TOOL_PACKAGE}" \
  "${TOOL_PACKAGE} --version" \
```

**cargo:**
```bash
  "cargo install ${TOOL_PACKAGE}" \
  "${TOOL_COMMAND} --version" \
```

For **tools with config**, add the symlink script path as an 8th argument:

```bash
  "parse_version" \
  "${DOTFILES}/tools/${TOOL_LOWER}/link.bash"
```

### 5. Create `tools/{tool}/update.bash`

Same pattern — update command varies by installer, 8th arg is always the install script fallback:

**brew:**
```bash
#!/usr/bin/env bash
set -euo pipefail

source "${DOTFILES}/features/update/utils.bash"
source "${DOTFILES}/tools/{tool}/utils.bash" # source last to avoid env var overrides

update_and_symlink \
  "${TOOL_LOWER}" \
  "${TOOL_UPPER}" \
  "${TOOL_COMMAND}" \
  "${TOOL_EMOJI}" \
  "brew upgrade --formula ${TOOL_PACKAGE}" \
  "brew list --version ${TOOL_PACKAGE}" \
  "parse_version" \
  "${DOTFILES}/tools/${TOOL_LOWER}/install.bash"
```

Update commands by installer: `brew upgrade --formula {pkg}` / `npm install --global {pkg}@latest` / `bun install --global {pkg}` / `uv tool upgrade {pkg}` / `cargo install {pkg}`

For **tools with config**, add the symlink script path as a 9th argument:

```bash
  "${DOTFILES}/tools/${TOOL_LOWER}/install.bash" \
  "${DOTFILES}/tools/${TOOL_LOWER}/link.bash"
```

### 6. Create `tools/{tool}/uninstall.bash` (non-brew tools only)

**Brew-managed tools**: skip this file. Uninstall with `brew uninstall {formula}`,
then move the tool folder to `tools/@archive` (or delete it). `u` installs what
the Brewfiles declare but never removes anything they no longer declare.

**Non-brew tools** (npm/bun/uv/cargo): create a plain script with only the
cleanup steps brew can't handle — no wrapper needed:

```bash
#!/usr/bin/env bash
set -euo pipefail

source "${DOTFILES}/tools/bash/utils.bash"

info "🔧 Uninstalling {tool}"
{uninstall command}
```

Uninstall commands: `npm uninstall --global {pkg}` / `bun uninstall --global {pkg}` / `uv tool uninstall {pkg}` / `cargo uninstall {pkg}`

### 7. Create `tools/{tool}/shell.zsh` (only if needed)

Only create this file if the tool requires explicit shell setup: environment variables, aliases, or completions not auto-installed by the package manager.

Brew auto-installs completions to `/opt/homebrew/share/zsh/site-functions` — no `shell.zsh` needed for those. Only add completion setup if the tool's docs require something like `eval "$(tool completion zsh)"` or `source <(tool completions zsh)`.

```zsh
########################
# ENVIRONMENT VARIABLES #
########################

# export SOME_VAR="value"

###########
# ALIASES #
###########

# alias x="tool --flag"

###############
# COMPLETIONS #
###############

# eval "$(tool completion zsh)"
```

### 8. For tools with config: create `link.bash`

`tools/{tool}/link.bash`:

```bash
#!/usr/bin/env bash
set -euo pipefail

source "${DOTFILES}/tools/{tool}/utils.bash"
source "${DOTFILES}/tools/bash/utils.bash"

symlink "${DOTFILES}/tools/{tool}/config/settings.json" "${TOOL_CONFIG_DIR}/settings.json"
```

### 9. Verify

```bash
shellcheck tools/{tool}/*.bash
```

Run the install script to confirm it works end-to-end:

```bash
DOTFILES="$HOME/Repos/ooloth/dotfiles" bash tools/{tool}/install.bash
```

## Notes

- **Auto-discovery**: `features/install/tools.bash` and `features/update/tools.bash` use `find` to discover all `install.bash` / `update.bash` files under `tools/` automatically. No manual registration is needed.
- **Shell manifest**: `shell.zsh` is sourced automatically in every new shell via the generated manifest at `$DOTFILES/.cache/shell-files-manifest.zsh`. Regenerate it with the `symlinks` alias after adding a new tool.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.