Skip to content

feat: rename DeepSeek TUI agent to CodeWhale#87

Open
chenmofei wants to merge 18 commits into
nexu-io:mainfrom
chenmofei:feat/rename-deepseek-to-codewhale
Open

feat: rename DeepSeek TUI agent to CodeWhale#87
chenmofei wants to merge 18 commits into
nexu-io:mainfrom
chenmofei:feat/rename-deepseek-to-codewhale

Conversation

@chenmofei

@chenmofei chenmofei commented May 25, 2026

Copy link
Copy Markdown

Background

deepseek-tui 已正式更名为 CodeWhale(v0.8.41, 2026-05-23)。旧版 deepseekdeepseek-tui 二进制将作为废弃兼容 shim 保留一个发布周期,v0.9.0 移除。

为什么要改

  1. 安装 CodeWhale 的用户检测不到:当前 main 分支 agent id 为 deepseek、bin 为 deepseek,已安装 CodeWhale 的用户无法被识别
  2. deepseek 命名空间冲突:agent id deepseek 与未来 DeepSeek 官方的 Code Agent 可能产生冲突,本次完整释放,预留给官方
  3. GUI 名称过时:显示的是旧名,未反映更名后的 CodeWhale

Changes

两条独立 AgentDef,互相 fallbackBins 检测

安装任一二进制,GUI 两个卡片均显示为可用:

Property codewhale deepseek-tui
id codewhale deepseek-tui
label CodeWhale DeepSeek TUI
bin codewhale deepseek-tui
fallbackBins deepseek-tui codewhale
envOverride CODEWHALE_BIN DEEPSEEK_TUI_BIN
vendor CodeWhale DeepSeek
protocol argv argv
fallbackModels 相同 相同

调用逻辑

buildArgv / parseLineWithState / close-path 均新增 codewhale 分支,共享 exec --auto 调用。

命名空间释放

deepseek 不再作为 agent id、bin 或标识符出现,完整留给未来 DeepSeek 官方 Code Agent。

Behavior

GUI

安装的二进制 GUI 显示
codewhale CodeWhale / DeepSeek TUI 均可用
deepseek-tui CodeWhale / DeepSeek TUI 均可用
均未安装 两个卡片均显示未安装

CLI

命令 结果
-a codewhale 调用 codewhale
-a deepseek-tui 调用 deepseek-tui
-a deepseek unknown agent(预留官方)

Files Changed

  • next/src/lib/agents/detect.ts — 新增 codewhale / 改 deepseek-tui,互相 fallbackBins
  • cli/src/agents-detect.ts — 同上
  • next/src/lib/agents/argv.ts — buildArgv/parseLineWithState/错误提示 新增 codewhale
  • next/src/lib/agents/invoke.ts — close-path 新增 codewhale
  • cli/src/agents-invoke.ts — buildArgv/parseLineWithState/close-path/错误提示 新增 codewhale
  • next/src/components/settings-modal.tsx — VENDOR_GRADIENT 新增 CodeWhale
  • next/src/components/welcome-modal.tsx — VENDOR_HINT 新增 CodeWhale / DeepSeek
  • cli/README.md — CodeWhale + DeepSeek TUI 两行
  • cli/src/tests/agents-detect.test.ts — codewhale protocol 测试
  • cli/src/tests/agents-invoke.test.ts — codewhale close-path 测试

Test Results

7 files / 133 tests passed / CLI typecheck OK / Next typecheck OK

chenmofei added 17 commits May 22, 2026 10:55
- Add CLI package that converts Markdown to styled HTML via local AI agents
- Support 8 coding-agent CLIs (Claude Code, Codex, Cursor Agent, Gemini, etc.)
- 75 skill templates from next/src/lib/templates/skills/
- Spinner progress indicator with chunk count and elapsed time (zero deps, pure ANSI)
- Auto-save output to <input>.html when input is a file
- --output-dir / -d flag to specify auto-save directory
- Config management (default template, agent, model)
- Stdin support for piping content

Part of: nexu-io/html-anything
- extractHtml: return empty string instead of wrapping non-HTML in pre tag,
  so the CLI correctly surfaces agent errors (rate limits, auth failures)
  instead of silently saving a valid-looking HTML file around error text
- createSpinner: in the non-TTY branch, still flush the final status
  message to stderr so CI/piped scripts can diagnose failures
