diff --git a/apps/ui/src/components/ui/autocomplete.tsx b/apps/ui/src/components/ui/autocomplete.tsx
index c12aa0731..ab18d274a 100644
--- a/apps/ui/src/components/ui/autocomplete.tsx
+++ b/apps/ui/src/components/ui/autocomplete.tsx
@@ -13,6 +13,9 @@ import {
} from '@/components/ui/command';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
+/**
+ * Option item for the Autocomplete component
+ */
export interface AutocompleteOption {
value: string;
label?: string;
@@ -44,6 +47,19 @@ function normalizeOption(opt: string | AutocompleteOption): AutocompleteOption {
return { ...opt, label: opt.label ?? opt.value };
}
+/**
+ * A generic Autocomplete/Combobox component built on top of shadcn/ui.
+ *
+ * Features:
+ * - Searchable options list
+ * - Support for creating new values that don't exist in options
+ * - Custom icons and badges
+ * - Accessible Popover + Command implementation
+ * - Responsive width handling
+ *
+ * @param props - Component props
+ * @returns The rendered autocomplete component
+ */
export function Autocomplete({
value,
onChange,
@@ -87,9 +103,11 @@ export function Autocomplete({
// Filter options based on input
const filteredOptions = React.useMemo(() => {
- if (!inputValue) return normalizedOptions;
+ // Filter out options with undefined/null values
+ const validOptions = normalizedOptions.filter((opt) => opt.value != null);
+ if (!inputValue) return validOptions;
const lower = inputValue.toLowerCase();
- return normalizedOptions.filter(
+ return validOptions.filter(
(opt) => opt.value.toLowerCase().includes(lower) || opt.label?.toLowerCase().includes(lower)
);
}, [normalizedOptions, inputValue]);
@@ -98,7 +116,7 @@ export function Autocomplete({
const isNewValue =
allowCreate &&
inputValue.trim() &&
- !normalizedOptions.some((opt) => opt.value.toLowerCase() === inputValue.toLowerCase());
+ !normalizedOptions.some((opt) => opt.value?.toLowerCase() === inputValue.toLowerCase());
// Get display value
const displayValue = React.useMemo(() => {
@@ -184,7 +202,7 @@ export function Autocomplete({
setInputValue('');
setOpen(false);
}}
- data-testid={`${itemTestIdPrefix}-${option.value.toLowerCase().replace(/[\s/\\]+/g, '-')}`}
+ data-testid={`${itemTestIdPrefix}-${(option.value ?? '').toLowerCase().replace(/[\s/\\]+/g, '-')}`}
>
{Icon && }
{option.label}
diff --git a/apps/ui/src/components/ui/branch-autocomplete.tsx b/apps/ui/src/components/ui/branch-autocomplete.tsx
index 8e95442a7..c6af70622 100644
--- a/apps/ui/src/components/ui/branch-autocomplete.tsx
+++ b/apps/ui/src/components/ui/branch-autocomplete.tsx
@@ -14,6 +14,18 @@ interface BranchAutocompleteProps {
'data-testid'?: string;
}
+/**
+ * A specialized Autocomplete component for selecting git branches.
+ *
+ * Features:
+ * - Always shows 'main' at the top of the list
+ * - Displays card counts badges for branches if provided
+ * - Allows creating new branches (via allowCreate prop passed to Autocomplete)
+ * - Includes a GitBranch icon
+ *
+ * @param props - Component props
+ * @returns The rendered branch selection component
+ */
export function BranchAutocomplete({
value,
onChange,
@@ -27,7 +39,9 @@ export function BranchAutocomplete({
}: BranchAutocompleteProps) {
// Always include "main" at the top of suggestions
const branchOptions: AutocompleteOption[] = React.useMemo(() => {
- const branchSet = new Set(['main', ...branches]);
+ // Filter out undefined/null branches
+ const validBranches = branches.filter((b): b is string => b != null && b !== '');
+ const branchSet = new Set(['main', ...validBranches]);
return Array.from(branchSet).map((branch) => {
const cardCount = branchCardCounts?.[branch];
// Show card count if available, otherwise show "default" for main branch only
diff --git a/apps/ui/src/components/views/board-view.tsx b/apps/ui/src/components/views/board-view.tsx
index 2624514a3..e184192f1 100644
--- a/apps/ui/src/components/views/board-view.tsx
+++ b/apps/ui/src/components/views/board-view.tsx
@@ -93,6 +93,20 @@ const EMPTY_WORKTREES: ReturnType['getWo
const logger = createLogger('Board');
+/**
+ * The main board view component.
+ *
+ * Displays the project board in either Kanban or List layout.
+ * Manages all board state including:
+ * - Feature lists and categories
+ * - Drag and drop operations
+ * - Worktree management and panel
+ * - Dialogs (add feature, edit, pipeline settings, etc.)
+ * - Keyboard shortcuts
+ * - Auto-mode integration
+ *
+ * @returns The rendered Board view
+ */
export function BoardView() {
const {
currentProject,
@@ -349,7 +363,7 @@ export function BoardView() {
const result = await api.worktree.listBranches(currentProject.path);
if (result.success && result.result?.branches) {
const localBranches = result.result.branches
- .filter((b) => !b.isRemote)
+ .filter((b) => !b.isRemote && b.name)
.map((b) => b.name);
setBranchSuggestions(localBranches);
}
diff --git a/apps/ui/src/components/views/board-view/dialogs/create-pr-dialog.tsx b/apps/ui/src/components/views/board-view/dialogs/create-pr-dialog.tsx
index f072f7330..f2686b1f2 100644
--- a/apps/ui/src/components/views/board-view/dialogs/create-pr-dialog.tsx
+++ b/apps/ui/src/components/views/board-view/dialogs/create-pr-dialog.tsx
@@ -37,6 +37,20 @@ interface CreatePRDialogProps {
defaultBaseBranch?: string;
}
+/**
+ * Dialog for creating a GitHub Pull Request from a worktree.
+ *
+ * Features:
+ * - Automatically commits changes if needed
+ * - Pushes the branch to remote
+ * - Creates PR via GitHub CLI
+ * - Supports draft PRs
+ * - Allows selecting base branch
+ * - Fallback to browser if gh CLI is missing/failing
+ *
+ * @param props - Component props
+ * @returns The rendered Create PR dialog
+ */
export function CreatePRDialog({
open,
onOpenChange,
@@ -67,7 +81,10 @@ export function CreatePRDialog({
// Filter out current worktree branch from the list
const branches = useMemo(() => {
if (!branchesData?.branches) return [];
- return branchesData.branches.map((b) => b.name).filter((name) => name !== worktree?.branch);
+ return branchesData.branches
+ .filter((b) => b.name)
+ .map((b) => b.name)
+ .filter((name) => name !== worktree?.branch);
}, [branchesData?.branches, worktree?.branch]);
// Common state reset function to avoid duplication
diff --git a/apps/ui/src/components/views/board-view/dialogs/merge-worktree-dialog.tsx b/apps/ui/src/components/views/board-view/dialogs/merge-worktree-dialog.tsx
index 7bb1440a1..8eb422814 100644
--- a/apps/ui/src/components/views/board-view/dialogs/merge-worktree-dialog.tsx
+++ b/apps/ui/src/components/views/board-view/dialogs/merge-worktree-dialog.tsx
@@ -29,6 +29,18 @@ interface MergeWorktreeDialogProps {
onCreateConflictResolutionFeature?: (conflictInfo: MergeConflictInfo) => void;
}
+/**
+ * Dialog for merging a worktree branch into another branch (typically main).
+ *
+ * Features:
+ * - Select target branch (defaults to main)
+ * - Option to delete worktree and branch after merge
+ * - Conflict detection and resolution workflow
+ * - Warnings for uncommitted changes
+ *
+ * @param props - Component props
+ * @returns The rendered Merge Worktree dialog
+ */
export function MergeWorktreeDialog({
open,
onOpenChange,
@@ -56,7 +68,7 @@ export function MergeWorktreeDialog({
if (result.success && result.result?.branches) {
// Filter out the source branch (can't merge into itself) and remote branches
const branches = result.result.branches
- .filter((b: BranchInfo) => !b.isRemote && b.name !== worktree.branch)
+ .filter((b: BranchInfo) => !b.isRemote && b.name && b.name !== worktree.branch)
.map((b: BranchInfo) => b.name);
setAvailableBranches(branches);
}
diff --git a/apps/ui/src/components/views/graph-view-page.tsx b/apps/ui/src/components/views/graph-view-page.tsx
index 96dffb9a8..478505e4d 100644
--- a/apps/ui/src/components/views/graph-view-page.tsx
+++ b/apps/ui/src/components/views/graph-view-page.tsx
@@ -29,6 +29,19 @@ const logger = createLogger('GraphViewPage');
// Stable empty array to avoid infinite loop in selector
const EMPTY_WORKTREES: ReturnType['getWorktrees']> = [];
+/**
+ * The Graph View page component.
+ *
+ * Displays the project's features in a dependency graph visualization.
+ * Integrates with:
+ * - D3-based graph rendering (GraphView)
+ * - Worktree management
+ * - Feature management (Add, Edit, Delete)
+ * - Backlog planning
+ * - Auto-mode task execution
+ *
+ * @returns The rendered Graph View page
+ */
export function GraphViewPage() {
const {
currentProject,
@@ -134,7 +147,7 @@ export function GraphViewPage() {
const result = await api.worktree.listBranches(currentProject.path);
if (result.success && result.result?.branches) {
const localBranches = result.result.branches
- .filter((b) => !b.isRemote)
+ .filter((b) => !b.isRemote && b.name)
.map((b) => b.name);
setBranchSuggestions(localBranches);
}