Merge remote-tracking branch 'upstream/v0.13.0rc' into feat/react-query

# Conflicts:
#	apps/ui/src/components/views/board-view.tsx
#	apps/ui/src/components/views/board-view/dialogs/agent-output-modal.tsx
#	apps/ui/src/components/views/board-view/hooks/use-board-features.ts
#	apps/ui/src/components/views/board-view/worktree-panel/worktree-panel.tsx
#	apps/ui/src/hooks/use-project-settings-loader.ts
This commit is contained in:
DhanushSantosh
2026-01-20 19:19:21 +05:30
76 changed files with 5995 additions and 492 deletions

View File

@@ -123,14 +123,15 @@ export function useBoardActions({
const workMode = featureData.workMode || 'current';
// Determine final branch name based on work mode:
// - 'current': No branch name, work on current branch (no worktree)
// - 'current': Use current worktree's branch (or undefined if on main)
// - 'auto': Auto-generate branch name based on current branch
// - 'custom': Use the provided branch name
let finalBranchName: string | undefined;
if (workMode === 'current') {
// No worktree isolation - work directly on current branch
finalBranchName = undefined;
// Work directly on current branch - use the current worktree's branch if not on main
// This ensures features created on a non-main worktree are associated with that worktree
finalBranchName = currentWorktreeBranch || undefined;
} else if (workMode === 'auto') {
// Auto-generate a branch name based on primary branch (main/master) and timestamp
// Always use primary branch to avoid nested feature/feature/... paths
@@ -217,7 +218,7 @@ export function useBoardActions({
const api = getElectronAPI();
if (api?.features?.generateTitle) {
api.features
.generateTitle(featureData.description)
.generateTitle(featureData.description, projectPath ?? undefined)
.then((result) => {
if (result.success && result.title) {
const titleUpdates = {
@@ -250,10 +251,12 @@ export function useBoardActions({
updateFeature,
saveCategory,
currentProject,
projectPath,
onWorktreeCreated,
onWorktreeAutoSelect,
getPrimaryWorktreeBranch,
features,
currentWorktreeBranch,
]
);
@@ -287,7 +290,9 @@ export function useBoardActions({
let finalBranchName: string | undefined;
if (workMode === 'current') {
finalBranchName = undefined;
// Work directly on current branch - use the current worktree's branch if not on main
// This ensures features updated on a non-main worktree are associated with that worktree
finalBranchName = currentWorktreeBranch || undefined;
} else if (workMode === 'auto') {
// Auto-generate a branch name based on primary branch (main/master) and timestamp
// Always use primary branch to avoid nested feature/feature/... paths
@@ -402,6 +407,7 @@ export function useBoardActions({
onWorktreeCreated,
getPrimaryWorktreeBranch,
features,
currentWorktreeBranch,
]
);

View File

@@ -103,8 +103,25 @@ export function useBoardColumnFeatures({
// Historically, we forced "running" features into in_progress so they never disappeared
// during stale reload windows. With pipelines, a feature can legitimately be running while
// its status is `pipeline_*`, so we must respect that status to render it in the right column.
// NOTE: runningAutoTasks is already worktree-scoped, so if a feature is in runningAutoTasks,
// it's already running for the current worktree. However, we still need to check matchesWorktree
// to ensure the feature's branchName matches the current worktree's branch.
if (isRunning) {
if (!matchesWorktree) return;
// If feature is running but doesn't match worktree, it might be a timing issue where
// the feature was started for a different worktree. Still show it if it's running to
// prevent disappearing features, but log a warning.
if (!matchesWorktree) {
// This can happen if:
// 1. Feature was started for a different worktree (bug)
// 2. Timing issue where branchName hasn't been set yet
// 3. User switched worktrees while feature was starting
// Still show it in in_progress to prevent it from disappearing
console.debug(
`Feature ${f.id} is running but branchName (${featureBranch}) doesn't match current worktree branch (${effectiveBranch}) - showing anyway to prevent disappearing`
);
map.in_progress.push(f);
return;
}
if (status.startsWith('pipeline_')) {
if (!map[status]) map[status] = [];

View File

@@ -50,7 +50,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
} catch {
setPersistedCategories([]);
}
}, [currentProject]);
}, [currentProject, loadFeatures]);
// Save a new category to the persisted categories file
const saveCategory = useCallback(
@@ -87,11 +87,33 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
const { removeRunningTask } = useAppStore.getState();
const projectId = currentProject.id;
const projectPath = currentProject.path;
const unsubscribe = api.autoMode.onEvent((event) => {
// Check if event is for the current project by matching projectPath
const eventProjectPath = ('projectPath' in event && event.projectPath) as string | undefined;
if (eventProjectPath && eventProjectPath !== projectPath) {
// Event is for a different project, ignore it
logger.debug(
`Ignoring auto mode event for different project: ${eventProjectPath} (current: ${projectPath})`
);
return;
}
// Use event's projectPath or projectId if available, otherwise use current project
// Board view only reacts to events for the currently selected project
const eventProjectId = ('projectId' in event && event.projectId) || projectId;
if (event.type === 'auto_mode_feature_complete') {
if (event.type === 'auto_mode_feature_start') {
// Reload features when a feature starts to ensure status update (backlog -> in_progress) is reflected
logger.info(
`[BoardFeatures] Feature ${event.featureId} started for project ${projectPath}, reloading features to update status...`
);
loadFeatures();
} else if (event.type === 'auto_mode_feature_complete') {
// Reload features when a feature is completed
logger.info('Feature completed, reloading features...');
loadFeatures();
// Play ding sound when feature is done (unless muted)
const { muteDoneSound } = useAppStore.getState();
if (!muteDoneSound) {