Repository navigation
Releases: vlastimilbures/promptmend
Release list
v0.25.0
Changed
setupon a terminal always opens the full-screen wizard:--providerand--profile
preselect that choice,--deployputs the focus on the install button, and--no-deploy
and--no-smoke-testleave out those steps, instead of switching to a line-by-line mode (#244).--plainis now
the same as--non-interactive; line-by-line questions are gone, and without a terminal
(stdin or stdout)setupexits 3 unless one of them is given.- The interface reloads faster after each change: it asks Espanso for its folder and runs
each launcher-lookup command once per reload instead of up to three times (#242). promptmend --helpdescribes the tool in one sentence instead of a developer note, and
theconfig,profilesandhistorycommand lists giveconfig show,migrate,
rollback,profiles migrateandhistory prunea one-line summary; each
command's own--helpkeeps the full text (#247).
Removed
- The
shellcommand, the terminal command line with completion and live help (#239). Run
each command in your terminal as it is, or use the interface. - Home's command line in the interface (the
ckey) and its Recipes… button (#239). Every
button's tooltip and every result still show the command it runs; Home still lists this
session's commands and a few useful ones, and Copy last copies the latest. - The
prompt-toolkitdependency, used only by theshellcommand (#239). - The interface's intro screen and its setting
PROMPT_UI_INTRO(#245): the interface opens
straight on Home. A saved or environmentPROMPT_UI_INTROis now ignored (doctor and
config validatedo not flag it;config unsetrefuses it, so delete a leftover line in
config.tomlby hand), andpromptmend ui --no-introis still accepted and does nothing. The Settings tab loses its Interface group. - The Diagnostics tab's import-time check of the CLI (#245).
- The Diagnostics tab's table of every setting and its source (#245); the Settings tab and
promptmend config showlist each setting and where it comes from, andconfig showalso
names what each value overrides. - The pre-1.0 migration code (#246):
config retire,config migrate --from,
setup --migrate-from, the interface's Previous install screen (Home's "Previous install…"
and the setup wizard's "Earlier install…"), and doctor'sprevious_installcheck (its
warning about anotherpromptmendfirst onPATHstays, aspath_shadow). To move from
a checkout install, copy its.envinto the config folder and runconfig migrate, and
copy edited profiles withprofiles migrate --checkout PATH(docs/install.md, "From a
checkout install").
config rollbackstill puts back a checkout's.envthat an earlierconfig retiremoved. - Deploy no longer retires the
match/base.ymlthat versions before 0.9 installed (#246): if
it is still there, delete it by hand;promptmend espanso statuslists it as yours.
Fixed
- Usage history: a recorded run and
doctorreadhistory.spoolandhistory.lostonly
under their lock, so on Windows they can no longer make another run's update of those files
fail and lose that record, and a run that started before a management command moved the
oldprompt-workflowdata folder no longer creates that folder again (that run's record is
dropped) (#241). - A TLS certificate failure (a corporate proxy, a self-signed endpoint) is reported at once
instead of being retried a second later: the retry could not succeed, delayed the marker
and added a second record to the usage history (#247). - Setup's Done step no longer says "Match files: not installed" when the only files that
differ are ones you edited and kept, which the Espanso step already says it leaves alone;
it says how many of them were kept. - Setup's Next no longer gets stuck on the provider, profile and API keys steps while
PROMPTMEND_ENVis set: it says the choice is not saved (settings stay in that file) and
moves on; the keys step and the Done checklist say to add each key to that file. - Setup logs
config unsetwhen picking the default removed the setting, instead of a
config setit did not run. - The Profiles tab's "Migrate from checkout" asks for the checkout's folder when none was
found, instead of naming a--checkoutflag the interface cannot pass. - Saving a setting or a key no longer replaces a symlinked
config.tomlorsecrets.toml
with a plain file: the file the link points to is written, so a copy kept in a dotfiles
repo keeps getting the changes (#236). improve --copyno longer loses the rewrite when the copy fails: the error marker is
followed by the rewrite, as withPROMPT_OUTPUT=clipboard, and the history records
clipboard_failed.- A reply in a code fence that the model then cut off (output limit) or stopped early now
has its fence removed too, when the closing fence arrived before the stop; the note that it
stopped early stays at the end.--copycopies the rewrite without that note, as
PROMPT_OUTPUT=clipboarddoes. - An unexpected error's marker, or a library's
ValueError, no longer pastes a key its
message quotes: such a word is described (<redacted, N chars>) instead, and the whole
message when the secret spans several words (Bearer …,password: …, a spaced card
number). A failed copy's message is treated the same way. improve --source stdinreads at most one character past the 50,000-character limit, so
piping a huge file is refused at once (Input is too long (over 50000 chars)) instead of
being loaded whole first.promptmend config rollbackputs each.envback before it movesconfig.tomland
secrets.tomlinto the backup, so a trigger that fires during the rollback, or a.env
that cannot be put back, still runs on the saved settings and keys instead of the defaults
(#238).- When
promptmend config migratefails and cannot undo one of its own writes, the error
now names the reason the migration failed, thencould not undo: <file>, instead of only
the error of the undo step. The migration record is then kept, and the message asks to
runpromptmend config rollback, which removes what the migration left (#238).
v0.24.0
Added
promptmend setupin a terminal now opens a full-screen setup, one step at a time:
welcome, earlier settings (only when there is a.envor an earlier checkout install),
provider and profile picked from a list that says what each is and which triggers use it,
the API keys, Espanso's match files in words (not installed yet, up to date, you edited it)
with an install button, the stub test, and a checklist of what is left with how to try
-i-. Home's "Setup…" opens it, and a barepromptmendopens it by itself once while
nothing is set up.setup --plain(or any of--provider,--profile,--migrate-from,
--deploy/--no-deploy,--no-smoke-test) asks line by line as before;
--non-interactiveis unchanged.
Fixed
setupasks for every key the triggers need, not only the chosen provider's: picking
Ollama no longer skipsOPENROUTER_API_KEY, which-i-still needs, and no longer ends
with "Setup finished" while-i-cannot work.setuprun again with the same choices no longer rewritesconfig.toml.
Changed
setupline by line lists the providers and profiles with what each does, shows each
match file's state in words, says "Not installed" (not "Dry run") when you answer no, and
ends with how to try-i-or how many things are left to do.
v0.23.0
Changed
config showand the other commands that list settings show a key only as<set>or
<not set>, no longer with its length (#222).- Interface: in the Settings list,
u(asconfig unset) puts a setting back to its default
or opens Remove key;rnow reloads there too, as on every other tab, so a reload can no
longer undo a setting (#215). - Faster start, most of all on Windows with endpoint scanning (#223):
promptmend --version
answers before the CLI loads (108 modules instead of 221 on macOS), and the triggers load
httpx only for the provider call and the clipboard library only when they touch the
clipboard (149 modules up to the call instead of 221). - The install scripts (
install.ps1,install_windows.ps1,install_macos.sh) compile the
bytecode at install, so the first run after an install is no longer the slow one (#216). doctor'skeyscheck names the triggers that need a missing key, read from the match
files Espanso has (or, before a deploy, the ones a deploy would write):-i-, -iok-, -ip-, -if- use OpenRouter, but OPENROUTER_API_KEY is not set.PROMPT_PROVIDERonly counts for a
barepromptmend improveand the Try tab, and alone it is a warning; its data gains
triggers(#217).- Interface:
PROMPT_PROVIDERmoved from Output to the Models group of the Settings tab, and
its help says what it affects (a barepromptmend improveand the Try tab) (#217). doctor's newlocal_serverscheck (information only) and the Triggers tab's State column
sayneeds Ollama/needs LM Studiowhen a deployed local trigger's server does not
answer. It asks only a server on this machine, with one short request; the deploy is
unchanged:-il-and-ilm-stay active,-ic-stays commented out (#220).promptmend statsprints one line on the history before the numbers; the full explanation
and the notes moved to--verbose(the JSON is unchanged). A real Try tab run is listed as
trigger-try-, and a barepromptmend improveasno trigger (direct call)instead of
(none)(#222).- Interface: Home shows "Previous install…" only while there is one to offer (#222).
install.ps1's next steps namepromptmend(the interface) too (#222).
Fixed
doctorno longer warns about the launcher on Windows when the match files call the same
path written with/(or in another case) as this install (#212).- The deployed
prompts-template.ymlno longer has the CLI path pasted into its header
comment (#214). - Interface: the Triggers tab shows the new states (
in sync, ormissingafter a detach)
as soon as a deploy or detach has written the files, without pressingrand without
waiting for Espanso's restart (#215). - Usage history: a write that misses its time budget (the first one on a slow machine, or
under antivirus or endpoint scanning) is no longer lost. It waits inhistory.spoolin the
data folder (the same metadata columns, at most 100 records) until the next recorded run,
promptmend stats,promptmend history exportorpromptmend doctorstores it.
history.lostnow counts only records that could not be kept at all. doctor'shistory
warning says why and what to do, and its data gainsspooled(#213). doctor'sclicheck names the.exeon Windows, as theinstallcheck does (#222).doctorsaysno calls yetinstead of0 call(s) recorded in …while the history file
does not exist (#222).- The deploy diff (
espanso status --diff, Triggers "Show diff") says(missing)instead of
(on disk)for a file that is not there yet (#222). - Windows: PromptMend no longer reads Espanso's output, which
espansod.exewrites to the
terminal instead of to PromptMend (#209, #210, #211). The config folder is worked out as
Espanso does (a portable.espansofolder next to the runningespansod.exe, else
%APPDATA%\espanso), so a portable Espanso gets the triggers; "running" is Espanso's
named pipe, sodoctorno longer calls a stopped Espanso running; Espanso is found in the
running process, on PATH, in%LOCALAPPDATA%\Programs\Espansoor its uninstall entry,
so a stale Windows Terminal PATH no longer reports it missing. A restart after a deploy
runs Espanso in a hidden console of its own: quitting the interface no longer hangs, and
Ctrl+C or closing the terminal no longer stops Espanso.espanso.cmdis not used any more,
which also saves about half a second per Espanso call.
Documentation
- Troubleshooting: slow start on Windows, with bytecode, an antivirus exclusion or a Dev
Drive for uv's folders (UV_TOOL_DIR,UV_PYTHON_INSTALL_DIR) (#223, #216). - Usage: the rule for which triggers a deploy turns on, and what a local trigger needs (#220).
- Install: why
prompt-workflowis installed next topromptmend, and that it goes in 1.0.0
("Update", "Uninstall"; #221). - Troubleshooting:
doctor's daily update check (update-check.json,
PROMPT_UPDATE_CHECK=false) and its local server check (#222). - Install: "Espanso on Windows": open Espanso once from the Start menu and accept its PATH
and autostart offer, close every Windows Terminal window before the firstpromptmend
command, and where a portable Espanso keeps its configuration (#218). - Troubleshooting: on Windows,
promptmendorespansonot recognized right after an
install, and two Espanso instances after installing next to a portable copy (#218). - Usage: the trigger list gives each trigger's key and whether the draft leaves the machine,
and links the data-protection gate and-iok-; "The -if- form" lists the shipped models
and howmodel@endpointsets the endpoint pin (#219). - Neutral wording:
-risk-is described as a risk-analysis scaffold, and the gate's ID
check as national ID numbers (Vietnamese formats only today); the match files and patterns
are unchanged (#219).
v0.22.0
Added
- A portable Windows build (#185): each GitHub Release now carries
promptmend-<version>-windows-x64.zip, the CLI frozen with PyInstaller (a folder with
promptmend.exeand its_internalfolder, its own Python included), attested like the
other release files. It is the base of the coming WinGet packagevlastimilbures.PromptMend;
until that is published it is a preview (unsigned). See docs/install.md, "Portable zip". doctorknows thewingetinstall channel (the WinGetLinksalias, else the package
folder, neither holding the version, so the match files survivewinget upgrade) and names
winget upgrade vlastimilbures.PromptMendwhen a newer release exists. Itsclicheck data
gainsfrozen. A frozen exe still in a temporary or versioned folder (straight from the
zip orDownloads\promptmend-<version>-windows-x64) is refused as a launcher, with a hint
to move it to a fixed place such as%LOCALAPPDATA%\Programs\promptmend.
Changed
- In the frozen build, the interface's command line,
promptmend shelland the setup check run
the exe itself instead ofpython -m promptmend.cli, and the interface's import check says
it does not apply. - WinGet manifests for the coming package
vlastimilbures.PromptMend(#185) are prepared:
CI validates them and installs the Windows zip throughwingeton every pull request. The
package is not on WinGet yet.
v0.21.0
Added
promptmend shell(#183): a plain command line for the terminal with the completion and
live help of the interface's command line (Tab completes, the bar below explains the line).
Enter runs the command in the same terminal, soespanso deploy,config migrateand
secrets set NAMEask there as usual.improve,personaandshellare refused. A
line that holds a key or gives a key's value is never run, and Enter replaces it on screen
with<value withheld>; the history (memory only) keeps only lines that ran. It needs a
terminal (exit 3 otherwise). New dependency: prompt_toolkit, loaded only byshell. See
docs/commands.md, "shell".- An update check (#197):
promptmend doctorand a new first Home row,Version, say
whether a newer release exists (0.22.0 available); doctor and the About screen give the
update command for your install channel (for example
brew update && brew upgrade promptmend). It is information only: doctor never warns about
it, and Home stays "Ready". It asks pypi.org once a day, and at most once an hour after a
failure (cached inupdate-check.jsonin the data folder), within 3 seconds, never from a
trigger; pypi.org sees your IP address and a User-Agent naming PromptMend. New setting
PROMPT_UPDATE_CHECK(defaulttrue);false, a value it rejects or a settings file it
cannot read makes no request.doctor --jsonadds
latest,update_availableandchecked_atto theversioncheck. See docs/privacy.md,
"Update check".
Changed
- The interface's tab
2 Providersis now2 Settings(#199): every setting and both API
keys in one keyboard list, grouped (Output, Keys, Privacy, Models, History, Interface), each
with a dot (a green●for a switch on, a key set or a value that is not the default; a
grey○otherwise), its value and the file it comes from. Up/Down (orj/k) move, Space
switches a true/false setting, Enter picks from a list or edits the value (checked as the
CLI reads it, in the dialog),rresets it (config unset),/filters. On a key's row,
Enter andropen the Set key and Remove key dialogs. The persona shows only as
<set, hidden>. Each change logs itspromptmend config set|unset …command on Home. The
"Change setting", "Set key" and "Remove key" buttons are gone; the provider overview and
thePROMPT_LOCAL_ONLYsentence moved to the Diagnostics tab. Home's Output row now names
the tab that switches it, so paste or clipboard output is one Enter away (#198). See
docs/interface.md, "Settings tab". - The interface's command line also refuses
config set|get|unsetwith a key's name and a
value, orconfig setwith a key-like value, before the value reaches a child's argv. - A value your
PROMPT_EXTRA_PATTERNSmatch now counts as a key wherever a command checks
for one:config setrefuses it,config getandconfig showdo not print it, and the
command lines of the interface andpromptmend shellwithhold it. The pattern setting
itself is never hidden by its own patterns.
Fixed
- The usage history uses SQLite's WAL journal only on SQLite 3.51.3 or later (#196). Before
3.51.3, WAL could rarely reset a database, and the SQLite bundled with uv's and Homebrew's
Pythons is older, so the history now keeps the rollback journal there and switches an
existing WAL database back on its next recorded run.promptmend doctor'ssqlitecheck
is OK on such a SQLite ("SQLite 3.50.4, rollback journal") instead of warning on every
install, so Home says "Ready"; it warns only while a database is still in WAL on an old
SQLite, and an unreadable version is INFO. The check's data gainsjournal_mode, and the
interface's Diagnostics tab shows it.
Documentation
- README is simpler (#200): no ASCII logo, a flow diagram (
docs/flow.mmd, rendered into a
light and a dark SVG byscripts/render_diagram.py) instead of the text one, one-sentence
highlights, only the Home screenshot, the example collapsed, and one install command per OS.
The channel and update/uninstall tables are in docs/install.md, whose "Update" section now
also has the Windows redeploy note for upgrades from 0.20.0 or earlier (#18). - The benchmark figures behind CONTRIBUTING's "Known gaps in the default prompt" moved to
docs/benchmark.md ("Per-draft findings of the 2026-10 round"), its only home; the gaps link
there. Gap 10 now says the v0.19.0 prompt copies a short pasted email on flash-lite.
v0.20.1
Fixed
- Windows: every trigger that runs the CLI (
-i-,-iok-,-ip-,-if-,-il-,-ilm-,
-ic-and-p-) failed under Espanso and pasted[Espanso]: An error occurred during rendering(#18). Espanso runs atype: shellvar through PowerShell on Windows, which
cannot run a quoted path followed by arguments. The match files now call the CLI from
type: scriptvars: Espanso starts the CLI directly with an argument list, no shell, on
every system. After upgrading, runpromptmend espanso deployto replace the deployed
match files (it recognises the old ones as its own). A trigger of your own copied from an
older deployed file keeps the shell shape: rewrite it as shown in docs/usage.md, "Your own
triggers". improveandpersonaprint nothing on stderr, not even a Python warning or a library's
message (they discard it), since a script var treats any stderr output as a failed
expansion. A crash still exits nonzero, so Espanso still reports it. Thepromptmendandprompt-workflow
commands now start throughpromptmend.entry:main.promptmend espanso deployno longer refuses a CLI path holding$, a backtick or (on
Windows)% ^ & | < >, which no shell reads any more. It still refuses a quote, a
backslash, a control character, and what Espanso itself would replace in the path:{{
(its variables) and%HOME%,%CONFIG%and%PACKAGES%.
Documentation
- The Homebrew install adds
brew tapandbrew trust --formula: recent Homebrew refuses
to load a formula from an untrusted third-party tap.
v0.20.0
Added
- The interface's Home tab has a command line (#111): press
cfrom any tab and type a
promptmendcommand. It completes commands, options, setting names (afterconfig set,
getandunset; never a key name there), key names (only aftersecrets setand
secrets remove) and profile names, and shows the command's usage and help as you type, or
the usage error the CLI would print. Up and Down go back through this session's lines;
Escape leaves it. A line that looks like it holds a key is never shown back or kept in that
history. The footer showsc Command. - Enter on that command line acts on the command (#111). A command that reads, previews,
changes a setting or writes an export, asking nothing (doctor,config show|get|set|unset|validate,secrets status,profiles list,
stats,espanso status,history export, any--dry-run,--helpor--version) runs
in a separate process, one at a time, with no input and at most 120 seconds; its output
(keys redacted) and exit code appear below the line, and Home's session log lists it.
doctorskips the clipboard there unless you pass--clipboard.espanso deploy,
espanso detach,config migrate,secrets set NAME,secrets remove,history prune
andhistory resetopen their tab's dialog, which asks first as the buttons do
(--yesand the like are ignored).setup,config rollback,config retireand
profiles migratesay to run them in a terminal;improve,personaanduiare
refused.secrets setwith a value (or--stdin) after the key's name is cleared, not
run or kept. - Home has two more buttons (#111). Recipes… lists the commands Home suggests before you
have done anything; picking one puts it on the command line, ready to edit, and runs
nothing until Enter (secrets set OPENROUTER_API_KEYonly fills in the key's name: Enter
then opens the hidden key dialog). Copy last sends this session's latest command, exactly
as Home's session log shows it (withheld values such as<value withheld>and placeholders
included), to the terminal's clipboard (OSC 52; a terminal may need it allowed, or drop
it); it is disabled until there is one, and the interface never reads the clipboard. - The interface has a seventh tab, Try (#111, key
7): type a draft, pick Local stub or Real
provider, the provider, profile ("as configured" by default, as a trigger) and tier, and
press Run to see the rewrite a trigger would paste. It never reads or writes the clipboard
and runs in process, so the draft never goes into a command line. It reads the settings as
a trigger does (an invalid one stops it with the marker), every run goes through the
data-protection gate (a stub run exactly as its real call would), and a blocked draft
shows the trigger's marker with nothing sent. Local stub (the default) answers on
127.0.0.1with a placeholder key, calls no provider and records nothing. Real provider asks
first (provider, model, base URL) and is recorded in the usage history as a direct call;
Home's session log shows it as
promptmend improve --provider openrouter --source argument --text '<draft withheld>'
(with its profile and tier too). A line under the result gives the time, requests, tokens and the cost as
reported,unknownornot applicable, never 0 for a cost nobody reported. The command
line's refusal ofimprovenow points at the Try tab, and the set-up smoke test's stub
replies report token counts. OLLAMA_NUM_CTXsets Ollama's context window (options.num_ctx); empty (the default)
sends nothing (#168).- Windows: a one-command install (#186). Each GitHub Release now attaches
install.ps1, with
its version filled in;powershell -ExecutionPolicy ByPass -c "irm https://github.com/vlastimilbures/promptmend/releases/latest/download/install.ps1 | iex"
installs uv if it is missing (winget, else uv's official installer), then the attested
wheel andconstraints.txtattached to that same Release, runsuv tool update-shell
andpromptmend doctor --no-clipboard, and printspromptmend setupand
promptmend espanso deployas the next steps without running them. It prints every
command it runs; running it again updates in place.PROMPTMEND_VERSIONpicks another
release (0.19.0 or later). CI runs it on Windows against the wheel it built, once with no uv
on PATH, and dry-runs a rendered copy.
Changed
- The interface's second tab is now labelled "2 Providers" (it was "Providers & keys"), so
all seven tabs fit on an 80-column terminal. It still holds the keys. - The interface's intro and About screen show the new PromptMend logo (a mended speech bubble
beside the name), the same one that opens the README.
Documentation
- The README is rewritten as a short landing page: what PromptMend does, screenshots of the
Home and Try tabs, an example, install paths for macOS (Homebrew, uv), Windows (the
one-command install, uv) and Linux, a quick start, the triggers and the key commands. It
renders on PyPI too (absolute links, no GitHub-only syntax). - The details moved to pages in
docs/: install (verify, update, switch channel, uninstall,
upgrading from older versions), usage, commands (options, scripting contract, exit codes),
configuration (the complete settings reference), profiles, privacy, interface and
troubleshooting.docs/benchmark.mdopens with the current results at a glance, the only
place the numbers are kept, which removes the README's contradicting figures. - The package metadata names the project, its keywords and classifiers, and links the docs,
issues and source on PyPI. The bug report form asks for the install channel,
promptmend --versionandpromptmend doctor --json.
v0.19.0
Breaking
- The project is renamed PromptMend (#169): the package (PyPI and wheel) is
promptmend
instead ofespanso-prompt-rewriter, the command ispromptmendinstead of
prompt-workflow, and the Python package ispromptmendinstead ofprompt_workflow.
prompt-workflowstays installed as a deprecated alias until 1.0.0: the triggers,
--helpand--versionbehave exactly as withpromptmend, and every other command
(and the interface) first prints one stderr line, "prompt-workflowis deprecated; use
promptmend(removed in 1.0.0)". Usage lines namepromptmendeither way, and the
interface's header, intro and About show PromptMend. - The marker the triggers paste for an error or a note is
[promptmend: …](was
[prompt-workflow: …]): a script that looks for the old prefix must look for the new one. - The match labels in Espanso's search bar start with
PromptMend:(wasprompt-workflow:),
and a deployed file's stamp is# promptmend <version> (managed; edit at your own risk).
A file an earlier release deployed, with its# prompt-workflowstamp andprompt-workflow
launcher, still counts as ours (stale), so the next deploy replaces it without a
conflict. A side-by-side copy is now<name>.promptmend-new. The__PROMPT_WORKFLOW__
placeholder in the packaged match files is unchanged. - OpenRouter calls carry
X-Title: promptmend(wasespanso-prompt-rewriter; the benchmark
sendspromptmend-bench), so the dashboard lists them under the new name. - Upgrade: first
uv tool uninstall espanso-prompt-rewriter(the triggers stop until the
deploy), thenuv tool install promptmend==<version> -c <the release's constraints.txt>,
promptmend espanso deployandpromptmend doctor. The old tool must go first: both
install aprompt-workflowlauncher, so uv refusespromptmendnext to it, and after a
--forceinstall, uninstalling the old tool later deletes the alias. If you already used
--force, runuv tool uninstall espanso-prompt-rewriter, then installpromptmendagain
with--force.scripts/install_macos.shandinstall_windows.ps1uninstall
espanso-prompt-rewriterthemselves (one line says so) before installing.doctorwarns while the deployed
matches still call aprompt-workflowlauncher, and its previous-install check,setupand
profiles migratestill find a checkout from before the rename (its old project name,
src/prompt_workflow/prompts, a.venv/bin/prompt-workflowlauncher or uv receipt). - The repository moved to https://github.com/vlastimilbures/promptmend (#169). GitHub
redirects the oldespanso-prompt-rewriterURLs, but update bookmarks and remotes
(git remote set-url origin https://github.com/vlastimilbures/promptmend.git). The PyPI
project ispromptmend, and the Homebrew formula ispromptmend
(brew install vlastimilbures/tap/promptmend).
Added
-
promptmend doctorhas afolderscheck (#169): it warns while an old
prompt-workflowfolder is still in use, or holds entries left behind because the new
folder has its own. -
OPENROUTER_DATA_COLLECTION(allowordeny, empty by default) sets OpenRouter's
provider.data_collectionrouting field on every OpenRouter call, both tiers (#29).deny
routes only to endpoints that do not store or train on requests; empty, the default, leaves
the request body unchanged. The defaultgoogle-ai-studio/flexpin served adenycall
when checked; if a pinned endpoint does not qualify, OpenRouter falls back to one that does
whileOPENROUTER_ALLOW_FALLBACKS=true, and otherwise the trigger pastes an HTTP error
marker. README "What is sent, and to whom" and the Configuration table describe it. -
The release workflow can upload each release's sdist and wheel to PyPI as
promptmend, by trusted publishing (no stored token), once the owner turns it
on with the repository variablePYPI_PUBLISH(#95). The newpypijob runs after the
GitHub Release is published, holds only the OIDC token, and uploads the files the release
job attested, never a rebuild. -
scripts/brew_formula.pywrites the Homebrew formula for the project's own tap
(vlastimilbures/homebrew-tap, formulapromptmend) fromuv.lock: the Release's
sdist plus one resource per runtime dependency, each pinned by SHA-256 (#95). -
README "Install", "Updating" and "Uninstall" cover each channel: uv from PyPI or from a
GitHub Release, and Homebrew (the PyPI and Homebrew channels start with 0.19.0), with the
tap trust note and how to switch channels (#96). Scoop and WinGet are no longer planned. -
promptmend espanso statusand the interface's Triggers tab also list every other
.yml/.yamlfile in Espanso'smatch/folder, such as Espanso's ownbase.ymlor your
own variants, asyours: not managed, never touched bydeployordetach(#38). Our
backups, side-by-side copies and subfolders such aspackages/are not listed; a symlinked
file is listed and marked as a link. These lines never change the exit code. -
Clipboard output: with
PROMPT_OUTPUT=clipboard(promptmend config set PROMPT_OUTPUT clipboard, or the interface) the improve triggers put the rewrite on the clipboard and paste
nothing, so the trigger just vanishes and you paste when ready; switching windows during a
long wait no longer sends the paste elsewhere. Error markers, the-iok-"sent despite" note
and the cut-off note still paste; if the clipboard cannot be written, the rewrite is pasted
after a marker.improve --output paste|clipboardoverrides the setting for one call. README
"Clipboard output" (#134, #23). -
The interface opens with a short intro: an ASCII wordmark (text only on a terminal of 70
columns or fewer), the name, version and a tagline (#112). It closes after 0.8 s or on any
key or click, and that key does nothing else.promptmend ui --no-intro, or the new
settingPROMPT_UI_INTRO=false(defaulttrue; the triggers ignore it), skips it. When an
earlier checkout install is found, its screen opens once the intro has closed. -
ain the interface opens an About screen: version, install channel, Python and Textual
versions, the settings and history folders, licence and repository (#112). -
The interface teaches its headless commands (#111). Each button's tooltip shows the command
that does the same in a terminal; each result of a change starts with the command it was
($ promptmend config set PROMPT_TIMEOUT_SECONDS 45, a value that looks like a key
shown as<value withheld>); and Home lists this session's commands, or six recipes such
aspromptmend espanso status --diffbefore there are any. A test parses every one
of these commands against the CLI.
Fixed
promptmend espanso deployfinds Homebrew's stablebin/launcher also when Python
reports the formula's virtualenv through Homebrew'sopt/<formula>link rather than its
Cellar path; before, it fell back to the running script, labelledscriptinstead of
homebrew(#95).- Interface: the header puts the status (
ok,1 problem, 2 warnings) before "set up and
manage", so a narrow terminal cuts the tagline instead of the status (#174). - Interface: the Diagnostics settings table and the Providers & keys key table show a file
under your home folder as~/…(in "From", "Also set in" and the "overrides …" and
"invalid value in …" notes), so the column keeps the file name;config showstill prints
the full path (#174). - Interface: Home's headline no longer shows a check's Markdown backticks around a command
(#174).
Changed
- The user folders are named
promptmendnow (#169): settings in~/.config/promptmend/
(%APPDATA%\promptmend\), the usage history and deploy manifest in
~/.local/share/promptmend/(%LOCALAPPDATA%\promptmend\). The first management command
or the interface (notimprove,persona, help,--version, an unknown command or shell
completion) renames the oldprompt-workflowfolders and prints one line per folder on
stderr; until then the triggers keep using the old ones. A config folder holding the.env
thatPROMPTMEND_ENV/PROMPT_WORKFLOW_ENVnames, or a symlinked old folder, is not moved
(doctor says what to do). A rename keepssecrets.tomlprivate; when both folders
exist only what the new one lacks is moved and nothing is overwritten;migration.json's
paths follow, soconfig rollbackstill works; a move that fails is retried by the next
command. README "Updating" describes it. PROMPTMEND_ENVnames the legacy-mode.env(#169).PROMPT_WORKFLOW_ENVstill works as
its old name; when both are set,PROMPTMEND_ENVwins.- Interface: the intro no longer closes by itself after 0.8 s; it stays until you press
Enter, Escape or any other key, or click, and that key does nothing else (#173). A muted
line under it says so and names the command that turns it off:
promptmend config set PROMPT_UI_INTRO false.ui --no-introstill skips it once. - README "CLI" documents the
[promptmend: …]marker as the stable way for a script to
tell a failedimproverun from a rewrite, since the exit code is always 0 (#32). - The
defaultprompt's two examples now openCONTEXTwith yourPROMPT_PERSONA(a new
{{PERSONA_OPENING}}token, filled like{{PERSONA_RULE}}; without a persona they read as
before), so a rewrite no longer drops the persona the rule asks for (#49). On the bench with
a fictitious persona it openedCONTEXTin 105 of 105 scored runs on both default models
(before: 100 on flash-lite, 96 on gpt-6-luna). - The inputs step no longer contradicts "Execute, but state assumptions up front": it adds
"if step 1 says execute, state your assumption instead and ask only about a gap that blocks
the task" (#46). The independent review now reads "Use a separate agent with [domain] domain
knowledge if you can run one, otherwise r...
v0.18.0
Security
- Every repeat in the gate's built-in patterns is now bounded, as the code comment already
claimed (#36). The JWT pattern's three unbounded segments made a 50,000-character draft of
eyJ-repeats take about 0.29 s of CPU to scan (and 4.5 s at 200,000 characters); the worst
50,000-character case found now takes about 0.05 s. A pattern now reads only what it needs
to know a secret is there: a vendor key's minimum length, a JWT's header and the start of
its payload (eyJand 7 more characters; the signature is no longer required), and a
Bearer token's first 16 characters, one of them a letter, digit or underscore (no word
boundary required after it). A JWT header longer than 8,192 characters or a private-key
label longer than 64 counts as a match. So the gate flags what it flagged before, plus a
JWT without its signature, a very longeyJ…run and a long upper-case-----BEGINlabel,
with three exceptions it no longer flags: a "JWT" whose payload does not start witheyJ
(real payloads are JSON objects, so they always do), a Bearer token whose first 16
characters are only dots and hyphens, and more than 256 whitespace characters between
Bearerand its token. A test checks that no built-in pattern has an unbounded repeat. - An invalid
PROMPT_EXTRA_PATTERNSregex (including a repeat count too large for the regex
engine, or nesting too deep to compile) is now rejected when settings load, naming the
entry but never the pattern (#36).config validate,config set,doctorand the
interface report it instead of only the cloud triggers, and every rewrite trigger, local
ones too, prints a marker until it is fixed (-p-still pastes the persona; before, the
local triggers ran and the usage history silently dropped each record). README and.env.examplenow warn against patterns with
nested quantifiers, which can take exponential time on every draft. - An Ollama cloud model is recognised in any letter case and with a pinned digest
(GPT-OSS:120B-CLOUD,gpt-oss:Cloud,gpt-oss:120b-cloud@sha256:…) (#33). Before, such a
spelling counted as local: the data-protection gate did not run,PROMPT_LOCAL_ONLY=truedid
not refuse it, the interface's Providers tab showed it as local and the usage history
recorded its cost asnot_applicable. Only the tag counts, so a name such as
cloudy-llama:7bstays local. - An error that quotes a rejected value no longer repeats one that matches your
PROMPT_EXTRA_PATTERNS(#32): for examplePROMPT_PROVIDER=PRJ-12345with the pattern
PRJ-\d+now pastesUnknown provider <redacted, 9 chars>, and a bad
PROMPT_TIMEOUT_SECONDSmatching a pattern is described in the marker and in
config validate.config showstill shows the
patterns themselves. --tierwith an unknown value is quoted like every other rejected value (#32), so a
key-shaped one is described instead of pasted back.
Added
config validate,doctorand the interface's Home and Diagnostics tabs now scan
PROMPT_PERSONAwith the data-protection gate's patterns (the built-in ones and
PROMPT_EXTRA_PATTERNS) once (#29). The persona goes with every cloud call in the system
prompt and the gate never scans it, so a match is now reported by finding name only, never
the text:config validatelists it as a problem (exit 4) and doctor's newpersonacheck
warns. Nothing changes on the triggers. A persona that is never sent is not flagged: when
neitherPROMPT_PROFILEnorPROMPT_PRO_PROFILEuses{{PERSONA_RULE}}(e.g.general),
or withPROMPT_LOCAL_ONLY=trueandPROMPT_GATE_LOCALoff. A persona that cannot be read
shows as such in doctor instead of "not set".PROMPT_GATE_LOCAL(defaultfalse) (#33):trueruns the data-protection gate for Ollama
and LM Studio onlocalhosttoo, with the same override rules, for a local server that
relays to a cloud API (LiteLLM, an SSH tunnel).PROMPT_LOCAL_ONLYstill allows them. A
draft it blocks pastes[prompt-workflow: Blocked call to the local server …].- The interface's header now shows the installed version next to the name
(prompt-workflow 0.17.0 — set up and manage) on every screen (#112). OPENROUTER_PRO_MAX_TOKENS(default empty) (#31): the output cap for the pro tier (-ip-,
-if-,--tier pro), whose reasoning counts against it. Empty keeps today's behaviour, the
pro tier sharingOPENROUTER_MAX_TOKENS(2400) with the standard tier.--max-tokensand
a max-tokens pick in-if-still win; the popup'sdefaultnow keeps this setting.
Changed
config setandconfig unset(and the interface's settings and profile forms) warns, naming the findings
only, when a saved change leaves aPROMPT_PERSONAthat the gate's patterns match and that
the profiles send, asconfig validatewould flag it. The value is still saved (exit 0).- With
PROMPT_GATE_LOCAL=true, a blocked cloud call no longer suggests a local trigger
(-il-) or a local model, which the gate would block too. The message without it is
unchanged. - An invalid
PROMPT_EXTRA_PATTERNSregex is now named asentry N (custom_N), the name a
finding of that entry gets (both count the non-empty entries from 1). config showprints(empty)for a setting set to an empty value on purpose (such as
PROMPT_TEMPERATURE=), instead of a blank;config getstill prints the raw value.- README Triggers: a tip to use
-ip-for a draft that pastes an email or thread, since-i-'s
faster model often summarises short pasted material instead of copying it (#42). - Match files (#38, #23, #41): every match, the commented-out
-ic-too, has alabel:
(prompt-workflow: …), so Espanso's search bar names each trigger instead of showing
{{output}}. The-if-model list now starts with, and defaults to,default, which keeps
OPENROUTER_PRO_MODELand itsOPENROUTER_PRO_PROVIDERpin, as the comment and README
already said (with the shipped settings that is the sameopenai/gpt-6-luna@openaias
before; with a custom pro model it now follows that model and usesPROMPT_PRO_PROFILEif
set, where the old fixed default usedPROMPT_PROFILE). The-if-timeout list stops at 120 s (180removed), since Espanso blocks every
other trigger while a call runs. The-ip-comment quotes the 2026-10-04 bench (about 6 s
median, 9 s p95) instead of "~5-8 s". Runprompt-workflow espanso deployto update the
deployed matches; until thendoctorreports them asstale. - README and
.env.examplesay that anhttp://Ollama or LM Studio base URL on another
machine sends the draft in clear text over the network (only the cloud providers require
https://) (#33). - README "Privacy and data protection" has a new "What is sent, and to whom" section (#29): the
system prompt, including yourPROMPT_PERSONA, goes with every call and is not scanned by the
data-protection gate; OpenRouter calls reach OpenRouter and an upstream endpoint (the pinned
one, or another whileOPENROUTER_ALLOW_FALLBACKSistrue, the default) and carry an
X-Titleheader naming this app; keys travel only as the authentication header. The
introduction no longer suggests that only the draft is sent. - Docs (#41): the README,
.env.exampleand CONTRIBUTING say that on macOS and Linux the config
folder (config.toml,secrets.toml,.env,profiles/) is$XDG_CONFIG_HOME/prompt-workflow/
whenXDG_CONFIG_HOMEis set (Windows always uses%APPDATA%\prompt-workflow\); the pro
tier's latency is quoted from the 2026-10-04 bench (median 5.6 s, p95 9.2 s) everywhere;
CONTRIBUTING says CI runs the tests with--covand a 95 % floor, and its project tree lists
previous_install.py(a test now checks the tree names every module). - README tips (#23, #38): do not type or switch windows while a rewrite runs, since Espanso
pastes it wherever the focus is when the answer arrives (the usage history write adds up to
0.25 s, 1 s on the first write); on Linux the clipboard needsxclip,xselor
wl-clipboard; select, copy and type-i-to rewrite text in place; with a release wheel,
put your own trigger variants in a match file of your own, whichprompt-workflow espanso deploynever touches, since editing a deployed file stops its updates. The Windows install
script is now run with-ExecutionPolicy Bypass -Fileinstead of changing the policy. - The benchmark's method and result tables moved from README to
docs/benchmark.md(#40);
README keeps a short "Model benchmark" summary (the default models, their pass counts and
latency) and links there. Each results heading now names the release whose prompt it scored
(the current table: the prompt of v0.14.0, unchanged since), and the README's
example output says it came from v0.7.0 ongoogle/gemini-3.5-flash-lite. - The bench (
scripts/bench_models.py) reports what its pass rates measure (#48). Each check's
name starts with its kind (struct:,branch:ordraft:), and below the unchangedpass
column the report gives each kind's pass rate, over the runs scored on it, and the overall
one with a Wilson 95% interval. Two new checks: the configured persona must be inCONTEXT
unless the draft states its own role (not scored on a--persona nonerun; #49), and an
INPUTSorOUTPUTSthat opens with "None" must not then ask for something with
[REVIEW: …].keptno longer counts a key found only inINPUTS, unless the draft pastes
material. So pass counts andkeptfrom earlier runs are not directly comparable with new
ones (docs/benchmark.md). The bench's note on the drafts once held out now says they are no
longer held out, and the benchmark notes say the blind pairwise judgement of 0.11.0 was a
one-off by hand whose harness is not in the repository. - A
.envthat is not UTF-8 (UTF-16, say) is now a marker on the triggers,
`[prompt-workflow...
v0.17.0
Upgrading from the install scripts (an editable checkout install) to the release wheel: you no
longer need to migrate from the old install first. Install the wheel, then run
prompt-workflow setup (or open the interface): it finds the checkout, copies its settings,
key and edited profiles with your consent, and deploys the match files. If it finds nothing,
name the checkout: prompt-workflow setup --migrate-from PATH. The checkout's .env stays in
place until prompt-workflow config retire --from PATH.
Added
prompt-workflow doctorhas a newprevious_installcheck (#110). Before any setting is
saved (noconfig.toml, no secret store, not inPROMPT_WORKFLOW_ENVmode) it looks for an
earlier checkout install whose.enva wheel install does not read: the launcher in the
deployed match files or the deploy manifest (<checkout>/.venv/bin/prompt-workflow, or
.venv\Scripts\prompt-workflow.exeon Windows) and the uv tool receipt of an editable
install. It never scans the disk and never reads that.env, and only a folder whose
pyproject.tomlnames this project counts. It also warns when theprompt-workflowthat
PATHfinds first is not the installed launcher, for example while a checkout's.venvis
active.doctor --jsonadds the check with the datagated,roots,signals,
env_file,retire_pendingandshadow.prompt-workflow config migrate --from PATHcopies the settings and key of an earlier
checkout's.env(#110). It fills only the settings still at their default, so nothing you
use today changes (the others are listed as kept), and it leaves that.envwhere it is, so
the old triggers keep working until the match files are deployed again. A.envin use is
migrated in the same step as before.prompt-workflow config retire --from PATHthen moves
the old.envinto the backup; it is refused while a match file still runs that checkout's
CLI. Both show a preview first, andconfig rollbackundoes either.prompt-workflow setup
offers the copy (and the checkout's edited profiles) when it finds such a checkout, or the
one--migrate-from PATHnames, and the retire after the deploy step;doctorwarns while
a copied.envis still in place. A broken.envin a checkout setup found by itself is a
to-do, not a failure, and setup then saves nothing, so the offer stays open.- The interface (
prompt-workflow ui) opens a "Previous install" checklist once per session
when it finds such a checkout, or a copied.envstill in place (#110): copy the settings
and key, copy the edited profiles, deploy the match files, then retire the old.env. Each
step shows its preview in a dialog whose Cancel has the focus and runs the same service as
its command; keys are named, never shown. "Enter a path…" looks at a checkout you name, and
"Skip this checkout" stops the offer for it. Home has a "Previous install…" button and the
previous_installcheck, and the Profiles tab's "Migrate from checkout" uses the checkout
found.
Changed
prompt-workflow profiles migrate, the interface's profile copy and setup now say how to use
every copied file named like a built-in profile, not onlydefault.md: it replaces the
built-in only oncePROMPT_PROFILE_OVERRIDESlists it, and the message gives the
prompt-workflow config set PROMPT_PROFILE_OVERRIDES ...command (keeping the names already
listed).doctor'sprofileswarning for such a file gives the same command.- When
profiles migratefinds nothing to copy, it says what it compared with (the commit the
branch shares with its upstream, or HEAD without one) and how to compare with an older
commit (--rev <commit>), since a profile committed on a branch without an upstream counts
as unchanged. prompt-workflow config rollbackafterconfig migrate --from(#110) no longer says "the
.env is read again" when only the earlier checkout's.envcomes back: this install never
reads it. The preview says so, notes that the match files still call this install, and names
the way back (that checkout's install script) orconfig migrate --fromto copy again.- Setup's to-do lines name what they are about ("copy the settings of PATH", "copy the profiles
added or edited in PATH"), and its migration preview no longer reads as if the copy ran. The
interface's "Previous install" screen notes that rollback leaves the match files deployed.