CS-10630: Reimplement boxel realm remove command#4655
Conversation
Soft-removes a realm URL from the active profile's `app.boxel.realms` Matrix account_data list. Server-side files are untouched. - New `removeRealm()` programmatic API in commands/realm/remove.ts (returns a result object on every code path; never prompts, never calls process.exit). - CLI wrapper with TTY confirmation, `-y/--yes` to skip, and `--dry-run` to preview the change. - `removeRealmFromMatrixAccountData()` helper in lib/auth.ts and `ProfileManager.removeFromUserRealms()` for reuse by other commands. - Integration tests against a real test realm server cover: happy path, not-in-list, dry-run, trailing-slash normalization, soft- remove (server files preserved), and no-active-profile. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 845a5fc7fc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!existingRealms.includes(realmUrl)) { | ||
| return false; | ||
| } | ||
| let next = existingRealms.filter((url) => url !== realmUrl); |
There was a problem hiding this comment.
Normalize stored realm URLs before matching/removing
removeRealm() normalizes URLs with ensureTrailingSlash, but removeRealmFromMatrixAccountData() compares and filters against raw existingRealms values. If account data contains a legacy entry like https://host/realm (no trailing slash), the preview path in realm/remove.ts treats it as present while this function returns false and performs no PUT, so boxel realm remove fails with “Removal did not complete.” for valid realms. Normalize existingRealms during membership/removal (or remove both slash variants) so runtime behavior matches the command’s normalization logic.
Useful? React with 👍 / 👎.
|
so this seems very subtle--its doesn't actually remove the realm, it just unlinks the realm from the user's matrix data. if the user tries to make a new realm with the same name they will get an error that this realm already exists--which is probably confusing since they thought they removed it. is it really the intent that we are just "unlinking" the realm here instead of removing it? if so should we name it something that makes that more clear? |
There was a problem hiding this comment.
Pull request overview
This PR ports the standalone CLI “remove realm” behavior into the monorepo as boxel realm remove <realm-url>, implementing a soft remove that updates the active profile’s app.boxel.realms Matrix account_data list (without deleting server-side realm files).
Changes:
- Added a new
boxel realm removecommand with--dry-runand--yessupport plus a programmaticremoveRealm()API. - Introduced
ProfileManager.removeFromUserRealms()andremoveRealmFromMatrixAccountData()to mirror existing “add realm” helpers. - Added an integration test suite covering removal, dry-run behavior, normalization, and “soft remove” semantics.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/boxel-cli/tests/integration/realm-remove.test.ts | New integration coverage for removing realms from the user list and verifying server realms remain accessible. |
| packages/boxel-cli/src/lib/profile-manager.ts | Adds removeFromUserRealms() wrapper to update Matrix account_data via the auth helper. |
| packages/boxel-cli/src/lib/auth.ts | Adds removeRealmFromMatrixAccountData() to remove a realm URL from app.boxel.realms. |
| packages/boxel-cli/src/commands/realm/remove.ts | New command implementation + programmatic removeRealm() API with confirmation/dry-run flows. |
| packages/boxel-cli/src/commands/realm/index.ts | Registers the new remove subcommand under boxel realm. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let existingRealms = await getUserRealmsFromMatrixAccountData(matrixAuth); | ||
|
|
||
| if (!existingRealms.includes(realmUrl)) { | ||
| return false; | ||
| } | ||
| let next = existingRealms.filter((url) => url !== realmUrl); |
| let normalized = existing.map(ensureTrailingSlash); | ||
| let previousCount = normalized.length; | ||
|
|
||
| if (!normalized.includes(realmUrl)) { | ||
| return { | ||
| realmUrl, | ||
| removed: false, | ||
| previousCount, | ||
| nextCount: previousCount, | ||
| notInList: true, | ||
| error: 'Realm is not in app.boxel.realms. Nothing to remove.', | ||
| }; | ||
| } | ||
|
|
||
| let nextCount = previousCount - 1; | ||
|
|
||
| if (options.dryRun) { | ||
| return { realmUrl, removed: false, previousCount, nextCount }; | ||
| } | ||
|
|
||
| try { | ||
| let removed = await pm.removeFromUserRealms(realmUrl); | ||
| return { realmUrl, removed, previousCount, nextCount }; | ||
| } catch (err) { |
Summary
boxel removefrom the standalonecardstack/boxel-cliintopackages/boxel-cli/src/commands/realm/remove.tsasboxel realm remove <realm-url>.app.boxel.realmsMatrix account_data list. Server-side realm files are not touched.ProfileManager.removeFromUserRealms()+removeRealmFromMatrixAccountData()helper, mirroring the existingadd*pair.Safety
-y, --yes.--yeserrors out instead of writing silently.--dry-runpreviewspreviousCount -> nextCountwithout sending a Matrix PUT.removeRealm()returns a structured result on every path (neverprocess.exits), so other commands can call it programmatically.Linear
CS-10630 — part of Incorporate Boxel CLI to Monorepo.
Test plan
pnpm --filter @cardstack/boxel-cli lint:typespnpm --filter @cardstack/boxel-cli lint:jspnpm --filter @cardstack/boxel-cli exec vitest run tests/integration/realm-remove.test.ts(requires Synapse + realm server running locally)boxel realm remove <url>prompts y/N, writes ony, cancels onN.boxel realm remove <url> --yesskips the prompt.boxel realm remove <url> < /dev/nullerrors out (non-TTY without--yes).boxel realm remove <url> --dry-runprints the count delta and exits without writing.boxel realm list --all-accessiblestill shows the realm (server files preserved).🤖 Generated with Claude Code