Một ngày làm việc trước FleetView

Buổi sáng. Mở MacBook ở quán cà phê. Mở tmux session blog. Pane 1 chạy npm run dev, pane 2 mở nvim, pane 3 đang chạy claude để hỏi nó refactor một component. Tab khác trong iTerm có session kisstour, một session cisgenx, một session fibretrace. Bốn dự án, bốn tmux session, mỗi cái 3-4 pane.

Đổi sang iPad mở Claude desktop app, tab Chat đang dở một câu hỏi về kiến trúc, tab Code đang chạy một sub-session viết Spec. Tab Code rất ngon cho lúc đang đi cà phê, không phải kéo MacBook ra. Nhược điểm: khi LLM gặp permission prompt hoặc cần user chọn option (ví dụ AskUserQuestion), Code tab không render được dialog đó. Câu trả lời lơ lửng. Phải về MacBook giải.

Buổi trưa. Đứng dậy, đóng MacBook, đi ăn. Phone trong túi. Vài tiếng sau ngồi xuống mở phone, SSH vào homelab Ubuntu qua Termius để check một agent batch đang viết series blog. tmux ls. Có ba session. Cái nào? tmux attach -t a. Sai. Detach. tmux attach -t b. Đúng rồi nhưng đang ở window 4. Ctrl+B 1, Ctrl+B 2, không phải. Ctrl+B w để xem tree, lướt, chọn. Cuối cùng cũng thấy log agent đang chạy bài thứ 8. Mỗi lần reconnect là một bài giải đố.

Buổi tối. Về nhà. MacBook. Vẫn nhớ session blog ở quán cà phê đang dở một câu hỏi. Mở tmux. Attach. Pane đó còn không? Có khi claude đã exit vì idle, có khi vẫn còn nhưng context đã bị compact, có khi vẫn đó nguyên vẹn. Không biết trước được. Mở thêm một claude mới, hỏi lại, vì rẻ hơn ngồi tra cứu state cũ.

Có nhiều surface không có nghĩa là biết session nào đang ở đâu.

FleetView là gì

FleetView là UI hiện ra khi gõ claude agents trong terminal. Tên gọi nội bộ thôi, tài liệu chính thức không hay nhắc tên này. Bản chất là một TUI dạng task manager cho toàn bộ Claude Code session trên một máy: liệt kê, dispatch, attach, kill.

Bốn cột chính trên màn hình: tên session, trạng thái (active, idle, blocked, completed), nguồn (cli, slash, fleet, spare, bg), và cwd. Một search bar phía trên để filter, một prompt input phía dưới để dispatch session mới ngay tại chỗ.

Feature timeline gần đây (tính đến CC 2.1.143):

  • Flag --cwd <path> để filter list view chỉ những session start dưới một thư mục cụ thể.
  • Flag --effort, --model, --permission-mode, --add-dir để set default cho mọi session dispatch từ FleetView.
  • Flag --settings, --mcp-config, --plugin-dir, --strict-mcp-config để áp dụng cấu hình cụ thể vào view và vào dispatched sessions.
  • Cú pháp @<alias> trong prompt FleetView để dispatch session vào một cwd preset.
  • Setting worktree.bgIsolation (CC 2.1.143) cho phép background session edit working copy trực tiếp thay vì bị force vào worktree riêng. Liên quan tới cách FleetView label background session.

Bản thân claude agents đã có từ trước, nhưng những flag và setting trên thì mới ra mắt trong vài bản gần đây. Đó là lúc nó từ “có thêm cho biết” trở thành “thay được hẳn workflow tmux”.

Pain 1: không biết session nào đang ở đâu

Workflow cũ với tmux: tmux ls cho ra danh sách session, không hiển thị window và pane. Phải attach từng cái, scan từng pane bằng mắt để tìm cái có Claude Code. Một số session có Claude, một số chỉ là shell. Một số session có Claude đã exit từ lâu, prompt rỗng, không phân biệt được với pane đang đợi input.

