Skip to content

v1.5.0

Latest

Choose a tag to compare

@github-actions github-actions released this 18 Oct 18:16
· 24 commits to main since this release
v1.5.0
856c728

Added

  • Agent chat in xpo board: talk to your local Claude Code or Codex about the project, and let it implement issues.

    • Opening it: the Agent button in the status bar (⌘J / Ctrl+J) opens a chat popup from any view, centered above its pill. The popup is resizable from its top edge, sides and top corners, from 360×400 up to 600×900, and remembers its size. Minimized chats stay as pills in the status bar.
    • Agent view: Agent is the first entry in the personal section of the sidebar (g a). It opens a full-page chat at #/agent, and each chat has its own URL (#/agent/<id>). The new-chat page asks "What should we build in ?" and shows suggestion cards and the 5 most recent chats. The header has a chat switcher and a "⋯" menu to rename or delete a chat. The popup's expand button opens its chat in the Agent view and removes its pill. A dock button puts it back as a popup and returns to the view you were on.
    • Agents: the board detects which of claude and codex are installed. chat.agent sets the default, and an unknown or uninstalled agent logs a warning and falls back to the first installed one. With 2+ agents, the composer shows an agent picker, which locks once a chat has messages. Each chat keeps one agent process across turns, in the project root with your settings. Codex runs through codex app-server, and its models and effort levels come from model/list at board start.
    • Composer: one footer row with the model, effort and mode: ✳ Claude Opus 5.5 ⌄ | Medium · 1M ⌄ | Chat. One model menu lists every installed agent's models, grouped by agent. Models show their full names, learned from the model each turn ran. Medium effort is pre-selected, and the trigger shows the model's context window once known. A chat restores the agent, model and effort of its last message, and your last pick is remembered for new chats.
    • Issue context: opening an issue adds it to the composer as a chip, and the next message carries it. Sending clears the chip, and a removed chip stays removed until you open an issue again.
    • Drafts: each chat keeps its own composer draft, text and attachments, also when it moves between the popup and the Agent view. The popup's new chat and the Agent view's new chat have separate drafts. Drafts last until a reload.
    • Keyboard: in the Agent view, Esc leaves the composer and keeps the draft, so global shortcuts work, and / focuses it again. In the popup, Esc minimizes.
    • Look: the popup, the composer, cards, message bubbles and their menus use their own monochrome surface scale (--color-*-ai-* tokens, .surface-ai), a step above the app's.
    • Storage: chats are stored per user in .xpo/chats/ (git-ignored).
    • API: /api/local/agent lists every agent with its models and efforts. /api/local/chats/* holds the chats, with PATCH (rename) and DELETE on /api/local/chats/{id}. POST /messages takes provider and returns 409 for a chat of another agent.
    • Config: chat.agent and chat.model. The stream-json parser is shared by drive and chat.

    (xpo-56ae3a, xpo-c047ce, xpo-5b3157, xpo-74390c, xpo-fb4f9b, xpo-b45049, xpo-b204f1, xpo-c94d85, xpo-4e78ba, xpo-de9f71, xpo-127a26, xpo-296ea8, xpo-03fa37)

  • Agent chat: Chat Mode and Work Mode in one chat. A chat can implement an issue in its worktree, and the conversation carries over.

    • Chat Mode: the default. The agent reads the repo and changes issues. merge is denied, and start asks first: "Enter Work Mode on xpo-…?". After you allow it, the chat continues in Work Mode on its own.
    • Work Mode: the agent runs in the issue's worktree. Read, Edit, Write and Bash run there without a card, and so do edits to the issue's own .xpo/artifacts/<issue>/ folder. Other edits outside the worktree, and merge, ask first. After a successful merge, the chat goes back to Chat Mode.
    • Start Work: opens a new chat in Work Mode, titled with the issue title. The first message is prefilled but not sent. If a chat is already working on the issue, Start Work opens it.
    • Continue Work: on an in-progress issue, opens its Work Mode chat, or creates one on the existing worktree. It is a secondary button while a chat is in Work Mode on the issue.
    • Composer: the mode segment reads "Chat", or shows the hammer and the issue ID, with "Go to issue" and "Exit work mode". Exiting keeps the worktree, and the issue stays in progress.
    • Transcript: "Entered Work Mode on xpo-…" and "Entered Chat Mode" dividers mark each switch.
    • Skills: the agent loads skills without an approval card. Its prompt tells it to load the xpo skill before it plans, files issues or changes anything, so Work Mode follows the project's workflow.
    • Codex: read-only sandbox in Chat Mode, workspace-write in Work Mode, with approval policy on-request.
    • API: POST /api/local/issues/{id}/work-chat (optional {"chat_id"}), POST /api/local/chats/{id}/leave-work. Chats include mode, work and worked_issues, and a turn_continued event opens a continued turn.

    (xpo-00f0e5, xpo-219de9, xpo-ad6069, xpo-374792, xpo-2c724e, xpo-a058b4)

  • Agent chat: approval cards. The agent asks before a tool call runs, and the chat shows a card.

    • Layout: the headline ("Merge upe-1 via merge"), the call's arguments as key/value rows in a monospace panel, then [Deny] [Allow once ▾]. "Allow for this chat" is in the chevron's menu. Cards with arguments open collapsed on one row; click the title to expand them.
    • Keyboard: the waiting card takes focus, unless you are typing in a field. Enter allows once and Esc denies. Focus moves to the next card, then back to the composer.
    • Allow for this chat: xpo keeps the grant with the chat. It never writes to Claude Code's settings files.
    • States: cancelling a turn resolves its cards as "Cancelled", and a card whose turn ended shows "Expired".
    • Agents: Claude Code turns use --permission-prompt-tool stdio. Codex command, file-change and MCP-tool approval requests go to the same cards.
    • API: POST /api/local/chats/{id}/approvals/{approval_id} with {decision}. Chat lists include waiting.

    (xpo-7bc86e, xpo-aaf8b1, xpo-8579fb)

  • Agent chat: Claude can ask you questions with AskUserQuestion, in both modes. The question card asks one question at a time.

    • Answering: pick one option or several, or type into "Other…". A single-choice pick moves on after 250 ms. With focus in the card, ←/→ switch steps, 1–9 pick an option, and Enter presses Next or "Submit answers". The card takes focus when it appears, unless you are typing in a field.
    • Several questions: tabs list the questions (✓ when answered), and a Review tab shows each answer, or "Not answered", with a "Change" button. "Submit answers" sends from there.
    • Chat about this: tells the agent to end its turn and wait for your message. The thread reads "Chat instead".
    • Thread: keeps each question with your answer.
    • API: POST /api/local/chats/{id}/approvals/{approval} takes {"decision":"answer","answers":{…}}, and approval_resolved events carry answers.
    • Claude only for now; Codex follows in xpo-eafa9b. Animations are off under reduced motion.

    (xpo-447687, xpo-46afc0, xpo-9761dd)

  • Agent chat: the transcript shows the agent's work live.

    • Work list: narration is a regular message. The actions between messages collapse into one line: while the agent works, the current action's icon and target with a shimmer and a timer; afterwards "6 actions · 2 failed". Click it to see the actions on a timeline. A Lightbulb line shows while the agent thinks. The turn's total time sits under its last message.
    • Actions in words: "Read board.go", "Searched the code for …", "Created issue …", "Wrote the spec of xpo-1". Each action shows an icon, and its verb is the tooltip and screen-reader label. Calls that did not run read as what was asked ("Merge upe-1").
    • Failures: a denied or failed action has a red icon and ends with "failed: "; the full error opens on hover, focus or click. A denied call is not counted as a failure.
    • Following: an open chat follows new output while you're at the bottom, also when a card or code block grows. Scrolling up stops following, and scrolling back to the end resumes it.
    • Turn rail: in the Agent view, one bar per user message on the thread's left edge. Hovering shows the message, and clicking scrolls to it.
    • Cold cache divider: "Context cache expired" marks when the next message re-sends the whole conversation. The lifetime comes from what each Claude turn reports (1h or 5m); Codex assumes 5 minutes.
    • Events: tool_call and tool_start carry tool_id, tool_call has status (done, error, denied) and message, and turn_done includes cache_ttl.

    (xpo-855e28, xpo-8673f4, xpo-6afc58, xpo-63b755, xpo-260a28, xpo-0e4df5, xpo-219de9)

  • Agent chat: background tasks. A process the agent started (a dev server, a monitor) does not block the chat; a turn ends when the agent replies.

    • Composer: shows "Monitoring N tasks", or "N agents working" for subagents. A card lists the tasks, and its Stop button ends the agent with its tasks. The next message resumes the session.
    • Agent turns: when a task ends and Claude Code replies on its own, the reply shows as its own turn, marked with what woke the agent.
    • Stop interrupts the turn only; background tasks keep running. Model and effort changes apply without restarting the agent.
    • Lifecycle: a mode change restarts the agent. Agents idle for 30 minutes without tasks, deleted chats and board shutdown end their agents. A starting board stops agents a crashed board left running.
    • API: POST /api/local/chats/{id}/background/stop, background on GET /chats/{id}, and background_tasks and agent_turn events.

    (xpo-fb4f9b)

  • Agent chat: more tools. Both modes get WebSearch, WebFetch and LSP, and Work Mode also gets the task list (TaskCreate, TaskUpdate, TaskList, TaskGet) and subagents (Agent). WebFetch asks before every fetch. Codex turns get live web search. (xpo-80bf05)

  • Agent chat: attach files and images by pasting, dropping, or the paperclip button next to Send. Up to 10 per message, images up to 5 MB, other files up to 10 MB, and a message can have attachments and no text.

    • Agents: PNG, JPEG, GIF and WebP go in as images, detected from the file's content. Every attachment is also listed with its absolute path in the prompt, so the agent can read text files and PDFs with its own tools.
    • Storage: files are saved with the chat in .xpo/chats/<id>/attachments/ and deleted with it. The user_message event records them as attachments.
    • Transcript: image thumbnails open the full image; other files download. GET /api/local/chats/{id}/attachments/{aid} serves only real images as images.

    (xpo-c1d423)

  • Agent chat: chats get a short, generated title after their first finished reply. Claude chats use Haiku and Codex chats use Codex's fast model. A title you renamed is never replaced, and titles keep up to 200 characters. Chats carry auto_title while their title is a placeholder, and the chat stream sends a live title event. (xpo-714245, xpo-1446f2)

  • Agent chat: chat state at a glance.

    • Status dots: chat pills and the recent-chats lists show yellow while an approval waits, red when interrupted, and blue for a finished reply you have not seen. A red alert circle marks a chat whose last turn failed, with the error in its tooltip. Running chats show a spinner, and idle Work Mode chats a hammer.
    • Favicon: a dot when a chat needs you: amber while any chat waits on an approval, blue when a reply finished while the tab was hidden.
    • Desktop notifications: opt in with the bell in the Agent view header. When a chat starts waiting on you while the tab is hidden, a notification shows "Agent needs your input" with the chat title. Clicking it opens the chat. Each chat has at most one notification, and it closes when the chat stops waiting or the tab becomes visible.
    • API: chats include last_error.

    (xpo-f2d4c8, xpo-c56e70, xpo-bbcab4, xpo-a3f62b, xpo-714245)

  • Agent chat: an "All chats" page (#/agent/history), opened from the Agent view header and the status-bar history menu.

    • Find: search by title, Active/Archived tabs, and agent, period and issue filters (?issue=<id>).
    • Usage: a summary strip totals chats, tokens in/out and cost for the filtered rows. Chats without a reported cost (Codex) show "—".
    • Manage: rename, archive, unarchive and delete per row, or in bulk.
    • Archive: an archived chat leaves the recent lists and the pills, and opens read only with an Unarchive banner. Archive is refused while a reply runs or the chat is in Work Mode. At startup the board archives chats older than 30 days; only you delete chats.
    • API: POST /api/local/chats/{id}/archive and /unarchive. Chats carry archived_at and usage (turns, tokens, cost).

    (xpo-1446f2, xpo-66fce3)

  • Issues show the chats that worked on them.

    • Chat pill: backlog rows and board cards show a chat pill with the chat count. Its icon gives the most urgent state: waiting on you, running, or interrupted. A click opens the chat, or a searchable list of the issue's chats.
    • Issue details: a "k chats" row below Relations opens the same list, with "N archived chats".
    • Context menu: "Open work chat" and a "Chats" submenu.
    • Back to work: when no chat is working on the issue, a chat's row actions (right-click, ⋯ or Shift+F10) put it back into Work Mode, prefilled with "Let's continue with xpo-…".
    • API: chats include cache_ttl and cache_expires_at.

    (xpo-219de9, xpo-1446f2)

  • Issue ID chips: issue IDs in chat messages, tool rows, descriptions, specs, walkthroughs and comments show as chips that start with the issue's status icon. A click opens the issue and hovering shows a preview. IDs in inline code are chips too, and IDs in fenced code blocks stay plain. An ID inside a link is not wrapped again, and unknown IDs show a gray chip with a "not found" tooltip. Chips sit centered on the line and don't make it taller. (xpo-289f40, xpo-b83f1c, xpo-1d9d5d, xpo-b47189)

  • Merged issues get a "View changes" button that opens a read-only Merged view: the walkthrough, commits, diff and conversation, read from the merge commit, with who merged it and when. Merging while the merge view is open switches it to the Merged view. GET /api/issues/{id}/merged returns the merge's commits and diff, and issues in the API carry merged: true. (xpo-64be51)

  • Issue drafts: the new issue dialog has a "Save draft" button, and the discard prompt offers "Save draft" too. Drafts are stored per user in .xpo/drafts/ (git-ignored, outside the event log). A "Drafts" sidebar item with a count badge appears while drafts exist (g r). Reopen a draft to keep editing, create the issue from it, or discard it. New /api/issue-drafts endpoints (GET/POST/PUT/DELETE, issue.create capability). (xpo-758398)

  • Read-only git MCP tools for the local xpo mcp server: git_status, git_diff, git_log, git_show and code_history. Agents can see what an issue changed and which issues touched a file or lines. xpo builds every git command from an issue ID, a path, a SHA, a line range and a limit. External diff, textconv, fsmonitor and hooks never run, and the tools take no index locks. Output is capped at 64 KiB, with truncated: true and a marker line. (xpo-a7f115)

  • MCP add/update take before or after (an issue ID) to place an issue directly above or below another issue in the manual order, so agents can reorder the board. The CLI gets the same as xpo add/xpo update --before/--after and in --json payloads. An issue already in place is left alone ("X is already after Y"). (xpo-61232d)

  • README section "Upgrading from 1.4.x" (xpo-bb6beb)

Changed

  • Live updates: each board tab holds one event stream, GET /api/events. Issues, drafts, the chat list and every open chat arrive over it, so tabs no longer use up the browser's 6 connections per host.

    • Topics: a new broadcaster, internal/live, carries signal topics (issues, drafts, chats: "changed, refetch") and log topics (chat:<id>: ordered events with seq ids). A tab adds and removes chats with POST /api/events/{stream}/topics and the last seq it has. The chat list updates live in every open browser and is no longer polled.
    • Never silently behind: before, a frozen background tab could stop receiving events while its connection stayed open, and the board missed new issues and status changes until a reload. Now repeated signals merge into one, and a chat whose queue overflows replays from the store. After any reconnect (sleep, network drop, board restart), the tab refetches the signal topics and replays each open chat. The browser retries with backoff (1s up to 30s) and reconnects as soon as the tab is visible again.
    • Server: a 10s deadline on each write, a keepalive every 25s, and open streams end on shutdown.
    • Removed: the issue_* SSE events.

    (xpo-6d1102, xpo-ae6385, xpo-7e9c4f, xpo-bbcab4)

  • A blocked_by blocker counts as resolved only when it is DONE. CANCELED and DUPLICATE blockers still block, until you remove the link or point it at another issue.

    • Board: Start Work is disabled while any blocker is unresolved. Its tooltip lists each blocker and its status, with a one-click Remove link for canceled and duplicate blockers.
    • start and drive: start refuses such issues, and the error says to remove the link. Drive skips them.

    (xpo-35943d)

  • Agent instructions: agents search before they file an issue. Step 1 of the xpo skill asks for a list (match) search and a rationale search before any add, and the agent tells the user what the searches found when it proposes the issue. (xpo-ed312f)

  • Labels: creating a label in the label picker (Create "…") applies it to the issue in one step, uncolored; the color step is gone. Colors are managed in the Labels view.

    • Labels view: "Built-in labels" and "Custom labels" are separate sections. Clicking a custom label opens an Edit label dialog with the name, a 12-color palette, and a live preview of the label on its issues. Delete label sits in the dialog behind a confirmation. "N issues" on each row links to the filtered issue list.
    • Built-in labels (default_labels) are read-only: the board and the API (400) refuse to rename, recolor, re-add or delete them, or to rename another label onto one.
    • Palette: the 12 swatches avoid the built-in colors and are clearly different from each other.
    • Persistence: coloring a label that was only used on issues saves it to config.yaml, so the color survives a restart.
    • Dialogs: the Edit label dialog and every confirm dialog share one frame (DialogPanel).

    (xpo-e35f16)

  • Deleting an issue: the context menu (click, Enter, ⌘⌫) and the issue sidebar open the same confirmation dialog, which replaces the two hand-built popovers. The dialog shows the issue in a card (status, ID and title). With sub-issues it offers Keep sub-issues (soft red) or Delete all (solid red). A toast confirms the delete: "Deleted ", or "Deleted and its sub-issues". Every confirm dialog has a footer bar with an "esc to cancel" hint and a secondary Cancel button. Red menu items such as Delete stay red on hover and keyboard focus. (xpo-33c92b)

  • Issue rows: one pill style for row indicators, after Linear. Sub-issue progress, branch, chats and attachments sit in the same faint, fully rounded pill, with a muted 12px icon. The branch, chat and attachment pills show their counts; details are in the tooltips. Pills and label chips have the surface background, so they stay dark on a hovered or highlighted row. Applies to backlog rows, board cards and My Issues. (xpo-219de9, xpo-199a69)

  • Links in Markdown prose (chat transcript, descriptions, comments) use a new --color-link token, #adbbff, in place of the accent color. They have about 9–11:1 contrast instead of about 4:1. They have no underline at rest and underline on hover. (xpo-1d9d5d)

  • Buttons look the same on any surface. secondary is a wash of its own text color for rest, hover and pressed, with an optional bordered outline and a tone (danger, warning) for colored text. ghost uses the same wash on hover. primary darkens when pressed. danger rests a little darker and hovers a little lighter than its base color. A new warning variant works the same way, with dark text. The "Delete branch" checkbox in the merge view uses the accent color. (xpo-aaf8b1, xpo-1446f2)

  • Dependencies upgraded. Frontend: vite 8 (Rolldown; vendor chunks move to rolldownOptions.output.codeSplitting.groups), @vitejs/plugin-react 6, eslint 10, fractional-indexing 4, React 19.3, recharts 3.10, tiptap 3.32, lucide-react 1.57 and the rest to latest. TypeScript stays on 5.9 (typescript-eslint supports <6.1) and eslint-plugin-react-hooks on 7.0 (xpo-09fa6f). bun.lock is regenerated and the unused web/package-lock.json is removed. Go: glamour 1.0, MCP go-sdk 1.8, golang.org/x/* and the rest to latest. Building from source now requires Go 1.26 (required by golang.org/x/*). (xpo-b2ca41, xpo-855e28)

Removed

  • Remote mode: xpo is local-only. Issue data travels with the code through git (.xpo/issues.db merge=union), and teammates get the latest copy when they sync. Remote mode reported branch state, and ran start and merge, in the server's checkout, which nobody works in. Team mode will be designed separately. (xpo-67281a)
    • Commands: xpo serve, xpo login and xpo logout are gone. xpo whoami prints only the identity xpo records on events: the configured user, else git config.
    • Board: xpo board no longer proxies to a remote server. The board's --host access token still works. GET /api/local/mode no longer returns remote. The interim remote-mode guards for Start Work, Review Changes and the MCP start and merge tools are gone with it (xpo-a60065).
    • Config: remote.url, remote.token (XPO_REMOTE_URL, XPO_REMOTE_TOKEN), permissions, and the servers: entries in ~/.config/xpo/user.yaml are ignored. The .xpo/authorized_keys and .xpo/server.key files are no longer used.
    • Other: the Dockerfile, docker-compose.yml and make docker are gone, and golang.org/x/crypto is no longer a dependency.

Fixed

  • Start Work in a project without a git repository no longer moves the issue to DOING and then fails with "not a git repo". The board refuses before anything changes, with Work Mode needs a git repository. Run "git init" in <path>. With an agent available, Start Work and "Enter work mode" are disabled, and their tooltip says the same. Without an agent, Start Work still only starts the issue. Running git init takes effect without restarting the board. GET /api/local/mode also returns git and root. (xpo-e627e1)

  • Issue sidebar: Start Work is disabled while the issue is in BACKLOG, since start requires PLANNED. Before, clicking it only showed an error toast. Its tooltip says "Move the issue to Planned to start work", followed by any unresolved blockers. (xpo-31520e)

  • Review Changes shows when a worktree's only changes are new, untracked files. Branch stats count them as uncommitted (files and line insertions; gitignored files and .xpo/ excluded). The dialog opens on "Uncommitted" when the branch has no commits yet, instead of an empty "All changes". When Review Changes shows, it is the primary, top button, and Start Work or Continue Work turns secondary. (xpo-926f0b)

  • Merging an issue while its merge view is open no longer shows a raw {"error":"no branch found for this issue"} screen. An issue without a branch shows a clear "No branch found" screen, and API errors across the UI show their message instead of raw JSON. (xpo-64be51)

  • MCP update and xpo update --json clear assignee, cycle_id, priority, story_points and parent when the field is null. They used to fail with "no fields set", or leave the field unchanged when another field was set. null on description, labels and links still means "unchanged"; clear those with "" or []. A literal quoted empty string ("\"\"") is treated as "" instead of failing with issue "" not found. Each nullable field's schema description says how to clear it. (xpo-bbfcf9, xpo-328824)

  • Issue activity timeline: every field change is described, cleared values read as words, and rows read as muted sentences. (xpo-ca763f, xpo-bbfcf9)

    • New descriptions: priority ("set priority to High" / "cleared the priority"), parent ("set parent to [issue]" / "removed the parent issue") and cycle ("added this issue to Cycle 12" / "removed this issue from its cycle").
    • Cleared values: a cleared estimate reads "cleared the estimate" instead of "set estimate to 0 Points". Removing the assignee reads "removed the assignee"; before, such an event had no text. Removing all labels reads "removed all labels" on the web and in the CLI.
    • Same wording in the CLI: xpo history uses the same phrases, replacing "unassigned", "removed parent", "removed from cycle" and "added to cycle X".
    • Muted rows: actor names and values stay muted and turn white with a pointer cursor on hover. Comment headers keep the white name.
    • Spacing and alignment: each row is one run of text with normal word spacing instead of 8px gaps, and long rows wrap like a sentence. Status, estimate and priority chips line up with the row's text, and the estimate triangle no longer hangs below it (xpo-d3578a).
  • Menus: items that open a next step inside their menu keep it open. In the label picker, Create "…" creates the label, where before it closed the picker and did nothing. In the issue context menu, Delete shows its confirmation; before, only ⌘⌫ / Ctrl⌫ did. MenuItem takes a new keepOpen prop for such items. (xpo-2790cd)

  • Menu items with a suffix (relative times, filter counts, "⌘⌫") no longer overflow their row. (xpo-c047ce)

  • XPO_* environment variables set every config key, as the README promised. Before, a key without a default was ignored unless a config file already set it. List keys take a comma-separated value. Map keys (labels) can still only be set in a config file. (xpo-e81612)

  • Unknown /api/…, /auth/… and /mcp/… paths return 404 again in builds with the web app embedded. Before, the app's catch-all route answered them with index.html and a 200.

  • Markdown: rendered checklist items wrap like the editor. Wrapped lines line up with the first line of text instead of the checkbox, and the checkbox sits at the editor's height. (xpo-d4e6e4)

  • Dashboard: Active Epics rows no longer nest the copyable issue ID button inside the row button, which was invalid HTML and logged a React error on every Dashboard load. (xpo-66c6a4)