a timer-boxed writing app. pick how long you have, write in markdown, and when the clock hits zero your post ships through a hook you provide. no extensions, no drafts rotting in a folder. the deadline is the editor.
- you point keystroke at an executable (the hook)
- you pick a duration: 10, 15, 30, 45, or 60 minutes
- you write, with an optional live markdown preview
- at zero, keystroke writes your post to a markdown file and runs your hook with it
the hook is required. keystroke will not let you write without one, because a post with nowhere to go is just a draft.
requires node >= 20.
git clone https://github.com/iteratedcomputing/keystroke.git
cd keystroke
npm installjust trying it out? start with the bundled word-count hook, which counts your words and publishes nothing:
make demofor real use, create a hook (any executable works). start from the word-count example and overwrite it with your own publishing logic:
cp hooks/wordcount.sh hook
chmod +x hookthen start the app:
make devit opens http://localhost:7777 in your browser automatically. set
KEYSTROKE_OPEN=0 to skip that.
your hook stays yours: ./hook and hooks/local/ are gitignored, so
personal hooks never end up in the repo. set KEYSTROKE_HOOK to use hooks
anywhere else, and PORT to change the port.
your hook receives the finished post as a markdown file path in $1, plus
KEYSTROKE_TITLE, KEYSTROKE_SLUG, and KEYSTROKE_DURATION_MINUTES in the
environment. exit 0 means published; anything else surfaces the failure in
the ui with your draft path so nothing is lost.
chain hooks by separating paths with colons. they run in order, and each must exit 0 before the next starts:
export KEYSTROKE_HOOK=./hooks/front-matter.sh:./hooks/publish.shpass per-run values to your hooks with --x- flags instead of exporting env
vars. --x-blog-tags=tech becomes KEYSTROKE_BLOG_TAGS=tech in the hook
environment (uppercased, dashes to underscores). a bare flag like
--x-blog-dry-run is set to 1:
node src/server.js --x-blog-tags=tech
make dev ARGS="--x-blog-tags=tech"make treats ---prefixed words as its own options, so pass them through
the ARGS variable rather than make dev --x-....
see docs/hooks.md for the full contract and a publish-to-blog example.
make format # prettier
make test # node --test
make build # no-op, nothing to compile
make dev # run the server
make demo # run the server with the bundled word-count hook

