Path: blob/main/extensions/copilot/src/extension/chronicle/common/standupPrompt.ts
13399 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import type { RefRow, SessionRow } from '../../../platform/chronicle/common/sessionStore';67/** A session row annotated with its source. */8export interface AnnotatedSession extends SessionRow {9/** Where this session came from: 'vscode', 'cli', or 'cloud'. */10source: 'vscode' | 'cli' | 'cloud';11}1213/** A ref row annotated with its source. */14export interface AnnotatedRef extends RefRow {15source: 'vscode' | 'cli' | 'cloud';16}1718/** Sessions query — SQLite dialect, last 24 hours */19export const SESSIONS_QUERY_SQLITE = `SELECT *20FROM sessions21WHERE updated_at >= datetime('now', '-1 day')22ORDER BY updated_at DESC`;2324/** Build refs query for a list of session IDs */25export function buildRefsQuery(sessionIds: string[]): string {26const ids = sessionIds.map(s => `'${s.replace(/'/g, '\'\'')}'`).join(',');27return `SELECT session_id, ref_type, ref_value FROM session_refs WHERE session_id IN (${ids})`;28}2930/** Build files query for a list of session IDs */31export function buildFilesQuery(sessionIds: string[]): string {32const ids = sessionIds.map(s => `'${s.replace(/'/g, '\'\'')}'`).join(',');33return `SELECT session_id, file_path, tool_name FROM session_files WHERE session_id IN (${ids})`;34}3536/** Build turns query for a list of session IDs (user messages + assistant response summaries, truncated) */37export function buildTurnsQuery(sessionIds: string[]): string {38const ids = sessionIds.map(s => `'${s.replace(/'/g, '\'\'')}'`).join(',');39return `SELECT session_id, turn_index, substr(user_message, 1, 120) as user_message, substr(assistant_response, 1, 200) as assistant_response FROM turns WHERE session_id IN (${ids}) AND (user_message IS NOT NULL OR assistant_response IS NOT NULL) ORDER BY session_id, turn_index`;40}4142/** A file row from the session_files table. */43export interface SessionFileInfo {44session_id: string;45file_path: string;46tool_name?: string;47}4849/** A turn summary from the turns table. */50export interface SessionTurnInfo {51session_id: string;52turn_index: number;53user_message?: string;54assistant_response?: string;55}5657/**58* Build a standup prompt from pre-fetched session and ref data.59*/60export function buildStandupPrompt(61sessions: AnnotatedSession[],62refs: AnnotatedRef[],63turns: SessionTurnInfo[],64files: SessionFileInfo[],65extra?: string,66): string {67if (sessions.length === 0) {68return 'The user ran /standup but no sessions were found. Let them know there\'s no recent activity to report.';69}7071const sessionLines = sessions.map(s => {72const branch = s.branch ?? 'unknown';73const repo = s.repository ?? 'unknown';74const agent = s.agent_name ?? s.source;7576// Include turn summaries for this session (first few user messages + assistant responses)77const sessionTurns = turns.filter(t => t.session_id === s.id).slice(0, 5);7879// Use first turn's user_message as summary when sessions.summary is empty80const firstTurnMessage = sessionTurns[0]?.user_message;81const summary = s.summary || firstTurnMessage || 'No summary';8283const turnLines = sessionTurns84.filter(t => t.user_message || t.assistant_response)85.map(t => {86const parts: string[] = [];87if (t.user_message) { parts.push(`User: ${t.user_message}`); }88if (t.assistant_response) { parts.push(`Assistant: ${t.assistant_response}`); }89return ` - ${parts.join(' → ')}`;90});9192// Include files touched in this session (capped to avoid noise)93const sessionFiles = files.filter(f => f.session_id === s.id);94const uniqueFiles = [...new Set(sessionFiles.map(f => f.file_path))];95const shownFiles = uniqueFiles.slice(0, 5);96const fileLines = shownFiles.length > 097? [` - Files (${uniqueFiles.length} total): ${shownFiles.join(', ')}${uniqueFiles.length > 5 ? `, +${uniqueFiles.length - 5} more` : ''}`]98: [];99100return [101`- ${s.id} | ${repo} (${branch}) | ${agent} | ${summary} | updated ${s.updated_at}`,102...turnLines,103...fileLines,104].join('\n');105});106107const refLines = refs.map(r => `- ${r.session_id} | ${r.ref_type}: ${r.ref_value}`);108109let prompt = `The user ran /chronicle standup. Generate a concise standup update from the pre-fetched data below.110111## Pre-fetched Session Data (last 24 hours)112113### Sessions (${sessions.length})114${sessionLines.join('\n')}115116### References (PRs, Issues, Commits)117${refLines.length > 0 ? refLines.join('\n') : 'No references found.'}118119## Instructions1201211. Analyze the turn data (user messages and assistant responses) to understand the actual work done in each session.1222. Use file paths to identify which components, modules, or areas of the codebase were affected.1233. For any PR/issue references, mention them with links.1244. If a session has no turns or summary, note it briefly but don't skip it entirely.125126## Output Format127128Format the update grouped by work stream (branch/feature). Use this structure:129130Standup for <date>:131132**✅ Done**133134**Feature name** (\`branch-name\` branch, \`repo-name\`)135- Summary of what was accomplished (1-2 sentences grounded in the user messages and assistant responses)136- Key files: list 2-3 most important files changed137- Tools used: mention key tools if visible (e.g., apply_patch, run_in_terminal, search)138- PR: [#123](link) — merged/closed (if applicable)139140**🚧 In Progress**141142**Feature name** (\`branch-name\` branch, \`repo-name\`)143- Summary of current work (1-2 sentences based on turn content)144- Key files: list 2-3 most important files being worked on145- PR: [#789](link) — draft/open (if applicable)146147Formatting rules:148- Use the turn data (user messages AND assistant responses) to understand WHAT was done, not just that something happened149- Use file paths to identify which components/areas were affected150- Group related sessions on the same branch into one entry151- Link PRs and issues using markdown link syntax152- Classify as Done if the session has no recent activity or the work appears complete, In Progress otherwise153- If a session has no branch or repo, still include it under an "Other" section`;154155if (extra) {156prompt += `\n\nAdditional context: ${extra}`;157}158159return prompt;160}161162163