Repository navigation
Conversation
…fer destructive ops
- ha-dashboard set now takes a required file argument (was stdin-only,
so the documented set workflow failed); stdin input is removed entirely
so the only syntax is `ha-dashboard set <url_path> <file>`
- delete/update resolve the real dashboard id via lovelace/dashboards/list
instead of guessing url_path.replace("-", "_"), which could target the
wrong dashboard
- validate config shape (object with views list or strategy) before push
- catch connection/auth errors in main() instead of dumping tracebacks
- error on update without any metadata flags instead of no-op "success"
- render HA error message/code instead of a raw dict; drop dead --host flag
- list shows the addressable "default" main dashboard
- SKILL.md: backup-before-set/delete rule, file-path-only rule (no pipes),
default url_path, gotchas for config_not_found and strategy dashboards,
broader trigger vocabulary, honest concurrency note;
CLAUDE.md.template: dashboards section
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- replace the TOON npx pipeline with PyYAML output (yaml default, --format json opt-in): measurements showed YAML beats TOON on this tool's shapes, and this drops the unpinned runtime npm fetch, the missing-npx traceback, and the no-timeout hang entirely - declare pyyaml in requirements.txt (already shipped via yamllint) - list: default --limit 100 (0 = unlimited), returned/truncated fields, count renamed to total, zero-match hint listing valid domains/areas - list: --search substring filter over entity_id and name, multi-value --domain, case-insensitive domain/state filters, --exclude-unavailable - check success on every WS response instead of silently returning wrong data (e.g. all areas null with a non-admin token) - get: always returns a list, exits 1 when no entity resolves - top-level error handler: one-line errors instead of tracebacks - slim output: omit empty key_attr/area, cap unknown-domain attribute fallback to device_class/unit_of_measurement/state_class, add sensor device_class; symlink-safe realpath import; argparse parent/func cleanup - SKILL.md: document yaml/--format, corrected output shapes, search-first and token-cost guidance; CLAUDE.md.template: Find Entity IDs section Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- ha-reload: capture curl failures with a diagnostic instead of dying
silently under set -e; add --connect-timeout 5 --max-time 120 so a
wedged HA cannot hang the script forever; reject arguments (exit 2)
and support -h/--help so `ha-reload --help` no longer triggers a real
reload; send an explicit '{}' JSON body; guard non-numeric http_code
- honest success message: reload_all returns HTTP 200 even when a domain
fails to load, so the script now points at 'ha core logs' instead of
enumerating domains
- remove the false "validates first" claim from DOCS.md and
CLAUDE.md.template — the script never validated; validation is a
separate documented step
- reconcile the four divergent "what gets reloaded" lists: SKILL.md's
detailed list is canonical, script/DOCS/template point to it
- SKILL.md: trigger now covers "restart Home Assistant" requests (check
whether a reload suffices first), right-sized validation (yamllint
always, ha core check only for configuration.yaml/pre-restart),
all-or-nothing note, new Troubleshooting section, trimmed examples
- DOCS.md: fix skill invocation name (/HomeAssistantReload) and Claude
persistence path (/share/.claude, not /data/.claude)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- WS.recv now reads the FIN bit and opcode: replies to server pings with pongs, ignores pongs, raises a clean error on close frames, and reassembles fragmented messages (previously any control frame or fragmented get_states payload crashed json.loads with a traceback) - WS.close sends a proper close frame (code 1000) so HA stops logging an abnormal disconnect for every CLI invocation - ha_call raises RuntimeError on success:false with HA's message and code, bounds its receive loop with a 60s deadline, converts socket.timeout into a descriptive error, and no longer mutates the caller's command dict - _ensure_connection no longer caches a broken, unauthenticated socket as the singleton when the handshake or auth fails - new ha_result() helper returns the unwrapped result; ha-entities drops its local success-checking wrapper and imports it Verified with socketpair protocol tests: ping/pong echo, 3-frame fragmentation, close handling, success:false raise, id skipping, timeout error, close-frame emission. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Delete the private get_token/WS/ha_call copies (~110 lines) and use ha_lib.ha_result like ha-entities does, per the repo convention. The dashboard tool thereby inherits the hardened WebSocket client (ping/pong, fragmentation, clean close) and the raise-on-failure contract; the per-call success checks and _fail helper are gone, and delete/update now reuse one singleton connection instead of opening a second one for the id lookup. Connect timeout changes from 10s to the lib's 30s (irrelevant on localhost). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bash(ha *) auto-approved ha core restart (a 30s+ outage and the Reload skill's documented fallback), ha host reboot/shutdown, ha backup restore, and more without any prompt — and the allowed supervisor POST curl could do all of that plus call any HA service (unlock locks, disarm alarms) promptless. - settings.json: new permissions.ask list — ask rules override allow, so ha * stays promptless for safe commands while ha core restart/stop/update, ha host reboot/shutdown, ha backup(s) restore, ha addons uninstall/stop, ha os/supervisor update, and supervisor POST curls always prompt first; the POST curl pattern is removed from allow (GETs remain) - init-user: merge permissions.ask additively like allow/deny, so existing installs (whose persisted allow lists still contain Bash(ha *) and the POST curl) get the same gating on next start - DOCS.md: document the always-ask set and the additive merge Verified: settings.json parses; jq merge simulation against an existing-install settings file yields the full ask list while preserving user-added allow entries and other settings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The shared app-ci workflow covers shellcheck/hadolint/yamllint/prettier but never runs the repo's own tooling: the Python CLI tools shipped in rootfs (ha-entities, ha-dashboard, ha_lib) got no ruff in CI and shfmt formatting was local-only. Add a lint-fmt job (SHA-pinned setup-pixi) running `pixi run fmt-check`. Also drop --fix from the fmt-check-py task: a check task that silently rewrites import order can never fail on it, so CI would not have flagged unsorted imports. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The npm package is deprecated as of 2.1.198 and the image was pinned to 2.1.74 from March; the runtime auto-updater was silently papering over the stale pin by updating the npm global tree in place. - Dockerfile: install via the official installer (claude.ai/install.sh) pinned to CLAUDE_VERSION=2.1.221, symlink /root/.local/bin/claude to /usr/local/bin so PATH resolution stays identical (the installer's own profile edits are overwritten by COPY rootfs /); add libgcc and ripgrep, the documented musl requirements; nodejs/npm stay for npx-based MCP servers - new /etc/profile.d/claude.sh exports USE_BUILTIN_RIPGREP=0 — the bundled ripgrep is glibc-only and does not run on Alpine - auto-updates stay enabled by design: the native updater keeps users on the latest version between add-on releases, same lifecycle as the npm-era behavior (survives restarts, refetches after image rebuilds) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Swap jantimon/ha-addon-ssh for Tyderion/ha-addon-claude-code in repository.yaml, config.yaml, README, and DOCS so this fork can be added as a custom add-on repository in Home Assistant. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The fork kept the upstream repository name, making it indistinguishable from the original jantimon repo in the Home Assistant store UI. A distinct name makes the already-registered Tyderion repository identifiable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rebuilds fail with apk exit code 5: base 20.0.1 pins musl=1.2.5-r21 in /etc/apk/world, but the Alpine 3.23 index has rotated to musl-1.2.5-r23, so installing build-base (via musl-dev) is unsatisfiable. Verified on both amd64 and aarch64 images against the live Alpine indexes. Base 21.0.1 (Alpine 3.24.1) pins musl=1.2.6-r2, which matches the current 3.24 index exactly; a full local amd64 build of the add-on succeeds with it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The native installer's `claude install` step runs at build time and leaves /root/.claude.json and /root/.claude/ in the image. init-user then fails twice at startup: `ln -s` for .claude.json exits 1 with "File exists" (stopping the whole container), and the .claude directory link is silently created inside the existing directory, breaking persistence. Fix in two layers: the Dockerfile removes the installer state after install, and init-user clears both paths before creating its symlinks so future installer changes cannot crash the add-on again. Verified in a full local build: image contains no /root/.claude*, claude --version works, and the rm+ln sequence produces correct symlinks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New skill for answering "why didn't my automation fire?" via HA's execution traces (trace/list, trace/get over the WebSocket API): - ha-trace list: all automations/scripts with stored trace counts and the latest run's outcome, traced items first - ha-trace runs <target>: per-run summaries (trigger, outcome, last_step, error), with a guidance hint when no traces exist - ha-trace show <target> [--run ID] [--full]: condensed step-by-step walkthrough (per-step result, offset, error; variables dropped for token efficiency), --full for the raw trace Targets resolve from entity_id or friendly name across both domains; YAML automations without an id: field get an explicit error since HA records no traces for them. SKILL.md documents the debugging workflow, outcome glossary, and the traces-only-exist-for-started-runs gotcha. CLAUDE.md.template gains a Debug Automations section. Verified with stub-based functional tests covering all commands, name/entity resolution, ambiguity, no-id, and empty-trace paths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New skill for past-tense questions ("when was the door last opened",
"how long was the light on", "average temperature yesterday") via the
recorder's WebSocket API:
- ha-history changes: state-change log (history/history_during_period)
with consecutive-duplicate merging, per-change durations, a
time-in-state summary over the full window (totals + percent), newest
changes first with --limit 50 default; numeric-looking entities get a
hint to use stats instead
- ha-history stats: long-term statistics buckets
(recorder/statistics_during_period) with per-entity unit resolution;
mean/min/max for measurements, sum/state/change for metered entities
- ha-history stats-list: discover which entities have statistics, with
kind (mean/sum), search and limit
Windows are relative (--last 30m/8h/3d/2w) or absolute ISO --from/--to
(local time unless an offset is given). SKILL.md explains changes vs
stats (raw history purged ~10 days; statistics forever but numeric
only) and the window-edge semantics. CLAUDE.md.template gains a Query
History section.
Verified with stub-based functional tests covering merging, summaries,
limits, window parsing errors, partial/missing entities, rounding,
unit fallbacks, and stats-list kinds.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The two new read-only query tools were missing from the default permission allow list, so every invocation would prompt. Same pattern as ha-entities; existing installs pick the entries up via the additive init-user settings merge on restart. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude Code checks file permissions against Edit() and Read() rules only; Edit(//tmp/*.json) already covers the Write and NotebookEdit tools, and a Write() path rule is never consulted and triggers a startup warning since v2.1.208. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Render Jinja templates against live HA state over the WebSocket render_template API before they land in automations, dashboards, or template sensors. - ha-template: inline or --file input, --var KEY=VALUE (JSON-parsed) for trigger/script variables, --watch to stream re-renders on state changes, --timeout, yaml/json output. Shows `listeners` (what the template subscribes to) — the key debug signal for stale templates. Template errors go to stderr with exit 1 via report_errors. - ha_lib: new ha_subscribe() generator for subscription-style commands — consumes the ack, yields event payloads, keep-alive pings on idle socket timeouts after the first event. - TestTemplate SKILL.md with verify-before-config workflow and gotchas, CLAUDE.md.template section, settings.json allow entry. Stub-tested: 16 tests covering one-shot/watch output, listener trimming, var parsing, input validation, and ha_subscribe framing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Discover services with their real field metadata (stops hallucinated parameters) and call them over the WebSocket API with guarded access to dangerous services. - ha-service list: compact domain map, or detailed rows with --domain/--search; marks services that return responses. - ha-service show: full field spec with selectors/required/examples, flattening collapsed field sections; target domains; danger marker. - ha-service call: --entity/--area/--device/--label targets, --data KEY=VALUE (JSON-parsed) plus --data-file (flags win), validates the service exists with did-you-mean hints, warns on unknown data keys, auto-requests responses for services that return them. - Dangerous services (homeassistant.restart/stop, hassio.*, recorder.purge*, shell_command.*) refuse to run without --unsafe. settings.json asks on any command containing --unsafe (doc-verified: ask rules beat allow rules; Bash wildcards match at any position), so the prompt cannot be dodged by flag reordering. Normal calls flow under the new Bash(ha-service *) allow entry. - CallService SKILL.md, CLAUDE.md.template section. Stub-tested: 18 tests covering list/show/call, section flattening, response auto-request, danger gating, data merging, and errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A call targeting a nonexistent entity either fails with HA's cryptic "did not match any entities" (response services) or silently does nothing (normal services). Check --entity ids against get_states first and fail with did-you-mean suggestions (difflib, same-domain fallback) plus a pointer to ha-entities. Area/device/label targets are unchanged. Found live: calling weather.get_forecasts against an entity id that does not exist on the box. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A deployed session ran `./ha-entities` instead of the bare name: the relative path missed the `Bash(ha-entities *)` allow rule (forcing a manual approval), failed because the tool is not in the working directory, and the session then bypassed the CLI with an ad-hoc ha_lib script. Three layers against a repeat: - CLAUDE.md.template: new "CLI Tools" section stating all ha-* tools are on PATH, must be invoked by bare name, and that a failing call is fixed rather than worked around with ad-hoc ha_lib scripts. - Every SKILL.md: same instruction inline, right after the intro. Skills are re-copied on every add-on start, so this reaches existing installs without user action. - settings.json: deny rules for `./ha-<tool>` forms (one per tool, not a blanket `./ha-*`, to spare user-owned scripts). The deny list is merged into existing installs on every start, turning the mistake into an instant, named denial instead of a permission prompt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reviewed against a real, long-lived instance file and pulled in everything that applies to any install, with no instance identifiers: - Environment: /homeassistant vs /config path duality (which prefix to use where), and reading versions via `ha core info` / `ha os info` instead of hardcoding them. - "Discover, Never Guess": question -> skill routing table plus ha-entities starting points, making look-it-up-first the headline rule rather than a per-task footnote. - Custom component enumeration via manifest.json as a Common Task. - Best practices: verify automations with traces, numeric script IDs, UI group-helper entities, multiple radio stacks, and seasonal `unavailable` devices not being faults. Instance-specific content (domain knowledge, config layout, git tracking, specs workflow) deliberately stayed out. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Safe mutation of virtual state: helpers (input_*), counter, timer, the var integration, and the device-backed select/number/text domains. `ha-state show <entity>` prints the current state plus exactly which values `set` accepts (select options, number min/max/step, timer keywords, datetime shape). `ha-state set <entity> <value>` resolves the right service for the domain, validates the value against the entity's own constraints before calling, then reads the state back and reports before/after with a verified flag — a mismatch prints a note and exits 1 so a silently ignored write cannot pass as success. Device command domains are refused with a pointer to ha-service, and the tool never touches HA's raw set-state API. Wiring: allow rule `Bash(ha-state *)` plus the `./ha-state` deny backstop, template routing-table row, Set Helper State common task, and the tool added to the PATH lists. 21 stub tests green (validation, per-domain service resolution, read-back verification, refusals). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`ha-service call` on input_*, counter, timer, and var now exits with a redirect to `ha-state set`, the same deterministic-gate pattern as the --unsafe check: a raw call on those domains has no value validation and no read-back, and ha-state covers every blocked call with both. The administrative `reload` and `configure` services are exempt. The CallService skill gains a Not For section drawing the tool boundaries explicitly: state reads belong to HAEntities, templates to TestTemplate, history to QueryHistory, virtual state to SetState, YAML reloads to ha-reload. Gate covered by the stub tests (refusal message, reload/configure exemptions). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Everything steering the in-container Claude was prose in CLAUDE.md.template, so it held only as long as the model remembered it. Move the two rules that break things when forgotten into hooks, and stop making the model ask for permission to do ordinary read-only work. - PreToolUse[Bash] rewrites path-form ha-* calls (./ha-entities, /usr/local/bin/ha-service, python3 ha-state.py) to their bare PATH names. Those forms miss the allow rules, prompt the user, and push Claude toward hand-rolled ha_lib scripts. Rewriting is limited to command position, so reading or grepping the tool sources is untouched. The matching deny rules stay as a backstop in case a Claude Code build ignores updatedInput. - PostToolUse[Write|Edit|MultiEdit] parses edited YAML under /homeassistant with a loader that tolerates HA's ! tags and rejects duplicate mapping keys, then hands failures back to Claude as a tool error. Syntax only; yamllint still owns style. - Permissions: add Write alongside Edit, Glob/Grep, colon-form ha-* rules so a bare no-arg call matches, and the read-only shell and git verbs. sed is deliberately left out so edits route through the tools the validator watches. Deny writes to .storage, and raise the Bash timeout, since `ha core check` outruns the default on slow hardware. - init-user only merged `permissions` into an existing settings.json, so hooks and statusLine would never have reached an existing install. It now also carries statusLine and hooks (image wins, they point at /etc/claude) and env (user wins).
Claude prompted for approval on nearly every command it ran inside the add-on. The cause was the permission model, not the rule list: with no defaultMode set, Claude Code runs in `default` mode, where anything not matched by an explicit allow rule asks. The shapes Claude actually writes here - heredoc python3, pipes, `cd x && y` - can never match a prefix rule, so widening the allow list could not have fixed it. The trust boundary is the container, not the individual command. This add-on runs as root and holds a Supervisor token, so anything that can reach the Supervisor can already restart Core and read every secret. So describe the container to Claude and let it approve routine work itself, while keeping an explicit gate on the operations that are genuinely consequential. - settings.json: `permissions.defaultMode: "auto"` plus an `autoMode` block describing this container, its mapped directories, and the bundled ha-* tools. Restarts, reboots, backup restores, Supervisor POSTs and secrets.yaml stay in `ask`; .storage edits stay denied. - Convert every `Bash(cmd *)` rule to the `Bash(cmd:*)` prefix form. The space-glob form is undocumented and does not appear to match, which is why the ha-* tools already carried both forms. Retired rules are listed in settings-stale.json and pruned from users' persistent settings on start, since the additive merge can never drop one on its own. - hooks/guard-destructive.sh: a PreToolUse guard that blocks recursive deletes of a mapped directory, mkfs, dd onto a device node, piping a download into a shell, and shell writes into .storage. Hooks run even under bypassPermissions, so this is the floor that holds when the permission rules are switched off. It deliberately does not cover restarts or reboots; those are things the user legitimately asks for. - config.yaml: new `claude_permission_mode` option (default|acceptEdits|auto| bypassPermissions, defaulting to auto), validated in init-user/run and written into permissions.defaultMode on every start. - init-user/run: the settings merge now treats autoMode as image-managed, fills skipAutoPermissionPrompt as a user-overridable default, applies the permission-mode option, and prunes stale rules. Without this, none of the new keys would reach an existing install. - ssh/tests/: table test for the guard (35 cases), wired to `pixi run test` and a new CI job. The autoMode schema is modelled on a working local Claude Code install rather than on published documentation, so it is unverified against the build inside this image until the add-on runs.
Replaces four init_commands workarounds with real add-on options: - claude_session_name: /usr/local/bin/claude is now a wrapper that passes --name to every session, so Claude never re-derives the name. Subcommands (read from the binary's own --help, cached per version) and an explicit -n/--name pass through untouched. - claude_continue_session: the first auto-start in the web terminal's tmux session continues the last conversation. SSH logins, extra tmux windows and a manual claude start fresh, so two processes never share one conversation. - claude_autostart_session: ttyd/run starts the tmux session at boot so Remote Control works without opening the web terminal. Off by default because of the idle memory cost. Claude Code's versions directory now lives in /data/claude-versions (excluded from backups), so auto-updates survive restarts instead of rolling back to the bundled version. The bundled binary is copied only when it is newer than everything persisted; an old /share/.claude-versions is migrated once. Bash now gets the same cd + auto-start as zsh via .bashrc. Options get UI labels via translations/en.yaml. The wrapper has a table test wired into pixi and CI. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lbctb8bNXdgkb7WT5hX5ci
Base 21.0.1 pins libcrypto3/libssl3 3.5.7-r0 in /etc/apk/world, while the Alpine 3.24 repository has moved to openssl-dev 3.5.8-r0, so the build-dependencies install fails with "unable to select packages". 21.0.5 pins 3.5.8-r0; the full package list resolves against it (checked with apk --simulate on amd64). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lbctb8bNXdgkb7WT5hX5ci
claude doctor warns that the native install dir is not on PATH and suggests prepending it, which would put the real binary ahead of the /usr/local/bin/claude wrapper and silently drop the pinned session name. Appending it instead clears the warning (verified on the device) and keeps the wrapper first. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lbctb8bNXdgkb7WT5hX5ci
Claude Code 2.1.280 no longer appends a collision suffix: two live sessions launched with the same --name both register it with nameSource "user" (checked on the device). Document that, and how to name one session differently. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lbctb8bNXdgkb7WT5hX5ci
The shipped settings.json is user settings (~/.claude/settings.json), where a single leading slash anchors at ~/.claude, not at /. So Edit(/homeassistant/**) matched /root/.claude/homeassistant/**, and every path rule was dead: the allow rules, the secrets.yaml ask rule and the .storage deny (the guard-destructive hook still blocked .storage edits on its own). All Read/Edit rules now use the // absolute form. Write rules are dropped: Claude Code warns at startup that file permission checks only match Edit rules, which cover all file-editing tools. The old single-slash and Write rules go into settings-stale.json so the start-time merge prunes them from existing installs (checked by running the merge filter against the previous defaults). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lbctb8bNXdgkb7WT5hX5ci
The option sets remoteControlAtStartup in the persistent settings on every start (option-driven, like defaultMode), so interactive sessions connect to Remote Control without running /rc. With claude_autostart_session the session is reachable from the Claude app right after boot. The wrapper now always passes `claude remote-control` through: it is not listed in --help, so it would have received --name, and the Remote Control docs say it refuses to start when a wrapper adds a global flag in front of it. Covered by new wrapper test cases. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lbctb8bNXdgkb7WT5hX5ci
The secrets.yaml ask rules only cover the Read and Edit tools. On the device, Claude created and wrote the file with a shell command instead, and auto mode approved it as routine /homeassistant work, so no prompt appeared. A new PreToolUse[Bash] hook returns an "ask" decision for any command naming secrets.yaml; per the hooks docs a hook ask forces the prompt in auto mode too. Commands that reach the file without naming it (grep -r over /homeassistant) are not caught, which DOCS.md now says. Hooks are image-managed in the settings merge, so existing installs pick it up on the next start. Table test wired into pixi and CI. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lbctb8bNXdgkb7WT5hX5ci
guard-destructive.sh hard-blocks a fixed list of shell writes into .storage (tee, sed, cp, mv, redirects), but python, rm, rsync, ln, xargs or `cd .storage && ...` got through, and auto mode approves them as routine work. ask-secrets.sh becomes ask-sensitive.sh and covers both files: secrets.yaml asks on any mention as before; a command naming .storage passes only when every part of it (split on ; & | $( ) and backticks) is a known read-only command and output is redirected only to /tmp or /dev/null. Everything else asks, so reading the registries stays prompt-free but nothing writes HA state without the user seeing the command. Parsing drops single-quoted text first (where jq filters keep their | and >), keeps double-quoted text so a $( ) inside it still counts, and asks on an unterminated quote. Table test extended to 36 cases, including quote-hiding and cd-then-write attempts. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lbctb8bNXdgkb7WT5hX5ci
There was no sanctioned way to rename an entity or move it to an area, so Claude would reach for the UI or .storage/core.entity_registry. `ha-entities update` changes name, area, icon, hidden and disabled through config/entity_registry/update, or name and area on the device with --device (config/device_registry/update). Areas resolve by name or id, unchanged fields are not sent, --dry-run previews, and each changed field is printed before and after with a read-back check that exits 1 on a mismatch. Entities without a unique_id get an explanation instead of a failure. `ha-entities rename` changes an entity_id (same domain, target must be free) and lists every YAML file, UI dashboard and UI helper that still names the old id, since Home Assistant does not rewrite them. ask-sensitive.sh prompts for a rename unless that command segment is a --dry-run, so the reference listing stays prompt-free. The WebSocket fields and responses were checked against home-assistant/core. New stdlib-only unit tests run the tool against a fake registry (17 cases, PyYAML optional), wired into pixi and CI; the hook table grows to 44 cases. The UpdateEntity skill, the HAEntities skill, the CLAUDE.md template and DOCS.md point at the new commands. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lbctb8bNXdgkb7WT5hX5ci
`ha-entities refs <entity>...` answers "what uses this?" on its own, not only as a rename dry-run. It merges Home Assistant's search/related (the UI's Related tab: automations, scripts, scenes, groups, persons, including device triggers; upward relations like device and area are dropped) with the existing text scan of the YAML config and the UI dashboard/helper stores, which also finds templates and dashboards that search/related does not cover. `--device` expands to every entity of the device plus references by device_id. rename now reports the Related-search hits too. search/related was checked against home-assistant/core. The EntityReferences skill triggers on "what uses / is it safe to rename, remove or replace"; UpdateEntity now runs refs before any rename, and says that renaming a device alone breaks nothing. The CLAUDE.md template and DOCS.md point at it, and the stale DOCS "Built-in Skills" section (which listed one of ten) now lists them all. Unit tests grow to 23 with a faked search/related. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lbctb8bNXdgkb7WT5hX5ci
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
zizmor (artipacked) flagged the lint-fmt and test jobs: actions/checkout writes GITHUB_TOKEN into .git/config unless told not to. Neither job pushes, so drop it, matching the upstream reusable workflow. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lbctb8bNXdgkb7WT5hX5ci
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.
Everything the fork carried on top of jantimon/main, collected onto one branch now that
maintracks upstream again (35 commits).What's in it
claude_*add-on options, aclaudewrapper that pins the session name, Claude Code updates persisted under/data, aclaude_remote_controloption, native installer instead of the deprecated npm package.secrets.yamlor writing into.storage, file rules anchored at the filesystem root.ha-trace/DebugAutomation,ha-history/QueryHistory,ha-template/TestTemplate,ha-service/CallService (with target validation and virtual-state refusal),ha-state/SetState,ha-entitiesupdate/rename/refs with UpdateEntity and EntityReferences.ha-dashboardmigrated onto a hardened sharedha_lib.pixi fmt-checkin CI, repository URLs pointed at the Tyderion fork, CLAUDE.md template folded in from a live install.🤖 Generated with Claude Code
https://claude.ai/code/session_01Lbctb8bNXdgkb7WT5hX5ci