mergd/ap

Agent-portable local secrets CLI — bundle-aware doctor, global/project vaults

1

stars

29

commits

TypeScript

primary language

Aug 30, 2026

updated

README

ap

ap

Agent-portable local secrets. Declare bundles of credentials in committed manifests, store global secret values inline in TOML, and keep project secrets in .ap/secrets.json. Agents check readiness with ap show <bundle> --check before calling external APIs.

Install

Requires Node 18+.

npm install -g @mergd/ap

From source:

git clone https://github.com/mergd/ap.git
cd ap
npm install
npm run build
npm link   # or: ln -sf "$(pwd)/bin/ap" ~/.local/bin/ap

Quick start

Agents: run ap guide first (YAML contract for show → run → set).

# Optional: seed catalog var stubs into the global manifest
ap init -g                      # all catalog vars → ~/.config/ap/manifest.toml
# ap init -g cloudflare         # or pick a catalog bundle's vars

# Per repo (catalog owns bundle identity — no need to copy [bundle.*] globally)
ap init                         # ap.toml + .ap/ + project agent skill
# edit ap.toml → bundles = ["namecheap", "cloudflare"]
eval "$(op signin)"
ap setup                        # SOPS + 1Password — safe to commit .ap/secrets.json

# Set project secrets (default → .ap/secrets.json)
echo "$TOKEN" | ap set DEPLOY_TOKEN

# Set deliberately shared credentials globally (explicit opt-in)
echo "$NC_API_KEY" | ap set NC_API_KEY --global
echo "$KEY" | ap set CF_GLOBAL_API_KEY --global

# Inspect secrets and check readiness
ap show --check
ap show cloudflare --check

# Run commands with secrets injected
ap run cloudflare -- curl ...

Install the agent skill (Cursor, Claude Code, Codex):

ap skill install              # ~/.agents/skills/ap/ (+ symlinks for claude/cursor)
ap skill install --project    # same paths under current repo

npm install -g @mergd/ap runs ap skill install via postinstall (global). ap init installs the skill into the repo.

How it works

Bundles group related env vars for a capability (e.g. namecheapNC_API_USER, NC_API_KEY, NC_CLIENT_IP). Bundle identity (keys, ask/docs/prompt) lives in the built-in catalog; the global manifest only stores var stubs and values.

FilePurpose
~/.config/ap/manifest.tomlGlobal public vars and secret values (value =)
ap.tomlWhich bundles this repo uses; project var declarations; [encryption] for 1Password age key
.ap/secrets.jsonProject vault — all project-scoped secrets (SOPS after ap setup)
.sops.yamlSOPS encryption rules (committed after ap setup)

There is no global secrets.json. Project secrets always live in .ap/secrets.json:

# ap.toml
version = 1
scope = "project"

[var.DEPLOY_TOKEN]
visibility = "secret"
ask = "Deploy token for this repo"
echo "$TOKEN" | ap set DEPLOY_TOKEN

Project secrets use SOPS + age with the private key in 1Password (same pattern as lockbox). Run ap setup once per repo; teammates need op access to decrypt.

Public bundle values surface immediately in ap show. Secrets are never shown — only status and set_with commands.

ap guide              # agent contract
ap catalog            # built-in templates
ap help               # full command reference

Commands

ap -V, --version                 Print version and update status
ap guide [--human]               Agent contract (primary entrypoint for agents)
ap show [BUNDLE] [-g] [--check] [--validate]
ap catalog
ap set KEY [-g|--global|--project] [--from-env]
ap unset KEY [-g|--global|--project]
ap run [BUNDLE] -- <cmd...>
ap init [-g|--global] [BUNDLE...]
ap setup
ap edit <secrets|global|project> [--ui] [--port N] [--no-open]
ap skill install [--project]

ap set and ap unset default to the current repo vault (.ap/secrets.json). -g is short for --global and is required to modify the global manifest. --project remains as an explicit, backward-compatible spelling of the default.

Hooks

ap set and ap unset run a script if you bind one. after_run fires after ap run in the background and never fails the command.

# ap.toml
[hooks]
after_set = ".ap/hooks/sync"
after_unset = ".ap/hooks/sync"
after_run = ".ap/hooks/sync"
# .ap/hooks/sync
#!/bin/sh
git pull --rebase && git push

chmod +x .ap/hooks/sync. If [hooks] omits an event, ap still runs .ap/hooks/<event> when that file exists. after_set / after_unset fail closed. before_show is opt-in (warns and continues). after_run is detached: handle errors in the script. Set after_set = "none" or AP_NO_HOOKS=1 to disable.

ap edit --ui opens a local Bootstrap-era page on 127.0.0.1 to edit project ap.toml (or -g / global for manifest.toml, including global secret values).

Output is human-readable in a terminal and YAML when piped. Catalog bundles: cloudflare, namecheap, openrouter.

ap checks npm for updates at most once per day and prints upgrade notices to stderr. Set AP_NO_UPDATE_CHECK=1 to disable the check.

Development

npm install
npm run build
npm test
npm run check
npm run dev -- show

License

MIT

Contributors

mergd

29 commits

mergd/ap

