refactor/fix: code quality, filter fixes, expand hints, smoke-v3#25
Merged
chenliuyun merged 10 commits intomainfrom Apr 21, 2026
Merged
refactor/fix: code quality, filter fixes, expand hints, smoke-v3#25chenliuyun merged 10 commits intomainfrom
chenliuyun merged 10 commits intomainfrom
Conversation
added 10 commits
April 21, 2026 14:16
…xes, MCP shutdown, expand hints - output.ts: add exitWithError() overload (string | options) to eliminate ~17 repeated isJsonMode()/emitJsonError()/process.exit() blocks - index.ts, config.ts: replace private isJsonRequested() duplicates with shared isJsonMode() import - field-aliases.ts: remove 'category' from controlType aliases to prevent collision with the standalone category filter key - devices.ts: add controlType to LIST_FILTER_CANONICAL / LIST_FILTER_TO_RUNTIME / matchesFilter entry shape so --filter controlType=X works; replace 3 inline error blocks with exitWithError(); add EXPAND_HINTS map and tip line in renderCatalogEntry() + expandHint field in --json output for device types supported by 'devices expand' (AC, Curtain, Curtain 3, Blind Tilt, Relay Switch 2PM) - batch.ts: replace FilterSyntaxError and no-targets error blocks with exitWithError() - mcp.ts: remove dead loadConfig import; replace 2 inline error blocks with exitWithError(); replace EOF-only stdin handler with graceful shutdown (SIGTERM/SIGINT + stdin end, isShuttingDown guard, 30s force-exit timeout)
- devices.test.ts: --filter controlType=Bot (verifies the new canonical key), --filter roomName=Living (verifies alias works + null roomName excluded), read-only device with --allow-unknown-device still rejected with exit 2 - mcp.test.ts: send_command case normalization (turnon → turnOn) - mcp-stdio.test.ts: MCP stdio graceful shutdown (stdin EOF, SIGTERM) exits cleanly without hanging
… suite
- analyze.py: remove redundant 'if dim == "real.mutate"' branch that was
double-counting alongside meta.real_mutate; meta.real_mutate is now the
sole counter
- runner.py: extract check_real_mutate_success(); add --json to allowlist
mutate commands so output is parseable; check ok:true in the CLI envelope
instead of string-matching statusCode; add meta={real_mutate:True} so
analyze.py counter fires correctly
- .gitignore: ignore smoke-v3/results/
- smoke-v3/: initial commit of the v3 smoke test suite (runner, analyzer,
fuzzer, common utilities)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Post-review improvements across three areas: code quality refactoring, two bug fixes, and smoke-v3 test infrastructure.
Refactoring
exitWithError()helper inoutput.ts(string or options overload) — eliminates ~17 repeatedisJsonMode() ? emitJsonError() : console.error()+process.exit()blocks acrossbatch.ts,mcp.ts,devices.tsisJsonRequested()duplicates inindex.tsandconfig.tswith the sharedisJsonMode()importBug fixes
field-aliases.ts: remove'category'fromcontrolTypealiases — prevents collision where--filter category=Xcould matchcontrolTypeinstead of the physical/IR classification fielddevices.ts: addcontrolTypetoLIST_FILTER_CANONICAL,LIST_FILTER_TO_RUNTIME, and thematchesFilterentry shape so--filter controlType=Xactually works (was documented but silently ignored)mcp.ts: remove deadloadConfigimport; replace EOF-only stdin handler with unified graceful shutdown (SIGTERM+SIGINT+stdin end,isShuttingDownguard, 30 s force-exit timeout)Feature
devices describenow shows a tip line for device types that supportdevices expand(Air Conditioner, Curtain, Curtain 3, Blind Tilt, Relay Switch 2PM);--jsonoutput includes an optionalexpandHint: { command, flags, example }fieldTests
devices.test.ts:--filter controlType=Bot,--filter roomName=Living, read-only device still rejected with--allow-unknown-devicemcp.test.ts:send_commandcase normalization (turnon→turnOn)mcp-stdio.test.ts: MCP stdio graceful shutdown (stdin EOF, SIGTERM)smoke-v3 fixes
analyze.py: remove duplicateif dim == "real.mutate"branch that was double-counting alongsidemeta.real_mutaterunner.py: extractcheck_real_mutate_success(); add--jsonflag to allowlist mutate commands; validateok:truein CLI envelope; addmeta={real_mutate:True}so the counter in analyze.py firesTest plan
npx vitest run— all tests pass (1 pre-existingevents.test.tsfailure unrelated to these changes)npx tsc --noEmit— no type errorsswitchbot devices describe <acId>— shows expand tip at the endswitchbot devices describe <acId> --json | jq .expandHint— returns{ command, flags, example }switchbot devices list --filter controlType=Bot— returns matching devices