npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

zen-gitsync

v2.19.4

Published

Auto commit, scheduled sync, and visual GUI for Git. Run `g` in any repo for one-key commit & push, AI commit messages, scheduled background sync, and a drag-and-drop workflow builder.

Readme

zen-gitsync

English | 中文

A Git automation platform with interactive commits, scheduled sync, custom command orchestration, file locking, and a visual GUI.

Table of Contents


Installation

Install globally via npm:

npm install -g zen-gitsync

v2.x.x — What's New

  • Visual GUI — Full graphical interface for Git operations
  • Branch management — Create, switch, and track local/remote branches
  • Remote management — Manage multiple remotes (add / rename / retarget / delete) from one dialog, configure multi push URLs, and push to a chosen remote or to all remotes at once
  • Repository browser — GitHub / Gitee tabs listing every repository your CLI account can see (private ones included), with search, sorting (recently pushed / recently created / most starred / name), grouping by workspace, and cards that carry last-push date, fork count, default branch and license
  • Stash management — Save and restore stashes with locked-file filtering
  • Tag management — Create lightweight and annotated tags
  • Merge support — Detect and complete in-progress merges
  • Flow orchestration — Drag-and-drop visual workflow designer
  • NPM scripts panel — Discover and run npm scripts from package.json
  • AI startup suggestions — A collapsible panel (expanded by default) above the NPM scripts panel: your configured model reads the scanned scripts, marker files and README, then lists the ways this project can be started, in startup order — one click runs any of them in a new terminal
  • Built-in terminal — Run commands with real-time streaming output
  • Custom commands — Save, parameterize, and reuse shell commands
  • Project startup — Auto-run commands or workflows when a project opens
  • Built-in code editor — Monaco-based file editor with Markdown preview
  • Workbench — a multi-project board with a kanban view and a master-agent dispatch console; task-driven agent execution (Claude Code or OpenCode) with prompt presets, isolated per-task processes, live streaming output, AI-generated presets and task-level attachments
  • Repository cloning — clone any GitHub / Gitee repository into a folder straight from the repo browser, with an Already cloned badge (and its local path(s), including the count when a repo lives in several folders) backed by a whole-disk local-repository scan
  • Skill / MCP marketplace — install skills and MCP servers from the Agent view into the current project or the g ai agent
  • One-click MCP install — right-click an editor / AI tool button in the header (VS Code, Claude Code, Codex, OpenCode, CodeBuddy Code, Pi Agent) and choose Add zen-gitsync MCP to register zen-gitsync as that tool's MCP server: merged into its config in place with a .bak backup (never hand-edit config files again), and the menu shows whether it is already added. The same registry is available from the CLI via g mcp install
  • Reset to remote — One-click git reset --hard origin/<branch> from the Git panel (auto-refreshes branch info first to avoid wrong-target resets)
  • AI commit message — Generate commit message from staged diff automatically
  • AI commit & push — One click does the whole loop: AI writes the commit message from the diff, then stages → commits → pushes (no need to type a message first)
  • Selection-scoped diff — AI commit message and quick commit/push use only the diff of currently selected files when the Git view is the active tab
  • Commit templates — Save type/scope/description/message templates
  • Theme & language — Light/dark theme and Chinese/English UI; one-click theme toggle in the header (no need to dig into settings)
  • Network error banner — Global banner appears when the backend is unreachable, with one-click retry and relative-time status
  • Accessibility (WCAG 2.1 AA) — Dialog focus trap & restore, role-based separators, keyboard-only panel resize (← →), screen-reader friendly commit context menu, ARIA-pressed toggle buttons, commit button aria-busy during in-flight commits, Git SHA hashes meet ≥ 4.5:1 contrast in both light and dark themes
  • Faster cold start — monaco-editor / @vue-flow / flow-mindmap / dagre are split into independent chunks and lazy-loaded so the Git panel boots without waiting on the code editor or visual workflow designer
  • Project manager view — manage the project list as an asset: add / remove without switching the current project, see each project's open-with actions, with recommended additions
  • Content-aware file search — the editor's search box now also matches file contents: name hits filter the tree as before, content hits arrive as a flat list with line numbers and jump-to-line

Detailed per-release changes can be found via git log or the GitHub Releases page.


GUI

Launch the GUI:

$ g ui

The GUI runs as a local web server and opens in your default browser on the first free port it finds in 4000–6000 (set PORT to pin a fixed one). It attaches to the current Git repository automatically. The activity bar on the left switches between Git, Console, Agent, Editor, Workbench, Projects, System Monitor and Mindmap, top to bottom. See the Core Git Panel screenshot below for what the main view looks like.

Architecture at a glance

                  ┌─────────────────────────────────────────────┐
                  │  Header:  current dir · theme · instances   │
                  ├─────────────────────────────────────────────┤
                  │  Activity Bar (left rail)                   │
                  │  ┌───┐                                      │
                  │  │Git│────► Git panel  (files + commit)     │
                  │  └───┘                                      │
                  │  ┌──────┐                                   │
                  │  │Consol│─► Saved commands + terminal       │
                  │  └──────┘                                   │
                  │  ┌──────┐                                   │
                  │  │Agent │─► Web agent + Skill/MCP plaza     │
                  │  └──────┘                                   │
                  │  ┌──────┐                                   │
                  │  │Edit  │─► Monaco editor + file tree       │
                  │  └──────┘                                   │
                  │  ┌──────┐                                   │
                  │  │Bench │─► Board: projects·kanban·agent│
                  │  └──────┘                                   │
                  │  ┌──────┐                                   │
                  │  │Proj  │─► Project list management         │
                  │  └──────┘                                   │
                  │  ┌──────┐                                   │
                  │  │Monit │─► System monitor                  │
                  │  └──────┘                                   │
                  │  ┌──────┐                                   │
                  │  │ Mind │─► Mindmap                         │
                  │  └──────┘                                   │
                  └─────────────────────────────────────────────┘
                       ▲              ▲              ▲
                       │              │              │
                  Pinia stores ──── EventBus ──── Socket.IO
                       ▲
                       │
                  Backend Express server (free port 4000–6000) → git / npm / shell

A typical day in the GUI

  1.  g ui            → browser opens on the first free port in 4000–6000
  2.  Glance header   → current dir, branch, instance count, theme toggle
  3.  Edit files      → Activity Bar → Editor, save with Ctrl+S
  4.  Stage & commit  → Activity Bar → Git, pick files, fill commit form, push
  5.  AI commit msg   → click ✨ AI 生成 in commit form, diff → Conventional Commits
  6.  Background job  → Activity Bar → Workbench, run task, watch live logs
  7.  Quick command   → Activity Bar → Console → pick saved command → run

Core Git Panel

Git panel — file list, structured commit form, history

Single-screen view of "what changed → what to commit → what was committed". The left column lists changed files grouped by staged / unstaged / untracked / conflicted; the right side stacks the structured commit form on top of a chronological commit history. No tab switching required for the 80% case.

| Feature | Description | |---|---| | File list | Shows all changed files grouped by staged / unstaged / untracked / conflicted — plus intent-to-add files as their own "to be staged" group | | View toggle | Switch between flat list and directory tree view (persisted) | | Selection mode | Multi-select files to stage or stash only chosen files. When the Git view is the active tab, Quick Commit / Quick Push and AI commit message automatically scope their action to the current selection (button label switches to Commit Selected / Push Selected). | | Per-file actions | Stage, unstage, or revert individual files | | Stage | Stage all or selected files (respects locked files) | | Commit | Structured form (type / scope / description / body / footer) or free-text | | AI commit message | Generate commit message from staged diff using an AI model | | Push | Push to remote with live progress modal | | Quick commit+push | One-click stage → commit → push | | AI commit & push | One-click AI writes the message → stage → commit → push (form is filled in first, so you can see what was committed). When the branch is already committed and only needs pushing, AI is skipped and it pushes directly | | Pull / Fetch | Pull from or fetch the upstream branch | | Reset to remote | One-click git reset --hard origin/<branch>; auto-refreshes branch info first to avoid stale-branch targets; hidden when working tree is clean and no unpushed commits | | Merge | Merge another branch; detects and surfaces in-progress merge state | | Diff viewer | Monaco-based side-by-side diff for any changed file | | In-diff preview | Toggle a preview pane below the diff for .html / .htm / .svg (sandboxed iframe with JavaScript enabled — interactive reports work, isolated from the app via an opaque origin), .md / .markdown (rendered Markdown) and Office documents (.doc / .docx / .xls / .xlsx / .ppt / .pptx / .odt / .ods / .odp, converted server-side) — same preview experience as the built-in editor, with a draggable vertical resizer; split ratio is persisted per project | | Commit log | Browse commit history with author, date, branch tags, and changed files | | Remote URL | Display and one-click copy the remote repository URL; the gear icon beside it opens Remote Management (multi-remote setups, multi push URLs) | | Auto-refresh | Silently refreshes status and branch info when the window gains focus, the tab becomes visible, or you switch back to the Git view in the Activity Bar | | Rail badge | The Git icon in the left rail carries the counts you would otherwise have to open the panel for: uncommitted files at the top-right, and the current branch's ahead / behind counts at the bottom (↑2 ↓3). Behind is amber (something to pull), ahead-only is green (something to push), diverged is red; the tooltip spells both out |

Structured Commit Form

The commit form supports two modes toggled by a switch:

  • Standard mode — separate fields for type (feat / fix / docs / style / refactor / test / chore), scope, short description, body, and footer — produces a Conventional Commits message automatically
  • Free-text mode — single text area for any commit message

In either mode, click AI Generate to fill in the fields automatically based on the staged diff.


GitHub / Gitee Repositories

The Git view has three tabs: 当前项目 (current project), GitHub 仓库, and Gitee 仓库. The latter two list every repository your gh / gitee account can see — private ones included. ZenGitSync never touches your token: both panels shell out to the official CLI (gh, @gitee/gitee-cli), which keeps its own credentials.

  • Zero-config guidance — a missing CLI shows the install command for your platform (winget / Homebrew / npm install -g @gitee/gitee-cli) with one-click install and auto-refresh; an installed but signed-out CLI shows the sign-in command plus one-click sign-in, then polls until you finish the interactive flow in the terminal
  • Search — filters by name, full path and description; the header switches to 匹配 M / 共 N 个仓库 so you can tell how much got filtered out
  • Sort — recently pushed (default) / recently created / most starred / name. Sorting happens in the frontend, so both tabs behave identically — their CLIs do not (gh returns most-recently-pushed first, gitee returns owner/name alphabetical)
  • Group by workspace — repositories are grouped by their owner by default, so everything under one account or organisation sits together instead of being scattered across the grid by push date; group order follows the current sort rule (the workspace pushed most recently comes first) and so does the order inside each group, with the header showing how many repositories that workspace holds. Switch to No grouping for a flat, cross-workspace timeline
  • No refetch on tab switch — the list is cached per account, so coming back to the tab paints instantly instead of shelling out to gh repo list again; once the cache is a minute old it paints from cache first and refreshes quietly in the background, while 刷新 always pulls for real
  • Informative cards — repository name, description and privacy / fork / language / star badges, plus a third line with last-push date, fork count, non-main default branch and license (each omitted when there is nothing to say)
  • Clone straight to a folder — a repository that is not on your disk yet offers Clone to folder…: pick a directory and the clone runs over SSH ([email protected]:owner/repo.git), with an https:// URL normalised first so it never stalls on a Git Credential Manager prompt
  • "Already cloned" badge — the server keeps a whole-disk index of local Git repositories (built in the background and refreshable on demand), so a card for a repo you already have shows its local path instead of offering another clone; cloned the same repo into several folders and the badge calls out the count (Cloned ×2), the tooltip lists every path, and Ctrl+click opens a menu to pick which copy to launch g ui in
  • One click to open or copy — clicking a card opens the repository page in your browser; the actions that appear on hover copy the URL or open it

Quick directory switch

Directory switcher dialog

Click the directory name in the header (or the folder icon) to open this dialog. Type a path, hit 浏览 to use the OS file picker, or pick from 常用目录 for one-click switching. 使用新标签打开 spawns a new GUI tab on that path so you can keep the current project open.

The header row beside the directory name carries its own quick actions: open in the file manager, open in a terminal, copy the folder name (last path segment only), open with g ai, and one button per detected editor / AI tool — VS Code, Codex, OpenCode, CodeBuddy Code, Pi Agent, Kimi Code, ZCode, DeepSeek Harness and Claude Code. Right-clicking a tool button opens its menu: the tools whose CLI can skip approval prompts (Claude Code, Codex, OpenCode, CodeBuddy Code, Kimi Code) list their permission tiers there — default plus that CLI's own fully-approved switch, each row naming the flag it passes — above update to the latest version. Tools that are not installed are collected into a more menu, where clicking one opens the install guide.

When the GUI is opened on a directory that is not a Git repository, the right pane shows the Recent projects list instead — every recent directory with its Git badges (behind / ahead / uncommitted) and one-click "open in a new tab". Each page load runs a git fetch pass over all of them automatically, so the ahead/behind badges show the real state rather than the snapshot from the last fetch; the 刷新全部 button does the same thing on demand. The switcher dialog shows the same list as 常用目录 and carries the very same 刷新全部 button at the right end of its heading — the automatic pass stays panel-only (opening a dialog should not fetch a dozen repositories behind your back), but the manual one is identical in both places, right down to the progress readout. Both places also have a search box above the cards: type any fragment of a path to filter the list (the AI status summary below keeps describing the whole set, not the filtered view).

The same list carries a short note underneath the cards — in the full-screen directory switcher dialog the path field and the 常用目录 cards take the left side, and that note lives in a right-hand column beside them. With an AI model configured it is an AI status summary — one paragraph written by the model from the freshly fetched states, naming the projects that need a pull, have unpushed commits or uncommitted changes, and saying so when everything is in sync. It is generated once per distinct state right after the 刷新全部 pass finishes (never mid-refresh), cached for the page, and there is a regenerate button on the right; the summary is shared between the panel and the dialog, so opening the switcher never triggers a second call. Without a model configured it falls back to a static note explaining what the badges mean.

In the switcher dialog, that right column continues with a g ai follow-up box: ask which project to handle first, or how far one of them is behind, and it answers from the very same status the cards show — every turn carries the current directory states (plus the summary text) as request-scoped context, so there is no need to restate the background. Four one-click question cards sit in the empty state (what to handle first / who is behind / pull everything behind / what is uncommitted) — clicking one sends it straight away, for the same reason the box exists: you should not have to retype the background. It always runs on the built-in g ai, and each time the dialog opens it starts a fresh session, so the context is never a stale snapshot. The always-on recent-projects panel is not rebuilt on close, so its box would otherwise accumulate one session all day long — that is why a New chat button appears above it once there are messages: it starts a fresh session and stops whatever was still generating. The previous session is kept rather than deleted (it is already saved, and still listed in the Agent view).


Branch Management

  • View all local and remote branches
  • Create a new branch from any base branch
  • Switch branches
  • Track upstream status (commits ahead / behind)

Remote Management

  • Keep any number of remotes (origin, upstream, backup, …) in one dialog: add, rename, retarget the URL, or delete
  • Each remote shows its fetch URL plus any explicit push URLs, with Upstream / Push default badges so you can tell at a glance which one the current branch tracks
  • Give one remote several push URLs (e.g. GitHub + Gitee) so a single push reaches several hosts, or clear them all to fall back to the fetch URL
  • The Push dropdown appears once more than one remote is configured: push to a specific remote, push to every remote at once (with per-remote success / failure results), or jump into remote management. With a single remote the button looks and behaves exactly as before
  • Deleting the remote that the current branch tracks automatically unsets the upstream, so later pulls don't trip over a dangling config

Open it from the gear icon next to the remote URL in the status bar, or via Manage remotes… in the Push dropdown.


Stash Management

  • Save stash with an optional message
  • Optionally include untracked files
  • Optionally exclude locked files from stash
  • Apply, pop, or drop individual stash entries

Tag Management

  • Create lightweight or annotated tags
  • Target a specific commit
  • List, push, or delete tags

Commit Message Templates

Save reusable templates for:

  • Type — feat, fix, chore, …
  • Scope — component or module name
  • Description — short summary
  • Full message — complete commit message

Custom Commands

Command Orchestration

Create, manage, and run shell commands from the sidebar (Console view):

  • Define commands with a name, shell command, and working directory
  • Add parameters with names, descriptions, and default values (referenced via {{paramName}})
  • Run a command instantly in a new terminal session
  • Save command templates for quick reuse
  • Each command has its own enable / disable toggle so you can stage a suite of commands without running them

Scheduled commit (pinned to the bottom of the same sidebar): auto git add -A + git commit on an interval.

  • Interval in minutes / hours / days, with an optional commit right on start
  • Commit message: the configured default message, a per-schedule message, or AI-generated
  • Push to the remote after every successful commit (can be turned off)
  • The panel echoes the equivalent g CLI command — e.g. g -y --interval=1800 --path="<dir>" (--interval is in seconds) — with a one-click copy button, so the same schedule can be reproduced from a terminal without the GUI

Flow Orchestration (Visual Workflow Designer)

Build automated pipelines with a drag-and-drop canvas:

| Node type | Purpose | |---|---| | Start | Entry point of the flow (one per flow, not deletable) | | Command | Execute a saved custom command | | Wait | Pause execution for 1–3600 seconds | | Version | Bump package.json version (patch / minor / major) or modify a dependency | | Confirm | Pause the flow and wait for the user to confirm before continuing | | User input | Pause the flow and collect parameter values from the user | | Code | Run an inline code snippet and pass its output to downstream nodes | | Condition | Branch the flow according to a condition |

  • Nodes are executed in topological order
  • Flows are saved and editable
  • Each node can be individually enabled or disabled

NPM Scripts Panel

The panel lives inside the Git view (left column). It scans every package.json in the repo on demand, groups scripts by package, and lets you click any script name to run it directly. The panel's own settings dialog configures the scan root and exclusion patterns.

  • Automatically discovers all package.json files in the project tree
  • Lists their scripts entries
  • Run any script with one click
  • Configure the scan root and exclusion patterns per package

AI Startup Suggestions

AI startup suggestions panel — above the NPM scripts panel, expanded by default

