This release is a pre-release and may not be stable for production use.
hey-dave
A terminal coding-agent harness that runs D.A.V.E. (Digital Assistant for
Various Endeavors) natively — not as a plugin inside someone else's harness.
D.A.V.E. is an independent implementation derived from the legacy skill bundle
in thesawdawg/agent-skills at commit ca9118c; see
src/hey_dave/dave/NOTICE for attribution.
D.A.V.E. is a personal assistant persona — opinionated, occasionally witty, not a productivity product. The banner shown at startup says so on purpose: unstable, opinionated, and sometimes unhelpful. Not a productivity tool.
An animated, watch-only tour lives in
docs/walkthrough/
(open index.html from a checkout in a browser). Nine goal paths, from setup
to delegation, each with scripted terminal scenes and a strip showing what the
harness does underneath.
Install
Python 3.14+ required. hey-dave is in alpha: releases are pre-releases, so installers need to be told to take one.
# uv tool (recommended)
uv tool install --prerelease=allow hey-dave
# or pip into any environment
pip install --pre hey-dave
# or from a checkout
uv tool install .
# Docker (includes SearXNG and Ollama profiles)
docker compose -f docker/compose.yaml run --rm dave --version
Upgrading from dave-harness. This project used to be called
dave-harness. Remove the old tool (uv tool uninstall dave-harness) and
install hey-dave; the command is still dave. On its first start, dave
moves your folders to the new name: ~/.config/dave-harness,
~/.local/share/dave-harness (sessions and the vault) and
~/.local/state/dave-harness become …/hey-dave, and a project's
.dave-harness/ becomes .hey-dave/ when you open it. Each move is
printed. Folders not yet moved keep working, and DAVE_HARNESS_CONFIG is
still read when HEY_DAVE_CONFIG is not set. Hook commands now see
HEY_DAVE=1 instead of DAVE_HARNESS=1.
First run
dave init # nine-step guided setup: models, permissions, tracker, vault
dave init --vibe # a model, then D.A.V.E. from three plain-language answers
dave doctor # environment + config health checks (--json for machines)
dave # the TUI
dave --resume # the TUI, starting with a list of sessions to pick up
dave run "summarise this repo" # headless
The wizard guides you through setup. Each step opens with a progress rail
(── ●●◉○○○ 3/6 vault ───) and a short card that says what the
step is for and what to pick. Every answer says what it leads to, and typing
? at any question explains it in more detail.
The first step asks where D.A.V.E.'s model will run. It looks for a Codex
sign-in (~/.codex/auth.json), and for Ollama and LM Studio running on this
machine, then lists what it found next to Ollama on another computer and any
OpenAI-compatible API (OpenAI, OpenRouter, vLLM…). Found servers show how many
models they have, and Ollama models without tool support are marked. For an
API key it asks for the name of the environment variable, not the key, and
says whether that variable is set. Then you pick a permissions preset
(cautious or developer), a tracker backend and a search backend, and the
wizard initialises the D.A.V.E. vault. Every question is a numbered list:
Enter takes the default, a number picks, and open questions end with "type
your own…". Everything is shown as a diff and written only after
confirmation.
dave init --vibe (or /init vibe in the TUI, once a profile works) sets up
D.A.V.E. from three answers instead of the permission, tracker, search and
persona steps. Its first four steps (models, profiles, vault, theme) need no
model, and it will not move past the first until one is set up:
- What is this D.A.V.E.'s purpose?
- What is D.A.V.E. explicitly denied?
- What should I know about you? (optional)
The model may ask up to two short follow-ups per answer, then proposes settings. Examples:
- "ask before any git commit" becomes the rule
permissions.ask: bash(git commit*). - "keep everything in this directory" becomes
permissions.confine: "project". File writes outside the project are denied, and shell commands that name outside paths ask. - Anything no rule can express is saved as an advisory restriction. The model is told about it, but nothing enforces it.
dave init --vibe also asks for a theme. The same theme colours the TUI and
the interview itself, and telling the interview about contrast or colour
needs can propose one. Both wizards are in colour: step headings, questions,
option numbers and the default each have their own. NO_COLOR turns terminal
colour off.
You tick what to keep. Suggested allow rules start unticked, and the harness refuses broad allow rules and any change to providers, secrets or existing deny rules. Nothing is written until you approve, and the interview is not saved.
Using the TUI
| Key | Does |
|---|---|
| Enter / Shift+Enter | send / new line; during a turn Enter queues the message (up to 3, tui.limits.queue) and it is sent when the turn ends |
| Up (first line) / Down (last line) | step through sent-message history; with messages queued and the input empty, Up pulls the last one back to edit and Esc drops it |
/ |
open the command list — Up/Down/PageUp/PageDown move, Tab completes, Enter runs, Esc closes |
@ |
open the file list — Tab or Enter completes the path (a folder opens its entries), Esc closes |
! |
run a shell command yourself: ! git status shows its output in the chat; !! make test also sends the output with your next message |
| PageUp / PageDown | scroll the transcript (the input box always keeps focus) |
| shift+tab | toggle plan mode (between turns) |
| ctrl+p | command palette |
| ctrl+b | show or hide the sidebar; under 110 columns, open or close it as a drawer (Esc closes it too) |
| ctrl+t | pending tracker proposals |
| ctrl+o | the activity list: every tool call, injected command text and event in full (on a [pasted …] placeholder: expand it) |
| ctrl+c | with text selected: copy it. Otherwise cancel the turn (queued messages pause: Enter on an empty input sends the next); while idle, clears a paused queue first, and twice quits |
| right-click | with text selected: copy it; otherwise paste |
| ctrl+v / alt+v | paste text / paste an image from the clipboard |
The indicator beside the input shows what is running (thinking, the current
tool, waiting for you, subagents) and for how long.
Shell commands. Start a message with ! to run it in your shell
instead of sending it: ! echo "Hello" shows Hello and [exit 0 · 0.0s]
in the chat. It runs in the session's folder with your environment, and
D.A.V.E. never sees it. Use !! to share the output: !! pytest -x runs
it and attaches the output to your next message ("why does this fail?").
Permission rules don't apply, since you typed it, and the model can never
run a ! command. Each command starts fresh (cd does not carry over),
ctrl+c stops it, and during a turn it waits in the queue. Over Telegram !
is refused.
Screen sizes. The TUI is fully usable from 80×24 and fits the space it has:
- From 110 columns: the sidebar sits beside the chat: 32 columns, or about a quarter of a wide screen (up to 56).
- Below 110 columns: the chat gets the whole width.
ctrl+bopens the sidebar as a drawer over the chat, and Esc closes it. The header shows what the sidebar would, on one line: the D.A.V.E. focus and its minutes (amber or red when you drift), your tasks, running subagents. The activity indicator moves to one row under the input. - The input box is one line tall until you type more; it grows up to 8 lines (4 on a short screen).
- The header and status bars drop their least important fields rather
than being cut off;
ctx %and the error count always show. - Every picker and card (
/model,/theme,/resume,/music config,/vault config, permission and plan cards…) fits the screen, with its keys in view. - Permission cards show the change. An
editorwritecard shows a colored diff against the file on disk (+/−counts, line numbers), and says so when the edit would fail. Long diffs show their first lines;eexpands the rest.
Make it yours. The top-level tui section of the config changes the
layout and keys, and applies as soon as it is saved:
"tui": {
"sidebar": { "sections": ["now", "tasks", "activity"], "position": "left", "shown": true },
"header": ["name", "mode", "ctx", "usage", "profile"], // fields, in order
"status": ["tokens", "music"], // the error count always shows
"keys": { "sidebar": "ctrl+s", "permission.allow_once": "y" },
"layout": { "sidebar_inline_from": 120, "composer_lines": 12 },
"skin": { "placeholder": "Ask D.A.V.E.", "sections": { "now": "Today" } },
"skins": { "matrix": { "rain": false } },
"banner": false
}
sidebar: which sections show, in what order, on which side, and whether it starts open. D.A.V.E.'s six (dave,now,goals,today,proposals,subagents) scroll together as one panel.headerandstatus: which fields the bars show.keys: any key in the list above, and every card's letters./keyslists what can be changed. Esc, Enter, ctrl+c and the arrows stay fixed, and a key that would type into the input or clash on a screen is refused. The hints on each card show the key you chose.layout: the sizes above: where the sidebar goes inline, how wide it is, how tall the input grows.skin(every theme) andskins.<theme>(one theme): the words and glyphs a theme uses: the header name, sidebar headings, activity tags, spinner, placeholder, rain, andtune, the built-in music it plays.themes: your own themes (below).banner:falseturns the startup banner off.
The chat holds only the conversation. Tool calls, the text a slash command
injects for the model, and harness events (compaction, warnings, subagent
reports) are listed under Activity at the bottom of the sidebar, in time
order, each tagged with its type — TOOL, CTX (injected context) or EVT
— in its own colour. Click a line, or press ctrl+o, to see it in full.
There, c copies the entry to your clipboard, and s saves it to a file
under ~/.local/state/hey-dave/exports/.
Copy and paste.
- Copy. Drag over any text (the chat, Activity, the input box, an open card) to select it, then press ctrl+c or right-click. A toast says what was copied and how. With nothing selected, ctrl+c still cancels or quits. shift+drag makes your terminal's own selection instead, for its own ctrl+shift+c.
/copycopies without the mouse, word for word as it was said:/copy: D.A.V.E.'s last reply, as markdown;/copy 2: the reply before it;/copy code: the last code block of the last reply, without the fences (/copy code 2is the one before);/copy all: the whole conversation.
- How it reaches the clipboard. Copying uses
clip.exeunder WSL,pbcopyon macOS,wl-copyorxclip/xselon Linux, and otherwise the terminal (OSC 52, which some terminals ignore).harness.clipboard: nativeorosc52forces one way. - Paste with ctrl+v, or right-click with nothing selected. A long
paste (over 20 lines or 2,000 characters,
harness.paste) shows as[pasted #1 · 240 lines]:- the model still gets all of it, after your words;
- Backspace deletes the placeholder whole;
- ctrl+o on it puts the text back in the box.
- Images. alt+v pastes the image on your clipboard (a screenshot,
say) as
[image #1]. It is sent as an image with your message and saved under~/.local/state/hey-dave/pastes/. That file is deleted once no saved session uses it.
@ puts files and folders into the context. Type @src/app.py (or
@"notes/big plan.md" for a path with spaces) anywhere in a message:
- A file is sent to the model with its contents, after your words.
- A folder is sent as a list of the files in it. In a git repository,
files your
.gitignoreexcludes are left out. - An image (
.png,.jpg,.gif,.webp, up to 5 MB, 4 per message) is sent as an image, and so is one D.A.V.E. opens withread. - Typing
@namelists matches from the folder you're in and from anywhere in the project, skipping what.gitignoreexcludes. You can still type the full path to an ignored file to attach it.
The chat keeps what you typed. Each attachment is a CTX » @path row in
Activity. Some things are refused, with a warning in Activity:
- a path your
permissions.denyrules block; - other binary files, and
.bmpimages; - files over 4 MB.
A file longer than 100,000 characters is cut off, and the model is told
where, so it can read the rest. dave run "explain @src/app.py" works the
same way. It also works in the arguments of commands from files (D.A.V.E.,
skill and your own commands): /mycheck @src/app.py attaches the file
after the command's text. A command can turn that off with mentions: false in its frontmatter. Built-in commands like /title take @ as
plain text.
Slash commands:
-
/helplists them;/<command> --help(or/help <command>) shows a command's details, subcommands and examples without running it. -
A command missing a required argument shows its usage instead of running.
-
/resumepicks up a saved session. It lists this project's sessions, most recently active first. Each row has the title, when you last used it and how many messages it holds, plus the first line of your last message and of the last reply. Under the list, a preview shows the highlighted session's last few messages. Type to filter by title, text or id; Tab switches to every project; Enter resumes; Esc stays where you are.dave --resumestarts the TUI with the same list,/resume <id>anddave --resume <id>go straight to one, anddave --continuetakes the latest./sessionsprints the same details for the last ten. -
/modelopens a picker of profiles and every provider's models;/model <profile> [model],/model <model>and/model <provider>/<model>switch directly. The choice is kept with the session. -
/rewindundoes D.A.V.E.'s file changes, turn by turn, even without git. Pick a turn to put the files it (and later turns) changed back as they were before it:aalso cuts the conversation back to that message, and puts the message back in the input box;ccuts only the conversation.
Files you changed since D.A.V.E. wrote them are shown as conflicts and need a second yes. Changes made by shell commands are not recorded, so turns where bash ran say so. A rewind can itself be rewound. Settings live under
harness.checkpoints. -
/init vibeconfigures D.A.V.E. from three answers (see First run). -
/themeopens a theme picker that previews as you move;/theme <name>switches directly. Both saveharness.theme. Each theme shows its WCAG contrast rating (AAA or AA). Every theme is tuned for readability: all text, including placeholders and muted text, reaches at least AA on every background it sits on, with muted text at 6:1 or better. The dark classics are adjusted the same way and keep their colours wherever they already passed. The themes come in five groups, plus your own:-
D.A.V.E.:
dave-dark(the default) anddave-light. -
Accessible:
dave-high-contrastanddave-high-contrast-light(AAA), anddave-colorblindanddave-colorblind-light(Okabe–Ito colour-blind-safe colours, AA). -
Dark classics: Dracula, Nord, Gruvbox, Tokyo Night, Catppuccin Mocha, One Dark, Monokai, Solarized Dark and Rosé Pine.
-
Immersive:
matrix,dndandlotr. These restyle the whole TUI, not just its colours:matrixis The Matrix: green phosphor on black, digital rain down the left edge, square frames,// OPERATOR-style headings,EXEC/LOAD/SYSactivity tags and a katakana spinner.dndis Dungeons & Dragons: torchlit parchment and gold, double frames, headings such as "The Party" and "Adventure Log" in Fraktur,CAST/LORE/OMENtags and a dice spinner.lotris The Lord of the Rings: gold on the green-black of a Shire night, round frames like hobbit doors, slow glowing runes down the left edge, headings such as "The Fellowship" and "The Red Book",DEED/TOME/SIGNtags, a rune spinner, and background music.
-
Avengers:
iron-man,captain-america,thorandhulk. Immersive like the three above, and each plays its own music:iron-man: the armour's cherry red and gold, frames with a lit title bar like a heads-up display, hex telemetry down the left edge, headings such as "J.A.R.V.I.S." and "Iron Legion",FIRE/SCAN/PINGtags, and a hard-rock riff.captain-america: the shield's navy, white and red, heavy-rimmed frames, star headings such as "The Mission" and "Field Log",OPS/INTL/COMMtags, and a brass march.thor: storm blue, gold and red, heavy iron frames, lightning and runes down the left edge, headings such as "The All-Father",BOLT/RUNE/HORNtags, and a thunder saga with horns and a choir.hulk: gamma green and purple, solid slab frames, headings that shout ("SMASH LIST", "RAGE LOG"),HIT/LOOK/ROARtags, a rage-meter spinner, and a rampage that breaks into a lonely piano.
A terminal app can't change your font. These themes use Unicode letterforms instead (Fraktur, half-width katakana, runes), which need a font that has them or your terminal's fallback. Windows Terminal and most Linux terminals have one. With
TEXTUAL_ANIMATIONS=nonethe rain holds still. -
Yours: themes you define under
tui.themesin the config. Each starts from a built-in theme (base) and takes its frames, words, music and banner quotes, with any colours and skin parts you set on top:"tui": { "themes": { "ocean": { "base": "matrix", // unset: dave-dark "description": "deep sea blues", // shown by /theme "colours": { "primary": "#4aa3ff", "background": "#001018", "muted": "#7fa0b8" }, "skin": { "app_name": "~ dave ~", "tune": "thor" } }, "nord": { "colours": { "accent": "#ffcc00" } } // your version of Nord } }The colour roles are
primary,secondary,accent,success,warning,error,foreground,background,surface,panel,mutedandborder, as#rrggbb. Your themes are made readable like the rest: a colour that falls under AA is lightened or darkened just enough, and/themeshows the rating. They are listed under Yours, and a theme named after a built-in replaces it. A change applies at once, even to the theme on screen;/davecan make it for you.
-
-
/musicplays music while you work.-
/music configis the easiest way to set it up. It opens a screen where you pick a source and fill in its settings. It checks what works as you type: the player, the folder's tracks, and the Spotify sign-in and open devices. Nothing plays while you set up. Enter saves, and ctrl+l signs in to Spotify. -
Or use
/music source …to pick what plays. The choice is saved: -
tune(the default): the theme's own music, forlotrand the four Avengers. It starts with the theme, changes when you switch to another of them, and stops when you switch to a theme without music. Each piece is original (none is a film's score), synthesised once and cached: a folk air forlotr, a rock riff, a march, a thunder saga and a rampage for the Avengers. Setharness.music.fileto play your own audio file instead, in any of those themes. -
local <folder>: your own.mp3(or.flac,.ogg,.m4a, …) files, in any theme, as a playlist. For example,/music source local ~/Music/focus. -
spotify: your Spotify account. The harness controls Spotify wherever it's open (the desktop app, your phone, a speaker), and the music plays there. You need Premium and your own client ID:- Create an app at https://developer.spotify.com/dashboard. Tick
Web API, and add the redirect URI
http://127.0.0.1:8898/callback. - Run
/music login <client-id>and sign in in the browser. - If the page after sign-in doesn't load, paste its address:
/music login <address>.
The tokens stay in the state directory (0600), and
/music logoutdeletes them./music play <link>plays a Spotify playlist, album or track link. - Create an app at https://developer.spotify.com/dashboard. Tick
Web API, and add the redirect URI
-
youtube(saved asmedia): the system media keys. They control YouTube in a browser or the YouTube Music app, or any other player that has the media session: Windows' under WSL, orplayerctlon Linux. You start what plays in that player; the harness only plays, pauses and skips.
-
-
Controls:
/musicsays what's playing, where, and at what volume.on,off,play,pause,resume,next,prev,volume <0-100>,shuffle on|offandrepeat off|all|onework where the source allows.- F7/F8/F9 are previous, play/pause and next.
- The status bar shows the current track, for example
♪ 02 Second (2/12).
-
The harness's own player (for
tuneandlocal) is the first one it finds:ffplay(from ffmpeg),mpv,pw-play,paplay,afplayoraplay.- Choose one with
harness.music.player, and set the volume with/music volume(harness.music.volume, default 40). - On WSL,
ffplayworks through WSLg's sound server. - It never starts loud. Before starting on its own, it reads your system
volume (Windows' volume on WSL, PipeWire, PulseAudio or ALSA on Linux,
or macOS's). If system × music volume would be over 25%, it holds and
asks: lower the volume, or
/music onto play anyway. That confirms it for the rest of the session.
- Choose one with
-
DAVE_NO_AUDIO=1keeps the harness silent: no player, no media keys, no Spotify calls.harness.music.player: "none"silences the harness's player only. -
/copycopies a reply, a code block or the conversation (see Copy and paste above). -
/errorslists what went wrong this session (see When something goes wrong below). -
/statussummarises the session: profile, context (and images in it), focus, subagents, waiting reports and proposals, and remote state. -
/remoteconnects the open session to a chat app (see Remote). -
/vaultshows vault sync;/vault configsets it up;/vault sync,/vault login [client_secret….json|client-id],/vault logout [--forget-client](see Sync across devices below). -
D.A.V.E. commands:
/brief,/focus,/item,/sidequest,/park,/check,/delegate,/mission,/project,/promise,/intake,/review,/standup,/supervise.
dave run "/<command> --help" prints the same help headless.
Permission cards
When D.A.V.E. needs your yes, the card shows the call and how long an answer can last:
| Key | Does |
|---|---|
a |
allow this call |
s / p / g |
allow this session / for this project / everywhere: every project, every open and future session |
d |
deny: d again denies this call; s / p / g deny for that long; Esc goes back |
n |
deny and tell the model why |
b |
widen what s/p/g save: the exact call, its prefix (bash(git status*), a folder, mcp(redmine:*)), or the whole tool (bash(*)). The card shows the rule |
| Esc | deny this call |
Project answers go to .hey-dave/config.json, everywhere answers to
~/.config/hey-dave/config.json, as permissions.allow or
permissions.deny. A file path saved everywhere is absolute. A project's
rules add to the global ones and never hide them. Every open session picks
up a saved rule on its next call. /permissions lists the rules by scope
and /permissions remove <rule> takes one out; /permissions reset-session drops this session's. Over /remote answers are for this
call only.
Plan mode
Plan first, then change things. /plan add a --json flag to dave doctor (or shift+tab, then type) puts the session in plan mode:
- D.A.V.E. can read, search and run look-only commands:
git status,git log,git diff,git show,ls,cat,head,tail,wc,rg,grepandfind. Change the list withpermissions.plan_bash. Every other command, every write and edit, and redirection are denied, whatever your allow rules say. So arefind -delete/-execand D.A.V.E. state writes. A listed command still needs your usual allow rule or a yes on its card. - The header says
mode: planand the input box takes the theme's warning colour. - It ends with a plan card:
aapproves: plan mode ends and D.A.V.E. implements the plan in a new turn, so/rewindcan undo it as one step;malso saves the numbered steps as the open mission's plan, ready for/supervise. It asks first;kkeeps planning, with your feedback;dor Esc discards the plan and stays in plan mode.
/plan or shift+tab toggles plan mode, and /plan on|off sets it. The mode
is saved with the session. Over /remote the card has Approve and Discard
buttons, and any message is feedback. dave run --plan "<task>" prints the
plan and stops (--json emits plan_proposed).
Unfocused sessions
General work, with your projects held back. /unfocused is for work
that belongs to no task: a quick script, a question, tidying notes.
- D.A.V.E. works normally, but files inside your registered projects are
read-only. Writes and edits there are refused. Shell commands that run in
a project or name a path in one ask first, unless they only look (the
permissions.plan_bashlist). Subagents inherit the hold. - Your current focus is paused under a general detour, so the time shows in the ledger and the weekly review as general time, not drift. After the drift threshold D.A.V.E. mentions it once per period, then stays quiet.
- Setting a focus ends unfocused mode and lifts the hold: picking a task is
how you get back into a project.
/unfocused offends it and returns to the paused focus. - Each session's unfocused periods are recorded in the vault under
sessions/general/<device>/<yyyy-mm>/<session-id>.md: when, why each ended, the files changed and how many calls were held back. Transcripts stay in the normal session store. - The header says
mode: unfocused. The mode is saved with the session, and a resume starts a new period. With no focus at session start, D.A.V.E. offers it once. It needs a vault.
Task list
On work of three or more steps D.A.V.E. keeps a short checklist with
todo_write. It shows in the sidebar's Tasks section, between the
D.A.V.E. panel and Activity: ☐ to do, ◐ in progress, ☑ done, and a
count in the heading (Tasks 2/5). The list belongs to this session only.
It is saved with the session, and it is not your Now list or a mission's
plan. It hides while empty, works in plan mode, and is switched off under
/supervise, where the mission's steps are the plan.
Notifications
When you're in another window, the terminal tells you that D.A.V.E.:
- finished a turn that took 15 s or more (
harness.notify_after); - is waiting on a permission card or a plan card;
- has a subagent report ready.
Nothing fires while the dave window has focus, unless you set
harness.notify_when_focused: true.
-
harness.notify: "bell"(the default) rings the terminal bell. Windows Terminal flashes the tab or plays its bell sound, depending on your profile's bell setting. -
"desktop"shows an OS notification with a short text such asturn finished (42s). It never includes tool output.dave doctorshows the route it picked for your terminal:Terminal Route Windows Terminal under WSL a Windows toast (via powershell.exe)Windows Terminal with compatibility.allowOSC777onset harness.notify_via: "osc777"iTerm2, Apple Terminal, ConEmu OSC 9 WezTerm, Ghostty, Warp, GNOME Terminal and other VTE terminals OSC 777 kitty, foot OSC 99 anything else the bell Inside tmux, add
set -g allow-passthrough on. -
"off"turns notifications off./remotehas its own list (remote.notify).
When something goes wrong
A failure shows as a card in the chat, never as a silent freeze or a crash:
✗ Internal error while showing a tool result — KeyError: 'path'
kept: the turn is still running; nothing lost
next: /errors copy 1 to report it
- The status bar counts errors, and
/errorslists them./errors 1shows the newest in full, and/errors copy 1copies a bug report with secrets removed. - The full traceback is in the log.
/errors logprints where the log, crash reports and stall logs are. - Nothing typed is lost. A reply that was streaming when the app closed comes back on resume, marked as interrupted. Your unsent message and queued messages come back too, with the queue paused.
- If the app ever does close on an error, it says so in the terminal. It
prints the command to resume your session (
dave --resume <id>) and the path of a crash report with secrets removed. The next start also notes that the last run did not close cleanly. - When quitting seems stuck, press ctrl+c again. When the whole app
seems stuck, run
dave debug stacksin another terminal. It prints what every part of the app is doing, even while it is frozen, and doesn't stop it. dave doctoralso checks where sessions are saved: whether the folder is writable, whether there is disk space, and whether it is on a slow filesystem (WSL's/mnt/c, network drives). It also lists runs that did not close cleanly and recent crash reports.- A subagent that has been silent for 5 minutes shows
idleon its sidebar card. If the disk fills up, the session is kept in memory and saved once there is room again. - A frozen screen is noticed. If the app stops responding for 2 s, what
it was doing is written to a file. After 30 s, the bell rings once and
the terminal title says
not responding. When it recovers, a card says how long it was frozen and where. When the model is silent, the indicator sayswaiting for <model> · 35s, thenno response for 60s · ctrl+c cancels. - ctrl+c always stops a turn, even when the model has gone silent. After about 1.5 s the turn is cut off, and the text so far is kept.
- When a provider fails and the request is retried, the indicator says so
(
retrying 2/3 after network error). Setharness.turn_stall_secondsto stop a model that has gone silent and retry once.
Remote
/remote connects the open TUI session to Telegram, so you can follow it,
approve single actions and talk to D.A.V.E. from your phone. Nothing polls or
leaves the machine until you configure a channel and run /remote on. The
bridge stops when the TUI closes.
Setup:
- In Telegram, message @BotFather, send
/newbot, and copy the token. - Export it:
export DAVE_TELEGRAM_TOKEN=<token>(the config only names the variable,remote.telegram.bot_token_env). dave config set remote.channel telegram, then startdave.- In the TUI,
/remote pairshows a 6-digit code. Send/pair <code>to your bot in a private chat within 5 minutes. Your Telegram user ID is stored inremote.telegram.allowed_user_id. /remote testsends a test message.dave doctorchecks the token and the pairing.
In the chat:
- Plain text is a prompt, and
/command argsruns a slash command. During a turn the phone is toldbusy: the terminal's message queue is terminal only. /cancelstops the running turn./helplists the commands allowed remotely.- Permission cards arrive with Allow once / Deny / Deny + reason. Whichever of the terminal or the chat answers first wins.
Remote can never grant a session or project rule, or Allow writes on a
delegate card. /quit, /resume, /export and a few other commands reply
"terminal only". Only the paired user in a private chat is heard; everything
else is dropped silently.
Telegram bot chats are not end-to-end encrypted. With remote.share: "summary" (the default), only these are sent:
- replies;
- the commands you are asked to approve;
- paths, diffstats and tool outcomes.
"full" also sends tool output and diffs as files. remote.notify picks the
notifications: turn_finished, permission, report_ready, error and
drift. remote.autostart: true starts the bridge with the TUI. Details are
in docs/spec/10-remote-channels.md.
Configuration
One JSON file: ~/.config/hey-dave/config.json (project overrides in
.hey-dave/config.json; ${ENV_VAR} references for secrets — never
literal keys). Manage it with dave config get|set|validate. Details:
docs/spec/01-config.md.
{
"providers": { "codex": { "type": "codex" } },
"profiles": { "main": { "provider": "codex", "model": "gpt-5-codex" } },
"harness": { "default_profile": "main" },
"roles": { "scout": "main" }, // roster role → profile routing
"permissions": { "allow": ["bash(git status*)"] },
"mcpServers": { "redmine": { "command": "…" } },
"tracker": { "backend": "local" }
}
Provider kinds: codex (Codex CLI auth), openai_compat, ollama,
scripted (tests). Every kind lists its models from the server: codex
asks the ChatGPT backend for your account's models, as the Codex CLI does.
Limits and timeouts
The sizes, counts and waits the harness used to fix are settings, each next to the feature it tunes, with the old value as the default:
harness.tools: the longestbashtimeout, thereadsize cap,globand search caps, the task list's length, a hook's timeout;harness.shell(!and!!),harness.mentions(@),harness.context(compaction,AGENTS.md),harness.housekeeping(backups, crash reports,dave doctor's disk and crash checks);tui.limits: the queue, the activity log, the busy line's "waiting", the notification gap, the sidebar refresh,/resume's preview;remote.limits: messages a minute, the pairing window and attempts;- per provider
max_attempts(and Ollama'scapability_ttl_hours, Codex'srefresh_early_seconds),plugins.web_fetch.max_redirects.
Each has a hard range, so no value switches a guard off, and raising a
remote rate or pairing limit through /dave is flagged as widening
access. /dave reads them all (harness_guide limits lists every one).
Set up from the chat: /dave
/dave <request> asks D.A.V.E. to work on the harness itself, without
leaving the TUI. For example:
/dave help me add my Ollama box as a provider;/dave route the critic role to the local model;/dave why is web search failing?
It enters harness mode (mode: harness in the header), and your replies
continue it until /dave off.
- What it can do. In this mode D.A.V.E. can only read the config (with secrets hidden), read built-in setup guides, run read-only checks, and propose changes. It has no file, shell or web tools.
- What it can see. Every section of the config and what each holds,
whether set or not, with each key's type; every key id you can remap;
each helper role as packaged; every command you can turn off. It can
also read D.A.V.E.'s own settings in the vault (your name, hours,
review day, which helpers are on…) and which of D.A.V.E.'s texts are
your versions, but it can't change those here: it tells you how
(
/dave offand ask,/init vibe, ordave prompts copy). - The card. Every change comes as a card: tick the items you want and
press Enter;
fsends feedback and Esc rejects.- Anything that widens access starts unticked and needs you to type
allow: an allow rule, a removed deny rule, turningconfineoff. - New MCP servers and skill folders (which run commands) and new provider hosts are flagged with a ⚠.
- Anything that widens access starts unticked and needs you to type
- API keys are never written into the config. D.A.V.E. uses an
api_key_envname, and the card lets you paste the key for this session only; export it in your shell profile to keep it. - After you apply, the files are backed up to
~/.local/state/hey-dave/config-backups/, and the change reloads at once. Plugins, MCP servers and skills load at the next start. Checks then run (the new provider's model list, for instance), and D.A.V.E. fixes what failed. /dave undoputs the files back. See docs/spec/02-providers.md.
Images go to every provider. Ollama is asked whether the model has vision;
for the others, set "images": false on a profile whose model can't take
them. The model then gets a short note instead of the image. Sessions keep
the image's path and hash, not its bytes, so an image moved or edited later
is sent as a note, not a different picture.
External agents (Claude, Devin)
D.A.V.E. can hand a charge to an external coding agent over
ACP — Claude Code through the
claude-agent-acp adapter, or Devin through devin acp — instead of a
harness subagent. They are workers, not model connections: they never
answer D.A.V.E.'s own turns, and they run with your own sign-in. There
is no credential field; the harness never logs in for them, and the agent
process gets your environment unchanged (a key the harness resolved for a
profile is not forwarded — if your shell exports ANTHROPIC_API_KEY,
Claude's worker bills that key, not your plan; the card and dave doctor
say which).
Setup:
- Claude Code:
claudeinstalled and signed in, then the adapter —dave plugins install @agentclientprotocol/claude-agent-acp --latest-stable(into the harness's npm prefix; needs Node ≥ 22). - Devin: the
devinCLI installed and signed in (Pro, Max or Teams);devin acpis Cognition's own ACP entry point.agents.providers.devin.cloud: truerunsdevin acp --cloud. - Check:
dave doctorgives each enabled provider anagent:<id>line — the binary (PATH, then the npm prefix), version, Node floor, aninitializehandshake and the sign-in method. It never opens a session, signs in, or installs anything. Bare/agentin the TUI lists the same, plus the external workers running.
Use:
/agent claude fix the login redirectdispatches a charge (theimplementerrole, or--role critic). The dispatch card always comes first — provider, role, model, local or cloud, the billing method, and a warning when the provider can act outside the harness — because it spends your agent's own quota; no allow rule skips it. One charge per provider at a time (agents.providers.<id>.max_concurrent); more queue.- D.A.V.E. can dispatch too:
delegate(agent="claude")for one charge, oragents.routesto route a role ("agents": {"routes": {"implementer": "claude"}}).agent="local"forces the harness worker.quartermasterandscribealways stay local — their work is the vault's own tools. - Reports come back like any subagent's and list the files changed.
/agent stop [id]cancels a running worker;delegate(resume="<id>")sends a follow-up charge (a live worker is reused; after a restart the stored ACP session is reloaded — nothing respawns on its own). - Headless:
dave run "/agent claude <charge>".--approve agentpre-approves only the dispatch card — the agent's own file and terminal asks still deny, so unattended runs stay read-only unless you also pass--approve allow.
What the harness enforces for an external charge:
- Its file writes, reads and terminals go through your permission rules
and cards. "Allow always" stays a harness grant — the agent keeps
asking. ACP-mediated writes are checkpointed, so
/rewindundoes them.git push, the vault,.hey-dave/and the agents' credential stores (~/.claude/.credentials.json,~/.config/devin/) are hard-denied, reads included. - Only safe modes are ever selected (
defaultfor Claude,askfor Devin;planfor read-only roles) — bypass modes are refused, and an agent-initiated switch to one is put back. - Claude is a bypass provider: its SDK edits files and runs commands
itself, so only its permission requests reach the harness — and allow
rules in your own
~/.claude/settings.jsoncan approve its actions without a card. The dispatch card warns, and the report lists what changed outside the harness's sight (a git diff), marked as not covered by/rewind.planmode is the stricter lever.
Policy links (checked 2026-09-26 — they change, so dave doctor prints
the date too):
- Claude Code: legal and compliance, Agent SDK overview, use the Agent SDK with your Claude plan.
- Devin: ACP docs, Platform Terms of Service, Acceptable Use Policy.
Other ACP agents can be added by config alone
(agents.providers.<id>.command). Details: spec 03 External agents.
Extending
- Skills — markdown
SKILL.mdunderskills.roots(default~/.config/hey-dave/skills,~/.agents/skills,./.hey-dave/skills); slash commands and hooks too.dave skills list. Command files can declareusage,helpandexamplesfrontmatter so--helpand argument checks work for them. docs/spec/04. - Bundled skill: archify — ask D.A.V.E. for an architecture, workflow,
sequence, dataflow or state diagram and it writes a validated, interactive
HTML page (pan, zoom, search, light and dark, exports). It ships with the
harness and needs Node.js 18+ on
PATH; without it the skill is hidden anddave doctorsays so. Your ownarchifyin a skills root replaces it;skills.disabled: ["archify"]turns it off. MIT, from tt-a1i/archify, pinned to the harness release with no update check. - Plugins — Python entry points (
hey_dave.plugins) or built-ins;dave plugins list. docs/spec/05-plugins.md. - MCP — paste a Claude-Desktop
mcpServersblock unchanged; tools appear asmcp__<server>__<tool>. docs/spec/05. - Trackers —
local(file-backed) orredmine_mcp(Redmine over MCP); outward writes are always proposed and approved individually (/trackerpanel).dave tracker list. docs/spec/05. - Web search —
searxngorbravebackends;dave search "query". - Web pages —
web_fetchreads a page as markdown: the main content, with headings, code and links, minus menus, footers and scripts.- Each new host asks first (
GET <url>on the card). Allow a host for good withnetwork(web_fetch:docs.python.org), or every host withnetwork(web_fetch:*). - Internal addresses (localhost,
10.x,192.168.x, cloud metadata) are refused, even after a redirect. Setplugins.web_fetch.allow_private: truefor intranet docs. - Page text is marked untrusted, and D.A.V.E. is told never to follow instructions in it.
- Only HTML, text, markdown, CSV and JSON are read; PDFs and images are
refused. Limits are under
plugins.web_fetch(20 s, 2 MB, 20,000 characters). It works in plan mode. Turn it off withplugins.disabled: ["web_fetch"].
- Each new host asks first (
Using your Claude Code plugins
hey-dave can read Claude Code plugin directories — nothing runs until you enable one, and foreign code never runs inside the harness's own process.
dave plugins listshows what was detected:plugins.sourcesentries always, plus~/.claude/plugins/marketplace installs and this project's own.claude-plugin/(setplugins.detect: falseto scan only named entries). Each source starts asavailable.dave plugins enable claude:<name>enables it (disabletakes it back), or add{"format": "claude", "path": "…", "enabled": true}toplugins.sourcesyourself./pluginsshows the same list in the TUI.- What then works: the plugin's skills, slash commands (
/<plugin>:<name>), hooks (SessionStart,PreToolUse,PostToolUse,Stop) and MCP servers (mcp__<plugin>-<server>__<tool>). Anything the format has that the harness cannot run — unknown hook events, LSP servers, output styles, themes — is listed asnot run/unsupported, never an error. - Hooks can only tighten:
denyrefuses the call,askforces the approval card, andallowis ignored — the permission gate decides. - Agents become
delegateroles named<plugin>:<name>that start read-only — no shell, no writes — until you bound them withdave.roster.<plugin>:<name>. dave plugins install <pkg>@<ver>(or--latest-stable, which takes the newest release at least 7 days old) installs an MCP server's npm package into the harness's own prefix, with install scripts off by default — nothing is ever installed automatically.
Using your Pi extensions
Pi extensions are TypeScript — hey-dave hosts them in
a small vendored Node shim, one process per extension, so nothing foreign
runs inside the harness itself. Needs Node 22+ on PATH (or installed into
the npm prefix).
dave plugins listfinds~/.pi/agent/extensions/, this project's.pi/extensions/,~/.pi/agent/skills/,.pi/skills/, andmcpServersin~/.pi/agent/settings.json. Without Node 22+ an extension showsunavailable: needs node ≥22.dave plugins enable pi:<name>loads the extension once to check it — that's the moment its code first runs — and refuses extensions that register no tool.- What then works: the extension's tools, arriving as
mcp__pi_<ext>__<tool>under the normal MCP permission kind (plan mode blocks them); skills roots andsettings.jsonMCP servers import aspi:skills/pi:project-skills/pi:mcp. - What is listed but never run: everything else the extension registered —
events (
on:<event>, includingtool_call), commands, shortcuts, flags, providers, renderers, messages, session entries andctx.ui. The shim records them asunsupportedrather than emulating them. - A missing dependency shows
cannot find module '<x>'; when the extension ships apackage.json,dave plugins install --from pi:<name>runsnpm installin its directory (scripts off by default).
D.A.V.E. state
The vault lives at $DAVE_VAULT, else dave.vault_path, else
~/.local/share/hey-dave/vault/ — an append-only JSONL journal plus
derived views, written only by hey_dave.dave.state. Inspect it without
starting a session:
dave brief dave today dave focus show
dave mission list dave review dave vault doctor
Goals and tasks are durable vault items. `/focus new annual|deadline|daily
<title>` creates and focuses one. `/sidequest <title>` captures an unplanned Daily Task for today without changing focus or Now; it accepts `--priority` and `--due`. Use `/item` to list, add, inspect, edit, link, complete, reopen, reschedule, backlog or skip items, `/item now ` to propose one for Now, and `/item plan` to see goals Now does not serve yet. The CLI equivalents are `dave focus new`, `dave sidequest`, and `dave item`. Annual Goals have a calendar year, Deadline Goals have a due date, and Daily Tasks have a work day.Now stays the one commitment list. A task is on plan when it or a goal it
supports is in the ranked list; anything else, a Daily Task included, counts
as drift. Priority expresses importance and never promotes anything: an
Urgent item outside Now makes D.A.V.E. ask whether it belongs there, through
the usual diff. The sidebar's Goals section shows each open Annual Goal's
progress and how long since it last moved; Today carries unfinished tasks
forward, marks which goal each serves or that it was a sidequest, and holds
priorities.max_today before D.A.V.E. asks what to move. The weekly review
sets planned work against sidequests, and on review.plan_day D.A.V.E. offers
to pick the week's goals for Now. Dates never complete items automatically,
and classifying an old focus ref is offered once, never required.
D.A.V.E.'s own settings live in the vault's config.json and are set with
dave config set dave.vault.<key> <value> (or /init vibe, or by asking
D.A.V.E.). Each one takes effect:
user.timezone(an IANA name such asEurope/Berlin) decides what "today" is for the journal, promises, the brief and the review; empty uses the machine's zone.user.work_hours(09:00-17:00) makes the brief say when you are working outside them.priorities.max_nowcaps Now: D.A.V.E. cannot grow it past the cap, and the sidebar shows that many.priorities.ranking_factorsis what it ranks by, in order.review.dayis the weekday the brief calls the weekly review due.priorities.max_todaycaps Today (5): a task over it is still saved, and D.A.V.E. asks what to reschedule.review.plan_dayis the weekday it asks which goals this week's Now serves;review.deadline_horizon_daysis how far ahead Deadline Goals show in the brief.kanban.boardsnames your intake boards; once the last intake iskanban.stale_after_daysold, the brief says the boards may be stale.
Make D.A.V.E. your own
Its words. Every prompt D.A.V.E. runs on (dave.md, persona.md,
first-run.md, supervisor.md, the six references), the ten role
contracts and its slash commands can be replaced or added to from the
vault, so your versions sync with it:
dave prompts list # every file, and whether yours replaces or adds to it
dave prompts copy persona # puts persona.md in <vault>/harness/prompts/ to edit
dave prompts copy roles/critic --append # starts roles/critic.append.md: added after the packaged text
dave prompts show roles/critic # the text in use
<name>.md replaces the packaged file; <name>.append.md is added after it,
so upgrades to the packaged text still reach you. A new file in
harness/commands/ adds a command. D.A.V.E. can't edit these files: the
vault is off limits to its write tools.
Its helpers' limits. Each role's tools, shell commands, write paths and
dave_* actions can be replaced in the config:
"dave": { "roster": {
"critic": { "bash_allow": ["git diff*", "uv run pytest*"] },
"scout": { "tools": ["read", "glob", "grep"] }
} }
A role never gets delegate, or writes to priorities, promises, focus or
the config, whatever this says. When D.A.V.E. proposes one of these changes,
it shows as widens access, unticked.
Its commands. "commands": {"disabled": ["standup", "music"]} turns
slash commands off: they disappear from the list and /help, and typing
one says it is off. /help, /quit, /keys, /dave, /errors and
/init always stay.
Sync across devices (Google Drive)
One D.A.V.E. on every machine: the vault stays a local folder that works
offline, and the harness mirrors it to a folder in your Google Drive. Each
device adds only to its own journal, so what you log on the laptop shows up
on the desktop. A file edited on two devices keeps both versions: Drive's in
place, yours beside it as <name>.conflict-<device>-<time>.md, and D.A.V.E.
mentions it until you merge it.
Setup, once per Google account, then /vault login on each device:
- In Google Cloud, enable the Google Drive API. Set up the OAuth consent screen (External, add yourself) and publish it: while it says Testing, Google signs you out every 7 days.
- Create an OAuth client ID of type Desktop app, then Download
JSON (
client_secret_….json). - In the TUI,
/vault login ~/Downloads/client_secret_….json: it keeps the client on this device, turns sync on and opens the browser. Or/vault config: a setup screen with these steps (ctrl+o opens the console), a field for the file, and checks that change nothing; ctrl+l signs in with the screen open and shows how it goes, with a box for the redirect address if its page doesn't load. From a shell:dave vault login --client-file ~/Downloads/client_secret_….json. /vault sync(it also syncs on its own from then on).
The client ID and secret stay on the device (the state directory, 0600,
never the config), so signing in again later, after a logout or when the
sign-in expires, is just /vault login: nothing to retype. /vault and the
setup screen say when a sign-in expired, or when Google says it will; /vault logout --forget-client also deletes the saved client.
Use the same client on every device. The harness asks only for drive.file:
it sees the files it created, nothing else in your Drive. Tokens are kept in
the state directory (0600), never in the config or the vault. In the TUI it
syncs at start, shortly after D.A.V.E. writes, every 5 minutes and at exit
(dave.sync.auto, interval_minutes, push_delay_seconds). /vault shows
where it stands, and dave vault sync --dry-run shows what would move.
Two machines with the same hostname need DAVE_DEVICE set on one of them.
Deletions don't sync: remove a file on every device. See
docs/spec/06-dave-state.md Sync.
Docker
docker compose -f docker/compose.yaml run --rm dave # TUI
docker compose -f docker/compose.yaml --profile search up -d # SearXNG
docker compose -f docker/compose.yaml --profile ollama up -d # Ollama
Volumes: ~/.config/hey-dave → /config (create it first — Docker makes
missing bind sources root-owned), named dave-data → /data (the vault lives
here), dave-state → /state, the directory you run compose from (or
$PROJECT_DIR) → /work, and ~/.codex read-write (Codex token refresh
writes back to the mounted file).
SearXNG needs a secret in the environment and refuses to start without one:
export SEARXNG_SECRET=$(python3 -c "import secrets; print(secrets.token_hex(32))")
docker compose -f docker/compose.yaml --profile search up -d
dave config set plugins.web_search.searxng.base_url http://searxng:8080 # inside compose
From the dave container SearXNG is http://searxng:8080 — localhost
there is the harness container itself.
A host Ollama is reachable as http://host.docker.internal:11434; dave discover inside Docker only scans the compose network. Non-interactive
provisioning: dave init --non-interactive --from config.json.
See docs/spec/08-packaging-deploy.md.
Development
bash scripts/validate.sh # frozen sync, quality gate, clean installed-wheel smoke
Releases publish themselves: bump the version in pyproject.toml and
src/hey_dave/__init__.py, commit, then push a tag such as v0.1.0a2.
GitHub Actions runs the full gate, checks the tag matches the version, and
publishes to PyPI through trusted publishing. See
spec 08
for the one-time PyPI and GitHub setup.
Tests use temporary personal-state paths and block external sockets by default. MCP and Spotify callback integrations are explicitly marked for loopback TCP. See the dependency policy for update boundaries and CI validation for the shared gate.
Normative specs and the staged plan live under docs/ — see
docs/README.md.
Interactive diagrams of the design (the system map, a turn, the permission
gate, delegation, persistence and the stage roadmap) are in
docs/diagrams/; bash scripts/diagrams.sh
re-renders them from their JSON sources.
To try features by hand, user-test-docs/setup.sh builds a throwaway demo
project in ~/dave-demo (a small calculator with a planted bug, test
images, and files that exercise @ limits).
user-test-docs/CHECKLIST.md lists the manual
checks, each with what you should see.
Resuming a session from another project asks before loading that project's
settings, skills and plugins. Headless resume requires starting in the saved
project. Changes to dave.vault_path take effect after restarting D.A.V.E.
--continue resumes this project's most recently active saved session.
Session listing reflects renamed titles and profile/model changes. An interrupted
final session write can be recovered; its damaged bytes are preserved in a
private .corrupt sidecar before the next write. Corruption in the middle
produces a file-and-line error while other sessions remain discoverable.
Streaming replies use batched Markdown rendering. Recursive searches skip
what your .gitignore ignores (or, without ripgrep and git, folders like
node_modules and .venv). They are cancellable and bounded: at most
10,000 files are searched, output stops at 1 MiB, and a content search has
60 seconds. A search that reaches a limit still returns what it found, with
a note to narrow the path or pattern. Deny rules hold for every file a
search opens. Allowing a search once covers the files it finds; files an
ask rule covers share one extra prompt. The session picker searches the newest
50 sessions per scope and reuses unchanged summaries between openings.
License
hey-dave is free for noncommercial use under the PolyForm Noncommercial License 1.0.0: personal projects, study, research, hobby use, and use by charities, schools, public research and government bodies. Using it for a commercial purpose, including work for a company, needs a separate license from the author. The bundled Archify skill and the Pi shim's inlined dependencies keep their own licenses (MIT, ISC, BSD-3-Clause, OFL-1.1); their texts ship with the package.
Metadata
Release files for hey-dave 0.1.0a2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hey_dave-0.1.0a2.tar.gz | 1.9 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hey_dave-0.1.0a2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.9 MB
Release files / hey_dave-0.1.0a2.tar.gz
| Download URL | hey_dave-0.1.0a2.tar.gz |
|---|---|
| Size | 1.9 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ddd8cd4fb6ba87334ff5ff413d74b9b24fa2edf307fe18430654d2c5bb07fd92
|
|
BLAKE2b-256 checksum How to use checksums |
22c4c6e0560658e23a13da33f5152d2026b0dc451f403b6c117ba436e6b5406e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency logRelease files / hey_dave-0.1.0a2-py3-none-any.whl
| Download URL | hey_dave-0.1.0a2-py3-none-any.whl |
|---|---|
| Size | 2.0 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
03118bbc5ed0bfd6359c9c7989b1c43c0563a8155ebc1506b1867802cc82ed06
|
|
BLAKE2b-256 checksum How to use checksums |
dd403d1a9c9b97fbe315c85af29f2a2cff77ebadcdbb551fb7062fe188887fc1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency log