Skip to content

About

Template expansion for AI prompts - variable substitution for Claude Code, Copilot, Goose, Bob, and more

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Promptargs

promptargs 🏴‍☠️

License

Template arguments for AI prompts.

You write a prompt with blanks ({{like_this}}), and promptargs fills them in.

Review {{file}} for {{focus}} issues.

becomes...

Review src/app.ts for security issues.

That's it. That's the whole idea.

Works as a CLI and as a skill in Claude Code, Copilot, Goose, and Bob.


Install

Requires Node.js 22 or newer.

CLI

npm install -g @hivecommons/promptargs

Or just run it without installing:

npx @hivecommons/promptargs help

As an Agent Skill

promptargs ships an Agent Skill at skills/promptargs/SKILL.md, loaded by GitHub Copilot (CLI, cloud agent, VS Code agent mode) and Claude Code.

With the GitHub CLI (2.90.0 or later):

gh skill install hivecommons/promptargs promptargs

Manually (copy the skills/promptargs/ directory):

# Copilot
mkdir -p ~/.copilot/skills
cp -r skills/promptargs ~/.copilot/skills/

# Claude Code
mkdir -p ~/.claude/skills
cp -r skills/promptargs ~/.claude/skills/

Use .github/skills/ or .claude/skills/ inside a project instead to share it with your team.

As a Slash Command (other tools)

Claude Code:

mkdir -p ~/.claude/commands
curl -o ~/.claude/commands/promptargs.md \
  https://raw.githubusercontent.com/hivecommons/promptargs/main/.claude/commands/promptargs.md

Goose:

mkdir -p ~/.config/goose/skills
curl -o ~/.config/goose/skills/promptargs.md \
  https://raw.githubusercontent.com/hivecommons/promptargs/main/.goose/agents/promptargs.md

Bob (IBM):

mkdir -p ~/.bob/skills
curl -o ~/.bob/skills/promptargs.md \
  https://raw.githubusercontent.com/hivecommons/promptargs/main/.bob/skills/promptargs.md

Quick Start (3 steps)

Step 1: Make some templates

promptargs init

This creates a .prompts/ folder with 3 starter templates. Done!

Step 2: See what you have

promptargs list
Available templates:

  review
    vars: {{file}}  {{focus=correctness}}  {{tone=concise}}

  explain
    vars: {{file}}  {{style=simple}}  {{audience=junior developer}}

  fix
    vars: {{issue}}  {{file}}  {{expected}}  {{actual}}

Step 3: Use a template

promptargs review --file=src/app.ts
Review src/app.ts for correctness issues.
Be concise in your feedback.
Focus on real bugs, not style nitpicks.

Variables with =something already have a default. Variables without one will ask you to fill them in.


How Blanks Work

Powered by Mustache — a logic-less template language available in every major language.

A blank looks like this: {{name}}

A blank with a default looks like this: {{name=hello}} (promptargs extension)

Padding inside the braces is fine too — {{ name }} and {{ name=hello }} mean the same thing, and so does {{ name = hello }} (padding around = is not part of the default).

Names may use letters, digits, _ and - ({{file-name}}). Dotted names such as {{repo.owner}} are rejected with an error — use {{repo_owner}}; the switch names no-interactive, cross, json, status and help are also rejected, as they are reserved for CLI switches. Likewise, the template file names help, env, ui, init, list and show are reserved for the subcommands of the same name; such a template is listed as unreachable by promptargs list (and show warns), because the subcommand always wins.

A default may appear on any occurrence of a blank ({{name}} … {{name=hello}}); if two occurrences give different defaults, the first one wins.

That's the basics. Mustache comments ({{! ignore me}}) work as documented and are safe to use.

