diff --git a/cli-ts/src/components/FocusChain.tsx b/cli-ts/src/components/FocusChain.tsx
new file mode 100644
index 0000000000..1f676142e6
--- /dev/null
+++ b/cli-ts/src/components/FocusChain.tsx
@@ -0,0 +1,185 @@
+/**
+ * Focus Chain / To-Do List component for CLI
+ * Displays a progress-tracked checklist of tasks
+ */
+
+import { isCompletedFocusChainItem, isFocusChainItem, parseFocusChainItem } from "@shared/focus-chain-utils"
+import { Box, Text } from "ink"
+import React, { useMemo } from "react"
+
+interface TodoInfo {
+ currentTodo: { text: string; completed: boolean; index: number } | null
+ currentIndex: number
+ completedCount: number
+ totalCount: number
+ progressPercentage: number
+}
+
+interface TodoItem {
+ text: string
+ checked: boolean
+}
+
+interface FocusChainProps {
+ focusChainChecklist?: string | null
+ expanded?: boolean
+}
+
+/**
+ * Parse the focus chain checklist text into TodoInfo
+ */
+function parseCurrentTodoInfo(text: string): TodoInfo | null {
+ if (!text) {
+ return null
+ }
+
+ let completedCount = 0
+ let totalCount = 0
+ let firstIncompleteIndex = -1
+ let firstIncompleteText: string | null = null
+
+ const lines = text.split("\n")
+ for (const rawLine of lines) {
+ const line = rawLine.trim()
+ if (isFocusChainItem(line)) {
+ const isCompleted = isCompletedFocusChainItem(line)
+
+ if (isCompleted) {
+ completedCount++
+ } else if (firstIncompleteIndex === -1) {
+ firstIncompleteIndex = totalCount
+ // Extract text after "- [ ] "
+ firstIncompleteText = line.substring(5).trim()
+ }
+
+ totalCount++
+ }
+ }
+
+ if (totalCount === 0) {
+ return null
+ }
+
+ const currentTodo = firstIncompleteText ? { text: firstIncompleteText, completed: false, index: firstIncompleteIndex } : null
+
+ return {
+ currentTodo,
+ currentIndex: firstIncompleteIndex >= 0 ? firstIncompleteIndex + 1 : totalCount,
+ completedCount,
+ totalCount,
+ progressPercentage: (completedCount / totalCount) * 100,
+ }
+}
+
+/**
+ * Parse all todo items from the checklist
+ */
+function parseTodoItems(text: string): TodoItem[] {
+ const items: TodoItem[] = []
+ const lines = text.split("\n")
+
+ for (const rawLine of lines) {
+ const line = rawLine.trim()
+ const parsed = parseFocusChainItem(line)
+ if (parsed) {
+ items.push(parsed)
+ }
+ }
+
+ return items
+}
+
+/**
+ * Render progress bar
+ */
+const ProgressBar: React.FC<{ percentage: number; width?: number }> = ({ percentage, width = 20 }) => {
+ const filled = Math.round((percentage / 100) * width)
+ const empty = width - filled
+ const bar = "█".repeat(filled) + "░".repeat(empty)
+
+ return (
+
+ {bar}
+ {Math.round(percentage)}%
+
+ )
+}
+
+/**
+ * Header view showing current task and progress
+ */
+const Header: React.FC<{
+ todoInfo: TodoInfo
+}> = ({ todoInfo }) => {
+ const { currentTodo, currentIndex, totalCount, completedCount } = todoInfo
+ const isCompleted = completedCount === totalCount
+
+ const displayText = isCompleted ? "All tasks completed!" : currentTodo?.text || "To-Do list"
+ const truncatedText = displayText.length > 50 ? displayText.substring(0, 47) + "..." : displayText
+
+ return (
+
+
+ [{currentIndex}/{totalCount}]
+
+ {truncatedText}
+
+ )
+}
+
+/**
+ * Expanded view showing all todo items
+ */
+const ExpandedList: React.FC<{
+ items: TodoItem[]
+ isCompleted: boolean
+}> = ({ items, isCompleted }) => {
+ return (
+
+ {items.map((item, index) => (
+
+ {item.checked ? "✓" : "○"}
+
+ {item.text}
+
+
+ ))}
+ {isCompleted && (
+
+
+ New steps will be generated if you continue the task
+
+
+ )}
+
+ )
+}
+
+/**
+ * Main FocusChain component for CLI
+ * Shows a progress summary of the current to-do list
+ * Use expanded={true} to show all items (e.g., in verbose mode)
+ */
+export const FocusChain: React.FC = ({ focusChainChecklist, expanded = false }) => {
+ const todoInfo = useMemo(
+ () => (focusChainChecklist ? parseCurrentTodoInfo(focusChainChecklist) : null),
+ [focusChainChecklist],
+ )
+
+ const todoItems = useMemo(() => (focusChainChecklist ? parseTodoItems(focusChainChecklist) : []), [focusChainChecklist])
+
+ // No content to display
+ if (!todoInfo) {
+ return null
+ }
+
+ const isCompleted = todoInfo.completedCount === todoInfo.totalCount
+
+ return (
+
+
+
+ {expanded && }
+
+ )
+}
diff --git a/cli-ts/src/components/MessageRow.tsx b/cli-ts/src/components/MessageRow.tsx
index 94446fa8ce..9c1aa74adb 100644
--- a/cli-ts/src/components/MessageRow.tsx
+++ b/cli-ts/src/components/MessageRow.tsx
@@ -206,11 +206,11 @@ const SayMessageContent: React.FC<{ message: ClineMessage; verbose?: boolean }>
return {text}
case "reasoning":
- return verbose ? (
+ return (
Thinking: {text}
- ) : null
+ )
case "error":
return (
diff --git a/cli-ts/src/components/TaskView.tsx b/cli-ts/src/components/TaskView.tsx
index 0bcdda55e2..42e80c4dbe 100644
--- a/cli-ts/src/components/TaskView.tsx
+++ b/cli-ts/src/components/TaskView.tsx
@@ -8,6 +8,7 @@ import React, { useEffect } from "react"
import { useTaskContext, useTaskState } from "../context/TaskContext"
import { useCompletionSignals, useIsSpinnerActive } from "../hooks/useStateSubscriber"
import { AskPrompt } from "./AskPrompt"
+import { FocusChain } from "./FocusChain"
import { MessageList } from "./MessageList"
import { LoadingSpinner } from "./Spinner"
@@ -74,6 +75,13 @@ export const TaskView: React.FC = ({ taskId, verbose = false, onC
)}
+ {/* Focus Chain / To-Do List */}
+ {state.currentFocusChainChecklist && (
+
+
+
+ )}
+
{/* Messages list */}
diff --git a/cli-ts/src/console.ts b/cli-ts/src/console.ts
index 5d3f1e763e..2ca3c7025f 100644
--- a/cli-ts/src/console.ts
+++ b/cli-ts/src/console.ts
@@ -10,6 +10,7 @@ export const originalConsoleLog = console.log.bind(console)
export const originalConsoleError = console.error.bind(console)
export const originalConsoleWarn = console.warn.bind(console)
export const originalConsoleInfo = console.info.bind(console)
+export const originalConsoleDebug = console.debug.bind(console)
// Check for verbose flag early (before commander parses)
const isVerbose = process.argv.includes("-v") || process.argv.includes("--verbose")
@@ -18,6 +19,8 @@ const isVerbose = process.argv.includes("-v") || process.argv.includes("--verbos
if (!isVerbose) {
console.log = () => {}
console.warn = () => {}
+ console.error = () => {}
+ console.debug = () => {}
}
/**
@@ -28,4 +31,5 @@ export function restoreConsole() {
console.error = originalConsoleError
console.warn = originalConsoleWarn
console.info = originalConsoleInfo
+ console.debug = originalConsoleDebug
}
diff --git a/cli-ts/src/index.ts b/cli-ts/src/index.ts
index 435687a142..821234842f 100644
--- a/cli-ts/src/index.ts
+++ b/cli-ts/src/index.ts
@@ -171,12 +171,14 @@ function waitForCondition(check: () => boolean, timeoutMs: number, intervalMs: n
async function runTask(
prompt: string,
options: {
- switch?: string
+ act?: boolean
+ plan?: boolean
model?: string
verbose?: boolean
cwd?: string
config?: string
thinking?: boolean
+ yolo?: boolean
images?: string[]
},
existingContext?: CliContext,
@@ -195,8 +197,10 @@ async function runTask(
// Use clean prompt (with image refs removed)
const taskPrompt = cleanPrompt || prompt
- if (options.switch) {
- StateManager.get().setGlobalState("mode", options.switch === "plan" ? "plan" : "act")
+ if (options.plan) {
+ StateManager.get().setGlobalState("mode", "plan")
+ } else if (options.act) {
+ StateManager.get().setGlobalState("mode", "act")
}
if (options.model) {
const selectedMode = (StateManager.get().getGlobalSettingsKey("mode") || "act") as "act" | "plan"
@@ -222,6 +226,11 @@ async function runTask(
const thinkingKey = currentMode === "act" ? "actModeThinkingBudgetTokens" : "planModeThinkingBudgetTokens"
StateManager.get().setGlobalState(thinkingKey, thinkingBudget)
+ // Set yolo mode based on --yolo flag
+ if (options.yolo) {
+ StateManager.get().setGlobalState("yoloModeToggled", true)
+ }
+
printInfo(`Starting Cline task...`)
printInfo(`Working directory: ${ctx.workspacePath}`)
if (imageDataUrls.length > 0) {
@@ -393,13 +402,15 @@ program
.alias("t")
.description("Run a new task")
.argument("", "The task prompt")
- .option("-s, --switch ", "Switch mode: act, plan")
+ .option("-a, --act", "Run in act mode")
+ .option("-p, --plan", "Run in plan mode")
+ .option("-y, --yolo", "Enable yolo mode (auto-approve actions)")
.option("-m, --model ", "Model to use for the task")
.option("-i, --images ", "Image file paths to include with the task")
- .option("-v, --verbose", "Show verbose output including reasoning")
+ .option("-v, --verbose", "Show verbose output")
.option("-c, --cwd ", "Working directory for the task")
.option("--config ", "Path to Cline configuration directory")
- .option("--thinking", "Enable extended thinking (1024 token budget)")
+ .option("-t, --thinking", "Enable extended thinking (1024 token budget)")
.action((prompt, options) => runTask(prompt, options))
program