Per-item AI sidebar
Most "AI in your tracker" features are a chat box bolted on the side of the project. You type a question, the model gets the project as context, you get a generic answer. Useful sometimes. Not the moat.
The Vibstr per-item AI sidebar is different. You press <kbd>a</kbd> on any item and the sidebar already has the item, its linked plan, sibling items, the comment history, the activity timeline, and the GitHub PRs and commits referencing #NN — all in the system prompt before you type a word.
It is built to dissolve the 40-60% of "I need to ask a teammate" moments where the answer was already in the system, just not in front of you.
Press a
Open any item in the drawer. Press <kbd>a</kbd>. Or click the AI bar at the bottom of the drawer. The sidebar expands inline. The textarea is focused. Cmd/Ctrl+Enter sends. Esc closes the modal cascade in the usual order (drawer pickers first, then drawer itself).
Conversation is per-item, per-session. Closing the drawer keeps the conversation in memory; switching items resets it. Signing out clears everything. We do not persist conversations to the database — the cost/storage trade-off does not earn its keep, and the alternative is "every project has thousands of half-thought-out AI threads buried in the schema".
What the sidebar sees
The system prompt is assembled by src/aiItemContext.js plus src/githubContext.js. The 8k token budget breaks down approximately:
- The item itself — description (truncated to 1000 chars), type, area, priority, status, labels, assignee, due date, estimate, parent item, plan link.
- The linked plan — body truncated to 3000 chars when
item.plan_idis set. - Sibling items — up to 5 most relevant. Scoring favours parent's other children, items sharing labels, items in the same
reported_build, items in the same plan. - Recent comments — last 5 of up to 20 fetched, each capped at 140 chars. Includes
via='slack'comments from the Phase 10 bridge. - Recent activity — last 5 of up to 20 fetched. Status changes, assignee changes, comment posts, items closed in builds.
- GitHub context — when
vibelog_projects.github_repois set, recent commits and open/merged PRs referencing#NNget pulled from the public GitHub REST API. Char-budgeted to 1500. Older history drops with a(and N more older commits and M more older PRs)hint so the model knows there is more it cannot see. - Project context — categories, areas, labels, recent build versions. So the AI cannot propose a type or area that does not exist in your project.
Each sub-fetch returns [] or null on any failure. A 403 on GitHub, a RLS denial on activity, a timeout — the sidebar still renders, with less context. No toasts, no console errors. Degrade silently.
Read tools auto-execute
Two read tools live in the item-scoped tool set:
list_items(filter)— search the project for matching items.list_plans(filter)— list plans in the project.
When the model calls a read tool, the dispatcher runs it immediately and feeds the result back into the conversation. The sidebar shows a small inline status row: Checked your items or Looked up plans. No approval card. You see what was run; the answer continues.
Write tools fire approval cards
Three write tools live in the item-scoped tool set (added in 1.13.3):
add_to_item_description(markdown_to_append)— append a markdown chunk to this item's description.create_subtask_of_this_item({type, area, description, priority})— create a subtask with parent = this item.post_comment_on_this_item(body)— post a comment to this item, attributed to you.
When the model calls a write tool, the sidebar renders an approval card with:
- Tool name + small icon (note-pencil / tree-structure / chats).
- A preview pane showing exactly what will be written. Description-append shows the last 80 chars of current description and the new appended chunk. Subtask shows the new
#NNshape with type / area / description / priority chip. Comment shows the rendered markdown body just like it will appear in the Comments tab. - Approve (accent) and Reject (red outline) buttons.
Nothing executes until you click Approve. Reject sends a tool_result back to the model saying you rejected it; the model can propose an alternative or move on.
The trust boundary
This is the load-bearing architectural invariant. The model never passes item_id in a write-tool call. The handler in src/aiTools.js ignores any item_id argument the model might invent and resolves the target from the open drawer's currentItem only.
If you add a new item-scoped write tool, follow the same pattern:
- Input schema has no
item_id, noitem_number, noparent_item_id. The model literally cannot specify the target. - The handler signature in
src/aiTools.jstakescurrentItemas its 4th argument. Throws if undefined. - The handler resolves the target item from
currentItem.idonly. - The handler reuses an existing
data.jshelper (createSubtask,postItemComment, etc.) so all the activity log, audit log, and notification fan-out keeps firing automatically. _summariseArgsForAuditreturns counts and lengths only — never full body text. Audit log gets a safe summary.isWriteToolreturns true so the approval card renders.
Tools that span multiple items dilute this boundary and are not in v1.
GitHub context (1.13.2)
When vibelog_projects.github_repo is set (e.g. custardsquare/vibelog), the sidebar pulls from the public GitHub REST API:
GET /repos/{repo}/commits?per_page=50— filtered JS-side to commits whose message matches the negative-lookbehind-and-lookahead-guarded#NNregex.GET /search/issues?q=%23NN+repo:{repo}+is:pr— PRs referencing the item.
5-minute in-memory cache per (repo, item_number) pair. Capped at 5 commits and 5 PRs each. Char-budgeted to 1500 in the prompt. The chip in the drawer is collapsible — click to see the latest 3 PRs and commits with permalinks that open in new tabs.
Private repos are unauthenticated-only in 1.13.2 (60 req/hour limit per IP). A PAT-based auth path is queued if user feedback demands.
Costs and telemetry
Every AI call hits your daily AI cap. BYO Anthropic key bypasses the cap (you are paying Anthropic directly). The cost toast appears after every call: Item AI · 2.3k in + 845 out · ~$0.013 (BYO key).
The activity log writes a telemetry funnel:
ai_item_assist_started— once per session per item. Activation metric.ai_item_assist_message_sent— every send. Volume metric. Payload carries{ length }only — never the message text.ai_item_assist_tool_approved/ai_item_assist_tool_rejected— per write-tool decision. Conversion metric. Payload carries{ tool_name }only — never the args or preview content.
The chris-only Platform Analytics pane (Settings → PLATFORM → Analytics) renders a First-Responder funnel card aggregating across users + projects.
What is NOT in v1
- Cross-item AI. The sidebar is strictly per-item. There is no "look at this whole milestone and propose plans" surface on the sidebar; the global AI Assist tab is project-scoped for that.
- Conversations persisted to the database. Session-only is the correct cost/storage trade-off.
- Voice and audio input. Out of charter for now.
- Tools that delete items, plans, or comments. Auth-sensitive. Defer until access-controlled.
- AI proposing UPDATE on labels/priority/due/estimate/assignee for the current item. The global
update_itemtool covers the broader case; defer adding per-item shortcuts until user feedback demands. - Persisting GitHub PAT for private-repo context. Public unauth API only in 1.13.2.
Deep dive
- Plans — when an item is linked to a plan, the sidebar reads the plan body. The plan-driven loop is where the sidebar is most useful.
- Claude Code integration — the GitHub linking that powers the recent-PRs section of the context envelope.
- Discuss in Slack — once your team discusses in Slack, the sidebar reads the mirrored
via='slack'comments as part of the item's history.