Feat/iteration 1 p0 4 5 8#30
Merged
chenliuyun merged 22 commits intomainfrom Apr 25, 2026
Merged
Conversation
added 22 commits
April 24, 2026 23:34
…ctor - output.ts: add `resolutionHint` and `candidateMatches` as top-level fields in ErrorPayload; promote from StructuredUsageError context on ambiguous-name-match and other structured errors; backward-compat context preserved unchanged - output.ts: add `ambiguous-name-match` to ErrorSubKind enum - output.ts: normalize candidate shapes (sceneName → name) across devices/scenes - doctor.ts: new `path` check — detects whether the switchbot binary is reachable on PATH via npm global bin; emits fix hint with shell-specific export command - json-contract.md: document resolutionHint, candidateMatches, and subKind fields - tests: 4 new doctor tests for path check; 30→34 total doctor tests; 1765→1769 total
P0-2: add RiskLevel/IdempotencyHint/RecommendedMode types and deriveRiskMeta() to capabilities.ts; enumerateLeaves() and commandMeta now include riskLevel, requiresConfirmation, supportsDryRun, idempotencyHint, recommendedMode for every command leaf. P0-3: add planId?: string to AuditEntry; executeCommand() accepts planId in options and stamps it on every audit line produced during plan run; plan run now generates a randomUUID() planId at invocation start for cross-step correlation. Destructive guard hint in devices command also mentions plan run --require-approval as an alternative to --yes.
P0-6: extend policy schema v0.2 rule definition with three new optional safety fields — cooldown (shorthand throttle), throttle.dedupe_window (deduplicate rapid sensor bursts), and requires_stable_for (hysteresis guard, schema-validated). ThrottleGate.check() gains a dedupeWindowMs parameter; ThrottleCheckResult adds dedupedBy field. lintRules() validates all three new fields and warns when cooldown and throttle.max_per overlap. dispatchRule() applies cooldown precedence and dedupe_window checks. P0-7: new src/rules/conflict-analyzer.ts detects opposing-action pairs, high-frequency catch-all MQTT rules without throttle, and destructive commands. rules.ts gains `rules conflicts` and `rules doctor` subcommands (combined lint + conflict analysis); both support --json for CI pipelines.
…in-first hints P1-8: add CircuitBreaker class to utils/retry.ts; wire module-level apiCircuitBreaker singleton in api/client.ts — opens after 5 consecutive 5xx/network failures, auto-probes after 60 s; 4xx excluded from failure count. P1-2: add utils/health.ts (quota, audit error-rate, circuit state, process) and commands/health.ts (`switchbot health`); supports --prometheus output for Prometheus/Grafana scraping. P1-5: add --explain flag to `devices command` — prints risk level, device type, and safety reason before execution without sending the request. P1-3: keychain-first credential hints — doctor `checkKeychain()` check reports when file-backend is in use; config set-token prints a tip to run `switchbot auth keychain store` when on darwin/win32/linux with a native keychain available. tests: reset apiCircuitBreaker in beforeEach to prevent state bleed across client.test.ts cases.
…te, rules summary P1-1: add `switchbot daemon` command with start/stop/status subcommands. start spawns `rules run` as a detached background process (cross-platform), writes PID to ~/.switchbot/daemon.pid and stdout/stderr to daemon.log. stop sends SIGTERM; status reports running state. P1-4: add `switchbot upgrade-check` — fetches latest version from npm registry, compares semver, prints update instructions; exits 1 when stale. P1-6: add `scenes validate [sceneId...]` (confirms scene IDs exist) and `scenes simulate <sceneId>` (shows what execute would POST without sending the request). Both support --json output. P1-7: add `rules summary` (aggregate fire/throttle/error counts per rule over a configurable time window; table output via printTable) and `rules last-fired` (show N most recent rule-fire/rule-fire-dry entries sorted newest-first; --rule filter supported). bump: 3.1.0 → 3.2.0
…rimitives P0-3: Add plan save/list/review/approve/execute lifecycle with persistent PlanRecord store at ~/.switchbot/plans/<planId>.json. Plans flow through pending → approved → executed; plan execute enforces approved gate; all steps emit audit entries with planId for traceability. P0-2: MCP send_command now returns riskProfile per action (riskLevel, requiresConfirmation, supportsDryRun, idempotencyHint, recommendedMode) computed from actual device type and command at request time. P0-6: Rules engine enforces maxFiringsPerHour (sliding 3600s count window via ThrottleGate.checkMaxFirings/recordFire), suppressIfAlreadyDesired (skips turnOn/turnOff when fetchStatus confirms powerState already matches), and hysteresis/requires_stable_for (fires only after trigger is continuously stable for the configured duration). Schema v0.2.json and types.ts updated.
…JSON contract
plan execute: only transition to 'executed' when summary.error===0 &&
summary.skipped===0; otherwise write 'failed' with failedAt and
failureReason so the plan can be re-approved and retried. Add 'failed'
to PlanStatus in plan-store.ts.
plan subcommands: replace bare console.error+process.exit with
exitWithError() in review/approve/execute so --json always emits the
standard { error: { code, kind, message, context } } envelope. Fix
plan approve JSON response to use ok:true instead of approved:true.
hysteresis: clear hysteresisFirstSeen when conditions are unmatched so
intermittent satisfaction never accumulates into a false "stable" window.
Add ingestEventForTest() to RulesEngine for time-controlled MQTT testing.
idempotencyHint: compute from CommandSpec.idempotent in the device catalog
instead of deriving from isDestructive, fixing toggle (non-idempotent) and
unlock (idempotent:true despite being destructive). Extract buildRiskProfile()
helper used by both dryRun and live paths of send_command.
Tests: add plan-resource.test.ts (11 cases) covering execute status
transitions, retry-after-failure, and JSON error envelopes; extend
engine.test.ts with hysteresis first-seen, stability, and reset-on-mismatch
cases; extend mcp.test.ts with 5 idempotencyHint cases (turnOn/toggle/unlock
dry+live).
…ppressIfAlreadyDesired verb extraction, and JSON error contracts - plan execute: marks status=failed (with failureReason) on error/skipped steps; only sets executed on full success; allows re-approve of failed plans - engine: extract verb via parseRuleCommand in suppressIfAlreadyDesired so "devices command <id> turnOn" format is recognized - engine: ingestEventForTest now increments stats.eventsProcessed - plan save: validation error path uses exitWithError() for proper JSON error envelope - tests: add regression tests for maxFiringsPerHour (cap + sliding-window reset), suppressIfAlreadyDesired (suppress/allow/best-effort), and CircuitBreaker state machine (closed→open→half-open→closed)
…cution hint Adds `scenes explain <sceneId>` to complete the validate/simulate/explain triad. The command fetches the scene by ID and emits a human-readable explanation including riskLevel (always "low" for scenes), idempotency note, and the exact command to run. JSON mode returns a structured envelope compatible with Agent/MCP consumers.
- Refactor `health` command into `health check` (one-shot report) and `health serve` (long-running HTTP server) - health serve exposes GET /healthz (JSON, 503 when circuit open) and GET /metrics (Prometheus text); handler extracted as createHealthHandler() for direct test use without binding a port - daemon start: add --healthz-port option that spawns a `health serve` process alongside the rules daemon and writes its PID to healthz.pid
- policy backup [file]: copies active policy to <policy>.bak.yaml (or explicit path); refuses to overwrite without --force; JSON envelope includes source, dest, sizeBytes - policy restore <file>: validates backup against schema before overwriting active policy; auto-saves pre-restore backup; --no-validate bypasses schema check; error paths use exitWithError per JSON error contract
…ofile template command doctor --json now includes maturityScore (0–100) and maturityLabel (production-ready/mostly-ready/needs-work/not-ready) computed from the ok/warn/fail check distribution. config agent-profile emits or writes a conservative starter profile (dailyCap: 100, auditLog: true) for AI agent / MCP integration, with --write and --force options. 9 new tests cover both features.
…step errors not skipped
…nd quiet-hours gap detection - rules explain <name> [path]: new subcommand showing trigger, conditions, actions, cooldown, hysteresis, maxFiringsPerHour, suppressIfAlreadyDesired, and last-fired time from the audit log; supports --json - upgrade-check --json: add breakingChange boolean (true when latestMajor > currentMajor) so callers can gate on API-incompatible upgrades - conflict-analyzer: add pattern 4 — event-driven (mqtt/webhook) rules without a time_between guard flagged as warnings when quiet_hours is configured in policy; analyzeConflicts() now accepts optional quietHours param (backward compatible); rules conflicts and rules doctor both thread it through - tests: 4 rules explain tests, 3 breakingChange tests, 8 conflict-analyzer unit tests (98 files, 1856 tests green)
…, agent-profile, maturityScore, scenes explain, rules explain/conflicts/doctor, quiet-hours, policy backup/restore, test count 1856
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.
♻️ Current situation
Describe the current situation. Explain current problems, if there are any. Be as descriptive as possible (e.g., including examples or code snippets).
💡 Proposed solution
Describe the proposed solution and changes. How does it affect the project? How does it affect the internal structure (e.g., refactorings)?
⚙️ Release Notes
Provide a summary of the changes or features from a user's point of view. If there are breaking changes, provide migration guides using code examples of the affected features.
➕ Additional Information
If applicable, provide additional context in this section.
Testing
Which tests were added? Which existing tests were adapted/changed? Which situations are covered, and what edge cases are missing?
Reviewer Nudging
Where should the reviewer start? what is a good entry point?