FleetView gộp toàn bộ session vào một bảng. Mỗi dòng có cwd, có trạng thái, có nguồn. Filter bằng / hoặc gõ trực tiếp vào search bar. Không phải attach để biết bên trong có gì.

Cơ chế bên dưới: FleetView merge hai nguồn dữ liệu là ~/.claude/daemon/roster.json (danh sách worker process đang sống) và ~/.claude/jobs/<short>/state.json (state mỗi session do worker tự ghi). Phần kiến trúc daemon và thuật toán Fp9 merge hai nguồn đã được mô tả riêng ở bài Một vài session không phải là không xuất hiện, mà là xuất hiện trễ. Trong khuôn khổ bài này chỉ cần biết: FleetView nhìn thấy toàn bộ session trên máy, không cần bạn nhớ tên tmux session đặt theo project nào.

Pain 2: agent background biến mất trong tmux

Tháng 4 vừa rồi tôi viết một series blog về LLM, 13 bài. Workflow là spawn 8 agent sonnet chạy song song trong background, mỗi agent một bài. Tmux setup cũ:

tmux new -d -s llm-batch
tmux send-keys -t llm-batch:0 "claude --print 'viết bài N' > out.md" Enter
tmux split-window -h -t llm-batch:0
... # lặp lại 8 lần

Spawn xong, detach, đi làm việc khác. Khi quay lại sau một tiếng, tmux attach -t llm-batch, scan từng pane. Pane nào còn đang chạy, pane nào đã xong, pane nào bị stuck đợi user input mà tôi không biết. Một pane bị stuck là một tiếng agent đó không tiến.

FleetView render trạng thái mỗi session realtime. Pane nào đang Working, pane nào Idle (đang đợi user prompt), pane nào Completed (đã xong và xả). Cột nguồn cho biết session đó được dispatch kiểu gì. Background session do FleetView dispatch có nguồn fleet, dispatch từ RemoteTrigger có nguồn slash.

Một chi tiết hữu ích: telemetry tengu_bg_roster_orphan_adopted được emit mỗi lần FleetView phát hiện một worker chưa có state.json trên disk và tự sinh entry cho nó. Nếu bật telemetry và đếm event này, có thể track chính xác số session vừa “lộ ra” trong FleetView. Cơ chế orphan adoption giải thích lý do session foreground (gõ claude trong terminal) có độ trễ trước khi xuất hiện trong FleetView, không phải instant như session do FleetView tự dispatch.

Pain 3: dispatch session mới rườm rà

Tmux thì dispatch một Claude session mới đòi nhiều bước. Open window mới (Ctrl+B c), cd vào đúng project path, gõ claude, đợi nó load. Nếu quên cd trước khi gõ claude, session đó sẽ có cwd là home directory. Sửa cwd của session đã start không trivial.

FleetView có prompt input nằm sẵn trong UI. Gõ thẳng câu prompt đầu tiên rồi Enter, session mới spawn với cwd lấy từ chính process đang chạy claude agents. Tức là nếu bạn cd ~/WORK/blog && claude agents, mọi session dispatch từ UI đều có cwd là ~/WORK/blog.

Cho preset nhiều cwd, có cú pháp @<alias> trong prompt FleetView. Parser nhận diện token @blog, lookup trong cwdMap (sinh ra từ agents có field cwd và routines bạn đã định nghĩa), thay thế alias bằng cwd thật. Chi tiết cú pháp và pitfall của @<alias> đã được trình bày ở bài Cách start session trong folder cụ thể từ claude agents. Một anti-pattern phổ biến mà bài đó cảnh báo: flag --cwd của claude agentsfilter list view, không phải set cwd cho dispatch.

Pain 4: Claude desktop tab Code và những action không render

Tôi vẫn dùng Claude desktop song song với terminal vì có nhiều lúc cần cái không gõ shell. iPad ở quán cà phê. Phone trên tàu. MacBook khi muốn vừa code vừa chat hỏi kiến trúc, mà không phải tách layout terminal ra.