Agent exit-code & stderr (A): track done.code and stderr; if the agent
exits non-zero, report the failure instead of silently saving a (possibly
truncated) HTML file with exit 0.

Format validation (B): reject unknown --format values with a list of
supported formats (markdown, text, csv, json).

Config write guard (C): catch filesystem errors in saveConfig() so disk-
full/permission failures show a readable message instead of an uncaught
exception.

Overwrite prompt (D): ask before overwriting an existing output file
in TTY mode; skip the prompt (auto-overwrite) when piped/CI.

EPIPE handler (E): catch broken-pipe errors on stdout so piping to
head(1) or early-closing consumers does not print a noisy stacktrace.

-o/-d conflict (F): error when both --output and --output-dir are set.

Multi-file support (G): accept multiple positional input files, process
each sequentially, then summarise failures.
When multiple input files would produce the same output basename (e.g.
dir1/readme.md and dir2/readme.md both -> readme.html), the CLI now
pre-scans before any work begins:

1. Collision detection — lists conflicting basenames and asks whether to
   resolve by preserving relative directory paths (dir1/readme.html).
2. Overwrite check — after resolving all output paths, checks whether any
   target files already exist and asks for confirmation before overwriting.
3. On N at any step, the CLI aborts with a clear error before any agent
   work starts.
- Batch overwrite now skips the interactive prompt outside TTY (matching
  the single-file promptOverwrite auto-overwrite behaviour), so scripted
  CI runs don't abort when existing outputs are present.
- resolveCollisionOutput now derives relative paths from the common
  ancestor of all colliding inputs (findCommonPath) instead of cwd, and
  strips '..' segments so outputs stay inside --output-dir, even when
  inputs live outside the current working directory.
…d default agents

- agents-invoke: aider/deepseek close path now enqueues stdoutBuf directly
  instead of running it through both parse() AND a raw enqueue, which
  was producing duplicate HTML (two <!DOCTYPE html> blocks).

- handleConfig set-default-agent: now rejects agents that are not
  installed (!available) or use an unsupported protocol (unsupported),
  with a clear error listing available supported alternatives.

- findAgent: when resolving config.defaultAgent, now also filters out
  unsupported agents so a stale default (e.g. from manual config.json
  edit) automatically falls through to the next available agent.
…ult agents

- agents-invoke: aider/deepseek close path now enqueues stdoutBuf directly
  instead of running it through both parse() AND a raw enqueue, which
  was producing duplicate <!DOCTYPE html> blocks.

- findAgent: when resolving config.defaultAgent, now also filters out
  unsupported agents so a stale default (e.g. from manual config.json
  edit) automatically falls through to the next available agent.

- handleConfig set-default-agent: now rejects agents that are not
  installed or use an unsupported protocol, with a clear error
  listing available supported alternatives.
detectAgents() previously only accepted *_BIN overrides as absolute
paths (existsSync). Relative command names like GEMINI_BIN=fake-claude
were dropped even though invocation (resolveBinForAgent) can find them
on PATH. Now falls back to resolveOnPath() when existsSync fails, so
detection and config flows match the actual invoke behaviour.
Based on all reviewer feedback across 10 rounds, added a complete regression
test suite covering every reported failure path:

- extract-html.test.ts (9): non-HTML content returns empty, no scaffold wrapping
- prompt.test.ts (11): TTY/non-TTY behavior for promptYesNo & promptOverwrite
- collision-resolve.test.ts (8): findCommonPath & resolveCollisionOutput edge cases
- agents-detect.test.ts (20): *_BIN env overrides, PATH resolution, unsupported protocols
- agents-invoke.test.ts (19): DeepSeek/Aider close path no double-enqueue, exit code propagation
- index.test.ts (22): param validation, config set-default-agent guards, convert integration

Refactored for testability:
- Extracted collision-resolve.ts (findCommonPath + resolveCollisionOutput)
- Extracted prompt.ts (promptYesNo + promptOverwrite)

All 89 tests pass. Typecheck and build clean.
tryPath() in resolveBinForAgent previously only handled absolute
paths (starting with / or C:\) and command names on PATH. Relative
paths like ./mock-deepseek or ../wrappers/claude fell through to
resolveOnPath() which only searches PATH directories, causing a
mismatch where detectAgents() reported the agent as available but
invokeAgent() could not find it.

