fix: adress pr comments

- Added validation to check if the specified worktree path exists before generating commit messages.
- Implemented a check to ensure the worktree path is a valid git repository by verifying the presence of the .git directory.
- Improved error handling by returning appropriate responses for invalid paths and non-git repositories.
This commit is contained in:
Shirone
2026-01-12 21:41:34 +01:00
parent cca4638b71
commit 47c188d8f9
6 changed files with 75 additions and 61 deletions

View File

@@ -8,6 +8,8 @@
import type { Request, Response } from 'express';
import { exec } from 'child_process';
import { promisify } from 'util';
import { existsSync } from 'fs';
import { join } from 'path';
import { query } from '@anthropic-ai/claude-agent-sdk';
import { createLogger } from '@automaker/utils';
import { DEFAULT_PHASE_MODELS, isCursorModel, stripProviderPrefix } from '@automaker/types';
@@ -87,6 +89,27 @@ export function createGenerateCommitMessageHandler(
return;
}
// Validate that the directory exists
if (!existsSync(worktreePath)) {
const response: GenerateCommitMessageErrorResponse = {
success: false,
error: 'worktreePath does not exist',
};
res.status(400).json(response);
return;
}
// Validate that it's a git repository (check for .git folder or file for worktrees)
const gitPath = join(worktreePath, '.git');
if (!existsSync(gitPath)) {
const response: GenerateCommitMessageErrorResponse = {
success: false,
error: 'worktreePath is not a git repository',
};
res.status(400).json(response);
return;
}
logger.info(`Generating commit message for worktree: ${worktreePath}`);
// Get git diff of staged and unstaged changes

View File

@@ -20,14 +20,16 @@ export function AgentView() {
const { currentProject } = useAppStore();
const [input, setInput] = useState('');
const [currentTool, setCurrentTool] = useState<string | null>(null);
// Initialize session manager state based on screen size (hidden on mobile)
const [showSessionManager, setShowSessionManager] = useState(() => {
// Initialize session manager state - starts as true to match SSR
// Then updates on mount based on actual screen size to prevent hydration mismatch
const [showSessionManager, setShowSessionManager] = useState(true);
// Update session manager visibility based on screen size after mount
useEffect(() => {
// Check if we're on a mobile screen (< lg breakpoint = 1024px)
if (typeof window !== 'undefined') {
return window.innerWidth >= 1024;
}
return true; // Default to showing on SSR
});
const isDesktop = window.innerWidth >= 1024;
setShowSessionManager(isDesktop);
}, []);
const [modelSelection, setModelSelection] = useState<PhaseModelEntry>({ model: 'sonnet' });
// Input ref for auto-focus

View File

@@ -10,42 +10,6 @@ import type { PipelineConfig } from '@automaker/types';
import { RowActions, type RowActionHandlers } from './row-actions';
import { getColumnWidth, getColumnAlign } from './list-header';
/**
* Format a date string for display in the table
*/
function formatRelativeDate(dateString: string | undefined): string {
if (!dateString) return '-';
const date = new Date(dateString);
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
if (diffDays === 0) {
// Today - show time
return date.toLocaleTimeString('en-US', {
hour: 'numeric',
minute: '2-digit',
hour12: true,
});
}
if (diffDays === 1) {
return 'Yesterday';
}
if (diffDays < 7) {
return `${diffDays} days ago`;
}
// Older - show date
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: date.getFullYear() !== now.getFullYear() ? 'numeric' : undefined,
});
}
/**
* Get the priority display configuration
*/

View File

@@ -258,10 +258,19 @@ function GraphCanvasInner({
let previousWidth = window.innerWidth;
let previousHeight = window.innerHeight;
// Track timeout IDs for cleanup
let orientationTimeoutId: ReturnType<typeof setTimeout> | null = null;
let resizeTimeoutId: ReturnType<typeof setTimeout> | null = null;
const handleOrientationChange = () => {
// Clear any pending timeout
if (orientationTimeoutId) {
clearTimeout(orientationTimeoutId);
}
// Small delay to allow the browser to complete the orientation change
setTimeout(() => {
orientationTimeoutId = setTimeout(() => {
fitView({ padding: 0.2, duration: 300 });
orientationTimeoutId = null;
}, 100);
};
@@ -279,9 +288,14 @@ function GraphCanvasInner({
const isOrientationChange = widthDiff < 100 && heightDiff < 100;
if (isOrientationChange) {
// Clear any pending timeout
if (resizeTimeoutId) {
clearTimeout(resizeTimeoutId);
}
// Delay fitView to allow browser to complete the layout
setTimeout(() => {
resizeTimeoutId = setTimeout(() => {
fitView({ padding: 0.2, duration: 300 });
resizeTimeoutId = null;
}, 150);
}
@@ -297,6 +311,13 @@ function GraphCanvasInner({
return () => {
window.removeEventListener('orientationchange', handleOrientationChange);
window.removeEventListener('resize', handleResize);
// Clear any pending timeouts
if (orientationTimeoutId) {
clearTimeout(orientationTimeoutId);
}
if (resizeTimeoutId) {
clearTimeout(resizeTimeoutId);
}
};
}, [fitView]);

View File

@@ -82,10 +82,13 @@ export function ClaudeUsageSection() {
useEffect(() => {
// Initial fetch if authenticated and stale
if (canFetchUsage && isStale) {
// Compute staleness inside effect to avoid re-running when Date.now() changes
const isDataStale =
!claudeUsageLastUpdated || Date.now() - claudeUsageLastUpdated > STALE_THRESHOLD_MS;
if (canFetchUsage && isDataStale) {
void fetchUsage();
}
}, [fetchUsage, canFetchUsage, isStale]);
}, [fetchUsage, canFetchUsage, claudeUsageLastUpdated]);
useEffect(() => {
if (!canFetchUsage) return undefined;

View File

@@ -175,28 +175,29 @@ export function SettingsNavigation({
}: SettingsNavigationProps) {
// On mobile, only show when isOpen is true
// On desktop (lg+), always show regardless of isOpen
const shouldShow = isOpen;
if (!shouldShow) {
return null;
}
// The desktop visibility is handled by CSS, but we need to render on mobile only when open
return (
<>
{/* Mobile backdrop overlay */}
<div
className="fixed inset-0 bg-black/50 z-20 lg:hidden"
onClick={onClose}
data-testid="settings-nav-backdrop"
/>
{/* Mobile backdrop overlay - only shown when isOpen is true on mobile */}
{isOpen && (
<div
className="fixed inset-0 bg-black/50 z-20 lg:hidden"
onClick={onClose}
data-testid="settings-nav-backdrop"
/>
)}
{/* Navigation sidebar */}
<nav
className={cn(
// Mobile: fixed position overlay
// Mobile: fixed position overlay with slide transition
'fixed inset-y-0 left-0 w-72 z-30',
// Desktop: relative position in layout
'lg:relative lg:w-64 lg:z-auto',
'transition-transform duration-200 ease-out',
// Hide on mobile when closed, show when open
isOpen ? 'translate-x-0' : '-translate-x-full',
// Desktop: relative position in layout, always visible
'lg:relative lg:w-64 lg:z-auto lg:translate-x-0',
'shrink-0 overflow-y-auto',
'border-r border-border/50',
'bg-gradient-to-b from-card/95 via-card/90 to-card/85 backdrop-blur-xl',