Local-first macOS dev environment scanner: scan, annotate, diff—no account required.
devsnap is a CLI-only project for macOS: it captures a structured snapshot of your machine (OS and hardware, Homebrew, Node, runtimes, Docker, databases, browsers, terminals, editors, LLM / AI CLIs, CLI tools), saves everything under ~/.devsnap, and diffs locally. Optional devsnap cloud commands can talk to your own HTTP API (for example a private dashboard backend you host separately)—this repository does not ship a server or web UI.
Built for developers who want one command instead of twenty screenshots.
| You need… | devsnap gives you… |
|---|---|
| A single inventory of “what’s on this Mac?” | devsnap scan → terminal, Markdown, HTML, or JSON |
| To see what changed after an upgrade | devsnap diff between two snapshots |
| Context for teammates or support | devsnap share clipboard or gist (Markdown/HTML) |
| To remember why a tool is installed | devsnap annotate on tool keys |
| A starting point to reproduce a machine | devsnap export brewfile or bootstrap |
| Low-friction drift awareness | devsnap schedule install (daily launchd scans) |
| A lightweight security pass | devsnap audit (optional --html) |
| Optional upload to your own API | devsnap cloud push (after cloud auth or cloud register) |
Requirements: macOS · Node.js ≥ 22
npm install -g @aiherrera/devsnapThe devsnap command name is unchanged (see bin in package.json). Scoped packages are private on npm by default; the first publish must use npm publish --access public.
From source:
git clone https://github.com/aiherrera/devsnap.git
cd devsnap
npm install
npm run build
npm link # or: node dist/cli.js# Full scan, print to terminal and save JSON snapshot under ~/.devsnap/snapshots/
devsnap scan
# Scan + write Markdown and HTML reports (HTML opens in browser by default)
devsnap scan --md --html
# Machine-readable output only (still can save with default behavior)
devsnap scan --json
# Compare the last two snapshots
devsnap diff
# Search the latest snapshot
devsnap search docker| Command | Description |
|---|---|
devsnap scan |
Scan environment. Flags: --html, --md, --json, --no-save |
devsnap list |
List saved snapshots |
devsnap diff [id1] [id2] |
Diff two snapshots (defaults to latest pair) |
devsnap open |
Open the latest HTML report in your browser |
devsnap annotate |
Add or manage notes on tools (--list, --show, --remove) |
devsnap search <query> |
Search latest snapshot; -s, --snapshot <id> for a specific one |
devsnap export <format> [output] |
brewfile or bootstrap from latest snapshot |
devsnap clean |
Drop old snapshots; -k, --keep <n> (default 5) |
devsnap schedule <action> |
install | uninstall | status — automatic scans via launchd |
devsnap share <target> |
clipboard | gist — --format md|html (gist needs gh CLI; see Privacy below) |
devsnap audit |
Security-oriented audit; --html for report |
devsnap config show |
Print config |
devsnap config set <key> <value> |
Update ~/.devsnap/config.json (only documented keys are accepted) |
devsnap cloud register |
Create an account on your API; saves API key to ~/.devsnap/cloud.json |
devsnap cloud auth |
Save API key, API base URL, optional dashboard URL (--key, --url, --dashboard) |
devsnap cloud logout |
Remove ~/.devsnap/cloud.json |
devsnap cloud status |
Show whether cloud credentials are configured |
devsnap cloud push |
POST latest (or --id) snapshot JSON to your API; optional --tag, --note, --redacted |
devsnap cloud list |
List snapshots from your API |
devsnap cloud open |
Open the saved dashboard URL in a browser |
Run devsnap --help or devsnap <command> --help for details.
Environment: DEVSNAP_CLOUD_API overrides the API base URL when not passed via cloud auth --url (default for local development: http://localhost:3001).
This repo stays CLI-only. If you run a separate private service, implement endpoints compatible with the CLI:
| Method | Path | Notes |
|---|---|---|
POST |
/api/auth/register |
Body: { "email": string | null }. Response: { "apiKey": string } (plaintext key, show once). |
POST |
/api/snapshots |
Header: Authorization: Bearer <apiKey>. Body: { "payload": <snapshot JSON>, "tag": string | null, "note": string | null }. Response: { "id": string, "localId": string }. |
GET |
/api/snapshots?limit=50 |
Same auth. Response: array of { id, localId, createdAt, tag, note }. |
GET |
/api/snapshots/:id |
Same auth. Full row including payload. |
snapshot objects are the same shape as devsnap scan --json (see What gets scanned). New scans include optional schemaVersion for forward compatibility.
Snapshots include (when available on your system):
- System — macOS version, architecture, chip, memory, disk
- Homebrew — formulae and casks
- Node — versions, package managers, global packages
- Runtimes — e.g. Python, Ruby, Go, Rust (as detected)
- Docker — version, images, running containers
- Databases — common DB CLI presence / versions
- Browsers — installed browsers
- Terminals — terminal apps
- Editors — VS Code, Cursor, JetBrains, etc.
- LLMs — AI/LLM-related CLIs and tooling
- CLI tools — curated list of developer utilities (configurable)
Exact coverage evolves with releases; use devsnap scan --json to inspect the schema.
--json output can include paths, tool names, and versions. Avoid piping it into shared logs or CI artifacts if that is sensitive for your environment.
devsnap share gistuploads your Markdown report to GitHub Gists (public by default for anonymous gists, or per yourghaccount defaults). Treat it like publishing environment inventory.- Clipboard copies the report into the system pasteboard; anyone with access to the machine or universal clipboard may see it.
Config lives at ~/.devsnap/config.json. You can edit it or use the CLI:
devsnap config show
devsnap config set autoOpenHtml false
devsnap config set staleDays 365| Key | Purpose |
|---|---|
disabledScanners |
Skip categories: brew, node, runtimes, docker, databases, browsers, terminals, editors, llms, cliTools, system (case-insensitive) |
extraCliTools |
Extra binary names to probe (letters, digits, ., _, - only; no paths). Duplicates of built-in tools are ignored. |
staleDays |
Days before a tool is treated as stale (default 180) |
cleanKeep |
Default keep count for devsnap clean (default 5) |
autoOpenHtml |
Open browser after devsnap scan --html (default true) |
| Path | Contents |
|---|---|
~/.devsnap/snapshots/ |
Snapshot JSON files (timestamp-style ids; devsnap list shows valid ids for diff) |
~/.devsnap/reports/ |
Generated .html and .md reports |
~/.devsnap/config.json |
User configuration |
~/.devsnap/annotations.json |
Tool annotations |
~/.devsnap/cloud.json |
Optional cloud API key + URLs (mode 600; created by devsnap cloud auth / register) |
npm install
npm run dev -- --help # tsx src/cli.ts
npm run build # tsc → dist/
npm run typecheckCI (.github/workflows/ci.yml) runs typecheck, tests, and a pack dry-run on pushes and PRs. It does not publish to npm.
Automated versioning and releases (Release Please)
This repo uses Release Please (.github/workflows/release-please.yml):
- Use Conventional Commits on
main, for example:fix: …→ patch bumpfeat: …→ minor bumpfeat!: …orBREAKING CHANGE:in the body → major bumpchore:,docs:, etc. usually do not trigger a release by themselves (see Release Please rules).
- On each push to
main, the workflow opens or updates a Release PR that bumpspackage.json, updatesCHANGELOG.md, and prepares the next version. - When you merge that Release PR, Release Please creates the GitHub release and tag, then
npm publish --access publicruns in the same job (so you do not depend on a second workflow).
Setup
- Add the
NPM_TOKENActions secret (npm automation/publish token), same as below. - Under Settings → Actions → General, allow “Read and write permissions” for the workflow token and (if prompted) allow workflows to create pull requests.
Other tools people use: semantic-release (fully automated from commits, no merge step), Changesets (human-written changeset files). Release Please fits GitHub-centric teams who like a visible Release PR.
Actions → Publish to npm (manual) → Run workflow publishes whatever version is on main today. Use for hotfixes or if you skip Release Please. Still requires NPM_TOKEN.
- Create an automation (or publish) token at npmjs.com → Access Tokens.
- Repo Settings → Secrets and variables → Actions →
NPM_TOKEN.
Scoped packages need npm publish --access public; the workflows already pass that. Your npm user must be allowed to publish @aiherrera/*.
MIT © aiherrera
If devsnap saves you time, a ⭐ on GitHub helps others find it.