mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-01-30 06:12:06 +00:00
feat: add configurable CLI command and UI improvements
Add support for alternative CLI commands via CLI_COMMAND environment variable, allowing users to use CLIs other than 'claude' (e.g., 'glm'). This change affects all server services and the main CLI launcher. Key changes: - Configurable CLI command via CLI_COMMAND env var (defaults to 'claude') - Configurable Playwright headless mode via PLAYWRIGHT_HEADLESS env var - Pin claude-agent-sdk version to <0.2.0 for stability - Use tail -500 for progress notes to avoid context overflow - Add project delete functionality with confirmation dialog - Replace single-line input with resizable textarea in spec chat - Add coder agent configuration for code implementation tasks - Ignore issues/ directory in git Files modified: - client.py: CLI command and Playwright headless configuration - server/main.py, server/services/*: CLI command configuration - start.py: CLI command configuration and error messages - .env.example: Document new environment variables - .gitignore: Ignore issues/ directory - requirements.txt: Pin SDK version - .claude/templates/*: Use tail -500 for progress notes - ui/src/components/ProjectSelector.tsx: Add delete button - ui/src/components/SpecCreationChat.tsx: Auto-resizing textarea - ui/src/components/ConfirmDialog.tsx: New reusable dialog - .claude/agents/coder.md: New coder agent configuration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { useState } from 'react'
|
||||
import { ChevronDown, Plus, FolderOpen, Loader2 } from 'lucide-react'
|
||||
import { ChevronDown, Plus, FolderOpen, Loader2, Trash2 } from 'lucide-react'
|
||||
import type { ProjectSummary } from '../lib/types'
|
||||
import { NewProjectModal } from './NewProjectModal'
|
||||
import { ConfirmDialog } from './ConfirmDialog'
|
||||
import { useDeleteProject } from '../hooks/useProjects'
|
||||
|
||||
interface ProjectSelectorProps {
|
||||
projects: ProjectSummary[]
|
||||
@@ -18,12 +20,42 @@ export function ProjectSelector({
|
||||
}: ProjectSelectorProps) {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [showNewProjectModal, setShowNewProjectModal] = useState(false)
|
||||
const [projectToDelete, setProjectToDelete] = useState<string | null>(null)
|
||||
|
||||
const deleteProject = useDeleteProject()
|
||||
|
||||
const handleProjectCreated = (projectName: string) => {
|
||||
onSelectProject(projectName)
|
||||
setIsOpen(false)
|
||||
}
|
||||
|
||||
const handleDeleteClick = (e: React.MouseEvent, projectName: string) => {
|
||||
// Prevent the click from selecting the project
|
||||
e.stopPropagation()
|
||||
setProjectToDelete(projectName)
|
||||
}
|
||||
|
||||
const handleConfirmDelete = async () => {
|
||||
if (!projectToDelete) return
|
||||
|
||||
try {
|
||||
await deleteProject.mutateAsync(projectToDelete)
|
||||
// If the deleted project was selected, clear the selection
|
||||
if (selectedProject === projectToDelete) {
|
||||
onSelectProject(null)
|
||||
}
|
||||
setProjectToDelete(null)
|
||||
} catch (error) {
|
||||
// Error is handled by the mutation, just close the dialog
|
||||
console.error('Failed to delete project:', error)
|
||||
setProjectToDelete(null)
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancelDelete = () => {
|
||||
setProjectToDelete(null)
|
||||
}
|
||||
|
||||
const selectedProjectData = projects.find(p => p.name === selectedProject)
|
||||
|
||||
return (
|
||||
@@ -70,28 +102,39 @@ export function ProjectSelector({
|
||||
{projects.length > 0 ? (
|
||||
<div className="max-h-[300px] overflow-auto">
|
||||
{projects.map(project => (
|
||||
<button
|
||||
<div
|
||||
key={project.name}
|
||||
onClick={() => {
|
||||
onSelectProject(project.name)
|
||||
setIsOpen(false)
|
||||
}}
|
||||
className={`w-full neo-dropdown-item flex items-center justify-between ${
|
||||
className={`flex items-center ${
|
||||
project.name === selectedProject
|
||||
? 'bg-[var(--color-neo-pending)]'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<FolderOpen size={16} />
|
||||
{project.name}
|
||||
</span>
|
||||
{project.stats.total > 0 && (
|
||||
<span className="text-sm font-mono">
|
||||
{project.stats.passing}/{project.stats.total}
|
||||
<button
|
||||
onClick={() => {
|
||||
onSelectProject(project.name)
|
||||
setIsOpen(false)
|
||||
}}
|
||||
className="flex-1 neo-dropdown-item flex items-center justify-between"
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<FolderOpen size={16} />
|
||||
{project.name}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
{project.stats.total > 0 && (
|
||||
<span className="text-sm font-mono">
|
||||
{project.stats.passing}/{project.stats.total}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => handleDeleteClick(e, project.name)}
|
||||
className="p-2 mr-2 text-[var(--color-neo-text-secondary)] hover:text-[var(--color-neo-danger)] hover:bg-[var(--color-neo-danger)]/10 transition-colors rounded"
|
||||
title={`Delete ${project.name}`}
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
@@ -124,6 +167,19 @@ export function ProjectSelector({
|
||||
onClose={() => setShowNewProjectModal(false)}
|
||||
onProjectCreated={handleProjectCreated}
|
||||
/>
|
||||
|
||||
{/* Delete Confirmation Dialog */}
|
||||
<ConfirmDialog
|
||||
isOpen={projectToDelete !== null}
|
||||
title="Delete Project"
|
||||
message={`Are you sure you want to remove "${projectToDelete}" from the registry? This will unregister the project but preserve its files on disk.`}
|
||||
confirmLabel="Delete"
|
||||
cancelLabel="Cancel"
|
||||
variant="danger"
|
||||
isLoading={deleteProject.isPending}
|
||||
onConfirm={handleConfirmDelete}
|
||||
onCancel={handleCancelDelete}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user