diff --git a/src/backends/progressModel.ts b/src/backends/progressModel.ts index 44a27fb2..3222c19e 100644 --- a/src/backends/progressModel.ts +++ b/src/backends/progressModel.ts @@ -7,6 +7,7 @@ import { AgentBuilder, LLMist, type ModelSpec } from 'llmist'; +import { getAgentLabel } from '../config/agentMessages.js'; import type { Todo } from '../gadgets/todo/storage.js'; export interface ProgressContext { @@ -21,7 +22,7 @@ export interface ProgressContext { completedTasks?: { subject: string; summary: string; timestamp: number }[]; } -const PROGRESS_SYSTEM_PROMPT = `You are a progress reporter for an AI coding agent called CASCADE. Write a brief, informative progress update based on the agent's current state. Be concise (3-5 sentences max). Focus on what has been accomplished, what's currently in progress, and what remains. Synthesize the agent's own commentary, tool call details (file paths, commands), and completed task summaries into a coherent narrative β€” do not just list tool names. Use markdown formatting. Write in first person (e.g. "I'm implementing...", "I've completed...", "I'm currently working on..."). Start with a bold header like "**Progress update** (X min)". Do not include a progress bar β€” the system adds that separately.`; +const PROGRESS_SYSTEM_PROMPT = `You are a progress reporter for an AI coding agent called CASCADE. Write a brief, informative progress update based on the agent's current state. Be concise (3-5 sentences max). Focus on what has been accomplished, what's currently in progress, and what remains. Synthesize the agent's own commentary, tool call details (file paths, commands), and completed task summaries into a coherent narrative β€” do not just list tool names. Use markdown formatting. Write in first person (e.g. "I'm implementing...", "I've completed...", "I'm currently working on..."). Start with a bold header using the exact header provided in the user prompt context (e.g. "**πŸ§‘β€πŸ’» Implementation Update** (X min)"). Do not include a progress bar β€” the system adds that separately.`; function formatProgressUserPrompt(context: ProgressContext): string { const { @@ -35,8 +36,11 @@ function formatProgressUserPrompt(context: ProgressContext): string { completedTasks, } = context; + const { emoji, label } = getAgentLabel(agentType); + const sections: string[] = [ `Agent: ${agentType}`, + `Progress header: **${emoji} ${label}** (${Math.round(elapsedMinutes)} min)`, `Task: ${taskDescription.slice(0, 500)}`, `Time elapsed: ${Math.round(elapsedMinutes)} minutes`, `Iterations: ${iteration}`, diff --git a/src/config/agentMessages.ts b/src/config/agentMessages.ts index fc2b7d18..06171d6e 100644 --- a/src/config/agentMessages.ts +++ b/src/config/agentMessages.ts @@ -1,3 +1,30 @@ +/** + * Agent-specific emoji and label for progress update headers. + * + * Used by: + * - progressModel.ts β€” LLM prompt to produce correct header + * - statusUpdateConfig.ts β€” template fallback header + */ +export const AGENT_LABELS: Record = { + briefing: { emoji: 'πŸ“‹', label: 'Briefing Update' }, + planning: { emoji: 'πŸ—ΊοΈ', label: 'Planning Update' }, + implementation: { emoji: 'πŸ§‘β€πŸ’»', label: 'Implementation Update' }, + review: { emoji: 'πŸ”', label: 'Code Review Update' }, + 'respond-to-planning-comment': { emoji: 'πŸ’¬', label: 'Planning Response Update' }, + 'respond-to-review': { emoji: 'πŸ”§', label: 'Review Response Update' }, + 'respond-to-pr-comment': { emoji: 'πŸ’¬', label: 'PR Comment Response Update' }, + 'respond-to-ci': { emoji: 'πŸ”§', label: 'CI Fix Update' }, + debug: { emoji: 'πŸ›', label: 'Debug Update' }, +}; + +/** + * Get the emoji and label for a given agent type. + * Falls back to a generic label for unknown agent types. + */ +export function getAgentLabel(agentType: string): { emoji: string; label: string } { + return AGENT_LABELS[agentType] ?? { emoji: 'βš™οΈ', label: 'Progress Update' }; +} + /** * Human-readable initial messages per agent type. * diff --git a/src/config/statusUpdateConfig.ts b/src/config/statusUpdateConfig.ts index c9453cf6..edbaaaa9 100644 --- a/src/config/statusUpdateConfig.ts +++ b/src/config/statusUpdateConfig.ts @@ -6,6 +6,7 @@ */ import { formatTodoList, loadTodos } from '../gadgets/todo/storage.js'; +import { getAgentLabel } from './agentMessages.js'; /** * Configuration for periodic status updates. @@ -70,8 +71,10 @@ export function formatStatusMessage( const doneCount = todos.filter((t) => t.status === 'done').length; const totalCount = todos.length; + const { emoji, label } = getAgentLabel(agentType); + const lines = [ - `**I'm making progress** (${agentType})`, + `**${emoji} ${label}** (${agentType})`, '', `${progressBar} ${progress}% (iteration ${iteration}/${maxIterations})`, ]; diff --git a/tests/unit/config/statusUpdateConfig.test.ts b/tests/unit/config/statusUpdateConfig.test.ts index 3b406285..049b8d11 100644 --- a/tests/unit/config/statusUpdateConfig.test.ts +++ b/tests/unit/config/statusUpdateConfig.test.ts @@ -1,5 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; +import { getAgentLabel } from '../../../src/config/agentMessages.js'; import { formatGitHubProgressComment, formatStatusMessage, @@ -54,13 +55,65 @@ describe('config/statusUpdateConfig', () => { }); }); + describe('getAgentLabel', () => { + it('returns correct emoji and label for implementation', () => { + const result = getAgentLabel('implementation'); + expect(result).toEqual({ emoji: 'πŸ§‘β€πŸ’»', label: 'Implementation Update' }); + }); + + it('returns correct emoji and label for review', () => { + const result = getAgentLabel('review'); + expect(result).toEqual({ emoji: 'πŸ”', label: 'Code Review Update' }); + }); + + it('returns correct emoji and label for briefing', () => { + const result = getAgentLabel('briefing'); + expect(result).toEqual({ emoji: 'πŸ“‹', label: 'Briefing Update' }); + }); + + it('returns correct emoji and label for planning', () => { + const result = getAgentLabel('planning'); + expect(result).toEqual({ emoji: 'πŸ—ΊοΈ', label: 'Planning Update' }); + }); + + it('returns correct emoji and label for respond-to-review', () => { + const result = getAgentLabel('respond-to-review'); + expect(result).toEqual({ emoji: 'πŸ”§', label: 'Review Response Update' }); + }); + + it('returns correct emoji and label for respond-to-ci', () => { + const result = getAgentLabel('respond-to-ci'); + expect(result).toEqual({ emoji: 'πŸ”§', label: 'CI Fix Update' }); + }); + + it('returns correct emoji and label for respond-to-pr-comment', () => { + const result = getAgentLabel('respond-to-pr-comment'); + expect(result).toEqual({ emoji: 'πŸ’¬', label: 'PR Comment Response Update' }); + }); + + it('returns correct emoji and label for respond-to-planning-comment', () => { + const result = getAgentLabel('respond-to-planning-comment'); + expect(result).toEqual({ emoji: 'πŸ’¬', label: 'Planning Response Update' }); + }); + + it('returns correct emoji and label for debug', () => { + const result = getAgentLabel('debug'); + expect(result).toEqual({ emoji: 'πŸ›', label: 'Debug Update' }); + }); + + it('returns default fallback for unknown agent types', () => { + const result = getAgentLabel('future-unknown-agent'); + expect(result).toEqual({ emoji: 'βš™οΈ', label: 'Progress Update' }); + }); + }); + describe('formatStatusMessage', () => { - it('includes agent type and progress bar', () => { + it('includes agent-specific emoji/label and progress bar', () => { vi.mocked(loadTodos).mockReturnValue([]); const message = formatStatusMessage(5, 20, 'implementation'); - expect(message).toContain("**I'm making progress**"); + expect(message).toContain('**πŸ§‘β€πŸ’» Implementation Update**'); expect(message).toContain('implementation'); expect(message).toContain('25%'); // (5/20) * 100 expect(message).toContain('iteration 5/20'); @@ -151,7 +204,7 @@ describe('config/statusUpdateConfig', () => { const message = formatStatusMessage(10, 20, 'implementation'); const lines = message.split('\n'); - expect(lines[0]).toBe("**I'm making progress** (implementation)"); + expect(lines[0]).toBe('**πŸ§‘β€πŸ’» Implementation Update** (implementation)'); expect(lines[1]).toBe(''); expect(lines[2]).toContain('[β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘]'); });