Skill
plugins/monkeys/skills/monkeys/SKILL.md, the file a coding agent loads, served verbatim at monk3ys.dev/skill.
monkeys
When a command needs an API key, token or password, monkeys gives it one
without showing it to you:
monkeys run OPENROUTER_API_KEY ./hello.shEach secret sits in the operating system's own vault, the macOS keychain or the Linux Secret Service, under the name of the environment variable that carries it. No command prints a remembered secret, and none should: one that reaches you is in this transcript for good, and a vault cannot take it back.
If monkeys is not on PATH, say so and point at
https://github.com/eastriverlee/monkeys. Do not work around it by asking for
the secret yourself.
Use a secret
Name the keys the command reads, then the command:
monkeys run OPENROUTER_API_KEY ./hello.sh
monkeys run OPENROUTER_API_KEY,GITHUB_TOKEN ./deployNothing in that line holds a secret, so nothing you write can spill one. The
exit status and the signals are the command's own. Its output comes back
through monkeys, and a remembered secret in it comes back as [redacted KEY].
Reach for monkeys run --all <command> only when you cannot tell which keys
the command reads.
The command you start is what expands the variable, since that is where it
exists. $KEY written into the monkeys run line is expanded by the shell
you are already in, which does not have the secret:
monkeys run OPENROUTER_API_KEY curl -H "Authorization: Bearer $OPENROUTER_API_KEY" ...
# sends an empty Bearer
monkeys run OPENROUTER_API_KEY sh -c 'curl -H "Authorization: Bearer $OPENROUTER_API_KEY" ...'
# the single quotes reach the child intactA file that has to end up with the secret in it takes the redirect inside the
command, for the same reason. The secret then goes from the child straight
into the file, never passing monkeys, which would redact it on the way.
monkeys run OPENROUTER_API_KEY sh -c 'envsubst < template > config'Inside a project
A .monkeys file lists the keys the project needs. Read it before adding a
key; it is the list.
+foo
@test,production
DATABASE_URL
STRIPE_SECRET_KEY
PORT=3000In that directory, or below it within the git checkout, run takes only the
command and reads the file's first profile. A leading @profile picks
another one the file declares, and a KEY=value line is a value that is not
secret, which run puts in the environment too:
monkeys run ./hello.sh
monkeys run @production ./deployA profile never falls back to the keys with no profile. A key missing in
@production stays missing there even when a copy with no profile exists,
so one project never quietly reads another's secret. Reaching those keys from
inside a project is a bare @, with the key named again:
monkeys run @ ANTHROPIC_API_KEY claudeWhen the code you write starts reading a new variable, the key belongs in
that file. remember puts it there itself, under the profile the secret went
to, so ask a human to run it rather than editing the file around them:
monkeys remember STRIPE_SECRET_KEYWhen the code stops reading one, monkeys drop STRIPE_SECRET_KEY forgets the
secret and removes the key from the file. It asks a person before it does,
so that one is theirs to run as well.
Adding a profile is the one edit remember cannot make for you, since no
command writes an @ line. A new @test goes at the top, because the first
profile is the one run takes when none is given and the default should be
the harmless one; anything else goes after the profiles already there. Where
the person says it goes wins.
The rest of the file's grammar, namespaces and several profiles among it, is
in monkeys help. Reach for it when a file surprises you; the file is
usually written already, and reading it is enough.
Never write a .env
Every environment variable a project needs belongs in .monkeys, secret or
not. Do not create a .env, do not add a line to one, and do not tell someone
to put a variable there.
- A variable that is not secret goes in as
KEY=value, which you write yourself:monkeys remember --public PORT. - A secret goes in as a bare key, and a human remembers it.
When a tool insists on a .env file, monkeys poo writes one from that same
list, keys only and no secrets, and monkeys run still carries the values.
monkeys poo @profile --WITH_SECRETS writes each key as a vault lookup for a
shell to resolve, which is also safe to read. Both land in /tmp; add
--path . to put one in the project, which asks first. Write the .env one of
those two ways or not at all.
Eat a .env you find
A project that still has .env files is one command away. Name the keys that
are not secret; every other key becomes a secret in the vault, and the files
are deleted once everything is remembered:
monkeys eat --public PORT,NODE_ENVRun this yourself when you find a .env in a project you are working in.
eat reads the files, so no secret passes through you, and --public answers
every question before it is asked. Without it eat asks one question per key
and needs a terminal, which is a human's run.
When a key is missing
run stops before anything happens and names what to ask for:
monkeys: SUPER_SECRET is not remembered yet in @foo.test
nothing happened. a human types the secret into:
monkeys remember @foo.test SUPER_SECRET
then try again.Pass that on. monkeys doctor --short prints one missing @profile: A,B line
per profile with a gap, nothing when there is none, and exits non-zero while
any remains.
Check without reading
monkeys list gives the keys, as blocks by profile. monkeys preview gives
each secret masked, with its length, which is enough to tell one pasted whole
from one that lost a character.
Leave to a human
Remembering is a human's job: typing a secret yourself puts it in the conversation before it reaches the vault. Say what is needed and stop.
monkeys rememberremembers a secret, andmonkeys remember --public PORTremembers a value that is not secret, which you may run yourself.monkeys packwrites every secret of a profile into one file and takes over the clipboard to hand out its passphrase. Sharing is a decision, not a step, and a bundle that would carry@personalasks in a word before it goes.monkeys unpackfills a profile from a shared bundle. It asks for a passphrase and deletes the bundle.monkeys fill @production --with @testcopies what is missing between profiles, which may put a test secret into production.monkeys forgetandmonkeys dropdelete the only copy of a secret there is, so both ask first and neither can be answered from a pipe.
Never
echo "$SOME_KEY",env,printenv- writing a secret out yourself, into a file, a log, a commit or a bug report
monkeys poo --EXPAND_DANGEROUSLY, which puts the secrets themselves in a file; it refuses anything but a terminal, so this is a human's to run
Where a config file wants the secret, write whatever reference its format
offers, such as ${OPENROUTER_API_KEY}, and let the program expand it.
monkeys help carries the full contract.