@dailypulse/wq
v0.1.3
Published
The DailyPulse work queue, in your terminal
Maintainers
Readme
wq — the DailyPulse work queue, in your terminal
Your projects, ordered by how long you have left them alone, with one keystroke per decision. Built to sit in a narrow pane beside the terminal you actually work in.
npx @dailypulse/wqOr install it properly, which is worth doing if you use it daily:
npm install -g @dailypulse/wq
wqSetting it up
You need a personal access token: in the web app, open Profile → API tokens and mint one. Then:
mkdir -p ~/.dailypulse && touch ~/.dailypulse/credentials
chmod 600 ~/.dailypulse/credentials
cat > ~/.dailypulse/credentials <<'EOF'
DAILYPULSE_API=https://api-prd.dailypulse.ai
DAILYPULSE_TOKEN=dp_pat_...
EOFThat is the same file the Claude Code reporting hook reads, so if you already
have that set up there is nothing to do. wq refuses to read the file if
anyone else on the machine can — a token is a bearer credential, and tightening
the permissions after a leak changes nothing.
DAILYPULSE_API and DAILYPULSE_TOKEN work as environment variables too, and
override the file when set.
Keys
| Key | |
|---|---|
| w | worked it — records a touch, asks for an optional note |
| s | passed on it — the twin of work; also a touch, also a note |
| S | hide until tomorrow — queue state, records nothing |
| t | log hours — replaces today's figure rather than adding to it |
| j k | move (arrows work too) |
| g G | first / last |
| r | refresh |
| ? | help |
| q | quit |
The same keys as the panel in the web app.
The model
work and skip are twins: both instantaneous, both one entry in an
append-only history, differing only in the claim recorded. There is no
timer — nothing to start, nothing to stop, nothing to forget to stop. Hours
are separate and deliberate: t writes a real timesheet row when you choose to,
and nothing is ever inferred from a clock.
Which projects appear is decided by what you are assigned to, not by anything you can add here.
If you filed a check-in today, what you said about the selected project appears under it behind a gutter rule, with anything you flagged as blocking. That is the promise; the counters beside the project name are the record. Only the selected row shows it, because the text is unbounded and a list of them stops being a list.
Other flags
wq --doctor explain the configuration and check the token
wq --json print the queue as JSON, for scripts
wq --inline draw in the normal buffer instead of taking over the screen
wq --api URL point at a different API
wq --versionwq takes over the terminal while it runs and gives it back exactly as it was,
the way vim or htop do — your scrollback is untouched and quitting leaves no
copy of the list behind. --inline opts out of that, which is occasionally what
you want when piping or debugging a frame.
--doctor never prints the token itself.
Developing
The queue came from wq: a Textual TUI on a container, reached over SSH, that people kept open beside the terminal they were actually working in. DailyPulse absorbed the model but not the shape — it became a browser panel, and a browser panel is not that. This is the shape, back.
Deliberately one screen: the rotation, one keystroke per decision. Anything needing a form belongs in the web app.
| Path | What |
|---|---|
| src/cli.tsx | flags, config load, the pre-flight token check |
| src/config.ts | resolves API and token; shares the agent hook's credentials file |
| src/client.ts | the /cli/* client |
| src/ui/ | the Ink components |
| scripts/build-binaries.sh | optional standalone builds, see below |
bun install
bun test # 38 tests, no network and no terminal needed
bun run typecheck
bun run build && node dist/cli.js --doctorThe UI is tested with ink-testing-library, driving real keystrokes into a
rendered component and asserting on frames — test/ui.test.tsx is the place to
start reading.
To run against a local API, bin/dev.sh in the app root serves one on :3001:
DAILYPULSE_API=http://localhost:3001 DAILYPULSE_TOKEN=dp_pat_... bun src/cli.tsxThe server side
Everything here talks to /cli/* (api/src/routes/wq-cli.ts), which
authenticates with a personal access token rather than Cognito, because a
terminal has no browser to complete an authorization-code flow with. The domain
logic is wq-core.ts — shared with the web app and the MCP tools, so all three
clients agree about what the queue means.
Adding a verb here usually means adding a route there, and that is a
permission decision: api_tokens has no scopes column, so every token already
issued gains the new route the moment it ships. The route list is pinned by a
test for exactly that reason.
Releasing
Tag wq-v<version>; .github/workflows/wq-release.yml publishes one npm
package. Releases are tag-driven rather than merge-driven because npm is not
idempotent the way CloudFormation is: a version number is burned permanently
once published, and unpublish only works for 72 hours.
Standalone binaries
scripts/build-binaries.sh <version> produces six bun --compile executables
for people without Node. Not part of the npm release — npx is the primary
channel and needs none of it.
They are 63–96MB, because Bun embeds its whole runtime alongside ~200KB of JavaScript. That is the price of not requiring Node, and it is why npm stays the default.
Two things about macOS that are easy to get wrong:
- Apple Silicon refuses to start a binary carrying no signature at all — the
kernel kills it, with nothing about signing in the message. Bun ad-hoc signs
darwin/arm64while linking, which satisfies that with no Apple account involved. So nothing may rewrite the binary afterwards: astrip, a packer or a repack invalidates it.scripts/check-macho-signature.pyruns at the end of every build to keep that honest. - Gatekeeper's "unidentified developer" dialog is a separate thing, and comes
from the
com.apple.quarantinexattr — which browsers set andnpm,curland Homebrew do not. So npx never triggers it, and a download page should lead with acurlline rather than a button.
