jkhaynes/ai-agent-skills/tree/master/mods/job-progress

供長時間執行的 Claude Code 工作(測試套件、評估、批次執行)使用的即時 Progress 面板,包含綻放/凋謝進度列、開發伺服器列,以及停止/綻放通知。 一個追蹤背景工作和開發伺服器、解析其輸出中的進度,並透過 ui.render hook 渲染面板的外掛。
jkhaynes/ai-agent-skills/tree/master/mods/job-progress

job-progress 是一個 Claude Code mod,為單元測試、整合測試和 e2e 測試套件、情境/評估執行,以及批次執行等長時間運作的背景工作加入即時 Progress 面板。
每個工作都會顯示一條帶有綠色生長尖端的粉紅色進度列、一株隨工作執行而成長的植物、通過/失敗數量、經過時間、預估剩餘時間和最新輸出列。完成後,工作會開花 🌸 或凋謝 🥀,並顯示提示訊息。/progress 切換面板,/progress clear 移除已完成的工作,/progress stop [n] 停止開發伺服器(會終止整個程序樹)。
這個 mod 接入 tool.call、session.append、tool.describe、1500ms 時鐘和 ui.render,從執行器輸出或明確的 PROGRESS n/m 列解析進度,並自動把已知測試執行器放到背景。它針對作者自己的儲存庫調校,並記錄 4 MiB 輸出上限、tail/head 管道隱藏進度,以及 Python 緩衝等限制。
安裝:使用 claude --plugin-dir <path> 載入,或將資料夾加入 ~/.claude/settings.json 的 CLAUDE_CODE_PLUGIN_DIRS。
注意:自動掃描尚未完成(有尚未解決的命令執行與工具攔截發現結果,還有一個未掃描的外部相依套件),因此發佈前需要人工審查。
請先查看作者 README,確認 marketplace 與外掛名稱;指令可能隨儲存庫結構而變動。
claude plugin marketplace add jkhaynes/ai-agent-skills claude plugin install job-progress
A Claude Code mod that shows a live Progress pane for long-running jobs Claude runs: unit, integration and e2e test suites, scenario/eval runs, batch runs such as test images through a capture pipeline.
Each job gets a soft pink bar with a green growing tip, a plant that grows as the job runs, pass/fail counts, elapsed time, an ETA and the latest output line. When the job ends it either blooms or wilts:

<sub>Three demo jobs over 20 seconds: the unit tests bloom, the e2e suite wilts after two failures, and a capture run of 30 test cards blooms. The frames come from the mod's own render hook on the terminal surface, mounted with claude plugin test and fed demo job states, then painted in Cascadia Mono. They show what the pane draws, but they aren't a screen recording.</sub>
| Running | Finished |
| --- | --- |
|
|
|
A toast says "🌸 … bloomed" or "🥀 … wilted" when a job finishes. To remove finished jobs, run /progress clear, which works everywhere. Clicking Clear finished works in the desktop app and the fullscreen terminal. In the regular terminal, the button only responds to the c key while the pane has focus.
/progress toggles the pane. To close it you can also:
/progress closex while the pane has focusThe pane opens again on its own when the next background job starts.
Dev servers (npm run dev:e2e, bench:serve, vite, wrangler dev, uvicorn) get their own rows at the top: 🌿, the port when the command or output names one, and uptime. They have no bar and never bloom or wilt. To stop them from any terminal, run /progress stop (all servers) or /progress stop 2 (the second row). Each row also has a Stop button, which in the regular terminal answers 1, 2, … while the pane has focus.
Stopping a server kills its whole process tree, not just its task. On Windows, TaskStop alone leaves an npm-launched server running. Git Bash starts each program through a helper process that exits, so no Windows parent link reaches the server, and the stop only kills the outer shell. The mod marks each background server's command with JOB_PROGRESS_TAG. To stop a server, it finds the marked shell, kills every process in that shell's Git Bash process group with taskkill /T /F, and only then calls TaskStop. It reports a stop only when none of the group is left. A server started before this marking existed gets a warning instead of "Stopped". This happens on every TaskStop, including when you just ask Claude to stop a server. The mod handles TaskStop itself, kills the marked tree first, then updates the pane. A TaskStop raises no task notification, so without this the pane would never hear about the stop. Stopping a job that isn't a server marks its row as stopped, with no bloom or wilt.
A runner only counts when it's the program a step of the command runs. Quoted text is ignored, and the start of each &&, ;, | or loop step is checked. A command that only mentions one (-notmatch 'playwright|vitest', cat vitest.config.ts) is left alone.
Every running job shows a bar from the start, at 0% until its output gives a count or percentage. A job that finishes without ever giving a count, percentage or pass/fail numbers gets one line with ✓ or ✗ instead of a full bar and a bloom. If it finishes in under 30 seconds, it's removed without a toast. Bars are at most 40 columns wide.
This copy is set up for my own repos rather than general use:
dotnet run … evaluate, ten-or-not attacks.py / tuning_report.py / make_e2e_fixtures.py / click_measure.py, gh run watch, gh pr checks --watch and timeout N … wrangler tail.sleep of 60 seconds or more is refused. The refusal tells Claude it will be notified when the job ends. Claude is also told not to redirect a background job into a log file of its own, since the pane can't see that file.Result: 13/20 scenarios fully passed counts as 13 passed and 7 failed. ten-or-not 165 cases 2 WRONG lines count as passed/failed. Card numbers (096/182) and centering ratios (42/58) aren't progress. 0.85 failed (a reading) and attempt 1 failed (a retry) aren't failures.| Piece | What it does |
| --- | --- |
| tool.call (Bash, PowerShell) | Remembers each shell call's description as the job's label. Moves known test runners (vitest, jest, Playwright, pytest, dotnet test, go test, cargo test, npm/pnpm/yarn/bun test·e2e·bench) and my project runs to the background when the call didn't choose. Refuses a long sleep while a job runs. The rules live in hooks/classify.ts. |
| session.append | Picks up a job from the shell result row ("running in background with ID: X. Output is being written to: P") and marks it finished from its <task-notification> row. |
| $.clock.every(1500) | Re-reads each running job's output file when it grows and parses progress from it. |
| tool.describe | Adds a line to the Bash/PowerShell tool descriptions asking Claude to background long jobs and to print PROGRESS <done>/<total> <item> lines from scripts it writes. |
| ui.render (Pane) | Draws the pane. /progress opens or closes it on demand. |
Only background jobs are tracked: a foreground command's output isn't visible until it ends.
hooks/parse.ts)In order of preference:
PROGRESS 3/10 label lines: the convention for custom scripts.[12/40] counters (Playwright list reporter).n/m.Running 40 tests / collected 40 items.Pass/fail counts come from runner summaries (88 passed, 3 failed, Passed: 41, Failed: 1), or from per-test marks (✓ ✗ PASS FAIL ok / not ok) while a run is still going.
The mod is a plugin folder. Load it in any session with:
claude --plugin-dir <path-to>/ai-agent-skills/mods/job-progress
To load it in every session (including the desktop app), add the folder to CLAUDE_CODE_PLUGIN_DIRS in the env block of ~/.claude/settings.json:
{
"env": {
"CLAUDE_CODE_PLUGIN_DIRS": "<path-to>/ai-agent-skills/mods/job-progress"
}
}
claude plugin validate mods/job-progress
node mods/job-progress/hooks/parse.check.mts
node mods/job-progress/hooks/classify.check.mts
node mods/job-progress/hooks/followed.check.mts
claude plugin test mods/job-progress
parse.check.mts, classify.check.mts and followed.check.mts are plain Node self-checks for the parser and the command rules (Node 22+ runs the TypeScript import directly). The parser checks use real output lines from my projects. hooks/tool-call.test.ts and hooks/my-runs.test.ts check the shell-call rewrite (backgrounding, dropping tail/head pipes, PYTHONUNBUFFERED), and hooks/pane.test.ts checks /progress opening and closing the pane, all under claude plugin test.
Gotcha found while building it: inside a Text, use arrays of Text children, not fragments (<>…</>). A fragment there is refused as "Box inside an inline element", and the engine draws an empty pane instead.
$.fs.read cap); the pane keeps the last reading.RUNNER rewrite in hooks/register.tsx if that gets in the way.| tail -N or | head -N shows no progress until it ends. The mod drops a trailing tail/head pipe from commands it backgrounds and asks Claude not to add one, but other filters (| grep …) still hide progress.python -u (or flush) for live PROGRESS lines.