Code tab trong Claude desktop là một surface chạy Claude Code trong khung GUI. Nó hoạt động tốt cho phần lớn flow text in text out. Vấn đề là một số tool đòi user interaction dạng select option, ví dụ AskUserQuestion với 2-4 lựa chọn, hoặc permission prompt khi muốn chạy một lệnh shell nguy hiểm. Trong terminal, mấy dialog đó render thành menu, phím mũi tên chọn, Enter confirm. Trong Code tab GUI, có lúc chúng không xuất hiện rõ, có lúc xuất hiện nhưng không bấm chọn được. Câu trả lời lơ lửng, hoặc agent đứng đợi.

Đây không phải vấn đề của FleetView. Nhắc tới vì nó là lý do tôi vẫn quay về terminal cho mọi tác vụ có interactive prompt. FleetView mạnh chính ở việc cho terminal workflow một mặt nhìn tổng quan, để cái surface có interactive prompt đầy đủ (TUI) không còn lép vế so với GUI về khả năng “thấy được session”. Trước đây surface terminal có khả năng tương tác mạnh nhưng visibility yếu (phải tmux). Bây giờ thêm visibility.

Pain 5: reconnect từ phone và mất state UI

Pain này cụ thể với workflow remote. Ngồi quán cà phê, SSH từ iPad qua Termius vào homelab, mở tmux, làm việc, mạng chập chờn hoặc tắt iPad đi ăn. Sau một tiếng quay lại. Reconnect SSH. Process trên server vẫn sống, nhưng client view không còn. Phải tmux ls, đoán session nào, tmux attach -t <name>, switch window đúng số, có khi switch pane đúng tỉ lệ chia. Mỗi lần reconnect là một bài giải đố thật.

FleetView không bị vấn đề này vì agent state nằm trên disk (state.json) và roster nằm trong daemon, không trong shell process của tmux. Reconnect SSH, gõ claude agents, một màn hình thấy hết. Không cần nhớ session name, không cần nhớ window number. Cwd, status, source đều có sẵn trong bảng.

Khía cạnh sâu hơn của thiết kế này: tmux session là persistent shell, agent state là persistent process plus disk file. Hai persistence model khác nhau cho cùng nhu cầu “đóng client đi xa, mở lại không mất việc”. Persistent shell yêu cầu user nhớ địa chỉ; persistent process plus disk file thì state tự khai báo, list view tự build lại sau mỗi connect.

Pain 6: multi-machine vẫn open, FleetView không sync

Đây là pain mà FleetView không solve. Tôi dual env: MacBook làm chính, Ubuntu homelab cho agent batch nặng, server riêng cho self-host và scheduled job. Mỗi máy có FleetView riêng, không có view chung. Muốn biết homelab đang có session nào phải SSH vào, gõ claude agents. Không khác gì tmux cũ ở khía cạnh này.

Mobile cũng vậy. FleetView vẫn là TUI cần terminal. iPad có terminal qua Termius thì OK, nhưng phone với màn hình nhỏ thì TUI bốn cột bóp vào không đọc được. Cách workaround duy nhất hiện tại là Telegram bridge (xem bài 23): dispatch và xem trạng thái session qua tin nhắn, không qua UI.

Tôi tự hỏi một ngày nào đó liệu có một FleetView web view hay không, sync qua tài khoản cloud, xem được từ mọi nơi. Hiện tại chưa có dấu hiệu. Vẫn ổn nếu chấp nhận TUI mỗi máy một bản, plus Telegram cho phone.

Pitfall đã gặp

Năm pitfall đáng nhớ khi mới dùng:

Session foreground không tự hiện, phải /bg.claude trong terminal (không dispatch từ FleetView) tạo session với dispatch.source: "spare". Source này không được re-assign sau đó (kiểm chứng binary 2.1.143), và worker foreground không tự ghi state.json. Hệ quả: session foreground không bao giờ tự xuất hiện trong FleetView nếu để nguyên. Cách deterministic là gõ slash command /bg trong session đó để worker chuyển sang background, ghi state.json, terminal detach. Sau đó FleetView hiện ngay. Chi tiết kiến trúc ở bài daemon architecture và workflow ở bài cwd dispatch cách 3.

