mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 21:23:07 +00:00
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:
@@ -8,6 +8,8 @@
|
|||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
import { exec } from 'child_process';
|
import { exec } from 'child_process';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
import { query } from '@anthropic-ai/claude-agent-sdk';
|
import { query } from '@anthropic-ai/claude-agent-sdk';
|
||||||
import { createLogger } from '@automaker/utils';
|
import { createLogger } from '@automaker/utils';
|
||||||
import { DEFAULT_PHASE_MODELS, isCursorModel, stripProviderPrefix } from '@automaker/types';
|
import { DEFAULT_PHASE_MODELS, isCursorModel, stripProviderPrefix } from '@automaker/types';
|
||||||
@@ -87,6 +89,27 @@ export function createGenerateCommitMessageHandler(
|
|||||||
return;
|
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}`);
|
logger.info(`Generating commit message for worktree: ${worktreePath}`);
|
||||||
|
|
||||||
// Get git diff of staged and unstaged changes
|
// Get git diff of staged and unstaged changes
|
||||||
|
|||||||
@@ -20,14 +20,16 @@ export function AgentView() {
|
|||||||
const { currentProject } = useAppStore();
|
const { currentProject } = useAppStore();
|
||||||
const [input, setInput] = useState('');
|
const [input, setInput] = useState('');
|
||||||
const [currentTool, setCurrentTool] = useState<string | null>(null);
|
const [currentTool, setCurrentTool] = useState<string | null>(null);
|
||||||
// Initialize session manager state based on screen size (hidden on mobile)
|
// Initialize session manager state - starts as true to match SSR
|
||||||
const [showSessionManager, setShowSessionManager] = useState(() => {
|
// 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)
|
// Check if we're on a mobile screen (< lg breakpoint = 1024px)
|
||||||
if (typeof window !== 'undefined') {
|
const isDesktop = window.innerWidth >= 1024;
|
||||||
return window.innerWidth >= 1024;
|
setShowSessionManager(isDesktop);
|
||||||
}
|
}, []);
|
||||||
return true; // Default to showing on SSR
|
|
||||||
});
|
|
||||||
const [modelSelection, setModelSelection] = useState<PhaseModelEntry>({ model: 'sonnet' });
|
const [modelSelection, setModelSelection] = useState<PhaseModelEntry>({ model: 'sonnet' });
|
||||||
|
|
||||||
// Input ref for auto-focus
|
// Input ref for auto-focus
|
||||||
|
|||||||
@@ -10,42 +10,6 @@ import type { PipelineConfig } from '@automaker/types';
|
|||||||
import { RowActions, type RowActionHandlers } from './row-actions';
|
import { RowActions, type RowActionHandlers } from './row-actions';
|
||||||
import { getColumnWidth, getColumnAlign } from './list-header';
|
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
|
* Get the priority display configuration
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -258,10 +258,19 @@ function GraphCanvasInner({
|
|||||||
let previousWidth = window.innerWidth;
|
let previousWidth = window.innerWidth;
|
||||||
let previousHeight = window.innerHeight;
|
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 = () => {
|
const handleOrientationChange = () => {
|
||||||
|
// Clear any pending timeout
|
||||||
|
if (orientationTimeoutId) {
|
||||||
|
clearTimeout(orientationTimeoutId);
|
||||||
|
}
|
||||||
// Small delay to allow the browser to complete the orientation change
|
// Small delay to allow the browser to complete the orientation change
|
||||||
setTimeout(() => {
|
orientationTimeoutId = setTimeout(() => {
|
||||||
fitView({ padding: 0.2, duration: 300 });
|
fitView({ padding: 0.2, duration: 300 });
|
||||||
|
orientationTimeoutId = null;
|
||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -279,9 +288,14 @@ function GraphCanvasInner({
|
|||||||
const isOrientationChange = widthDiff < 100 && heightDiff < 100;
|
const isOrientationChange = widthDiff < 100 && heightDiff < 100;
|
||||||
|
|
||||||
if (isOrientationChange) {
|
if (isOrientationChange) {
|
||||||
|
// Clear any pending timeout
|
||||||
|
if (resizeTimeoutId) {
|
||||||
|
clearTimeout(resizeTimeoutId);
|
||||||
|
}
|
||||||
// Delay fitView to allow browser to complete the layout
|
// Delay fitView to allow browser to complete the layout
|
||||||
setTimeout(() => {
|
resizeTimeoutId = setTimeout(() => {
|
||||||
fitView({ padding: 0.2, duration: 300 });
|
fitView({ padding: 0.2, duration: 300 });
|
||||||
|
resizeTimeoutId = null;
|
||||||
}, 150);
|
}, 150);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,6 +311,13 @@ function GraphCanvasInner({
|
|||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('orientationchange', handleOrientationChange);
|
window.removeEventListener('orientationchange', handleOrientationChange);
|
||||||
window.removeEventListener('resize', handleResize);
|
window.removeEventListener('resize', handleResize);
|
||||||
|
// Clear any pending timeouts
|
||||||
|
if (orientationTimeoutId) {
|
||||||
|
clearTimeout(orientationTimeoutId);
|
||||||
|
}
|
||||||
|
if (resizeTimeoutId) {
|
||||||
|
clearTimeout(resizeTimeoutId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [fitView]);
|
}, [fitView]);
|
||||||
|
|
||||||
|
|||||||
@@ -82,10 +82,13 @@ export function ClaudeUsageSection() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Initial fetch if authenticated and stale
|
// 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();
|
void fetchUsage();
|
||||||
}
|
}
|
||||||
}, [fetchUsage, canFetchUsage, isStale]);
|
}, [fetchUsage, canFetchUsage, claudeUsageLastUpdated]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!canFetchUsage) return undefined;
|
if (!canFetchUsage) return undefined;
|
||||||
|
|||||||
@@ -175,28 +175,29 @@ export function SettingsNavigation({
|
|||||||
}: SettingsNavigationProps) {
|
}: SettingsNavigationProps) {
|
||||||
// On mobile, only show when isOpen is true
|
// On mobile, only show when isOpen is true
|
||||||
// On desktop (lg+), always show regardless of isOpen
|
// On desktop (lg+), always show regardless of isOpen
|
||||||
const shouldShow = isOpen;
|
// The desktop visibility is handled by CSS, but we need to render on mobile only when open
|
||||||
|
|
||||||
if (!shouldShow) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Mobile backdrop overlay */}
|
{/* Mobile backdrop overlay - only shown when isOpen is true on mobile */}
|
||||||
<div
|
{isOpen && (
|
||||||
className="fixed inset-0 bg-black/50 z-20 lg:hidden"
|
<div
|
||||||
onClick={onClose}
|
className="fixed inset-0 bg-black/50 z-20 lg:hidden"
|
||||||
data-testid="settings-nav-backdrop"
|
onClick={onClose}
|
||||||
/>
|
data-testid="settings-nav-backdrop"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Navigation sidebar */}
|
{/* Navigation sidebar */}
|
||||||
<nav
|
<nav
|
||||||
className={cn(
|
className={cn(
|
||||||
// Mobile: fixed position overlay
|
// Mobile: fixed position overlay with slide transition
|
||||||
'fixed inset-y-0 left-0 w-72 z-30',
|
'fixed inset-y-0 left-0 w-72 z-30',
|
||||||
// Desktop: relative position in layout
|
'transition-transform duration-200 ease-out',
|
||||||
'lg:relative lg:w-64 lg:z-auto',
|
// 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',
|
'shrink-0 overflow-y-auto',
|
||||||
'border-r border-border/50',
|
'border-r border-border/50',
|
||||||
'bg-gradient-to-b from-card/95 via-card/90 to-card/85 backdrop-blur-xl',
|
'bg-gradient-to-b from-card/95 via-card/90 to-card/85 backdrop-blur-xl',
|
||||||
|
|||||||
Reference in New Issue
Block a user