Commit 8fd5227
authored
feat: rename DeepSeek TUI agent to CodeWhale (#87)
* feat(cli): add CLI tool with spinner progress and auto-save HTML
- 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
* fix(cli): fail hard on non-HTML output, flush status in non-TTY mode
- 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
* fix(cli): robust error handling, multi-file support, overwrite prompt
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.
* fix(cli): pre-scan batch outputs for basename collisions
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.
* fix(cli): non-TTY batch overwrite + collision output path safety
- 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.
* fix(cli): deduplicate aider/deepseek close output + reject unsupported 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.
* Revert "fix(cli): deduplicate aider/deepseek close output + reject unsupported default agents"
This reverts commit 19636bc.
* fix(cli): deduplicate aider/deepseek output + reject unsupported 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 <!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.
* fix(cli): resolve *_BIN env overrides via PATH for detection
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.
* fix: auto-enable relative paths for basename collisions in non-TTY mode
* feat(cli): add comprehensive test framework (89 tests across 6 suites)
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.
* fix(cli): resolve relative *_BIN overrides in tryPath
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.
* test(cli): add relative *_BIN override resolution tests
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.
* feat(cli): add auto command with intelligent template matching
Implements automatic template detection for the CLI, partially resolves #60
and supplements the CLI entrypoint introduced in #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
* fix(cli): word-boundary keyword matching, force-ai gating, EPIPE guard
- 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)
* fix(cli): address PR #80 review feedback + add skills-matcher 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
* feat: rename DeepSeek TUI agent to CodeWhale
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
* fix(next): deduplicate aide/codewhale/deepseek-tui close-path delta
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.1 parent b54a0fb commit 8fd5227
30 files changed
Lines changed: 4989 additions & 16 deletions
File tree
- cli
- src
- __tests__
- next/src
- components
- lib/agents
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
0 commit comments