Flag --cwd là filter, không set cwd cho dispatch. Lỗi này dễ phạm vì name flag misleading. Chạy claude agents --cwd ~/WORK/blog để mong session mới dispatch từ UI sẽ có cwd là ~/WORK/blog là sai. Thực ra UI chỉ filter chỉ hiển thị session đã có cwd đó. Để set cwd cho dispatch, dùng cách cd trước khi gõ claude agents, hoặc cú pháp @<alias> trong prompt. Bài cwd đã viết riêng.

Background session edit conflict với main session. Nếu spawn background session từ FleetView mà không bật worktree.bgIsolation (setting 2.1.143), session đó sẽ edit chung working copy với main session đang chạy. Hai bên ghi cùng file, last writer wins, không rollback được. Setting này default OFF ở phiên bản trước 2.1.143, default ON từ 2.1.143. Nếu vẫn đang dùng phiên bản cũ, bật thủ công. Chi tiết ở bài 27.

state.json cũ không phải session đang sống. Đếm ls ~/.claude/jobs/ không phải đếm session active. Có những state.json của session đã exit nhưng file disk vẫn còn. Đếm worker đang sống thật phải đọc roster.json: jq '.workers | length' ~/.claude/daemon/roster.json.

Kill tất cả session trong một cwd làm cwd biến mất khỏi list. FleetView group session theo cwd. Nếu trong group ~/WORK/some-project đang có 3 session và bạn Ctrl+X kill cả 3, cwd đó biến mất luôn khỏi FleetView list (không hiện như group rỗng). Hệ quả: muốn quay lại cwd đó để dispatch session mới ngay trong UI thì không có entry nào để chọn nữa. Phải mở terminal mới, cd vào folder, claude agents lại để cwd inherit qua process.cwd(). Recommendation: muốn cwd nào “luôn ở đó” cho dispatch nhanh trong FleetView, leave ít nhất một session sống trong cwd đó (kể cả session idle vừa /bg xong). Rẻ hơn nhiều so với phải re-dispatch từ terminal mỗi lần.

Cheatsheet

Bảng so sánh tmux workflow cũ và FleetView:

Tác vụtmuxFleetView
Xem toàn bộ session một lầntmux ls + attach từng cáiclaude agents
Filter theo project(không có)--cwd <path>
Dispatch session mớiopen window, cd, gõ claudeprompt trong UI plus Enter
Dispatch vào cwd cụ thểcd trước khi gõ claudecd trước hoặc @alias
Reconnect sau mất sóngtmux attach plus nhớ windowgõ lại claude agents
Theo dõi batch backgroundscan từng pane bằng mắtnhìn cột status
Cross-machineSSH vào từng máy plus tmuxSSH vào từng máy plus claude agents
Mobile phone(TUI khó dùng)(vẫn khó dùng, dùng Telegram bridge)

Phím tắt thông dụng trong FleetView:

PhímHành động
EnterDispatch session mới với prompt đang gõ
/Focus search bar
@Bắt đầu alias autocomplete (nếu cwdMap có entry)
EscDetach session đang attach, quay về list
qQuit FleetView (không kill session)
kKill session đang focus (có confirm)
oOpen cwd của session đang focus trong file manager

Flag hay dùng khi start claude agents:

# Filter list theo project
claude agents --cwd ~/WORK/blog

# Set default model và effort cho mọi dispatch trong session UI này
claude agents --model opus --effort high

# Set permission mode default cho dispatch (cẩn thận với bypassPermissions)
claude agents --permission-mode acceptEdits

# Apply plugin và MCP config cụ thể
claude agents --plugin-dir ~/my-plugins --mcp-config mcp.json

Hai cách “spawn session sẵn để dispatch sau” (bổ sung cho dispatch từ UI):

