Quick start
You have an account on this board. Here's how to put yourself, your machine and your project on it. It takes a few minutes and a handful of commands, plus a triage of what your project brings with it. Everything below runs in a terminal.
1. Install virta
virta runs on Bun. If you don't have it:
curl -fsSL https://bun.sh/install | bash. Then install the CLI and the MCP
server straight from this site:
bun add -g https://virta.tosijs.net/virta.tgz
virta --version
virta --version names the build you have. To update, run virta update. It installs the site's latest build. Re-running the bun add -g
line above won't update: bun caches a tarball by its URL and keeps the
copy it has.
2. Log in, once per machine
virta self login
This opens your browser. Approve the request with your account, and the
CLI keeps a token for this machine in ~/local-secrets (mode 0600).
Everything you write is attributed to you by the host.
Approved, but the terminal keeps waiting? After you approve, the page
hands the token back to a small listener virta runs on 127.0.0.1. A
VPN, a managed browser or security software can block that. Press Ctrl-C
and run virta self login --poll instead: the CLI asks the host until the
approval is there, and nothing needs to reach your machine. Only approve a
link you generated yourself.
3. Set your repo up and bring its work over
This is the one step you run yourself, or approve (board #3618). It
gives your agent hooks that run at every session start and every prompt,
and it writes your project's work to the shared board. An agent in a
permission-gated mode is refused it, and rightly so. Let it run the
--dry preview and show you the output, then run the real command
yourself (in Claude Code, type ! virta project setup).
In your project's checkout:
cd ~/my-project
virta project setup --dry # what it would write and import, nothing written
virta project setup # the same preview, one yes, then both
It does two things behind one preview and one yes:
- The agent config (
virta project init): it registers the virta MCP server in the repo's.mcp.jsonand adds two Claude Code hooks to.claude/settings.json:- When a session starts,
virta briefgives the agent the board's state: what's in progress, what's ready, open questions. - Each time you send a message,
virta brief --changestells the agent what changed on the board since it last looked. It says nothing when nothing changed. - When the agent would end its turn,
virta brief --stopasks it once to commit, push and take the next ready task instead of ending on "next up". It lets the stop through when the agent is waiting on your answer.
- When a session starts,
- The import (
virta project onboard), described next.
Both merge into what is there and are safe to run again. When it has
written the hooks, it ends by asking you to restart the session so they
load. The two halves still run on their own: virta project init and
virta project onboard, each with --dry.
4. What onboarding brings over
virta project onboard --dry --verbose # every title it would import
virta project onboard --skip UPSTREAM.md # leave a record file out (setup takes it too)
The dry run counts, for each source, the tasks it would create, how many arrive closed, and how many are already on the board. A long-lived TODO.md can hold hundreds of items, so read it before you go ahead.
Run in your repo's checkout, virta project onboard also enrolls the project:
from then on, its brief speaks in your agents' sessions. Only a repo can
enroll itself. Tasks others file against it are visible on the board but
change nothing until you onboard.
Onboarding imports, once:
- the repo's GitHub issues;
- its
TODO.md/UPSTREAM.md/ROADMAP.mditems. Each card keeps its why: the item's full text, the heading it sat under, any review finding it cites, and a link to its source line.- An item under a heading marked DONE, SHIPPED or RESOLVED
arrives closed. One under "mostly SHIPPED" is tagged
mostly-done. - An
UPSTREAM.mdkept as tables (| Owner | Finding | Issue |) imports a task per row. A struck-out owner is taggedfixed-upstreamand stays open (the workaround still has to go).
- An item under a heading marked DONE, SHIPPED or RESOLVED
arrives closed. One under "mostly SHIPPED" is tagged
- the project's description, repo and site, which it reads from the checkout.
Importing again adds nothing new.
Your project's page
Onboarding fills in what the checkout says: the description (from
package.json or the README), the repo (from the git remote) and the
site (the package's homepage). The package and version follow from
the repo. Check what it found, and fill in the rest:
virta project show my-project # what the board knows
virta project set my-project --site https://my-project.dev
virta project set my-project --icon 🧭 --color teal
virta project set my-project --repo owner/my-project --description "One line on what it is"
--site: the project's docs or demo. It's the link on its tile and its board's header.--repo:owner/repoon GitHub. Issues import from it, andvirta project pushreports back to it.--icon: an emoji, or the URL of an image (a favicon works well).--color: one ofsky,teal,moss,amber,coral,plum,slateorstone, or any CSS colour.--backgroundtakes any CSS background.--description: one line, shown on the project's tile.
Only the settings you give change. On the board, the gear on a project's tile opens the same settings.
5. Triage what arrived
An import is a first draft of your backlog, not the backlog. Do this in the repo's own agent session, which knows the history:
- Close what isn't a task, with a reason: design records, decisions,
history, things already done or overtaken (
virta done #n "…", orvirta close #n "…" --as obsoleteor--as rejected). - Leave real work in backlog. Moving a task to ready is the owner's go-ahead, so leave that to them.
- Give design records a home. Often half a long TODO.md is decisions
and their reasons, not work. Move them to a
DECISIONS.md(or your design doc) before the next step, so they aren't lost.
Then make TODO.md a pointer to the board, in prose, with no list
items (a list item there would be imported as a task). Two lists drift
apart, and the board is the one your agents read.
6. Open your board
https://virta.tosijs.net/host/#?virta.scope=<your-project>
Sign in, and your project's lanes are there: inbox, backlog, ready, in progress, review, done. Drag cards between them, or use a card's status menu.
Working from the board
The shell is the API, for you and for agents:
virta project brief # what's new, in progress and ready here
virta ls "status:ready" # a filter: tags, #numbers, ~text
virta show #42 # one task: body, tags, recent comments
virta new "Fix the thing" --tags kind:bug,priority:high # this repo's project
virta work #42 # pick it up: in progress, and you're on it
virta review #42 "check the lanes on a phone" # finished; a person should look
virta done #42 "shipped in 1.2.0" # finished; nothing to check
A task you create in an enrolled repo belongs to that repo's project
unless you pass --project (the repo is named by its git remote, so a
checkout under another directory name still counts). Created anywhere
else with no project, it is on no board, and create says so.
A few conventions keep the board readable:
- Tags are the model.
status:,priority:,kind:,size:andproject:hold one value each. Setting one replaces the old value. - A task ends as done, obsolete or rejected. Each closes it, and moving it out reopens it. Obsolete and rejected archive it too, and their lanes are hidden by default.
- Archived means "don't look at it." The board hides archived tasks until the filter menu's "archived tasks" asks for them. Archive done work when you've finished with it.
- Say why. A task's body should say why it matters, and for anything non-trivial, what done looks like.
- Review is for what a person can check by looking. Work that only tests can verify goes straight to done, with the tests named in the reason.
For agents has the full picture: every verb, MCP, the tag conventions, and how a project adopts the board.
If something is off
virta --version: which build you have;virta self updateinstalls the latest.virta self login: if the host says your token is refused.virta self cache reset: drops the local copy of the board. The next command loads it fresh. A reset host is noticed and rebuilt automatically.