diff --git a/CHANGELOG.md b/CHANGELOG.md index c0676f65..d49fa128 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## What's New in 2.0.1 + +### 🚀 New Features +- **Update Check with Release URLs**: Enhanced update checking functionality to include release URLs, allowing users to easily access release information +- **Markdown Renderer for Release Notes**: Added markdown renderer in advanced settings to properly display formatted release notes +- **Terminal Name Generator**: New feature for generating terminal names + +### 🔧 Improvements +- **LLM Provider Naming**: Updated project settings to reflect new LLM provider name +- **IPC Handlers**: Improved IPC handlers for external link management +- **UI Simplification**: Refactored App component to simplify project selection display by removing unnecessary wrapper elements +- **Docker Infrastructure**: Updated FalkorDB service container naming in docker-compose configuration +- **Documentation**: Improved README with dedicated CLI documentation and infrastructure status information + +### 📚 Documentation +- Enhanced README with comprehensive CLI documentation and setup instructions +- Added Docker infrastructure status documentation + ## What's New in v2.0.0 ### New Features diff --git a/auto-claude-ui/src/main/changelog/formatter.ts b/auto-claude-ui/src/main/changelog/formatter.ts index cf3c7947..fb084b8f 100644 --- a/auto-claude-ui/src/main/changelog/formatter.ts +++ b/auto-claude-ui/src/main/changelog/formatter.ts @@ -52,6 +52,43 @@ const AUDIENCE_INSTRUCTIONS = { 'marketing': 'You are a marketing specialist writing release notes. Focus on outcomes and user impact with compelling language.' }; +/** + * Get emoji usage instructions based on level + */ +function getEmojiInstructions(emojiLevel?: string): string { + if (!emojiLevel || emojiLevel === 'none') { + return ''; + } + + const instructions: Record = { + 'little': `Add emojis ONLY to section headings. Each heading should have one contextual emoji at the start. +Examples: +- "### ✨ New Features" or "### 🚀 New Features" +- "### 🐛 Bug Fixes" +- "### 🔧 Improvements" or "### ⚡ Improvements" +- "### 📚 Documentation" +Do NOT add emojis to individual line items.`, + 'medium': `Add emojis to section headings AND to notable/important items only. +Section headings should have one emoji (e.g., "### ✨ New Features", "### 🐛 Bug Fixes"). +Add emojis to 2-3 highlighted items per section that are particularly significant. +Examples of highlighted items: +- "- 🎉 **Major Feature**: Description" +- "- 🔒 **Security Fix**: Description" +Most regular line items should NOT have emojis.`, + 'high': `Add emojis to section headings AND every line item for maximum visual appeal. +Section headings: "### ✨ New Features", "### 🐛 Bug Fixes", "### ⚡ Improvements" +Every line item should start with a contextual emoji: +- "- ✨ Added new feature..." +- "- 🐛 Fixed bug where..." +- "- 🔧 Improved performance of..." +- "- 📝 Updated documentation for..." +- "- 🎨 Refined UI styling..." +Use diverse, contextually appropriate emojis for each item.` + }; + + return instructions[emojiLevel] || ''; +} + /** * Build changelog prompt from task specs */ @@ -61,6 +98,7 @@ export function buildChangelogPrompt( ): string { const audienceInstruction = AUDIENCE_INSTRUCTIONS[request.audience]; const formatInstruction = FORMAT_TEMPLATES[request.format](request.version, request.date); + const emojiInstruction = getEmojiInstructions(request.emojiLevel); // Build CONCISE task summaries (key to avoiding timeout) const taskSummaries = specs.map(spec => { @@ -86,6 +124,7 @@ export function buildChangelogPrompt( Format: ${formatInstruction} +${emojiInstruction ? `\nEmoji Usage:\n${emojiInstruction}` : ''} Completed tasks: ${taskSummaries} @@ -104,6 +143,7 @@ export function buildGitPrompt( ): string { const audienceInstruction = AUDIENCE_INSTRUCTIONS[request.audience]; const formatInstruction = FORMAT_TEMPLATES[request.format](request.version, request.date); + const emojiInstruction = getEmojiInstructions(request.emojiLevel); // Format commits for the prompt // Group by conventional commit type if detected @@ -155,6 +195,7 @@ Conventional commit types to recognize: Format: ${formatInstruction} +${emojiInstruction ? `\nEmoji Usage:\n${emojiInstruction}` : ''} Git commits (${commits.length} total): ${commitLines} diff --git a/auto-claude-ui/src/main/ipc-handlers/changelog-handlers.ts b/auto-claude-ui/src/main/ipc-handlers/changelog-handlers.ts index 321107c6..16b5bee7 100644 --- a/auto-claude-ui/src/main/ipc-handlers/changelog-handlers.ts +++ b/auto-claude-ui/src/main/ipc-handlers/changelog-handlers.ts @@ -1,7 +1,7 @@ import { ipcMain } from 'electron'; import type { BrowserWindow } from 'electron'; import path from 'path'; -import { existsSync, readFileSync } from 'fs'; +import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs'; import { execSync } from 'child_process'; import { IPC_CHANNELS, getSpecsDir } from '../../shared/constants'; import type { @@ -292,6 +292,48 @@ export function registerChangelogHandlers( } ); + // ============================================ + // Changelog Image Operations + // ============================================ + + ipcMain.handle( + IPC_CHANNELS.CHANGELOG_SAVE_IMAGE, + async (_, projectId: string, imageData: string, filename: string): Promise> => { + const project = projectStore.getProject(projectId); + if (!project) { + return { success: false, error: 'Project not found' }; + } + + try { + // Create .github/assets directory if it doesn't exist + const assetsDir = path.join(project.path, '.github', 'assets'); + if (!existsSync(assetsDir)) { + mkdirSync(assetsDir, { recursive: true }); + } + + // Decode base64 image data + const base64Data = imageData.includes(',') ? imageData.split(',')[1] : imageData; + const buffer = Buffer.from(base64Data, 'base64'); + + // Save image file + const imagePath = path.join(assetsDir, filename); + writeFileSync(imagePath, buffer); + + // Return relative path for use in markdown + const relativePath = `.github/assets/${filename}`; + // For GitHub releases, we'll use the relative path which will work when the release is created + const url = relativePath; + + return { success: true, data: { relativePath, url } }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : 'Failed to save image' + }; + } + } + ); + // ============================================ // Changelog Agent Events → Renderer } diff --git a/auto-claude-ui/src/main/ipc-handlers/roadmap-handlers.ts b/auto-claude-ui/src/main/ipc-handlers/roadmap-handlers.ts index c82b6f3f..68d40de9 100644 --- a/auto-claude-ui/src/main/ipc-handlers/roadmap-handlers.ts +++ b/auto-claude-ui/src/main/ipc-handlers/roadmap-handlers.ts @@ -324,8 +324,8 @@ ${(feature.acceptance_criteria || []).map((c: string) => `- [ ] ${c}`).join('\n' }; writeFileSync(path.join(specDir, 'task_metadata.json'), JSON.stringify(metadata, null, 2)); - // Start spec creation with the existing spec directory - agentManager.startSpecCreation(specId, project.path, taskDescription, specDir, metadata); + // NOTE: We do NOT auto-start spec creation here - user should explicitly start the task + // from the kanban board when they're ready // Update feature with linked spec feature.status = 'planned'; diff --git a/auto-claude-ui/src/preload/api/agent-api.ts b/auto-claude-ui/src/preload/api/agent-api.ts index a35212fc..c8cfc9af 100644 --- a/auto-claude-ui/src/preload/api/agent-api.ts +++ b/auto-claude-ui/src/preload/api/agent-api.ts @@ -121,6 +121,11 @@ export interface AgentAPI { options: GitHistoryOptions | BranchDiffOptions, mode: 'git-history' | 'branch-diff' ) => Promise>; + saveChangelogImage: ( + projectId: string, + imageData: string, + filename: string + ) => Promise>; // Changelog Event Listeners onChangelogGenerationProgress: (callback: (projectId: string, progress: ChangelogGenerationProgress) => void) => () => void; @@ -491,6 +496,13 @@ export const createAgentAPI = (): AgentAPI => ({ ): Promise> => ipcRenderer.invoke(IPC_CHANNELS.CHANGELOG_GET_COMMITS_PREVIEW, projectId, options, mode), + saveChangelogImage: ( + projectId: string, + imageData: string, + filename: string + ): Promise> => + ipcRenderer.invoke(IPC_CHANNELS.CHANGELOG_SAVE_IMAGE, projectId, imageData, filename), + // Changelog Event Listeners onChangelogGenerationProgress: ( callback: (projectId: string, progress: ChangelogGenerationProgress) => void diff --git a/auto-claude-ui/src/renderer/App.tsx b/auto-claude-ui/src/renderer/App.tsx index 02ecb939..832104ba 100644 --- a/auto-claude-ui/src/renderer/App.tsx +++ b/auto-claude-ui/src/renderer/App.tsx @@ -285,7 +285,7 @@ export function App() { /> {activeView === 'roadmap' && selectedProjectId && ( - + )} {activeView === 'context' && selectedProjectId && ( diff --git a/auto-claude-ui/src/renderer/components/Roadmap.tsx b/auto-claude-ui/src/renderer/components/Roadmap.tsx index 49569389..03258edb 100644 --- a/auto-claude-ui/src/renderer/components/Roadmap.tsx +++ b/auto-claude-ui/src/renderer/components/Roadmap.tsx @@ -14,7 +14,8 @@ import { BarChart3, Clock, AlertCircle, - Play + Play, + ExternalLink } from 'lucide-react'; import { Button } from './ui/button'; import { Badge } from './ui/badge'; @@ -44,11 +45,13 @@ import type { RoadmapFeature, RoadmapPhase } from '../../shared/types'; interface RoadmapProps { projectId: string; + onGoToTask?: (taskId: string) => void; } -export function Roadmap({ projectId }: RoadmapProps) { +export function Roadmap({ projectId, onGoToTask }: RoadmapProps) { const roadmap = useRoadmapStore((state) => state.roadmap); const generationStatus = useRoadmapStore((state) => state.generationStatus); + const updateFeatureLinkedSpec = useRoadmapStore((state) => state.updateFeatureLinkedSpec); const [selectedFeature, setSelectedFeature] = useState(null); const [activeTab, setActiveTab] = useState('phases'); @@ -67,8 +70,23 @@ export function Roadmap({ projectId }: RoadmapProps) { const handleConvertToSpec = async (feature: RoadmapFeature) => { const result = await window.electronAPI.convertFeatureToSpec(projectId, feature.id); - if (result.success) { - // Feature converted to spec - could show notification + if (result.success && result.data) { + // Update the store with the linked spec + updateFeatureLinkedSpec(feature.id, result.data.specId); + // Update the selected feature if it's the one that was converted + if (selectedFeature?.id === feature.id) { + setSelectedFeature({ + ...feature, + linkedSpecId: result.data.specId, + status: 'planned' + }); + } + } + }; + + const handleGoToTask = (specId: string) => { + if (onGoToTask) { + onGoToTask(specId); } }; @@ -203,6 +221,7 @@ export function Roadmap({ projectId }: RoadmapProps) { isFirst={index === 0} onFeatureSelect={setSelectedFeature} onConvertToSpec={handleConvertToSpec} + onGoToTask={handleGoToTask} /> ))} @@ -217,6 +236,7 @@ export function Roadmap({ projectId }: RoadmapProps) { feature={feature} onClick={() => setSelectedFeature(feature)} onConvertToSpec={handleConvertToSpec} + onGoToTask={handleGoToTask} /> ))} @@ -273,6 +293,7 @@ export function Roadmap({ projectId }: RoadmapProps) { feature={selectedFeature} onClose={() => setSelectedFeature(null)} onConvertToSpec={handleConvertToSpec} + onGoToTask={handleGoToTask} /> )} @@ -286,9 +307,10 @@ interface PhaseCardProps { isFirst: boolean; onFeatureSelect: (feature: RoadmapFeature) => void; onConvertToSpec: (feature: RoadmapFeature) => void; + onGoToTask: (specId: string) => void; } -function PhaseCard({ phase, features, isFirst, onFeatureSelect, onConvertToSpec }: PhaseCardProps) { +function PhaseCard({ phase, features, isFirst, onFeatureSelect, onConvertToSpec, onGoToTask }: PhaseCardProps) { const completedCount = features.filter((f) => f.status === 'done').length; const progress = features.length > 0 ? (completedCount / features.length) * 100 : 0; @@ -378,7 +400,18 @@ function PhaseCard({ phase, features, isFirst, onFeatureSelect, onConvertToSpec {feature.status === 'done' ? ( ) : feature.linkedSpecId ? ( - In Progress + ) : ( + ) : feature.status !== 'done' && ( + + ) : feature.status !== 'done' && (
+ +
+ + +
@@ -367,14 +537,34 @@ export function Step2ConfigureGenerate({ {/* Preview Content */} -
+
{generatedChangelog ? ( -