diff --git a/docs/Gemfile b/docs/Gemfile
new file mode 100644
index 0000000..3310869
--- /dev/null
+++ b/docs/Gemfile
@@ -0,0 +1,4 @@
+source "https://rubygems.org"
+
+gem "github-pages", group: :jekyll_plugins
+gem "jekyll-remote-theme"
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 0000000..c2b4c86
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1,18 @@
+title: cli-tools
+description: Personal shell utilities for git hygiene, GPG, video conversion, and Claude plugin static analysis.
+remote_theme: just-the-docs/just-the-docs@v0.10.1
+url: https://1shooperman.github.io
+baseurl: /cli-tools
+
+# Nav order for top-level pages
+nav_order_default: 1
+
+plugins:
+ - jekyll-remote-theme
+
+# Just the Docs config
+search_enabled: true
+heading_anchors: true
+color_scheme: dark
+
+footer_content: "MIT License — 1shooperman/cli-tools"
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..92eb530
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,31 @@
+---
+layout: home
+title: Home
+nav_order: 1
+---
+
+# cli-tools
+
+Personal shell utilities for git branch hygiene, GPG cache warming, video conversion, and Claude plugin static analysis.
+
+## Quick install
+
+```sh
+brew tap 1shooperman/tap
+brew install cli-tools
+```
+
+All commands are placed on your `$PATH` automatically. See [Installation](installation) for manual setup and dependencies.
+
+---
+
+## Tools at a glance
+
+| Command | What it does |
+|---------|-------------|
+| [`gitprune`](tools/gitprune) | Deletes stale local branches and compacts the repo |
+| [`gitrefresh`](tools/gitrefresh) | Resets to a clean, up-to-date state against any branch |
+| [`cache-gpg`](tools/cache-gpg) | Warms the GPG agent cache before a signing flow |
+| [`convert-video`](tools/convert-video) | Converts any video file to H.264/AAC MP4 via ffmpeg |
+| [`sast`](tools/sast) | Static analysis for Claude plugin `allowed-tools` frontmatter |
+| [`gh-actions-sast`](tools/gh-actions-sast) | Checks GitHub Actions workflows for outdated action pins |
diff --git a/docs/installation.md b/docs/installation.md
new file mode 100644
index 0000000..dc13fc9
--- /dev/null
+++ b/docs/installation.md
@@ -0,0 +1,41 @@
+---
+layout: default
+title: Installation
+nav_order: 2
+---
+
+# Installation
+
+## Homebrew (recommended)
+
+```sh
+brew tap 1shooperman/tap
+brew install cli-tools
+```
+
+All commands are placed on your `$PATH` automatically.
+
+---
+
+## Manual
+
+Clone the repo and add `bin/` to your `$PATH`:
+
+```sh
+git clone https://github.com/1shooperman/cli-tools.git
+export PATH="$PATH:/path/to/cli-tools/bin"
+```
+
+Add the `export` line to your shell rc file (`.zshrc`, `.bashrc`, etc.) to persist it across sessions.
+
+---
+
+## Dependencies
+
+Some commands require external tools. Install only what you need:
+
+| Tool | Required by | Install |
+|------|-------------|---------|
+| `ffmpeg` | `convert-video` | `brew install ffmpeg` |
+| `gpg` | `cache-gpg` | `brew install gnupg` |
+| `git` | `gitprune`, `gitrefresh` | ships with macOS |
diff --git a/docs/tools/cache-gpg.md b/docs/tools/cache-gpg.md
new file mode 100644
index 0000000..758c847
--- /dev/null
+++ b/docs/tools/cache-gpg.md
@@ -0,0 +1,33 @@
+---
+layout: default
+title: cache-gpg
+parent: Tools
+nav_order: 3
+---
+
+# cache-gpg
+
+Warms the GPG agent cache by performing a throwaway clearsign operation.
+
+## Usage
+
+```sh
+cache-gpg
+```
+
+No arguments. No output on success.
+
+## Why
+
+`git commit -S` prompts for your GPG passphrase when the agent cache is cold. Running `cache-gpg` before your commit flow pre-unlocks the key so the signing step doesn't interrupt with a passphrase prompt.
+
+## What it does
+
+Pipes the string `"test"` through `gpg --clearsign` and discards the output. That is enough to unlock the key and populate the agent cache for the configured cache TTL.
+
+## Requirements
+
+| Requirement | Install |
+|-------------|---------|
+| `gpg` installed and a signing key configured | `brew install gnupg` |
+| GPG agent running | started automatically by `gpg` on modern systems |
diff --git a/docs/tools/convert-video.md b/docs/tools/convert-video.md
new file mode 100644
index 0000000..4790a36
--- /dev/null
+++ b/docs/tools/convert-video.md
@@ -0,0 +1,47 @@
+---
+layout: default
+title: convert-video
+parent: Tools
+nav_order: 4
+---
+
+# convert-video
+
+Converts a video file to H.264/AAC MP4 using ffmpeg.
+
+## Usage
+
+```sh
+convert-video [--silent]
+```
+
+## Arguments
+
+| Argument | Description |
+|----------|-------------|
+| `input-file` | Path to the source video file (any format ffmpeg can read) |
+| `--silent` | Suppress ffmpeg output |
+
+## Examples
+
+```sh
+convert-video recording.mov # → recording.mp4
+convert-video --silent clip.avi # → clip.mp4, no output
+```
+
+Output is written to the same directory as the input with the extension replaced by `.mp4`.
+
+## Encoding settings
+
+| Stream | Codec | Settings |
+|--------|-------|----------|
+| Video | `libx264` | CRF 23, `medium` preset |
+| Audio | `aac` | 128k bitrate |
+
+CRF 23 is the ffmpeg default and gives a good quality/size balance for most content.
+
+## Requirements
+
+| Requirement | Install |
+|-------------|---------|
+| `ffmpeg` | `brew install ffmpeg` |
diff --git a/docs/tools/gh-actions-sast.md b/docs/tools/gh-actions-sast.md
new file mode 100644
index 0000000..c907c41
--- /dev/null
+++ b/docs/tools/gh-actions-sast.md
@@ -0,0 +1,48 @@
+---
+layout: default
+title: gh-actions-sast
+parent: Tools
+nav_order: 6
+---
+
+# gh-actions-sast
+
+Scans GitHub Actions workflow files for action references pinned below a minimum version.
+
+## Usage
+
+```sh
+gh-actions-sast
+```
+
+Must be run from the repository root. Scans `.github/workflows/*.yml` automatically — no arguments needed.
+
+## What it does
+
+Walks every `.yml` file under `.github/workflows/` and checks each `uses:` line for `actions/*@vN` references. Any action pinned below `v6` is flagged.
+
+## Output
+
+| Severity | Condition |
+|----------|-----------|
+| ERROR | Action version is below `v6` |
+| WARN | Action is below `v6` but present in the exclude list |
+
+```
+ERROR ci.yml: actions/checkout@v4 is below v6
+WARN ci.yml: actions/some-action@v3 is below v6 (excluded)
+```
+
+## Exit codes
+
+| Code | Meaning |
+|------|---------|
+| `0` | No ERROR findings |
+| `1` | One or more ERROR findings |
+
+## Notes
+
+- Only matches the pattern `actions/@v` — SHA-pinned or tag-pinned refs are not checked
+- Passes cleanly when `.github/workflows/` does not exist
+- Non-`.yml` files in `.github/workflows/` are skipped
+- Pairs with [`sast`](sast) for broader CI security coverage
diff --git a/docs/tools/gitprune.md b/docs/tools/gitprune.md
new file mode 100644
index 0000000..104baf6
--- /dev/null
+++ b/docs/tools/gitprune.md
@@ -0,0 +1,39 @@
+---
+layout: default
+title: gitprune
+parent: Tools
+nav_order: 1
+---
+
+# gitprune
+
+Deletes local git branches that no longer exist on the remote, then runs garbage collection and prunes stale remote-tracking refs.
+
+## Usage
+
+```sh
+gitprune # safe delete
+gitprune --force # force delete unmerged branches too
+```
+
+## Flags
+
+| Flag | Behavior |
+|------|----------|
+| _(none)_ | Uses `git branch -d` — refuses to delete unmerged branches |
+| `--force` | Uses `git branch -D` — deletes unmerged branches regardless |
+
+## What it does
+
+1. Lists remote branches
+2. Compares against local tracking branches
+3. Deletes any local branch with no matching remote
+4. Runs `git gc --prune=now` and `git fetch -p` to clean up stale refs
+5. Runs `git gc` again for a final compaction
+
+Output from gc and fetch is collapsed into a single overwriting status line so it doesn't flood your terminal.
+
+## Notes
+
+- Safe to run frequently — without `--force` it will never delete a branch with unmerged commits
+- `gitrefresh` calls `gitprune --force` automatically at the end of its flow
diff --git a/docs/tools/gitrefresh.md b/docs/tools/gitrefresh.md
new file mode 100644
index 0000000..7262218
--- /dev/null
+++ b/docs/tools/gitrefresh.md
@@ -0,0 +1,36 @@
+---
+layout: default
+title: gitrefresh
+parent: Tools
+nav_order: 2
+---
+
+# gitrefresh
+
+Resets a local clone to a clean, up-to-date state against any branch, then prunes stale local branches.
+
+## Usage
+
+```sh
+gitrefresh # resets to main
+gitrefresh dev # resets to dev
+```
+
+## Arguments
+
+| Argument | Default | Description |
+|----------|---------|-------------|
+| `branch` | `main` | Branch to check out and pull |
+
+## What it does
+
+1. Checks out `branch`
+2. Fetches from origin
+3. Pulls latest
+4. Runs `gitprune --force` **twice** — the second pass catches branches whose delete tracking refs were only cleaned up by the first pass
+
+All git output is collapsed into a single overwriting status line.
+
+## Notes
+
+`gitrefresh` is a "nuke and reset" convenience — it force-prunes unmerged branches. Use plain `gitprune` if you want to keep unmerged work.
diff --git a/docs/tools/index.md b/docs/tools/index.md
new file mode 100644
index 0000000..e10bf10
--- /dev/null
+++ b/docs/tools/index.md
@@ -0,0 +1,19 @@
+---
+layout: default
+title: Tools
+nav_order: 3
+has_children: true
+---
+
+# Tools
+
+cli-tools provides six commands covering git hygiene, GPG, video conversion, and static analysis.
+
+| Command | Summary |
+|---------|---------|
+| [gitprune](gitprune) | Delete stale local branches and compact the repo |
+| [gitrefresh](gitrefresh) | Reset to a clean, up-to-date state |
+| [cache-gpg](cache-gpg) | Warm the GPG agent cache |
+| [convert-video](convert-video) | Convert video to H.264/AAC MP4 |
+| [sast](sast) | Scan Claude plugin frontmatter for risky tool grants |
+| [gh-actions-sast](gh-actions-sast) | Check GitHub Actions for outdated action pins |
diff --git a/docs/tools/sast.md b/docs/tools/sast.md
new file mode 100644
index 0000000..b21b515
--- /dev/null
+++ b/docs/tools/sast.md
@@ -0,0 +1,56 @@
+---
+layout: default
+title: sast
+parent: Tools
+nav_order: 5
+---
+
+# sast
+
+Static analysis for Claude plugin markdown files. Scans `allowed-tools` declarations in YAML frontmatter for risky tool grants.
+
+## Usage
+
+```sh
+sast # scans ./plugins
+sast /path/to/plugins # scans a specific directory
+```
+
+## Arguments
+
+| Argument | Default | Description |
+|----------|---------|-------------|
+| `plugins-dir` | `./plugins` (relative to CWD) | Directory to scan for `.md` files |
+
+## Checks
+
+| Severity | Pattern | Why it matters |
+|----------|---------|----------------|
+| ERROR | Bare `Bash` | Grants unrestricted shell access to any command |
+| ERROR | `Bash(*)` | Wildcard constraint is effectively unrestricted |
+| ERROR | `[*]`, `Agent(*)`, `Skill(*)` | Grants access to all tools or agents |
+| WARN | Bare `WebFetch` | Allows fetching any domain without restriction |
+
+Only `allowed-tools` lines within the first YAML frontmatter block (between the opening and closing `---`) are checked. Occurrences in the body are ignored.
+
+## Exit codes
+
+| Code | Meaning |
+|------|---------|
+| `0` | No ERROR findings (WARN-only still exits 0) |
+| `1` | One or more ERROR findings |
+
+## Example output
+
+```
+[ERROR] plugins/my-agent/agent.md: bare 'Bash' grants unrestricted shell access
+[WARN] plugins/my-agent/agent.md: bare 'WebFetch' allows fetching any domain
+
+SAST complete. Findings: 2
+```
+
+## Notes
+
+- Designed to run in CI against a checked-out repo, or locally from a project root
+- Exits 0 cleanly when `plugins-dir` does not exist (0 findings)
+- Pairs with [`gh-actions-sast`](gh-actions-sast) for broader CI security coverage