lazynote is a local, terminal-first notes app for developer workflows.
Take notes instantly from the CLI, browse them in a small TUI, and expose the same notes to shell scripts, terminal tools, and coding agents. It is built for quick context capture without an account, server, database, or sync service.
Notes are stored locally as JSON. The released binary does not require a Go toolchain.
- Why lazynote?
- Quick Example
- Install and Update
- CLI Workflows
- Agent Plugins
- TUI
- Storage
- Development
- Roadmap
- Releases
- Acknowledgements
- License
- Capture notes from arguments, stdin, and shell pipelines.
- Retrieve context with plain commands such as
list,show,search, andexport. - Share one local notes file between humans, scripts, and coding agents.
- Browse notes in a fast terminal UI when you want a human view.
# take simple note: $ lazynote <title> <body>
lazynote showerthought 'Running from the cops is the ultimate double or nothing.'
# tag a note while capturing it
lazynote --tag work idea 'Use a single notes file so agents and humans share context.'
# piped body with an inferred title
printf '## Session abc123\n- fixed flaky test\n' | lazynote
# retrieve context
lazynote list
lazynote search flaky
lazynote search '#work'
lazynote export jsonRecommended for installing or updating to the latest release on Linux and macOS:
curl -fsSL https://raw.githubusercontent.com/rschoch/lazynote/main/install.sh | shThe same command handles both installation and updates. It downloads the latest
release, verifies checksums when possible, and installs or replaces lazynote
in ~/.local/bin.
Confirm the installed version afterward:
lazynote --versionIf your shell cannot find it:
export PATH="$HOME/.local/bin:$PATH"Inspect the installer first:
curl -fsSLO https://raw.githubusercontent.com/rschoch/lazynote/main/install.sh
sh install.shInstaller options:
sh install.sh --dir /usr/local/bin
sh install.sh --version vX.Y.ZUninstall a script-installed binary:
rm -f ~/.local/bin/lazynotePrebuilt archives and Linux .deb, .rpm, and .apk packages are available on
the GitHub Releases page. Direct
downloads do not add an apt/yum/apk repository, so package-manager auto-updates
are not configured yet.
From source:
go install github.com/rschoch/lazynote/cmd/lazynote@latestFrom a checkout:
make build
make installmake install uses /usr/local by default. Use PREFIX for another root:
make install PREFIX="$HOME/.local"Capture a note:
lazynote idea 'Use a single notes file so agents and humans share context.'
lazynote --tag work --tag idea idea 'Keep note metadata small and useful.'Capture from stdin:
echo 'Refactor release notes before tagging the next release.' | lazynote release
cat summary.md | lazynote 'session summary'
lazynote 'session summary' - < summary.mdIf stdin is piped without a title, the first non-empty line becomes the title:
printf '## Session abc123\n- shipped release prep\n' | lazynoteSuppress success output for scripts and agents:
some-command | lazynote --quiet 'session summary'
lazynote --quiet release 'Tag after CI passes.'Retrieve context:
lazynote list
lazynote show <id>
lazynote show --body <id>
lazynote search packaging
lazynote search '#work'
lazynote backup
lazynote export markdown
lazynote export json
lazynote pathlist prints tab-separated id, created_at, and title fields, plus a
metadata field when a note is pinned, archived, or tagged. show accepts a
full ID or a unique ID prefix.
Manage existing notes:
lazynote edit <id>
lazynote edit <id> 'new title' 'new body'
lazynote edit <id> 'new title' - < body.md
lazynote delete <id>
lazynote pin <id>
lazynote pin --toggle <id>
lazynote unpin <id>
lazynote archive <id>
lazynote unarchive <id>
lazynote tag <id> work idea
lazynote untag <id> work
lazynote tags <id>edit <id> opens $VISUAL, $EDITOR, or vi. Direct edit commands replace
the title and body without opening an editor. tag normalizes tags to
lower-case names and accepts optional leading #.
Search is case- and accent-insensitive, so queries such as cafe match
Café. CLI list, search, show, and export commands include archived
notes; archived entries are marked in summaries and exports. The TUI filters
them into its Archived view instead.
Convenience aliases are rm for delete, show -b for show --body, and
export md for export markdown. -h and -v are short forms of --help
and --version.
backup prints the backup file path. With no path it writes a timestamped JSON
copy under a backups directory next to the notes file. Pass a file path to
choose the exact destination, or an existing directory for a timestamped backup
inside that directory.
Command words such as list, show, search, edit, delete, rm, pin,
unpin, archive, unarchive, tag, untag, tags, path, backup,
export, help, and version are reserved when they are the first argument.
Use -- to use one as a title:
lazynote -- search 'a note whose title is search'Use single quotes for literal shell text, especially if the note contains
characters like !, $, or backticks.
Agent plugins are optional. The lazynote CLI works on its own; plugins only
teach tools like Codex or Claude Code how to save and retrieve notes through the
CLI. Install the lazynote binary first, then install the plugin for your
agent.
Both agent skills append a small source conversation footer to new notes, with
the agent, session name when known, session ID, project directory, and resume
command (codex resume <id> or claude --resume <id>). Unavailable fields are
omitted. The footer is ordinary note text and stays visible in exports; resuming
requires the original agent's saved conversation to still be available.
Add the GitHub marketplace source. This does not require cloning lazynote
first:
codex plugin marketplace add rschoch/lazynoteThen open /plugins in Codex and install lazynote, or install it directly:
codex plugin add lazynote@lazynoteTo pick up newer plugin instructions later, upgrade the marketplace source and
update the installed plugin from /plugins:
codex plugin marketplace upgrade lazynoteAsk Codex to use lazynote when you want it to persist or retrieve project
context:
"Use lazynote to save the proposed implementation plan."
"Search lazynote for notes about release packaging."
"Save a summary of this debugging session to lazynote."
Add the GitHub marketplace source, install the plugin, and reload plugins. This
does not require cloning lazynote first:
/plugin marketplace add rschoch/lazynote
/plugin install lazynote@lazynote
/reload-plugins
To pick up newer plugin instructions later, update the marketplace, reinstall the plugin, and reload if Claude Code asks you to:
/plugin marketplace update lazynote
/plugin install lazynote@lazynote
/reload-plugins
Invoke the namespaced Claude Code skill with /lazynote:lazynote:
/lazynote:lazynote Save the proposed implementation plan.
/lazynote:lazynote Search for notes about release packaging.
/lazynote:lazynote Save a summary of this debugging session.
Open the terminal UI:
lazynoteThe TUI shows note titles on the left and the selected note body on the right. It automatically reloads when the notes file changes, so notes added from another terminal tab, script, or coding agent appear while the TUI stays open. The bottom status line shows context-specific key hints.
Keys:
- left: focus note list
- right: focus note body
- down: move or scroll down in the active pane; the note list wraps to the top
- up: move or scroll up in the active pane; the note list wraps to the bottom
- Home/End: jump to the first/last note or the top/bottom of the note body
g/G: jump to the first/last note- PageDown: scroll note body down
- PageUp: scroll note body up
/: case- and accent-insensitive filter by title, body, or#tag; Enter applies, Esc cancels; matching title and body text is highlightedr: reload notes from disk nown: create a note in$VISUAL,$EDITOR, orvie: edit the selected note in$VISUAL,$EDITOR, orvip: pin or unpin the selected notet: add or remove tags; Space toggles, Enter saves, andncreates a taga: archive the selected note, or restore it from the Archived viewv: choose Active, Pinned, Recent, Untagged, Archived, or a#tagview?: show or hide the help overlayc: copy the selected title or note bodyC(Shift+C): copy the selected note ID and display it in the status lined/ delete: arm deletion; pressdagain to confirm- Esc: clear the active filter
q/ Ctrl-C: quit
Copy uses the native macOS clipboard when available and terminal clipboard
support on Linux, WSL, and Windows. To point an agent to an existing note, press
Shift+C and paste the ID into your prompt; the agent can read it with
lazynote show <id>.
Creating and editing open a temporary file
whose first line is the note title and whose remaining content is the note body.
The default Active view hides archived notes. Archiving also clears the note's
pin. Use v to open the view picker; Recent contains the 50 newest active
notes, and tag views match exact tags.
Tags have no separate registry: a tag disappears when no note uses it. Tags
used only by archived notes remain available in the tag picker but do not get
their own active tag view.
The note body pane shows tags and edited timestamps when present. Pinned notes
stay at the top of the list and use ▴ in the list gutter. Notes that arrive
from another process while the TUI is open use ● until selected. http://
and https:// URLs are highlighted in note bodies; opening them uses the
terminal's native Ctrl/Cmd-click behavior. Fonts, glyph rendering, and colors
depend on your terminal emulator. Note bodies wrap at word boundaries using
terminal display widths, including wide and combining Unicode characters.
Set your preferred external editor before launching lazynote:
export VISUAL="nvim"
export EDITOR="nano"VISUAL is preferred over EDITOR; if neither is set, lazynote falls back to
vi. GUI editors should wait for the file to close, for example
VISUAL="code --wait".
TUI behavior is configured in ~/.config/lazynote/config.json:
{
"tui": {
"refreshIntervalSeconds": 1,
"noteOrder": "oldest-first",
"autoSelectNewNotes": false
}
}Supported values:
refreshIntervalSeconds: how often the open TUI checks for external changesnoteOrder:oldest-firstornewest-firstautoSelectNewNotes: jump to newly arrived notes after auto-refresh
The TUI includes adaptive default, mono, and high-contrast themes, plus
fixed dark and light themes. The default inherits your terminal's foreground
and background colors, so it remains readable with either a light or dark
terminal profile.
LAZYNOTE_THEME=light lazynoteCustom theme overrides are configured in ~/.config/lazynote/config.json.
See THEMING.md for the full format and examples.
Default notes file:
~/.local/share/lazynote/notes.json
Print the active path:
lazynote pathUse a different notes file:
LAZYNOTE_PATH=/tmp/lazynote-dev.json lazynote listBack up your notes:
lazynote backup
lazynote backup ~/backup/lazynote
lazynote backup ~/backup/lazynote/notes.jsonBecause storage is one JSON file, it can be synced with tools like Syncthing,
Dropbox, iCloud Drive, or a private dotfiles repository. The TUI reloads when
the file changes, including atomic replacements. Writes use a small lock file
next to the notes file so concurrent CLI, TUI, script, and agent writes do not
silently overwrite each other. Newer versions may write optional tags,
updated_at, pinned, and archived fields; older notes without these fields
continue to load normally.
Run tests and build a dev binary:
make test
make build
bin/lazynote --versionTry the dev binary without touching your real notes:
LAZYNOTE_PATH=/tmp/lazynote-dev.json bin/lazynote 'dev smoke' 'hello from local build'
LAZYNOTE_PATH=/tmp/lazynote-dev.json bin/lazynote list
LAZYNOTE_PATH=/tmp/lazynote-dev.json bin/lazynote export markdown
LAZYNOTE_PATH=/tmp/lazynote-dev.json bin/lazynoteIf go is installed but not on PATH:
make GO=/usr/local/go/bin/go test
make GO=/usr/local/go/bin/go buildCheck the installer script:
sh -n install.sh
sh install.sh --helpUseful targets:
make build: buildbin/lazynotemake test: rungo test ./...make install: install under$(PREFIX)/binmake uninstall: remove from$(PREFIX)/binmake clean: remove local build and release artifactsmake release-snapshot: build local GoReleaser artifacts
See ROADMAP.md for lightweight future candidates and deliberately deferred ideas.
GoReleaser configuration lives in .goreleaser.yaml. Tagged releases build
Linux, macOS, and Windows binaries for amd64 and arm64, plus checksums,
archives, and Linux packages.
Create a local snapshot:
make release-snapshotPublish a tagged release:
git tag vX.Y.Z
git push origin vX.Y.ZGitHub Actions runs tests and publishes release artifacts. Publishing apt/yum/apk repositories or Homebrew taps is a separate distribution step.
lazynote takes a lot of inspiration from
LazyGit, especially around keeping a
terminal UI fast, keyboard-driven, and practical without making it feel heavy.
MIT