A collapsible panel right above the NPM scripts panel, expanded by default. Once an AI model is configured (Settings → AI Models), it scans the project the same way the NPM panel does — every package.json script, plus marker files (Dockerfile, docker-compose.yml, Makefile, go.mod, requirements.txt, …) and the README — and asks your default model to pick the ways this project can actually be started, in startup order. Each suggestion has a Start button that runs it in a new terminal.

  • One list answers "how do I start this project?" — no digging through dozens of scripts in a monorepo
  • Suggestions are validated server-side before they reach you: a script name that does not exist in package.json, or a working directory that was not scanned, is dropped (the model cannot invent a button that fails)
  • npm suggestions run straight away; raw shell suggestions (e.g. docker compose up -d) show a confirmation with the exact command first
  • Results are cached per project + language + model, so reopening the view does not call the model again; the refresh button in the panel header forces a fresh analysis
  • No model configured → the panel just tells you to add one, and sends no request at all
  • Nothing to show → the panel is not rendered at all: a directory with no package.json / startup files (and nothing for the model to pick) hides this panel and the NPM scripts panel, instead of leaving two empty shells in the sidebar

Console Panel

Console panel — saved commands on the left, execution terminal on the right

Three-pane layout: saved custom commands on the left, 自定义指令执行 (saved custom commands + terminal sessions) tab on top, and a live terminal on the right. Click any saved command in the sidebar to launch it in a new terminal session; the terminal streams output in real time over SSE and supports running multiple sessions side by side. The cmd.exe shell is used on Windows, sh on Unix.

  • Open new terminal sessions from within the GUI (one per command)
  • Commands stream output in real time (Server-Sent Events)
  • Running processes are tracked and can be stopped individually
  • Cross-platform: uses cmd.exe on Windows, sh on Unix

Project Startup

Configure commands or workflows to run automatically when a project is opened:

  • Toggle auto-run on / off
  • Drag to reorder startup items
  • Mix custom commands and flow workflows in any order

Project Manager

A dedicated view (folders icon in the activity bar) that manages the project list as an asset. It reads the same list the Workbench does (GET /api/workbench/projects), so anything you add or remove here shows up there too — the list is the union of recent directories and the paths your tasks remember.

  • Add without switching — add any directory to the list; adding never changes the current project (switching directories is a global state change that reaches every g ui instance)
  • Remove a dead project — a row whose folder no longer exists gets a single red Remove from list action; the confirm dialog spells out that no tasks are deleted, and the entry comes back on its own if you ever clone the folder again
  • Startup & open-with at a glance — each row leads with the project's own favicon (32 px tile, the same lookup the Workbench and the directory switcher use) beside its name and branch, with open-with actions for the file manager, a terminal, a new g ui tab, and every detected editor / AI tool
  • Recommended additions — directories that look like projects but are not on the list yet are suggested for one-click adding
  • Quiet refresh — the list reloads on mount, then silently polls every 10 s, so projects added or removed in another g ui instance show up here too

Views at a glance

| View | Purpose | Persistent state | Highlights | |---|---|---|---| | Git | Day-to-day staging, committing, pushing, history review | Per-project UI prefs (view mode, layout ratios) | Structured commit form, AI commit message, selection-scoped quick push | | Editor | Browse & edit project files without leaving the GUI | Open tabs, unsaved markers, recent files | Monaco editor with syntax highlighting, Markdown preview, file search (file names and contents) | | Workbench | Multi-project board for dispatching and running agent tasks | Tasks, prompts, board layout, log retention | Kanban board, master-agent console, executor choice, live chat-style logs | | Projects | Manage the project list as an asset | The same list the Workbench reads | Add / remove without touching the current project, recommended additions, 10 s silent refresh | | Agent | Chat with the built-in AI agent (web + CLI sessions) | Sessions, pending questions | Streaming answers, tool-call cards, Skill / MCP plaza, rail badge counting the conversations still generating |

Console, System Monitor and Mindmap are utility views on the same rail.


Built-in Code Editor

A full IDE-like editor (fourth icon in the activity bar) for browsing and editing project files without leaving the tool:

| Feature | Description | |---|---| | File tree | Collapsible directory tree with file-type icons; auto-refreshes every 60s to pick up changes made outside the GUI (skipped when the tab is hidden or the search box is non-empty) | | File search | Type in the sidebar search box to filter the tree (180 ms debounce); matched substrings are highlighted in node names; Ctrl+F / Cmd+F focuses the box; Esc clears the query or blurs the input. When the name filter has no hit, the query also goes to the server (POST /api/search_content) for a file-content scan: hits arrive as a flat list with line numbers, highlighted snippets and per-file match counts, and clicking one jumps straight to that line. The scan is a plain filesystem walk (no rg / git grep dependency) that skips node_modules / dist / .git plus files over 2 MB or binary, capped at 20 000 files / 100 hit files / 10 s, and aborted as soon as you keep typing; single-character queries skip the content pass | | Multi-tab editing | Open multiple files simultaneously; tabs show unsaved (●) indicator | | Sync with disk | The current tab re-checks the file on disk when the window regains focus, when you switch to that tab, or when you come back to the Editor view; a 30s fallback poll covers the case where something in the same window (the g ai panel) rewrote the file with no focus change. If it changed and you have no unsaved edits, it reloads silently (cursor position and undo history preserved); if you do have unsaved edits it asks first and never overwrites on its own | | Workspace restore | The tree's expanded folders and the open tabs (their order plus which one is active) are remembered per project and put back on reload, and when you switch back to that project. The snapshot lives in ~/.zen-gitsync/config.json under ui.editorWorkspaceByProject; only paths are stored — files are re-read from disk, so unsaved edits do not survive a reload, and files that no longer exist are skipped silently | | Sidebar width | Drag the divider to resize the file tree pane; unlike the workspace snapshot the width is global (one value for every project) and is stored in ~/.zen-gitsync/config.json under ui.editorSidebarWidth, restored on reload. Clamped to 140–400px | | Monaco editor | Syntax highlighting for JS, TS, Vue, Python, Go, JSON, CSS, and more | | Markdown preview | Toggle between source and rendered preview for .md files | | HTML preview / browser | .html / .htm render in a sandboxed in-app iframe; right-click one in the file tree → Open in Browser to hand it to the system default browser instead | | Save | Ctrl+S to save; auto-save on focus loss is on by default (can be turned off in settings) | | Create | New file or folder inline in the file tree | | Rename / Delete | Rename or delete any file or folder directly from the tree | | Resizable sidebar | Drag the divider to adjust file tree width | | g ai chat panel | A g ai chat panel on the right (toggle it from the editor toolbar) that keeps the currently open file as context. It carries the same engine selector as the Agent view — pick the built-in g ai or an external CLI (Claude Code / OpenCode / Codex); uninstalled engines are greyed out and click to open the install guide | | Theme sync | Editor theme follows the global light / dark setting |