Agent-portable local secrets CLI — bundle-aware doctor, global/project vaults

1

stars

29

commits

TypeScript

primary language

Aug 30, 2026

updated

README

ap

ap

Agent-portable local secrets. Declare bundles of credentials in committed manifests, store global secret values inline in TOML, and keep project secrets in .ap/secrets.json. Agents check readiness with ap show <bundle> --check before calling external APIs.

Install

Requires Node 18+.

npm install -g @mergd/ap

From source:

git clone https://github.com/mergd/ap.git
cd ap
npm install
npm run build
npm link   # or: ln -sf "$(pwd)/bin/ap" ~/.local/bin/ap

Quick start

Agents: run ap guide first (YAML contract for show → run → set).

# Optional: seed catalog var stubs into the global manifest
ap init -g                      # all catalog vars → ~/.config/ap/manifest.toml
# ap init -g cloudflare         # or pick a catalog bundle's vars

# Per repo (catalog owns bundle identity — no need to copy [bundle.*] globally)
ap init                         # ap.toml + .ap/ + project agent skill
# edit ap.toml → bundles = ["namecheap", "cloudflare"]
eval "$(op signin)"
ap setup                        # SOPS + 1Password — safe to commit .ap/secrets.json

# Set project secrets (default → .ap/secrets.json)
echo "$TOKEN" | ap set DEPLOY_TOKEN

# Set deliberately shared credentials globally (explicit opt-in)
echo "$NC_API_KEY" | ap set NC_API_KEY --global
echo "$KEY" | ap set CF_GLOBAL_API_KEY --global

# Inspect secrets and check readiness
ap show --check
ap show cloudflare --check

# Run commands with secrets injected
ap run cloudflare -- curl ...

Install the agent skill (Cursor, Claude Code, Codex):

ap skill install              # ~/.agents/skills/ap/ (+ symlinks for claude/cursor)
ap skill install --project    # same paths under current repo

npm install -g @mergd/ap runs ap skill install via postinstall (global). ap init installs the skill into the repo.

How it works

Bundles group related env vars for a capability (e.g. namecheapNC_API_USER, NC_API_KEY, NC_CLIENT_IP). Bundle identity (keys, ask/docs/prompt) lives in the built-in catalog; the global manifest only stores var stubs and values.

FilePurpose
~/.config/ap/manifest.tomlGlobal public vars and secret values (value =)
ap.tomlWhich bundles this repo uses; project var declarations; [encryption] for 1Password age key
.ap/secrets.jsonProject vault — all project-scoped secrets (SOPS after ap setup)
.sops.yamlSOPS encryption rules (committed after ap setup)

There is no global secrets.json. Project secrets always live in .ap/secrets.json:

# ap.toml
version = 1
scope = "project"

[var.DEPLOY_TOKEN]
visibility = "secret"
ask = "Deploy token for this repo"
echo "$TOKEN" | ap set DEPLOY_TOKEN

Project secrets use SOPS + age with the private key in 1Password (same pattern as lockbox). Run ap setup once per repo; teammates need op access to decrypt.

Public bundle values surface immediately in ap show. Secrets are never shown — only status and set_with commands.

ap guide              # agent contract
ap catalog            # built-in templates
ap help               # full command reference

Commands

ap -V, --version                 Print version and update status
ap guide [--human]               Agent contract (primary entrypoint for agents)
ap show [BUNDLE] [-g] [--check] [--validate]
ap catalog
ap set KEY [-g|--global|--project] [--from-env]
ap unset KEY [-g|--global|--project]
ap run [BUNDLE] -- <cmd...>
ap init [-g|--global] [BUNDLE...]
ap setup
ap edit <secrets|global|project> [--ui] [--port N] [--no-open]
ap skill install [--project]

ap set and ap unset default to the current repo vault (.ap/secrets.json). -g is short for --global and is required to modify the global manifest. --project remains as an explicit, backward-compatible spelling of the default.

Hooks

ap set and ap unset run a script if you bind one. after_run fires after ap run in the background and never fails the command.

# ap.toml
[hooks]
after_set = ".ap/hooks/sync"
after_unset = ".ap/hooks/sync"
after_run = ".ap/hooks/sync"
# .ap/hooks/sync
#!/bin/sh
git pull --rebase && git push

chmod +x .ap/hooks/sync. If [hooks] omits an event, ap still runs .ap/hooks/<event> when that file exists. after_set / after_unset fail closed. before_show is opt-in (warns and continues). after_run is detached: handle errors in the script. Set after_set = "none" or AP_NO_HOOKS=1 to disable.

ap edit --ui opens a local Bootstrap-era page on 127.0.0.1 to edit project ap.toml (or -g / global for manifest.toml, including global secret values).

Output is human-readable in a terminal and YAML when piped. Catalog bundles: cloudflare, namecheap, openrouter.

ap checks npm for updates at most once per day and prints upgrade notices to stderr. Set AP_NO_UPDATE_CHECK=1 to disable the check.

Development

npm install
npm run build
npm test
npm run check
npm run dev -- show

License

MIT

See what people are saying

Contributors

mergd

29 commits

Languages

TypeScript

99.8%