Now paths containing / or \ or starting with . are resolved via
path.resolve() + existsSync(), matching what detectAgents() does.
Two new test cases verify that invokeAgent correctly resolves
relative binOverride paths (e.g. ./mock-agent, ../bin/claude)
via path.resolve() + existsSync(), matching what detectAgents()
already does.
Implements automatic template detection for the CLI, partially resolves nexu-io#60
and supplements the CLI entrypoint introduced in nexu-io#75.

- Add skills-matcher.ts with three-layer matching strategy:
  1. ~80 strong-signal keyword rules (resume→resume-modern, etc.)
  2. Full-template scoring (tags + name + description + scenario)
  3. AI summary fallback only when confidence is low (~0 tokens)

- Add `auto` command: html-anything auto article.md
- Support --force-ai (skip rules) and --show-match-only flags
- Update README with consolidated parameter docs and decision flowchart

Examples:
  html-anything auto resume.md        # auto-match + convert
  html-anything auto article.md --show-match-only  # preview match only
- Add kwMatches() with \b word-boundary for ASCII keywords, substring for CJK
- Remove ambiguous short keywords: "X", "RED", "TODO", "done", "doing", "todo"
- Gate Layer-2 fallback on !forceAi so --force-ai reaches AI summary
- Add EPIPE guard to handleAuto stdin-to-stdout path (matching handleConvert)
…tests

