- Rust 96.2%
- Nix 3.8%
| .forgejo/workflows | ||
| .vscode | ||
| src | ||
| zed-extension | ||
| .envrc | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| flake.lock | ||
| flake.nix | ||
| LICENSE.md | ||
| README.md | ||
| renovate.json | ||
| REQUIREMENTS.md | ||
| streamd.svg | ||
strea.md
Strea.md is a personal knowledge management and time-tracking CLI tool. It organizes time-ordered markdown files using @tag annotations, letting you manage tasks, track time, and query your notes from the terminal.
Installation
Debian/Ubuntu (.deb package)
Download and install the latest release:
wget https://git.konstantinfickel.de/kfickel/streamd/releases/download/vX.Y.Z/streamd_X.Y.Z_amd64.deb
sudo dpkg -i streamd_X.Y.Z_amd64.deb
This includes shell completions for bash, zsh, and fish.
Static Binary
Download the statically-linked binary:
wget https://git.konstantinfickel.de/kfickel/streamd/releases/download/vX.Y.Z/streamd-X.Y.Z-linux-x86_64
chmod +x streamd-X.Y.Z-linux-x86_64
sudo mv streamd-X.Y.Z-linux-x86_64 /usr/local/bin/streamd
Nix
Using the flake directly:
nix run git+https://git.konstantinfickel.de/kfickel/streamd
Or add to your NixOS/Home Manager configuration using the provided homeManagerModules.default.
Core Concepts
- Shards — Sections of markdown files, organized hierarchically by headings. Each shard can contain markers, tags, and nested child shards.
- Markers — Special
@tagslike@Task,@Done,@Waiting, or@Timesheetthat give shards semantic meaning and place them into dimensions. - Dimensions — Classification axes (e.g. task state, project, timesheet) that categorize shards. Some dimensions propagate to child shards.
File Format
Markdown files are named with a timestamp: YYYYMMDD-HHMMSS [markers].md
For example: 20260131-210000 Task Streamd.md
An optional _file_type segment can follow the timestamp to classify the file:
YYYYMMDD-HHMMSS_<file_type> [markers].md
For example: 20260413-083000_daily.md — the daily prefix is stored as the file_type dimension and propagates to all child shards.
Within files, @-prefixed markers at the beginning of paragraphs or headings define how a shard is categorized.
Commands
streamd/streamd new— Create a new timestamped markdown entry, opening your editorstreamd daily [YYYYMMDD]— Open today's daily file (or create it if missing); pass a date to open that day's file insteadstreamd todo— Show all open tasks (shards with@Taskmarkers), numbered for easy referencestreamd todo N edit— Edit task N in your editor, jumping to the task's linestreamd todo N done— Mark task N as done by inserting@Doneafter@Taskstreamd todo --show-future— Include tasks with future dates in the listingstreamd edit [number]— Edit a stream file by position (1= most recent,-1= oldest)streamd timesheet— Generate time reports from@Timesheetmarkersstreamd tags— List every tag and marker used anywhere in the stream (subfolders included), alphabeticallystreamd lsp— Start the LSP server (stdin/stdout transport; see Editor Integration below)streamd cache— Show the local cache's location, size and contents (see Local Cache below)streamd cache rebuild— Discard the cache and reindex the corpus from scratchstreamd cache clear— Delete the cache
Configuration
User Configuration
Streamd reads its user configuration from ~/.config/streamd/config.toml (XDG standard). The main setting is base_folder, which points to the directory containing your stream files (defaults to the current working directory).
Set STREAMD_BASE_FOLDER to use a different stream folder for one invocation. A non-empty value takes precedence over config.toml, which is then not read at all:
STREAMD_BASE_FOLDER=~/work-notes streamd todo
Repository Configuration
For timesheet reporting, create a .streamd.toml file in your stream files directory:
timezone = "Europe/Berlin" # Optional: timezone for day boundaries
[timesheet]
[[timesheet.periods]]
start = "2026-01-01"
end = "2026-06-30"
hours_per_week = 38.0
[[timesheet.periods]]
start = "2026-07-01"
end = "2026-12-31"
hours_per_week = 40.0
The timesheet command will calculate expected vs actual working hours based on these periods, showing:
- Daily breakdown with expected/actual hours
- Special day types (sick leave, vacation, holidays, flex days)
- Warnings for missing entries and overlapping timecards
- Monthly and cumulative balance
Usage
Running streamd opens your editor to create a new entry. After saving, the file is renamed based on its timestamp and any markers found in the content.
Running streamd todo finds all shards marked as open tasks and displays them numbered in your terminal. Tasks with future dates are hidden by default (use --show-future to include them). Tasks are sorted by date with oldest first (task 1 is the oldest).
You can quickly edit or complete tasks by number:
streamd todo 1 editopens task 1 in your editor at the correct linestreamd todo 1 donemarks task 1 as done by inserting@Doneafter@Task
Local Cache
Streamd keeps a local cache of parsed stream files in an embedded graph database. Commands validate
it with stat alone — comparing each file's size and modification time — and re-read only the files
that actually changed.
On a local disk this makes little difference; parsing a few hundred small markdown files is already fast. It matters when the stream folder lives on a network-backed or synced share (OneDrive on WSL, for example), where opening each file is far more expensive than checking its metadata.
Location
Caches live under your XDG cache directory, not beside your notes:
~/.cache/streamd/<folder-name>-<hash>.grafeo
Each stream folder gets its own database, keyed by a hash of the folder's canonical path. Several
streamd instances with different config.toml files therefore never share a cache, and two folders
that happen to have the same name do not collide. Nothing is written into your stream folder, so
there is nothing to add to .gitignore and nothing to exclude from file sync.
Set STREAMD_CACHE_DIR to override the location:
STREAMD_CACHE_DIR=/fast/local/disk streamd todo
On the rare platform that offers no cache directory at all, streamd does not guess one — it skips
the cache and reads your markdown directly. Set STREAMD_CACHE_DIR to get the cache back.
Behaviour
The cache is derived state and never authoritative. It is rebuilt from your markdown whenever it is missing, corrupt, or written by a different schema version, and any failure to use it — including another process holding it open — silently falls back to reading the files directly. Deleting it is always safe.
streamd cache # where it is, how big, how many files, whether a graph projection exists
streamd cache rebuild # discard and reindex, including the graph projection
streamd cache clear # delete it
Set STREAMD_NO_CACHE=1 to bypass it entirely for one invocation:
STREAMD_NO_CACHE=1 streamd todo
Graph queries
Beyond caching, streamd cache rebuild builds a projection layer that models tags, dimension
values and calendar days as first-class graph nodes. This makes questions that previously required
walking every shard of every file into single-hop traversals — which tags co-occur, how many shards
sit in each project or task state, which files contribute to a given day. These are available in the
library API (streamd::cache::query).
streamd tags and the LSP's @ completions read the list of used tags and markers from this
projection. They rebuild it on demand whenever a file changed since it was last built, so there is
no need to run streamd cache rebuild first.
Editor Integration
streamd lsp starts a Language Server Protocol server that provides IDE features for your stream markdown files. The server communicates over stdin/stdout and auto-activates only when a .streamd.toml file is present in the workspace root.
Features
| Feature | Description |
|---|---|
@ completions |
Suggests known markers from your config, then every tag and marker already used anywhere in the workspace; conditional suggestions (e.g. @Done when @Task is on the line) come first. Newly used names become suggestible as soon as the file is saved |
| Temporal snippets | @ followed by a digit offers YYYYMMDD / HHMMSS format snippets |
| Diagnostics | File-name format warnings (R15); timesheet errors (overlapping timecards, unclosed days) |
| Workspace todo diagnostics | Every open/waiting @Task across all .md files in the workspace (not just open buffers) is reported as an Information-severity diagnostic via LSP 3.17 pull diagnostics (textDocument/diagnostic, workspace/diagnostic) — shows up in Zed's diagnostics: deploy panel as a global todo list, and refreshes automatically on save or external file changes |
| Document symbols | Shard tree exposed as outline symbols |
| "Mark task as done" | Quick-fix code action: inserts @Done after @Task |
| Workspace symbols | Search shards across all .md files; open/waiting todos are additionally indexed by their own text so they're findable by content |
| References | Find all occurrences of an @Marker across the workspace |
| Rename | Rename an @Marker across all files |
Zed
Install the streamd Zed extension — it's the only supported way to get streamd running in Zed. It works for local Windows and Linux Zed, and for Zed Remote Development (SSH or WSL2 remotes) — the extension runs the wasm inside whichever host Zed is talking to and downloads the matching binary there automatically.
Once the extension is installed, no settings.json changes are needed: extension.toml already declares languages = ["Markdown"], so Zed registers the streamd language server for Markdown files on its own.
If you want to point Zed at a specific streamd binary instead of letting the extension resolve one from PATH (or download it), add to ~/.config/zed/settings.json:
{
"lsp": {
"streamd": {
"binary": {
"path": "streamd",
"arguments": ["lsp"]
}
}
}
}
This only overrides which binary the already-registered streamd adapter launches — it does not register the server by itself, so it's only useful with the extension installed.
Zed Extension
The extension resolves the language server binary in this order:
- An existing
streamdalready onPATH(e.g. installed via Nix,apt, orcargo install) is used as-is. - Otherwise, it downloads the matching prebuilt binary for the current platform (Linux x86_64 or Windows x86_64) from the releases page into Zed's private extension directory, and reuses it on subsequent starts.
macOS and non-x86_64 platforms: no prebuilt binary is published yet. Install
streamdmanually and make sure it's onPATH— otherwise Zed will show a clear "no precompiled binary available" error instead of starting the language server.
1. Download the extension from the releases page:
streamd-zed-extension-X.Y.Z.zip
2. Extract the zip to a permanent folder, e.g. C:\Users\<you>\zed-extensions\streamd-zed-extension\ on Windows or ~/zed-extensions/streamd-zed-extension/ on Linux:
The folder must contain extension.toml and extension.wasm.
3. Install the extension in Zed via the command palette (Ctrl+Shift+P):
zed: install dev extension
Point Zed to the extracted folder. For Remote Development, install it in the local Zed client — Zed propagates the extension to the remote host automatically.
4. Verify by opening a Markdown file inside a directory that contains .streamd.toml — @ completions and diagnostics should become active.
Neovim (nvim-lspconfig)
1. Register the server — add to your Neovim config (e.g. ~/.config/nvim/init.lua or a plugin file):
local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')
if not configs.streamd then
configs.streamd = {
default_config = {
cmd = { 'streamd', 'lsp' },
filetypes = { 'markdown' },
root_dir = lspconfig.util.root_pattern('.streamd.toml'),
single_file_support = false,
},
}
end
lspconfig.streamd.setup {}
The server activates automatically when Neovim opens a Markdown file inside a directory that contains a .streamd.toml file.
2. Using LSP features — standard Neovim LSP keymaps apply (:help lsp):
| Action | Default keymap | Notes |
|---|---|---|
Trigger @ completions |
<C-x><C-o> (insert mode) |
Or via your completion plugin (nvim-cmp, blink.cmp, …) |
| Show diagnostics for current line | <C-w>d / gl |
File-name format warnings, timesheet errors |
| Jump to next / previous diagnostic | ]d / [d |
Navigate between warnings |
| Code actions (mark task as done) | <leader>ca (Neovim ≥ 0.10) |
Place cursor on a line with @Task |
| Rename marker across all files | <leader>cr / grn |
Renames the @Marker under the cursor everywhere |
| Find all references to a marker | grr / <leader>fr |
Lists every occurrence of @Marker across the workspace |
| Document outline (shard tree) | :lua vim.lsp.buf.document_symbol() |
Or via Telescope: :Telescope lsp_document_symbols |
| Workspace symbol search | :lua vim.lsp.buf.workspace_symbol() |
Or via Telescope: :Telescope lsp_workspace_symbols |
Note: default keymaps (
grn,grr,<C-w>d,]d/[d) are available from Neovim 0.10+. On older versions use:lua vim.lsp.buf.*commands or set up keymaps manually in youron_attachcallback.
VS Code (tasks.json / manual)
Use any extension that lets you configure custom LSP servers, pointing cmd to streamd lsp.