Quickstart
Zero to a working CLI in five minutes. The TOML format is identical across Python and Node — only the install step and the import differ.
1. Install
pip install runspec
npm install runspec-node
2. Scaffold a project
runspec init creates runspec.toml and a working code stub. The Python
CLI also has --write-project to lay down the surrounding project files
in one go.
mkdir greet && cd greet
runspec init --name greet --write-project
You get:
. ← parent dir (cwd before --write-project)
├── pyproject.toml ← entry point already wired up
├── .gitignore
├── CLAUDE.md
└── greet/
├── __init__.py
├── runspec.toml ← lives inside the package, not at the project root
└── greet.py ← parse() call ready to go
--write-project writes pyproject.toml to the parent directory (use
--project-dir to override). Add --example to scaffold worked
clean + scan runnables alongside, with confirmation prompts and
conditional deletion you can read for ideas.
mkdir greet && cd greet
npm init -y
npm install runspec-node
npx runspec init --name greet
You get:
greet/
├── package.json
├── runspec.toml ← the interface
└── greet.ts ← parse() call ready to go
Add the bin entry to package.json so the runnable is on PATH after
npm install:
{
"bin": { "greet": "./dist/greet.js" }
}
Compile (tsc or whatever your build is) so dist/greet.js exists.
3. Fill in the logic
The init scaffold already wires up parse(). Just write your runnable.
# greet/greet.py
from runspec import parse
def main():
args = parse()
message = f"Hello, {args.name}!"
if args.loud:
message = message.upper()
for _ in range(args.times):
print(message)
if __name__ == "__main__":
main()
// greet.ts
import { parse } from 'runspec-node';
function main(): void {
const args = parse();
let message = `Hello, ${args.name as string}!`;
if (args.loud) message = message.toUpperCase();
for (let i = 0; i < (args.times as number); i++) console.log(message);
}
main();
The interface itself is in runspec.toml:
[greet]
description = "Greet someone from the command line"
autonomy = "autonomous"
[greet.args]
name = {type = "str"}
loud = {default = false}
times = {default = 1}
Entry-point name must match
The runnable section name ([greet]) must match the binary name on
PATH. Python uses [project.scripts] in pyproject.toml; Node uses
bin in package.json. runspec init wires this up for you.
4. Install and run
pip install -e .
greet --name Ada --loud --times 3
npm install
npm run build # if you have a build step
greet --name Ada --loud --times 3
Either way:
HELLO, ADA!
HELLO, ADA!
HELLO, ADA!
greet --help works out of the box — no help text written:
Usage: greet --name <str> [--loud] [--times <int>]
Greet someone from the command line
Arguments:
--name (str, required)
--loud (flag, default: False)
--times (int, default: 1)
Autonomy: autonomous
-h, --help Show this message and exit
5. See what validation gives you
Missing required argument:
$ greet
✗ Missing required argument: --name
Type: str
Wrong type:
$ greet --name Ada --times abc
✗ Cannot coerce value 'abc' to type 'int' for argument '--times':
invalid literal for int() with base 10: 'abc'
Bad choice (with a fuzzy suggestion):
$ greet --name Ada --format yml
✗ Invalid value for --format: 'yml'
Expected one of: json, csv, parquet
Did you mean: json?
No argument parsing code. No error handling. Just your runnable and a TOML.
6. Try the worked examples
cd /tmp && mkdir runspec-sandbox && cd runspec-sandbox
runspec init --example --write-project
pip install -e .
clean --help
scan --help
--example scaffolds two runnables — clean and scan — that demonstrate
confirmation prompts, conditional deletion, autonomy escalation, and
agent-aware output. Read them as a learning sandbox.
7. See it as an agent tool
runspec local # list installed runnables, validate setup
runspec local --format mcp # emit MCP tool schemas
runspec serve # start the live MCP stdio server
Wire runspec serve into Claude Desktop, Cursor, or any MCP host once —
every runspec-aware package installed in the environment is immediately
available as a tool. See Agent Integration for the wiring.
Next steps
- Format Reference — every field, every option
- Logging — add
[config.logging]and get rotation, JSON file logs, sensitive-data redaction, and structuredextrafields - CLI Reference —
init,local,serve,jumpflag by flag - Python Library / Node Library — full API reference