Closes#102. Base: feat/101 (stacked — merge #101 first). Folds in the epic's old #7 "schema pick-lists" item.
Edit an external sidecar config (ssh-mcp's config.toml) from inside BCC instead of dropping to a terminal.
Dependency decision — no new runtime dep (justified)
The prompt floated tomli+tomli-w or a hand-roll with tomllib/tomli for reads. Neither survives the real CI constraint: the test job installs only pytest cryptography (not requirements.txt), the catalog-signature job imports bcc_core with only cryptography, and the workflow is off-limits. So a top-level TOML import is impossible, and any pip TOML dep would leave this new core untested or red on CI (option (b)'s tomllib read path would also be untested on the 3.10 runner, which has no tomllib). Instead: stdlib-only, uniform across 3.10/3.12/3.13 — a lenient reader + a surgical line-editing writer that rewrites only the one key it's asked to, so comments, formatting, ordering and unknown keys/tables round-trip untouched (strictly safer than parse→dict→reserialize; tomli-w wouldn't comment-preserve either). Runtime deps stay PySide6 + cryptography.
Core (pure, fully unit-tested)
read_toml_section · toml_sections · set_toml_value/update_toml (surgical, CRLF-safe, escaping, create-section, delete-on-None) · validate_sidecar_values (enum/port on managed fields only; unknown keys pass through — reconciles "reject out-of-enum" with "preserve unknown keys") · write_sidecar (atomic + rotating backup + chmod 0600/0700 via #93; never apply_servers). _make_backup/write_config refactored to share _atomic_write_text; _make_backup now keys on the file suffix (JSON naming unchanged).
GUI (thin wiring; smoke-tested headlessly)
"Edit config…" button beside the sidecar advisory → SidecarEditorDialog: section picker, enum pick-lists + range-bounded port spinner, raw-TOML fallback. Save applies only changed managed fields on top of the raw text, validates, writes, then re-runs advisories through #101's path so they update live.
GUI click-through (needs a human)
Add an ssh-mcp server (npx -y ssh-mcp --host=h --user=u), select it → an "Edit config…" button appears by the sidecar advisory.
Click it. If no file exists yet, the raw editor is empty; the section combo shows (top level). Pick role = admin (or type [server] + fields in raw), Save.
Confirm: a dialog reports the backup + 0600; ls -l the file shows -rw-------; the "args inert" advisory now shows (file exists) without a restart (that's #101).
Re-open, change port via the spinner and auth via the pick-list, add a # comment in raw text, Save → re-open and confirm the comment survived and values changed (surgical round-trip).
Try an invalid combo isn't possible via pick-lists by design; to see validation, it fires only on managed fields.
Closes #102. **Base: `feat/101`** (stacked — merge #101 first). Folds in the epic's old #7 "schema pick-lists" item.
Edit an external sidecar config (ssh-mcp's `config.toml`) from inside BCC instead of dropping to a terminal.
### Dependency decision — no new runtime dep (justified)
The prompt floated `tomli`+`tomli-w` or a hand-roll with `tomllib`/`tomli` for reads. Neither survives the real CI constraint: the **test job installs only `pytest cryptography`** (not `requirements.txt`), the **catalog-signature job imports `bcc_core` with only cryptography**, and the workflow is off-limits. So a top-level TOML import is impossible, and any pip TOML dep would leave this new core **untested or red** on CI (option (b)'s `tomllib` read path would also be untested on the 3.10 runner, which has no `tomllib`). Instead: **stdlib-only**, uniform across 3.10/3.12/3.13 — a lenient reader + a **surgical line-editing writer** that rewrites only the one key it's asked to, so comments, formatting, ordering and unknown keys/tables round-trip untouched (strictly safer than parse→dict→reserialize; `tomli-w` wouldn't comment-preserve either). Runtime deps stay `PySide6` + `cryptography`.
### Core (pure, fully unit-tested)
- `read_toml_section` · `toml_sections` · `set_toml_value`/`update_toml` (surgical, CRLF-safe, escaping, create-section, delete-on-None) · `validate_sidecar_values` (enum/port on **managed** fields only; unknown keys pass through — reconciles "reject out-of-enum" with "preserve unknown keys") · `write_sidecar` (atomic + rotating backup + chmod 0600/0700 via #93; never `apply_servers`). `_make_backup`/`write_config` refactored to share `_atomic_write_text`; `_make_backup` now keys on the file suffix (JSON naming unchanged).
### GUI (thin wiring; smoke-tested headlessly)
- "Edit config…" button beside the sidecar advisory → `SidecarEditorDialog`: section picker, enum **pick-lists** + range-bounded port **spinner**, raw-TOML fallback. Save applies only changed managed fields on top of the raw text, validates, writes, then re-runs advisories through #101's path so they update live.
### GUI click-through (needs a human)
1. Add an ssh-mcp server (`npx -y ssh-mcp --host=h --user=u`), select it → an **"Edit config…"** button appears by the sidecar advisory.
2. Click it. If no file exists yet, the raw editor is empty; the section combo shows `(top level)`. Pick `role = admin` (or type `[server]` + fields in raw), **Save**.
3. Confirm: a dialog reports the backup + **0600**; `ls -l` the file shows `-rw-------`; the "args inert" advisory now shows (file exists) **without a restart** (that's #101).
4. Re-open, change `port` via the spinner and `auth` via the pick-list, add a `# comment` in raw text, Save → re-open and confirm the comment survived and values changed (surgical round-trip).
5. Try an invalid combo isn't possible via pick-lists by design; to see validation, it fires only on managed fields.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The pure, fully-tested half of the in-app sidecar editor. No new runtime
dependency: CI's test job installs only `pytest cryptography` (not
requirements.txt) and the catalog-signature job imports bcc_core with only
cryptography, and the workflow is off-limits — so a top-level TOML import is
impossible and any pip TOML dep would leave this code untested/red on CI.
- read_toml_section() — lenient, section-aware scalar reader (string/int/
float/bool); values it can't confidently decode are omitted (the writer
preserves them regardless).
- toml_sections() — ordered profile/section groups for the editor's picker.
- set_toml_value() / update_toml() — SURGICAL writer: rewrites only the one
key it's asked to, so comments, formatting, ordering and unknown keys/tables
round-trip untouched (strictly safer than parse->dict->reserialize, which
tomli-w wouldn't comment-preserve either). CRLF-preserving; correct scalar
quoting/escaping; creates a missing section; deletes on value=None.
- validate_sidecar_values() — enum/port validation against ServerSpec.schema
for the MANAGED fields only; unknown keys pass through (preserved, never a
save-blocker) — reconciles "reject out-of-enum" with "preserve unknown keys".
- write_sidecar() — atomic temp-write + os.replace + rotating backup (reuses
the extracted _atomic_write_text + _make_backup) then chmod 0600/0700 via
#93's fix_permissions. Never routes through apply_servers (cardinal rule).
- _make_backup() generalised to the file's own suffix (JSON naming unchanged),
so a .toml sidecar and a .json config keep separate backup pools.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reached via a new "Edit config…" button beside the sidecar advisory,
shown whenever the selected server has a resolvable sidecar (even before
the file exists, so it can be created from BCC).
SidecarEditorDialog — a minimal, reviewable first pass:
- A section picker (from core.toml_sections), defaulting to [server].
- Pick-lists for the schema enum fields (auth/approvalMode/role) and a
range-bounded spinner for port — a layperson can't type auth="sshkey".
- A raw-TOML editor showing the full file: the always-available fallback
for anything the form doesn't model.
- Save applies ONLY the fields the user changed, surgically on top of the
raw text (core.update_toml), so comments/unknown keys/other sections
round-trip; validates the changed managed values; writes through
core.write_sidecar (atomic + backup + chmod 0600). Never touches
apply_servers. On success it re-runs the read-only advisories (the same
path #101's watcher uses) so "args inert"/permission advisories update
live, and confirms the backup + 0600.
All decision logic is in bcc_core (unit-tested); this is thin wiring,
smoke-tested headlessly (QT_QPA_PLATFORM=offscreen): dialog build,
section detection, changed-field diff, surgical save with comment
preserved, 0600 applied, and Edit-button visibility (shown for ssh-mcp,
hidden for plain stdio + remote).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
the_og
merged commit d4ce2647ae into main2026-08-13 00:55:21 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #102. Base:
feat/101(stacked — merge #101 first). Folds in the epic's old #7 "schema pick-lists" item.Edit an external sidecar config (ssh-mcp's
config.toml) from inside BCC instead of dropping to a terminal.Dependency decision — no new runtime dep (justified)
The prompt floated
tomli+tomli-wor a hand-roll withtomllib/tomlifor reads. Neither survives the real CI constraint: the test job installs onlypytest cryptography(notrequirements.txt), the catalog-signature job importsbcc_corewith only cryptography, and the workflow is off-limits. So a top-level TOML import is impossible, and any pip TOML dep would leave this new core untested or red on CI (option (b)'stomllibread path would also be untested on the 3.10 runner, which has notomllib). Instead: stdlib-only, uniform across 3.10/3.12/3.13 — a lenient reader + a surgical line-editing writer that rewrites only the one key it's asked to, so comments, formatting, ordering and unknown keys/tables round-trip untouched (strictly safer than parse→dict→reserialize;tomli-wwouldn't comment-preserve either). Runtime deps stayPySide6+cryptography.Core (pure, fully unit-tested)
read_toml_section·toml_sections·set_toml_value/update_toml(surgical, CRLF-safe, escaping, create-section, delete-on-None) ·validate_sidecar_values(enum/port on managed fields only; unknown keys pass through — reconciles "reject out-of-enum" with "preserve unknown keys") ·write_sidecar(atomic + rotating backup + chmod 0600/0700 via #93; neverapply_servers)._make_backup/write_configrefactored to share_atomic_write_text;_make_backupnow keys on the file suffix (JSON naming unchanged).GUI (thin wiring; smoke-tested headlessly)
SidecarEditorDialog: section picker, enum pick-lists + range-bounded port spinner, raw-TOML fallback. Save applies only changed managed fields on top of the raw text, validates, writes, then re-runs advisories through #101's path so they update live.GUI click-through (needs a human)
npx -y ssh-mcp --host=h --user=u), select it → an "Edit config…" button appears by the sidecar advisory.(top level). Pickrole = admin(or type[server]+ fields in raw), Save.ls -lthe file shows-rw-------; the "args inert" advisory now shows (file exists) without a restart (that's #101).portvia the spinner andauthvia the pick-list, add a# commentin raw text, Save → re-open and confirm the comment survived and values changed (surgical round-trip).🤖 Generated with Claude Code