- Add kwMatches() with \b word-boundary for ASCII keywords, substring for CJK
- Remove ambiguous short keywords: "X", "RED", "TODO", "done", "doing", "todo"
- Gate Layer-2 fallback on !forceAi so --force-ai reaches AI summary
- Add EPIPE guard to handleAuto stdin-to-stdout path
- Fix Layer-1 gate: strong-signal matching now works for any content length
- Export kwMatches for unit testing
- Add skills-matcher.test.ts with 39 tests covering kwMatches, strong-signal matching, false-positive prevention, --force-ai path, fallback, and reason output
Background: deepseek-tui has been officially renamed to CodeWhale
(see https://github.com/Hmbown/CodeWhale/releases/tag/v0.8.41).
The legacy deepseek and deepseek-tui binaries are deprecation shims
that will be removed in v0.9.0.

Changes:
- Add new AgentDef 'codewhale' (bin: codewhale, vendor: CodeWhale)
- Rename 'deepseek' AgentDef id to 'deepseek-tui' (bin: deepseek-tui)
- Both entries use mutual fallbackBins so GUI detects either binary
- Add codewhale branch to buildArgv, parseLineWithState, close-path
- Update error messages to list both codewhale and deepseek-tui
- Update GUI (settings-modal, welcome-modal) with CodeWhale vendor
- Update README with both CodeWhale and DeepSeek TUI rows
- Reserve 'deepseek' id for future official DeepSeek agent
- Add test coverage for both codewhale and deepseek-tui agents
- 133 tests pass, typecheck clean
@lefarcen lefarcen requested a review from PerishCode May 25, 2026 03:41
@lefarcen lefarcen added size/XXL PR size: 1500+ changed lines risk/high High-risk PR: dependencies, infra, security-sensitive, or broad runtime impact type/feature Feature or new user-facing capability labels May 25, 2026

@PerishCode PerishCode left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chenmofei thanks for the rename — splitting codewhale and deepseek-tui into two AgentDef entries with mutual fallbackBins is a clean way to ride out the v0.8.x deprecation cycle, and the GUI/CLI surfaces (vendor gradients, install hints, error-message agent lists, exec --auto argv) line up consistently across both packages. One non-blocking correctness finding on the Next-side close-path is noted inline; the equivalent fix already lives in the CLI sibling.

🔁 Powered by Looper · runner=reviewer · agent=claude-code · An autonomous AI dev team for your GitHub repos.

else if (part.kind === "meta") safeEnqueue({ type: "meta", key: part.key, value: part.value });
}
if (opts.agent === "aider" || opts.agent === "deepseek") {
if (opts.agent === "aider" || opts.agent === "codewhale" || opts.agent === "deepseek-tui") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate close-path delta for codewhale / deepseek-tui (and pre-existing aider).

The parse(stdoutBuf) loop above already emits the residual buffer as a delta for these three agents — the parser at next/src/lib/agents/argv.ts:235 returns [{ kind: "delta", text: trimmed + "\n" }] whenever the agent is aider | codewhale | deepseek-tui. This added safeEnqueue({ type: "delta", text: stdoutBuf }) then sends the same residual text a second time, so any final stdout chunk that doesn't end on a newline (the typical end-of-stream shape for deepseek-tui / codewhale running exec --auto) is delivered twice.

Why it matters: downstream consumers concatenate every delta into the rendered/saved HTML, so the duplicated tail — often a chunk of the actual document including a </html> closer — leaks into the preview and any persisted/exported file. Before this PR the buggy path covered one agent (deepseek); after the rename it covers three (aider, codewhale, deepseek-tui), so the regression footprint grows even though the bug itself pre-existed.

Evidence the fix is missing only on the Next side: the CLI sibling cli/src/agents-invoke.ts:595-604 uses if/else for exactly this case (commit 889878e — "fix(cli): deduplicate aider/deepseek output …"), and cli/src/__tests__/agents-invoke.test.ts:489-507 asserts expect(deltas).toHaveLength(1) for codewhale close-path with a no-trailing-newline payload. There is no equivalent test on the Next side, but the dispatch code is the only structural difference.

Suggested change: mirror the CLI structure so the raw enqueue replaces (rather than stacks onto) the parser dispatch for these three agents:

} else if (stdoutBuf) {
  if (opts.agent === "aider" || opts.agent === "codewhale" || opts.agent === "deepseek-tui") {
    safeEnqueue({ type: "delta", text: stdoutBuf });
  } else {
    for (const part of parse(stdoutBuf)) {
      if (part.kind === "delta") safeEnqueue({ type: "delta", text: part.text });
      else if (part.kind === "html") safeEnqueue({ type: "html", text: part.text });
      else if (part.kind === "meta") safeEnqueue({ type: "meta", key: part.key, value: part.value });
    }
  }
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 71204db — mirrored the CLI's if/else structure.

@lefarcen lefarcen requested a review from Eli-tangerine May 25, 2026 03:59
Mirror the CLI's if/else structure so the raw enqueue replaces
(rather than stacks onto) the parser dispatch. Previously, the
parser would emit the delta first, then the raw enqueue would
emit a duplicate — causing doubled tail output in the preview.
@chenmofei

Copy link
Copy Markdown
Author

Good catch! Fixed in 71204db — mirrored the CLI's if/else structure so the raw enqueue replaces (rather than stacks onto) the parser dispatch for aider/codewhale/deepseek-tui. Typecheck & tests still green.

@lefarcen lefarcen requested a review from PerishCode May 25, 2026 04:11

@PerishCode PerishCode left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chenmofei thanks for the quick turn-around on the close-path fix in 71204db — the if/else now mirrors the CLI sibling exactly, so the parser dispatch and the raw stdoutBuf enqueue are mutually exclusive for aider / codewhale / deepseek-tui and the duplicate-tail regression is gone. Spot-checked the rest of the rename surface (mutual fallbackBins in detect.ts, the new codewhale branches in buildArgv / parseLineWithState, the VENDOR_GRADIENT + VENDOR_HINT entries, and the updated agent list in UnsupportedAgentProtocolError) and everything lines up consistently. Nice clean follow-up.

🔁 Powered by Looper · runner=reviewer · agent=claude-code · An autonomous AI dev team for your GitHub repos.

@lefarcen

Copy link
Copy Markdown

Quick status update: @PerishCode has approved the current head, and I don’t see any author action needed right now.

@Eli-tangerine, when you have a chance, could you take the product pass on the user-visible agent rename / CLI ID change? Once product review and checks are in, this can move toward merge.

@chenmofei

Copy link
Copy Markdown
Author

最新情况:已经批准了现任负责人的人选,目前看来无需采取任何进一步行动。

@Eli-tangerine如果有机会的话,能否请你们处理一下用户可见的界面元素名称的更改,以及 CLI ID 的修改呢?一旦产品审查和测试工作完成,就可以进行合并了。

@Eli-tangerine 这个用户好像不存在了?我看账号页面提示 404

@lefarcen

Copy link
Copy Markdown

你说得对,我这边也确认这个 GitHub 用户页目前是 404。这个不是你需要处理的问题;我会把产品 review 改走内部通道,并让维护侧确认正确的公开 reviewer 配置。

当前代码侧 @PerishCode 已经 approve,后续主要还差产品确认和检查状态。谢谢提醒 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk/high High-risk PR: dependencies, infra, security-sensitive, or broad runtime impact size/XXL PR size: 1500+ changed lines type/feature Feature or new user-facing capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants