diff --git a/change-logs/2026/06/09/feature-sidebar-attention-mode.md b/change-logs/2026/06/09/feature-sidebar-attention-mode.md
new file mode 100644
index 00000000..176f3025
--- /dev/null
+++ b/change-logs/2026/06/09/feature-sidebar-attention-mode.md
@@ -0,0 +1 @@
+Added an "attention" mode to the Active Tasks sidebar. The project/global scope toggle was replaced by three icon buttons — folder (this project), globe (all projects), and a new bell. Clicking the bell filters the list cross-project to only tasks in `user-questions` or `review-by-user` status, sorted oldest-first. The bell shows a pulsing count badge when tasks are waiting and the mode is not already active.
diff --git a/src/mainview/components/ActiveTasksSidebar.tsx b/src/mainview/components/ActiveTasksSidebar.tsx
index 2d67858d..50f92d5f 100644
--- a/src/mainview/components/ActiveTasksSidebar.tsx
+++ b/src/mainview/components/ActiveTasksSidebar.tsx
@@ -14,13 +14,16 @@ import AgentLauncherBadge from "./AgentLauncherBadge";
import VariantDots from "./VariantDots";
import { getTaskAgentMeta } from "../utils/taskAgentMeta";
-type SidebarScope = "project" | "global";
+type SidebarScope = "project" | "global" | "attention";
const LS_SIDEBAR_SCOPE = "dev3-sidebar-scope";
+/** Statuses that require the user's attention — the "attention" scope shows only these. */
+const ATTENTION_STATUSES: TaskStatus[] = ["user-questions", "review-by-user"];
+
function readScope(): SidebarScope {
try {
const v = localStorage.getItem(LS_SIDEBAR_SCOPE);
- if (v === "global" || v === "project") return v;
+ if (v === "global" || v === "project" || v === "attention") return v;
} catch { /* ignore */ }
return "project";
}
@@ -96,9 +99,9 @@ function ActiveTasksSidebar({
return () => window.removeEventListener("keydown", handleKeyDown);
}, [disableGlobalFindShortcut]);
- // Fetch active tasks from all projects when in global scope.
+ // Fetch active tasks from all projects when in global or attention scope.
useEffect(() => {
- if (scope !== "global") return;
+ if (scope !== "global" && scope !== "attention") return;
let cancelled = false;
setGlobalLoading(true);
(async () => {
@@ -125,7 +128,7 @@ function ActiveTasksSidebar({
// Keep global tasks live across all projects.
useEffect(() => {
- if (scope !== "global") return;
+ if (scope !== "global" && scope !== "attention") return;
function onTaskUpdated(e: Event) {
const { task } = (e as CustomEvent).detail as { task: Task };
setGlobalTasks((prev) => {
@@ -151,9 +154,24 @@ function ActiveTasksSidebar({
return () => window.removeEventListener("rpc:taskUpdated", onTaskUpdated);
}, [scope]);
- const sourceTasks = scope === "global" ? globalTasks : tasks;
+ const sourceTasks = (scope === "global" || scope === "attention") ? globalTasks : tasks;
+
+ // Count of attention tasks across all available data (global when loaded, else project).
+ const attentionCount = useMemo(() => {
+ const pool = globalTasks.length > 0 ? globalTasks : tasks;
+ return pool.filter((t) => ATTENTION_STATUSES.includes(t.status)).length;
+ }, [globalTasks, tasks]);
let activeTasks = sourceTasks.filter((task) => ACTIVE_STATUSES.includes(task.status));
+ if (scope === "attention") {
+ activeTasks = activeTasks.filter((task) => ATTENTION_STATUSES.includes(task.status));
+ // Sort oldest-first so the longest-waiting task is always at the top.
+ activeTasks = activeTasks.slice().sort((a, b) => {
+ const aTime = a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
+ const bTime = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
+ return aTime - bTime;
+ });
+ }
if (searchQuery.trim()) {
activeTasks = activeTasks.filter((task) => matchesSearchQuery(task, searchQuery));
}
@@ -214,6 +232,7 @@ function ActiveTasksSidebar({
+ {/* Folder \u2014 this project only */}
-
+ {/* Globe \u2014 all projects */}
+ {/* Bell \u2014 attention mode: cross-project, filtered to tasks needing user input */}
+