# Cách A: start foreground rồi /bg trong session đó
cd ~/WORK/some-project
claude
# trong session: gõ /bg, terminal detach, session vào FleetView

# Cách B: start background ngay từ shell, không qua UI interactive
cd ~/WORK/some-project
claude --bg
# daemon trả short ID, session idle, dispatch prompt sau qua claude attach <id>
# hoặc dispatch trong claude agents UI

Cả hai đều khiến session hiện trong FleetView ngay. Khác biệt: cách A bắt đầu interactive (hữu ích khi đã làm việc rồi mới muốn detach), cách B start ở chế độ idle (hữu ích cho automation, pre-create session cho RemoteTrigger / Telegram bridge). Chi tiết ở bài cwd dispatch cách 3 và cách 4.

Update: v2.1.144

Hai cải tiến nhỏ ảnh hưởng trực tiếp tới cách FleetView và CLI surface background session.

/resume giờ list cả background session, mỗi entry có tag bg để phân biệt với foreground. Trước đây phải nhớ session nào đang chạy nền, hoặc đọc roster.json để confirm. Giờ chỉ cần claude --resume là thấy ngay danh sách session bg cùng foreground, không phải đoán.

Notification (Stop hook, RemoteTrigger, Telegram bridge) hiển thị thời gian session đã chạy, format dạng 3h 2m 5s. Hữu ích để biết session block trên một step lâu, hay vừa mới start. Không phải check timestamp file thủ công nữa.

Cả hai áp dụng cho bg session bất kể spawn qua /bg trong UI, claude --bg từ shell, hay dispatch trong FleetView. Phần model selection cũng được fix ở phiên bản này: background session giữ đúng model đã chọn, không bị nhận từ session khác (bug có ở vài bản trước).

Lời kết

Tôi viết bài này vì sau vài tháng dùng FleetView, tôi nhận ra mình đã không mở tmux để quản lý Claude session từ rất lâu. Tmux vẫn còn trong workflow của tôi, nhưng cho việc khác: theo dõi log Docker, chạy htop plus iftop plus tail-log ba pane, mở vim sửa config server qua SSH. Không phải để theo dõi đám Claude session nữa.

Setup hiện tại của tôi tóm tắt thế này. MacBook ở nhà và quán cà phê: terminal mở claude agents thường trực ở một workspace iTerm riêng, mỗi project có vài alias cd để Enter dispatch nhanh. Ubuntu homelab ngồi nhà: claude agents chạy qua SSH khi cần check, plus Telegram bot bridge cho push notification mỗi khi một session block đợi input hoặc done. iPad ở café: Termius SSH vào homelab plus claude agents. Phone trên tàu: Telegram bridge thôi, không thử TUI nữa.

Cái gì FleetView không solve được thì nó không solve được. Multi-machine vẫn cần một view chung mà hiện chưa có. Mobile vẫn cần bridge. Code tab desktop vẫn miss vài kiểu interactive prompt. Nhưng riêng việc thay thế tmux trong vai trò quản lý Claude session local, FleetView làm tốt đến mức tôi không nhớ lần cuối tmux ls để tìm Claude là khi nào.

Nếu bạn cũng đang dùng tmux để wrap Claude session, thử bỏ tmux ra khỏi vai trò đó một tuần. Vẫn giữ tmux cho server log và monitoring. Nhiều khả năng bạn cũng sẽ không quay lại.

Bài này thuộc nhóm Bonus của series, không có bài tiếp theo. Hai bài liên quan trực tiếp là daemon architecture (giải thích sâu cơ chế bên dưới FleetView) và worktree bgIsolation (giải thích cách background session edit working copy). Nếu workflow mobile của bạn cũng giống tôi, bài Telegram bridge bù đắp đúng phần FleetView không cover. Workflow nào của bạn khác hoặc có pain FleetView vẫn chưa solve, nhắn cho tôi, bài này tôi sẽ update.


Bài thuộc series Claude Code từ zero. Series plan tại bài giới thiệu.