Mustache sections ({{#flag}}...{{/flag}}) and partials ({{>name}}) are parsed too, but aren't practically usable yet: promptargs only resolves plain {{name}} / {{name=default}} tags from --flag=value, auto-detected context, defaults, or interactive fill — a section's condition name is never filled this way (so its content is always dropped, or always kept for an inverted {{^name}} section), and partial content is never supplied, so {{>name}} always expands to nothing. Stick to plain blanks and comments; avoid sections and partials for now.

Red means "you need to fill this in"

promptargs show review

Shows the template with red blanks (required) and yellow blanks (has a default).


Fill Blanks Your Way

From the command line

promptargs review --file=main.go --focus=security --tone=detailed

Interactively (just run it, it asks you)

promptargs review
  file: main.go
  focus: (correctness)     ← press Enter to keep default
  tone: detailed

Inline (no template file needed)

promptargs "Explain {{thing}} to a {{audience=5-year-old}}" --thing=recursion
Explain recursion to a 5-year-old

The Fun Part: Arrays! 🎰

Want to review 5 files? Don't run the command 5 times. Use a comma:

promptargs review --file=api.go,auth.go,db.go,cache.go,main.go

promptargs runs the template once for each value and prints each expanded result separated by ---:

Review api.go for correctness issues.
Be concise in your feedback.
Focus on real bugs, not style nitpicks.
---
Review auth.go for correctness issues.
Be concise in your feedback.
Focus on real bugs, not style nitpicks.
---
Review db.go for correctness issues.
...

Status lines showing iteration progress go to stderr:

✅ review [1/5]: file=api.go    focus=correctness  tone=concise
✅ review [2/5]: file=auth.go   focus=correctness  tone=concise
...

Three ways to pass arrays

Syntax What it does
--file=a.go,b.go,c.go Comma-separated list
--file="src/*.go" Glob pattern (expands to matching files)
--file="src/**/*.go" Recursive glob (** matches any depth)
--file=@list.txt Reads one value per line from a file

Multiple arrays: zip vs cross-product

Zip mode (default): arrays pair up 1:1:

promptargs "Review {{file}} for {{focus}}" \
  --file=api.go,auth.go,db.go \
  --focus=security,perf,correctness
✅ [1/3]: file=api.go   focus=security
✅ [2/3]: file=auth.go  focus=perf
✅ [3/3]: file=db.go    focus=correctness

When arrays have different lengths, the number of runs is the length of the longest array, and shorter arrays repeat (cycle around):

promptargs "Review {{file}} for {{focus}}" \
  --file=api.go,auth.go,db.go \
  --focus=security
✅ [1/3]: file=api.go   focus=security
✅ [2/3]: file=auth.go  focus=security
✅ [3/3]: file=db.go    focus=security

Or with two unequal arrays:

promptargs "Review {{file}} for {{focus}}" \
  --file=a.go,b.go,c.go \
  --focus=x,y
✅ [1/3]: file=a.go  focus=x
✅ [2/3]: file=b.go  focus=y
✅ [3/3]: file=c.go  focus=x

Cross-product mode (--cross): every combination of every value:

promptargs "Review {{file}} for {{focus}}" \
  --file=api.go,auth.go,db.go \
  --focus=security,perf,correctness \
  --cross
✅ [1/9]: file=api.go   focus=security
✅ [2/9]: file=api.go   focus=perf
✅ [3/9]: file=api.go   focus=correctness
✅ [4/9]: file=auth.go  focus=security
✅ [5/9]: file=auth.go  focus=perf
✅ [6/9]: file=auth.go  focus=correctness
✅ [7/9]: file=db.go    focus=security
✅ [8/9]: file=db.go    focus=perf
✅ [9/9]: file=db.go    focus=correctness

3 files × 3 focuses = 9 runs. Use --cross when you want the full matrix.


Where Templates Live

Location What it's for
.prompts/ Your project's templates (commit these!)
~/.prompts/ Your personal templates (available everywhere)

Project templates override personal ones with the same name.

Symlinked *.md entries are followed only when their real target is inside the same .prompts/ directory (a sub-directory is fine); a symlink whose target escapes the directory is listed with an error and refused when run, so a cloned repo cannot make promptargs read files elsewhere on your machine. Symlinks to directories and dangling symlinks are skipped. The project .prompts/ entry itself must be a real directory: if it is a symlink, it is refused with a diagnostic on stderr (loader_dir_symlink under PROMPTARGS_LOG=json) and no project templates are loaded (~/.prompts/ is yours and may be a symlink).


Auto-Magic Variables ✨

promptargs discovers variables from two sources:

Run promptargs env any time to preview exactly what's auto-detected in your current shell before you run a template:

promptargs env

1. Git context (always detected)

These variable names fill themselves automatically when you're in a git repo:

Variable Auto-fills with
{{branch}} Current git branch
{{repo}} Repository name
{{org}} GitHub org/user
{{user}} Git config user name
{{date}} Today's date
{{diff}} Staged or unstaged diff
{{pr}} Current PR number

2. Terminal environment (any env var)

Any environment variable in your terminal is available as a template variable. Use the same name:

# If your terminal has DATABASE_URL and AWS_REGION set:
promptargs "Connect to {{DATABASE_URL}} in {{AWS_REGION}}"

In the Builder UI (promptargs ui), click "Show terminal env" to see all available env vars from your shell. Click any one to insert it at the cursor.

Common useful env vars: NODE_ENV, AWS_REGION, CI, USER, EDITOR, PATH, SHELL.

Credential-looking names are intentionally excluded from auto-detection: anything containing TOKEN, SECRET, PASSWORD/PASSWD/PASSPHRASE, CREDENTIAL, API_KEY/ACCESS_KEY/PRIVATE_KEY, BEARER, COOKIE, WEBHOOK, JWT, HMAC, OAUTH, CONNECTION_STRING, or the _-delimited words AUTH, KEY/KEYS, PWD, PAT, PASS, DSN (so GITHUB_TOKEN, SIGNING_KEY, MYSQL_PWD, GITHUB_PAT and SLACK_WEBHOOK_URL are all refused, while PATH, PWD, KEYBOARD and AUTHOR still work). Credential-shaped values are excluded whatever the name: URLs with a password in them (postgres://user:pass@host/db), PEM blocks, JWTs, and well-known token prefixes (ghp_, github_pat_, glpat-, sk-, xoxb-, AKIA…, npm_…, …). A repo-controlled template can therefore never expand {{GITHUB_TOKEN}} or {{DATABASE_URL}} into a secret, even though an explicit --flag=value can still pass any value you choose. This also applies to the Builder UI's "Show terminal env" list.

Priority

You can always override any auto-detected value with a flag. The order is:

  1. --flag=value (explicit, always wins)
  2. Auto-detect from environment (git context and terminal env vars)
  3. Template default ({{name=fallback}})
  4. Interactive prompt — only for variables still unfilled, and only without --no-interactive

A variable that auto-detects or has a default is never prompted for; pass --name=value to override it.


Output Formats

Plain text (default)

promptargs review --file=main.go

JSON

promptargs review --file=main.go --json

Status line (for IDE integration)

promptargs review --file=main.go --status
✅ review: file=main.go  focus=correctness(default)  tone=concise(default)

Builder UI

Don't want to memorize flags? Open the visual builder:

promptargs ui

Opens a browser at http://localhost:3700/?t=<token> — the token is random per run and printed at startup — with:

  • Preset examples — 8 clickable templates (review, explain, fix, test, migrate, security, docs, refactor)
  • Template editor — type your template, variables auto-populate below
  • Variable table — set values, arrays (comma-separated), and sources (manual, glob, @file)
  • Environment panel — git context vars + all terminal env vars, click any to insert {{var}} at cursor
  • Mustache cheatsheet — collapsible syntax reference for template features
  • Zip / Cross toggle — switch array iteration mode
  • Live preview — see expanded output update as you type
  • CLI command — copy the generated command with one click

Use --port=N if 3700 is taken:

promptargs ui --port=4000

Privacy & Diagnostics

promptargs does not collect or export telemetry. There is no telemetry SDK, metrics endpoint, or observability backend configured anywhere in this package — nothing you run ever phones home.

All runtime diagnostics are human-readable console output (CLI status/errors, UI startup/port messages). The Builder UI (promptargs ui) binds to loopback only (127.0.0.1), rejects requests with a non-local Host header, and only serves the page to a client presenting the random per-run token in the URL it prints at startup, so the page — including the auto-detected Git and terminal environment values it embeds (see Auto-Magic Variables) — is never reachable from other machines on your network, nor from other accounts sharing the same machine's loopback interface.

Opt-in structured diagnostics: set PROMPTARGS_LOG=json to make diagnostics (CLI errors and iteration progress, UI Host and token rejections, asset failures, unreadable or symlinked prompts directories, and one line per UI request) print as JSON lines on stderr with a fixed field set: event, plus status, duration_ms, error_class (usage, not_found, io, port_in_use, unknown), count, current, total where relevant. Template contents, argument values, paths and error messages are never logged. When unset, output is unchanged. Nothing is sent over the network.

If observability is ever added in the future, it will be explicit opt-in, bounded to local diagnostics, and will exclude environment values, template/prompt content, file paths, diffs, and user identifiers. No network exporter will be added unless you explicitly configure a backend yourself.


Use as a Library

The package also exports its building blocks as an ES module (Node.js 22+). Install it first with npm install @hivecommons/promptargs.

import { parseVars, expand } from '@hivecommons/promptargs';

const template = 'Review {{file}} for {{focus=correctness}} issues.';

parseVars(template);
// [{ name: 'file', ... }, { name: 'focus', defaultValue: 'correctness', ... }]

expand(template, { file: 'src/app.ts', focus: 'security' });
// 'Review src/app.ts for security issues.'

expand(template, { file: 'src/app.ts' });
// 'Review src/app.ts for correctness issues.'  (defaults fill missing values)

Variables with no value and no default are left as {{name}} in the output. Other exports: autodetect, autodetectAll, AUTODETECT_VARS, loadTemplates, findTemplate, resolve and renderStatus.


Write Your Own Template

Create a file in .prompts/ with any name ending in .md:

.prompts/buddy.md

Hey {{name=buddy}}! Can you help me understand {{topic}}?
I learn best with {{style=examples and analogies}}.

Now use it:

promptargs buddy --topic="async/await"
Hey buddy! Can you help me understand async/await?
I learn best with examples and analogies.

Use with AI Coding Tools

Pipe to any CLI

promptargs works with any AI tool that accepts piped input:

# Claude
promptargs review --file=src/main.ts --no-interactive | claude -p "do this review"

# Goose
promptargs review --file=src/main.ts --no-interactive | goose run

# Copilot CLI (needs the standalone `copilot` CLI, installed and signed in)
copilot -p "$(promptargs review --file=src/main.ts --no-interactive)"

As a skill inside your AI tool

If you installed the skill (see Install), use it directly inside your session:

Claude Code:

/promptargs review --file=src/api.ts

Goose / Bob: The skill is automatically available after install — ask your agent to "use promptargs" or run a template.

Copilot: Installed skills load automatically; ask Copilot to use promptargs.


Cheat Sheet

Command What it does
promptargs ui Open the visual builder in your browser
promptargs init Create .prompts/ with examples
promptargs list Show all available templates
promptargs show review Preview template with highlighted blanks
promptargs review --file=x Run template with flags
promptargs review Run template interactively
promptargs "inline {{var}}" Use inline template
promptargs env Show auto-detected variable values
--no-interactive Skip questions, use defaults only
--cross Cross-product mode (all combinations)
--json Output as JSON
--status Output status line only
... | claude -p "do this" Pipe to Claude
... | goose run Pipe to Goose

The switch names no-interactive, cross, json, status and help are reserved and cannot be used as template variable names. Switches take no value: --json=false is rejected with a usage error. A flag that names no blank in the template (e.g. a typo like --fiel=x) is also an error. So is a blank left unfilled under --no-interactive or piped stdin: promptargs exits 1 with Missing value for {{name}}: ... rather than printing the raw placeholder.


Contributing & Community

promptargs is part of Hive Commons, a family of tools for working with AI agents.


License

Apache-2.0

Pirate flag icon: Twemoji, CC-BY 4.0.

About

Template expansion for AI prompts - variable substitution for Claude Code, Copilot, Goose, Bob, and more

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages