Brief: a session's opening digest

What an agent needs when it starts work in a repo, in a few lines: what changed since it last looked (tasks created or re-prioritised by someone else), the open questions, what is in progress and by whom, and the top of ready. virta brief prints it for the repo it runs in and remembers where this identity left off; a Claude Code SessionStart hook running it puts the board in the agent's context before it is asked anything (see For agents). It says nothing at all when the repo is not on the board, so the hook is harmless everywhere.

brief(store, { project, since }) is the pure part: since is the log position this identity last saw — omitted on a first look, when "changed" is empty rather than everything ever — and the result's cursor is the one to pass next time. It is also the brief verb, so MCP has it.

virta brief --stop is a Claude Code Stop hook (board #3636): when a turn would end with uncommitted or unpushed work in the repo, or a ready task on its board, it asks the agent once to carry on (commit and push, then take the task) instead of ending on "next up". stopReason is its pure part. It lets the stop through when nothing is owed, when the agent has just asked the owner a question on the board (an open kind:question it filed or touched in the last half hour), and always on the second try in a turn, so an agent that is answering the owner or waiting on them says so and stops. It reads; it never writes the board.

import { MemoryLog, VirtaStore, brief } from 'tosijs-virta'

const store = new VirtaStore({ log: new MemoryLog(), identity: 'agent' })
await store.load()
const other = new VirtaStore({ log: store.log, identity: 'owner' })
await other.load()
await store.create({ title: 'port the tests', tags: ['project:demo', 'status:ready'] })
const first = brief(store, { project: 'demo' })
await other.sync()
await other.create({ title: 'the build is red', tags: ['project:demo', 'priority:critical', 'status:ready'] })
await store.sync()
const next = brief(store, { project: 'demo', since: first.cursor })
preview.textContent = [
  `changed: ${next.changed.map((c) => `#${c.task.num} ${c.what.join(', ')}`).join('; ')}`,
  `ready: ${next.ready.map((t) => t.title).join(' | ')}`,
].join('\n')
test('the second look shows what someone else changed, and ready is by priority', () => {
  expect(preview.textContent).toBe(
    // a priority set at creation is part of "created"; a later one is news
    'changed: #2 created\nready: the build is red | port the tests'
  )
})