prompt2: AI CLI¶
Transform your terminal into an AI powerhouse ⚡ Craft dynamic prompts, automate workflows, and generate code at lightspeed - all through CLI sorcery 🧙♂️
Why prompt2?¶
🎮 Prompt Engineering Playground: Iterate faster than ChatGPT can say “hallucination”
🖥️ Terminal Native: Feels like home for vim-wielding keyboard warriors
✨ Jinja2 Turbocharged: Magic variables like
{{reads}},{{writes}}, and custom filters💸 Cache Commander: Automatic response caching slashes API costs by 40%+
🔌 Plugin Ecosystem: Extend with Python simplicity (backends, parsers, custom filters)
🚀 Blazing Start¶
# litellm pulls a lot of dependencies I don't need in my air-gapped networks
# as such, you need to pull it manually to use the litellm plugin:
pip install prompt2 litellm
export OPENROUTER_API_KEY=sk_...
prompt2 edit hello
prompt2 send hello
prompt2 is the AI CLI where YOU control the narrative.
Note
🔌 LiteLLM pros: Use any supported ENV vars instead of OPENROUTER_API_KEY
🕹️ Tutorial Time!¶
run
prompt2 edit hello, this will open your$EDITOR, there, type something likecreate a hello world in python. This created a prompt in ~/.prompt2/prompts/hello.txt and you can see it withprompt2 list.run
prompt2 send hello, you will see the AI responded with a tutorial to create a python script.run
prompte2 send hello wholefileto use the wholefile parser with that prompt, you will see that you only get the python source code as outputrun
prompt2 send hello wholefile > hello.py, you will see that prompt2 caches responses to save costs, so the second time running that command is very fastrun
prompt2 edit hello, change your text with:update this script to also print any sys arg with the hello string {{read('hello.py')}}
Hell yes, we’re using jinja2 inside the prompt and telling it to actually read the content of the file, you’ll be able to register your own jinja functions and template paths too!
run
prompt2 send hello wholefile > hello.pyprofit
I know prompt-fu
Troubleshooting¶
DEBUG=1 prompt2 send hello # See the magic behind the curtain
Models¶
Juggle between multiple models by defining environment variables.
- MODEL¶
An environment variable to configure the default LLM plugin, as far as the base plugin is concerned, it must be in the following format:
[<code2 backend plugin>] [<args>...] [<kwarg=value>...]
When the backend plugin is omited, or not found,
litellmis used by default which should be fine in most cases, leveraging the env vars you already have say for aider or anything using litellm, such as$OPENAI_API_KEY,$OPENROUTER_API_KEY, etc. For the litellm backend, the first arg is the model name and kwargs are optional.The default value for MODEL env var is:
MODEL=openrouter/google/gemini-2.5-pro-exp-03-25:free
You can add litellm kwargs as such:
MODEL='openrouter/deepseek/deepseek-chat:free max_tokens=16384'
- MODEL_name¶
MODEL_nameis not an actual variable, it represents other models configurations that you can have, ie:MODEL_EDITOR='deepseek/deepseek-chat:free max_tokens=16384' MODEL_ARCHITECT=xai/grok-2-latest
While you will be able to use the model names you want in your own code2 workflow plugins, code2 core prompt plugins will try to use the best one that is defined for a purpose based on standard names, “editor” and “architect” at this time. https://aider.chat/2024/09/26/architect.html
Python API¶
Script¶
You could script like this:
from prompt2 import Model, Prompt
async def ai_wizard():
# get the model defined in $MODEL_ARCHITECT, or $MODEL, or the default
model = Model('architect')
# load one of your prompts by name
prompt = Prompt('hello')
# call the model with the prompt, with a parser by name
result = await model(prompt, 'wholefile')
# there you go
with open('hello.py', 'w') as f:
f.write(result)
import asyncio
asyncio.run(ai_wizard())
Test¶
The pytest plugin was pretty easy to write, given that prompt2 already does
response caching. Just add the prompt2_env fixture in your pytest script
and it will:
store the cache in your repo instead of home dir, so that it can find it again
store prompts in temporary directories, so that you can mess with them
Plugins¶
prompt2 supports a bunch of plugin types, all registered by Python’s standard entry points.
- parser: those define 2 functions:
one allowing to change the messages before sending, such as to add something like “reply with a structured list”
another to convert the string reply into a python variable, ie. to parse a list, file, diff …
jinja2: register custom functions to have in your templates
backend: custom APIS backend: you probably won’t need that, the default litellm backend works great, but if you’re using prompt2 behind an air-gapped network with a custom AI API, at least you’re able to register another backend.
Jinja functions¶
Functions that will be exposed in Jinja2.
Add yours over the prompt2_jinja2 entry point plugin!
Note that all prompt paths are added to the Jinja2 loader, so, you can already include your prompts in your prompts:
{% include('your_prompt.txt') %}
Or even go crazy with {% extend %} and {% macro %}!
- prompt2.jinja2.dirs(path=None)[source]¶
Show the list of directories within a path.
Renders:
Directories: - path/to/directory1 - path/to/directory2
- Parameters:
path – Path to walk
- prompt2.jinja2.exec(*command, **env)[source]¶
Execute a command and return the full output.
- Parameters:
command – String or args list.
- prompt2.jinja2.file(path)[source]¶
Show a file path content with context markers.
It will render:
path/to/file source code: ``` <source code here> ```
- Parameters:
path – File path
- prompt2.jinja2.files(path=None)[source]¶
Show the list of files within a path.
Renders:
Files: - path/to/file1 - path/to/file2
- Parameters:
path – Path to walk
CLI Reference¶
prompt2¶
-
¶prompt2 Template based prompts on the CLI
Sub-Command |
Help |
|---|---|
|
Get help for a command or group |
Ask a question from the CLI |
|
Return prompt paths |
|
List available prompts |
|
Edit a prompt |
|
Show a prompt |
|
Render a prompt with a given template context |
|
Show a parser |
|
List registered parsers |
|
Render prompt messages with a given template context |
|
Send a prompt, rendered with a context, on a model with a parser |
prompt2 ask¶
-
¶prompt2 ask [ARGS]... [parser=PARSER] [model=MODEL] Function:
prompt2.cli.ask()Ask a question from the CLI
Example:
prompt2 ask write a hello world in python
Argument
Help
[ARGS]...Question to ask
Required: True
usage: Any un-named arguments, ie.:
something other
[parser=PARSER]Parser name if any
Required: False
Default: None
[model=MODEL]Model name to use, if any
Required: False
Default: None
prompt2 paths¶
-
¶prompt2 paths Function:
prompt2.cli.paths()Return prompt paths
prompt2 list¶
-
¶prompt2 list Function:
prompt2.cli.prompts()List available prompts
prompt2 edit¶
-
¶prompt2 edit NAME [local] Function:
prompt2.cli.edit()Edit a prompt.
$HOME/.prompt2
Argument
Help
NAMEPrompt name.
Required: True
[local]Enable this to store in $CWD/.prompt2 instead of $HOME/.prompt2
Required: False
Type: bool
Default: False
Accepted: yes, 1, true, no, 0, false
prompt2 show¶
-
¶prompt2 show PROMPT Function:
prompt2.cli.show()Show a prompt
Argument
Help
PROMPTPrompt name
Required: True
prompt2 render¶
-
¶prompt2 render PROMPT [CONTEXT=VALUE]... Function:
prompt2.cli.render()Render a prompt with a given template context.
Argument
Help
PROMPTPrompt name
Required: True
[CONTEXT=VALUE]...Context variables.
Required: True
Usage: Any number of named self.arguments, ie.:
something=somevalue other=foo
prompt2 parser¶
-
¶prompt2 parser NAME Function:
prompt2.cli.parser()Show a parser
Argument
Help
NAMEParser name to display
Required: True
prompt2 parsers¶
-
¶prompt2 parsers Function:
prompt2.cli.parsers()List registered parsers
prompt2 messages¶
-
¶prompt2 messages PROMPT [parser=PARSER] [model=MODEL] [CONTEXT=VALUE]... Function:
prompt2.cli.messages()Render prompt messages with a given template context.
Argument
Help
PROMPTRequired: True
[parser=PARSER]Parser name if any
Required: False
Default: None
[model=MODEL]Model name to use, if any
Required: False
Default: None
[CONTEXT=VALUE]...Context variables.
Required: True
Usage: Any number of named self.arguments, ie.:
something=somevalue other=foo
prompt2 send¶
-
¶prompt2 send PROMPT [parser=PARSER] [model=MODEL] [CONTEXT=VALUE]... Function:
prompt2.cli.send()Send a prompt, rendered with a context, on a model with a parser.
Argument
Help
PROMPTPrompt name
Required: True
[parser=PARSER]Parser name if any
Required: False
Default: None
[model=MODEL]Model name to use, if any
Required: False
Default: None
[CONTEXT=VALUE]...Context variables
Required: True
Usage: Any number of named self.arguments, ie.:
something=somevalue other=foo
Why I Built prompt2 Instead of Sticking with aider-chat¶
Let’s face it: someone is going to wonder. When I first explored aider-chat, I saw its potential, but over time, I found it didn’t align with my workflow or goals. Here’s why I decided to forge my own path with prompt2:
Flexibility with APIs: aider-chat leaned heavily on litellm, which made it tricky to integrate custom APIs—especially in air-gapped networks. I searched GitHub for plugin support and found an issue where users requested it, only to be told it wasn’t a priority. Plugins are a simple, powerful feature in Python, and I wanted a tool that embraces them fully.
CLI Over Prompt Toolkits: I grew tired of prompt-toolkit. I’m a fan of bash and the classic CLI experience—why reinvent something that already works well?
Control Over Commits: I didn’t like how aider-chat committed changes behind my back. I prefer to stay in charge of my version control flow.
Customizable Style: The fixed style of aider-chat wasn’t my taste. With
cli2.theme, I’ve made theming a core feature—because who doesn’t love a tool that looks the way they want? Heck, you can evenimport prompt2and do your python-click CLI if you really dislike cli2.Light Configuration: aider-chat relied on heavy configuration, which felt overkill. I love environment variables (hello, 12-factor apps!), but I also want the freedom to tweak settings from the CLI and only save them to a file when I choose to, in
export VAR=format.Owning My Tools: I’ve always preferred building my own CLI tools. Sure, they have quirks, but they’re mine. With cli2, extending and adapting them is a breeze, and I can make them as dynamic as I need.
Project Independence: I wanted a tool that wasn’t tied to a specific git project. prompt2 lets you work with code anywhere, whether it’s part of a repository or not.
In short, prompt2 is my take on a lightweight, flexible, and CLI-driven alternative. It’s built for people like me who value simplicity, control, and the ability to make a tool their own. If that resonates with you, give it a try!