Workbench (Task-Driven Agent Execution)

Workbench — multi-project board

Three panes on one board: the project list with its run monitor on the left, a kanban board in the middle, and the master-agent console on the right. Type an instruction into the console — the master agent decides which project it lands in, or you can target the project you selected yourself. Clicking a card opens the task editor as an overlay over the board, which stays mounted underneath.

A dedicated view for running coding agents across one or many projects. Every task carries its own prompt preset, attachments and executor, and runs as its own detached process, so context never piles up.

| Feature | Description | |---|---| | Task list | Create, edit, delete tasks. Rows are grouped by project (current project first), each a single line — the title, or the first characters of the description (ellipsised) when the title is empty. Each project is a block whose task list is capped at 46 vh and scrolls inside the block, so a project with dozens of tasks no longer pushes the other projects out of sight (the group header stays put while its list scrolls). Tasks with neither a title nor a description count as drafts that are never saved (switching away drops them) | | Multi-project board | Three resizable / collapsible panes: the project list with its run monitor, the kanban board, and the master-agent console. Drag a divider to resize (project list 180–420 px, console 260–560 px), double-click it to reset the responsive default, or fold a side away — the project list's own header button folds it (when folded, an expand button sits at the left of the board's title line), the console folds into a 32 px rail that expands on click. The run monitor has its own divider (120 px up to half the viewport height) for when several jobs run at once. All widths / heights are remembered across sessions. Below 860 px the panes stack vertically with the console above the board — a stacked board runs to five figures of pixels, so a console placed after it would be out of reach — and the progress report is shown in full instead of clamped to six lines | | Kanban board | Todo / Doing / Done columns with the done column sorted newest-first; a filter checkbox narrows the board to tasks whose last run failed, and a table view is one click away for a denser listing. A New task entry sits at the end of the Todo column whether or not that column already holds cards (it doubles as the empty state, and opens the same create dialog as the top-right button). Task attachments of the image kind get a cover across the top of the card — the first one, with a +N badge when there are more — and clicking it opens the full-size viewer with arrow-key paging through the whole set (clicking it does not open the task: the card is one big click target, so the cover opts out). An attachment whose file has gone missing drops the cover instead of leaving a broken-image icon on the board; the table view marks the same fact with an N image(s) chip next to the title so the two views never disagree | | Card status while running | A running task is more than a blinking dot: the card itself carries its executor's brand icon (hover for the product name), the elapsed time, how many tool calls it has made (hover for the mix of types), its most recent tool call, what it is thinking right now, and its newest reply — plus a silence figure once the job has gone quiet for a while. Above those lines sits the task's own progress bar with the master agent's estimate labelled AI estimate (hover explains where the number comes from); it is drawn only when a recent report covers this run — the model gave no number, gave a nonsense one, or the report only covers a previous run of the same task, and the bar stays away rather than showing 0%. A row with nothing in it is left out instead of being filled with "none yet", and a card that is not running has no such block at all. The facts come from the execution log, the same source the progress report reads, so they refresh with the board's 5-second poll and a task running in another g ui instance shows up here too; in table view the same summary sits under the task name (with the estimate as plain text). Hovering the card also reveals a Stop button in the very slot an idle card puts Run in (a running card does not show both — there is no second run to start), so killing a stuck run no longer means opening the editor and scrolling down to it; it goes through the same endpoint and the very same confirmation text as the editor's stop, and a job running inside another g ui instance cannot be stopped from this window — the server says exactly that instead of a flat "stop failed" | | Master-agent console | Give the master agent an instruction and it dispatches a new task, deciding the target project itself (or honouring the project you selected). Enter dispatches, Shift+Enter starts a new line | | Card after a run | A card in Done no longer stops at its title and timestamp: it keeps a short excerpt of the last thing the agent said — the tail of the newest run's reply, flattened to one line and capped at 100 characters. This is the case it was built for: a run often ends by asking you something ("shall I push?"), so a task sitting in Done may still be waiting on your answer, and the only way to find out used to be opening the editor and reading the log. The executor's brand icon sits at the head of that excerpt — the line is what it said, so the icon that says which CLI said it belongs there rather than somewhere else on the card. A task that is still running keeps showing the live block instead (the two are mutually exclusive), a run that wrote no body text leaves this line out entirely rather than borrowing an earlier run's words, and the full excerpt is in the hover tooltip; the table view carries the same line under the task name. Executor icons are drawn only when the executor is actually known — a run recorded before the agent field existed, or one whose value this build does not recognise, gets no icon rather than a guessed brand (in the table view, icons appear on the status line for a running task and on the last-reply line for a finished one) | | Progress report | The console's Instruction pane reports where your running tasks actually stand instead of listing what was dispatched and what finished (the board already shows that). The master agent reads each running job — elapsed time, its latest thinking, what mix of tools it has been calling, how long it has been silent, and the latest output — and writes a short paragraph per report: what each task is doing, how far it has got, and whether it looks stuck. Each report also carries a progress bar: the model is asked for its own estimate of overall progress plus one figure per task, and those numbers are drawn as bars labelled AI estimate (hover says what the guess is based on). It is an estimate, not a measurement — and when the model declines to give a number, the bar is simply not drawn instead of defaulting to 0%. The thinking line is what makes this work for tasks that never write a line of prose (they are the common case — their output stays empty from start to finish); "looks stuck" now has to come with evidence — a silence figure, or a tool mix going in circles — instead of being inferred from a high call count. Reports appear on an interval you pick (5 / 10 / 15 / 30 / 60 minutes, or off) or whenever you hit Report now, and the pane shows the newest report's own text plus its overall estimate — the per-task facts behind it (project, elapsed, tool mix, latest thinking, last tool call, silence) live on the board's In progress cards instead, where they are read in the same glance as the task itself, while earlier reports stay browsable from the history list. A report only stays in the main slot while the tasks it covers are still running — a report is a snapshot of the moment it was generated, so once those tasks finish the card's "{n} running" line becomes a lie, while the counter right above it already reads 0 running. When they do, the slot stops showing it and states what is actually true instead: "No task is running right now" once everything has finished, or "No progress report for the running tasks yet" when a new batch has started but nothing covers it yet; to read an older one, open it from the history list — it comes back tagged Finished Automatic reports are generated server-side, so the history keeps filling up while the tab is closed and is waiting for you when you come back. Nothing running means nothing filed: automatic reports are skipped outright, and Report now answers with a plain "no task is running right now" rather than filing an empty entry (no empty entries, no wasted model call), while two identical reports arriving seconds apart are collapsed into one so a double click leaves no duplicate. The paragraph is written in the UI language and, when a task's output is inconclusive, it says so rather than inventing progress. History and Project overview fold down to a single line by default — the report count, and branch plus working-tree state, stay on that line — and open again on click, with your choice remembered. Both are fixed-height blocks, so on a short screen they squeeze the report card into a slit (about a dozen pixels at 1366×768); folded away, the report's window roughly doubles | | Silent wrap-up | When a run has been silent for more than 10 minutes (no prose, no thinking, no tool call) the console asks the master agent to read the facts it has — latest thinking, the last thing it said, the tool mix — and decide whether it has finished or is still working / stuck. A "finished" verdict settles that run: the card moves from In progress to Done, with an AI marked done chip under the elapsed time whose tooltip carries the model's reasoning. When the evidence is thin it does nothing at all (it only books the check, waits another 10 minutes, and asks at most 3 times per run) — not acting beats marking a running task as done. It changes the record, never the process: killing a hung process stays the job of the Stop button on the card. Only runs owned by this instance are judged — a task started in another g ui window is settled by that window | | Marking a task done by hand | The columns are derived from execution facts, which cannot express the two things only you know: a task sitting in Todo that was really finished somewhere else, and an In progress task whose model has gone quiet while you can see the work is done. Both cards now hover out a Done button next to Run / Stop, and a card that got into Done by hand trades it for Undo. A card that finished on its own keeps showing neither — undoing a mark it never had would do nothing at all. Marking a still-running task done asks first and stops that run as part of the same action (a card cannot be Done and running at once), which holds across windows too: if the job lives in another g ui instance the server refuses with that very reason instead of pretending. The mark is stored on the task rather than faked as a run record, and a later run of that task supersedes it, so the card goes back to following the facts. Undo is one click with no confirmation — it is the "I clicked the wrong one" path — and hands the card back to the execution facts, which is why it is offered only where the mark is what keeps the card in Done | | Dispatch default prompts | Give every dispatch a standing prompt: one global entry that applies to all projects, plus one per project that is appended after it whenever you dispatch to that project (it supplements the global one rather than replacing it). Both are edited from the gear button in the console's composer; the combined text is prepended to the instruction, a "Default prompt (global + this project)" checkbox appears next to Run now so a single dispatch can opt out, and the instruction log records which level was attached. The prompt is copied onto the task at dispatch time, so editing the setting later never rewrites tasks that already exist; it lands in the task's own prompt field, where you can still edit it per task | | Project icon | Every project row leads with the project's own favicon instead of a generic folder — a 32 px tile spanning the name and branch lines, so the list reads at a glance. The same icon is used on the recent-directories cards in the switch-directory dialog, on the top-bar directory pill, and on the board's own title line (one lookup, one fallback — only the slot height differs per row: 32 px in the list, 24 px on the board, 20 px in the top bar). The server looks for the icon the project actually ships: first the <link rel="icon"> declared in its index.html, then a favicon.svg / .png / .webp / .ico found within two levels of the project root (which is what finds layouts like frontend/public/favicon.svg). The lookup is bounded (dependency, VCS and cache folders are skipped outright; build output is searched last) and cached for five minutes, so the board's five-second poll never re-walks the disk. Projects that ship no icon — and icons that fail to load, such as a CDN URL the browser refuses — fall back to the folder glyph, so a row never shows a broken image. Nested repositories are skipped on purpose: a folder that merely contains your projects keeps the folder glyph instead of borrowing one of theirs | | Open-with menu per project | Hovering a project row in the board's project list reveals two buttons: Open folder (straight to your file manager) and Open with, a menu holding file manager / terminal / g ui in a new tab plus every editor and AI tool (VS Code, Codex, OpenCode, CodeBuddy Code, Pi Agent, Kimi Code, ZCode, DeepSeek Harness). The tools whose CLI can skip approval prompts — Claude Code, Codex, OpenCode, CodeBuddy Code, Kimi Code — each add their own fully-approved entry (Codex also gets its auto-approve-but-still-sandboxed one). Tools that are not installed are dimmed and labelled "Not installed" — clicking one opens the very same install guide the top bar uses. Every action applies to that row's project only; the board's selection is never touched | | Remove a dead project | A row whose folder no longer exists still shows up — the list is the union of recent directories and the paths your tasks remember — so that row gets a single red Remove from list button instead of the open actions (opening a missing folder can only fail). The confirm dialog spells it out: no tasks are deleted, and the toast repeats how many were kept. The entry disappears from the list while every task, job and history record stays put; if you ever clone the folder back, the row returns on its own | | Task editor overlay | Clicking a board card opens the editor as an overlay: a flat sidebar holding the task list and prompt presets, then the task header, preset selector, executor split-button, the copy-execution / execution-log / clear-execution actions, and a chat-style execution body. The description collapses into a one-line "Task description (optional)" summary until clicked, showing a "Filled" badge and the attachment count once there is content. Opening the overlay lands the conversation on its newest turn rather than its first: the flow keeps re-pinning to the bottom while markdown highlighting and tool-call folding are still growing it after mount, and lets go as soon as you scroll yourself. The thin bar on top carries the back-to-board button, the current project, then a task-status badge (running / done / failed / to-do, in the same role colors as the board column headers, its dot pulsing while it runs; hover for the "AI marked done" / "marked done manually" reasoning — before this the bar showed only time and duration, so a running task could still wear last turn's "1 hour ago · took 13 min" and look finished), and — just left of the "Task execution" label at its right end — the task's relative time and how long it took (a "took x" line once it has run, a live "running for x" while it is, and only the time — no placeholder — if it never ran), word for word the same two values the board card shows. When the window narrows, this block and the status badge yield first (the badge is dropped entirely at roughly phone width rather than squashing the project name): the back button, project and execution-path chips never give up a single pixel | | Copy execution content | Copy execution content in the task header puts the whole task — every turn, not just whatever is on screen — on the clipboard as plain Markdown: a header line for the task and its project, then one section per turn (## Round N, with the executor, status and start time on the line under it) holding the user's prompt, the agent's thinking, its tool calls (arguments and results in fenced blocks) and the model's output. It is assembled from the run records rather than scraped from the DOM, so what you get never depends on what happens to be selected. The user side goes through the same trimming the chat bubbles use, so what you copy is what you typed — the injected environment / memory blocks and the attachment list stay behind in job.prompt. Turns with nothing in them are skipped without renumbering the ones after them, and a task that has never run answers with a "nothing to copy" toast instead of putting an empty string on the clipboard | | Executor choice | Run each task with Claude Code, OpenCode or Codex. The global default is set in Settings → General → Task executor (config.taskExecutor); the split-button next to the run button switches it for the next run and remembers that pick in the browser. A continued conversation always stays on the executor that started it — Claude's --resume, OpenCode's --session and Codex's thread id are not interchangeable | | Attachments | Any number of files per task (image / PDF / text / Markdown / CSV / JSON / log, ≤ 20 MB each); images over 3.5 MB are re-encoded / downscaled in the browser before upload so 4K screenshots still fit what the model and the reader can take. Their absolute paths are appended to the prompt so the agent reads them directly. Right-click an image attachment to copy it to the system clipboard (image/png / jpeg / webp / gif). The board's create-task dialog takes them too, before the task exists: files are staged server-side (workbench-images/_dispatch/) and claimed into the task's own folder the moment you hit Create, the create button stays disabled while an upload is in flight, and closing the dialog without creating deletes them instead of leaving screenshots behind. File names travel percent-encoded, so a name written in Chinese uploads like any other | | Prompt presets | Reusable prompt templates with {{task.title}} / {{task.desc}} / {{repo.path}} / {{branch}} variable interpolation | | AI prompt generation | The "New / Edit preset" dialog carries an AI Generate project architecture button plus an Edit instruction button: the server recursively finds every sub-project (a directory holding .git or one of 9 manifests), reads each one's key files on its own (manifest 20 KB / README 8 KB / a 2-level tree), calls the LLM concurrently to produce a per-sub-project architecture description, and merges them into one when there are several. Edit instruction customises the prompt used for generation (persisted to ~/.zen-gitsync/ai-instruction.json) | | Pipe-mode launcher | Spawns the selected executor as a detached process with stdout/stderr piped to the server — no external terminal window is opened, so output streams directly into the UI. Claude Code runs as claude -p - --output-format stream-json --verbose --permission-mode bypassPermissions --dangerously-skip-permissions (the prompt goes in over stdin to dodge Windows' 32 K command-line limit); OpenCode runs as opencode run --format json --auto --thinking; Codex runs as codex exec --json --skip-git-repo-check --dangerously-bypass-approvals-and-sandbox -, both following whatever default model their own CLI is configured with | | Isolated processes | Every run is its own detached process with fresh context, so memory and conversation state never accumulate across tasks | | Cross-run memory | Every dispatched task is told about a local memory library at ~/.zen-gitsync/memory/ — the lessons earlier agents in that same repository left behind. The library is deliberately two-tier: only INDEX.md (one ≤100-character line per lesson) ever enters the prompt, while the lesson bodies live in lessons/*.md and are read on demand, only when an index line actually matches the task. Nothing is preloaded and archive/ is never read — which is the whole point, since pasting the index into every prompt would make each dispatch pay for every unrelated historical lesson. When the workbench starts, the library is seeded if it does not exist yet (existing files are never overwritten). Before finishing, each agent self-checks four questions — stuck ≥3 min or a repeated wrong path, a wrong tool / wrong file / a grep drowned in build output, a correction from you, or a verified non-obvious shortcut — and writes one lesson if any of them is yes, always appending the matching INDEX.md line (a lesson without an index line is a lesson nobody will ever find). Set memoryContext: false on a task to opt out of the whole thing | | Memory library panel | Settings → Memory browses the same library: a scope picker (the two global files plus every repository the agents have worked in, labelled by its real path), the lesson list for the selected scope, and click-to-expand for the raw text. Deleting a lesson also removes its line from the index — leaving a line behind would point agents at a file that no longer exists. Lessons that were written without an index line are badged as Not indexed, since the agent can never find those. GLOBAL.md and INDEX.md themselves cannot be deleted (they are the accounting). Nothing here is saved through the dialog's Save button: every action takes effect immediately | | Live log | The "执行日志 / Execution log" panel opens by default and auto-scrolls, showing accumulated stdout + stderr (last 64 KB rendered client-side; the server keeps up to 100 MB per job) | | Live status | Task status (pending / running / done / error / cancelled) and PID stream in real time over SSE | | Tool-call stream | The model's tool calls are rendered inline in the conversation flow, so you can follow what the agent actually did | | Inline images in a reply | The agent can put a picture in its reply body: write it as a Markdown image pointing at a local file — ![caption](C:\...\docs\shot.png) — and it is rendered right in the conversation flow. That path is rewritten to a server endpoint that only serves images from the task's own repository (path traversal, absolute paths outside the repo and symlinks pointing out of it are all rejected, and the response carries nosniff plus a sandbox CSP), and the executor is told the syntax through the injected environment block — pasting a bare path is what it does otherwise, and a bare path or one wrapped in a code fence stays plain text. Follow-up turns carry a one-line version of the same hint. A file the run cannot read renders as a broken image rather than silently disappearing; png / jpg / jpeg / gif / webp / bmp / svg up to 20 MB | | Finish notice | When a run finishes or fails you can be told three ways, each with its own switch: an in-app toast (on by default), a browser notification (off by default), and a chime (on by default; a distinct tone for done vs. error, silent when you stop a run yourself). The three are independent — keep any combination. With the toast and the notification both on, the toast shows while the page is focused and the system notification takes over once it is not; with the toast off, the notification fires even in the foreground, since it is then the only channel you asked for. The browser notification switch asks for notification permission only at the moment you turn it on — it is off by default and nothing ever prompts you on its own. Sounds are CC0 assets under public/sounds/; see CREDITS.txt there to swap in another tone. An agent question rings a third tone: when the agent pauses on ask_user and waits for your answer, the chime plays even if you have switched to another window — that turn is not over yet, so it is sound only, with no toast | | Cross-view indicator | While any Workbench task is running, a pulsing dot appears on the Workbench icon in the Activity Bar so you can see job state from the Git or Editor view | | Execution log manager (dialog) | The "Execution logs" button in the workbench top bar opens a dialog with the list / filter / batch delete / clear / retention-policy UI (defaults: 500 records, 256 MB); the task execution view stays mounted so no work-in-progress state is dropped | | Continue chat | After a task reaches a done / error / cancelled state, a follow-up composer appears; sending a message resumes the previous session (claude --resume <session_id> for Claude Code, --session for OpenCode, codex exec resume <thread_id> for Codex), and each new turn stacks into the same chat-style flow. Since the resumed session already carries the previous turn's context, follow-up turns inject only a slim refresh of the run-environment block (current project + board totals + truth-file paths) instead of the full project list. The bubbles show only what the user actually typed: the injected environment / memory blocks and the attachment list stay in the raw job.prompt (readable and copyable in the run log), so copying a conversation out and pasting it back no longer drags a whole round of background along | | Local tool detection | On startup + every 10 min the server probes 7 CLIs (code, claude, codex, opencode, kimi, zcode, dsh). Tools that are missing are dimmed and labelled "Not installed" — clicking one opens the install guide, and right-clicking a tool button offers an update to the latest published version |

Prompt presets and tasks are persisted to ~/.zen-gitsync/prompts.json and ~/.zen-gitsync/tasks.json (cross-project, shared across repos); run history and the retention policy live in jobs.json / jobs-config.json, the master-agent console state in orchestrator.json (with its report history in orchestrator-reports.json — kept apart so the polled state file stays small), and task attachments under ~/.zen-gitsync/workbench-images/_task-<taskId>/.


AI Agent (Web)

A dedicated view (robot icon in the activity bar) for chatting with the built-in AI agent directly from the browser. The left sidebar lists all saved sessions (both Web and CLI origins); the right pane is a full chat interface with streaming responses, thinking process display, and tool-call visualization.

| Feature | Description | |---|---| | Session list | Browse, search, rename, and delete past conversations; sessions created via g ai in the terminal also appear here with a CLI badge. A finished scheduled-task run lands here on its own (no manual refresh needed), and whatever View session on a task card opens is always listed and highlighted — even when that session belongs to another project (the list itself is filtered per project; that one is merged in as the exception). The list collapses: a button at the right of the "New session" row folds the whole column down to a slim rail (icon plus vertical label, the rail itself is the way back), handing its 240px to the conversation. The same collapse works wherever the list appears side by side with the chat — the file-space g ai panel and the workbench's main Agent console — and the state is one shared preference, remembered across reloads (only wide layouts have a column to fold; when the layout is narrow the list is a page of its own and the button is not shown) | | Engine choice | Run new sessions on the built-in g ai or hand them to an external CLI — Claude Code, OpenCode or Codex. The selector sits at the right of the chat tabs; engines whose CLI is not installed are greyed out and clicking one opens the install guide. The same selector lives in the file-space g ai chat panel. The engine is locked once a session is persisted, so switching means starting a new session | | Live session entry | Sending the first message of a new session makes it show up in the list immediately with a "Generating..." badge, instead of waiting for the whole turn to finish; once the reply ends and the server persists the session, the entry is replaced by the real timestamp and message count | | Streaming chat | SSE-based real-time streaming with thinking process, content, tool calls, and tool results rendered inline | | Turn duration | Every reply carries that turn's total time at the right end of its header row, level with the g ai label (12ms / 3.2s / 8m 9s), always visible rather than on hover: while the turn is streaming the number climbs in real time — on a turn whose tool loop runs for minutes this line is the only "how much longer" signal there is — and when it ends the server's frozen value takes over (a turn you stopped counts too, which is exactly when you want the number). The figure is measured on the server and written into the session file, so reloading and reopening the session reads back the same number instead of the client computing it a second time. The CLI g ai records its turns the same way, so CLI sessions show per-turn times in the Web UI as well | | Automatic retry | A request that dies mid-stream, stalls, or comes back 5xx / 429 is retried automatically — up to 10 retries with exponential backoff (honouring the gateway's Retry-After when it sends one), while errors retrying cannot fix (a 400/401/403/404 — bad key, model without tool support) fail immediately instead of burning ten calls. The timeout is an idle timeout rather than a cap on the whole stream: the clock resets on every byte received, so a slow-but-alive answer (long thinking, a long tool loop, a big context's first-token latency) is no longer killed at the 5-minute mark — only a connection that has sent nothing at all for 5 minutes is declared dead. Every retry raises a toast with the attempt count, and the bubble rewinds to where that attempt started, so a half-written answer never ends up glued to the new one. The CLI g ai shares the same policy and prints one line per attempt | | Retry / regenerate a turn | When a turn does fail, the Retry button in the error bubble runs it again: the server picks the session up where it stopped, keeping the user message and every tool result that already ran, so the model does not redo work it has finished. The action bar's Regenerate on a finished answer works the same way — the old answer is dropped and the same question asked again. This works on the last turn only, and on the built-in g ai engine only: an external CLI starts a fresh process per turn, so there is no half-finished state to pick up (the button says so instead of silently redoing the whole prompt) | | Tool call display | Each tool invocation (run_command, read_file, read_image, edit_file, list_files, search_text, write_file) is shown as a collapsible card. The collapsed line carries a short truncated summary; expanding reveals the full arguments (no longer cut to a 200-character preview) together with the execution result | | Task plan | Multi-step work gets a visible plan: the agent calls the built-in update_plan tool to split the task into 3-8 verifiable steps before touching anything, then updates each step's status as it goes. Steps render as a checklist with completed / in-progress / pending states and a 2/5 progress header — in the terminal as a `✓