feat(desktop): edit project folders — move/redirect from UI - #61406
Closed
Clement-ccy wants to merge 4 commits into
Closed
feat(desktop): edit project folders — move/redirect from UI#61406Clement-ccy wants to merge 4 commits into
Clement-ccy wants to merge 4 commits into
Conversation
Adds the missing 'edit folders' half of the project folder lifecycle: the desktop previously supported add-folder and remove-folder but had no way to redirect a folder to a new absolute path after it was renamed or moved on disk, so users had to remove and re-add (losing is_primary / label / added_at and the project's primary_path pointer). Layers (all additive — no schema migration required): - hermes_cli/projects_db.move_folder: rewrite a project's folder path in place, preserving is_primary / label / added_at. Updates projects.primary_path when the moved folder was the primary, and rejects same-project collisions with a clear ValueError. - tui_gateway: projects.move_folder JSON-RPC method, registered alongside add_folder/remove_folder/set_primary. Bad args and unknown projects surface as the existing 5061/5062/5063 codes. - hermes_cli/projects_cmd: `hermes project move-folder <slug> <old> <new>` for CLI parity with add-folder/remove-folder. - apps/desktop: a new 'edit-folders' mode on the project dialog (stages several moves in one commit) + a kebab menu item + an optimistic moveProjectFolder store action that retargets primary_path in the cached / atoms and rolls back on failure. - i18n: 11 new strings per locale (en/ja/zh/zh-hant). Tests: 8 new projects_db tests + 3 new RPC tests + 3 new CLI tests covering primary-retarget, label preservation, non-primary move, idempotent same-path, collision, unknown old path, missing project, path normalization. Pre-existing 5 POSIX-only failures on Windows are unrelated (predate this PR). Refs the user-facing gap noted in NousResearch#53004 (folder→session flow has no post-hoc correction affordance) and the related wish in NousResearch#42525.
The edit-folders dialog introduced in the previous commit only supported moving a folder to a new path. After using it, the only way to drop a folder from a project was the legacy sidebar-rebuild flow: remove it from outside the app and let a project-tree refresh re-classify it. That left no place to remove a folder while keeping the project — and the backend + RPC + CLI for projects.remove_folder already existed but the desktop never wired it up. Wires the missing frontend half: - store: removeProjectFolder(id, path) — Apollo-style optimistic cache update (drop the row, clear primary_path when the removed folder was primary, null the tree node's path on the removed primary). The backend repoints primary to the next-oldest folder when the primary is removed; we don't try to predict it client-side and let the reconcile pass pick up the server's choice. - dialog: FolderEdit.removed flag + toggleRemoveFolderEdit handler. Each row gets a trash/keep toggle button (icon flips between `trash` and `discard`; row goes opacity-50 with a 'Will be removed' badge when toggled). When a row is removed, the pick + revert buttons hide so the user can't simultaneously move + remove the same folder. - submit() now runs removals first, then moves, sequentially — each RPC awaits the previous so the optimistic cache stays consistent. Save is disabled when every row is unchanged AND none are removed. Backend, RPC, and CLI for projects.remove_folder are unchanged — they already shipped with first-class projects (NousResearch#49037). The 3 new i18n strings ship across en/ja/zh/zh-hant. No backend test changes: existing tests/hermes_cli and tests/tui_gateway for remove_folder already cover the wire contract.
teknium1
reviewed
Jul 10, 2026
teknium1
left a comment
Contributor
There was a problem hiding this comment.
Thanks for covering the missing folder-redirect path across the DB, RPC, CLI, and desktop layers. The premise is still valid on current main: hermes_cli/projects_db.py:462-535 has add/remove but no in-place move, and apps/desktop/src/app/chat/sidebar/projects/project-menu.tsx:157-160 only exposes Add folder.
Problems
apps/desktop/src/i18n/ja.ts:1524duplicatesmenuEditFoldersin one object literal; the same duplicate is inapps/desktop/src/i18n/zh.ts:1724andapps/desktop/src/i18n/zh-hant.ts:1477. Remove the duplicate entries; TypeScript rejects duplicate object keys.apps/desktop/src/store/projects.ts:609directly calls the new RPC. Current stale-backend handling only establishes the generic project capability fromprojects.list/projects.tree(apps/desktop/src/store/projects.ts:237-244), while remote gateways are supported (website/docs/reference/cli-commands.md:1441). A backend predating onlyprojects.move_folderwill let the dialog open and then fail generically on Save. MatchcreateProject's missing-method handling atapps/desktop/src/store/projects.ts:458-460and add coverage.
Suggested changes
- Remove the three duplicate locale keys and run desktop typecheck.
- Convert a missing
projects.move_folderRPC into the existing stale-backend state/message, with a regression test.
Current main has no changes on these paths since the PR base 411d59976410bc6eabcd4d59b6f688de3e879f05, so salvage should otherwise be mechanical. This is an automated hermes-sweeper review.
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
Closes the missing half of the project folder lifecycle. The desktop already supports add folder and remove folder, but had no way to redirect an existing folder to a new absolute path after it was renamed or moved on disk — users had to remove and re-add, losing
is_primary/label/added_atand the project'sprimary_pathpointer in the process.This PR adds the edit/move capability end-to-end across every layer, plus a remove button in the edit dialog that closes the last UX gap (the desktop had no frontend wiring for
projects.remove_foldereven though the backend, RPC, and CLI all shipped it).What's new
hermes_cli/projects_db.pymove_folder(conn, project_id, old_path, new_path)— rewrites a folder's path in place, preservesis_primary/label/added_at, retargetsprojects.primary_pathwhen the moved folder was primary, rejects same-project collisions.tui_gateway/server.pyprojects.move_folderJSON-RPC method (registered alongsideadd_folder/remove_folder/set_primary). Bad-arg and unknown-project map to the existing 5063 / 5062 codes.hermes_cli/projects_cmd.pyhermes project move-folder <slug> <old> <new>CLI subcommand, dispatched via the existing_with_projectdecorator so collisions / unknown paths return rc=2.apps/desktop/src/store/projects.tsmoveProjectFolder+removeProjectFolderstore actions. Both Apollo-style optimistic: retarget the cached$projects/$projectTreeatoms in place, roll the snapshot back on failure.removeProjectFolderis brand-new — the desktop frontend had no path to drop a folder while keeping the project.apps/desktop/.../project-dialog.tsxedit-foldersmode: shows the project's existing folders, lets the user (a) pick a new path per row (move/redirect) or (b) toggle a trash button per row (remove). Staged moves + removals commit sequentially in one Save. Same-row move + remove is blocked at the UI level (the pick + revert buttons hide when a row is marked removed). Detects same-project collisions client-side and flags them in red.apps/desktop/.../projects/project-menu.tsx!project.isAutobranch).i18nThe dialog also disables Save when no edits AND no removals were staged, so an empty Save closes immediately without a no-op RPC.
Tests
tests/hermes_cli/test_projects_db.pycases — primary-retarget, label preservation, non-primary move, idempotent same-path, collision, missing old path, missing project, normalization.tests/tui_gateway/test_projects_rpc.pycases — RPC roundtrip with normalized path in response, structured 5063 on collision, structured 5062 on unknown project.tests/hermes_cli/test_projects_cli.pycases —move-folderCLI roundtrip, bad-arg rc=2 on unknown old path, bad-arg rc=2 on collision.apps/desktop/src/store/projects.test.tscase — missingprojects.move_foldermarks the backend stale, surfaces the existing friendly error, and rolls back the optimistic path update.The 14 new Python tests use
tmp_pathso they're cross-platform. The 5 pre-existing POSIX-only failures intest_projects_db.pyare unrelated to this PR and were failing on Windows before any of my changes —git stash && pytestreproduces them on a clean tree. No new tests forremoveProjectFolderbecause the wire contract (projects.remove_folder) was already covered by tests/hermes_cli and tests/tui_gateway from the original first-class-projects PR (#49037).Locally on Windows: baseline 5 failed / 29 passed → with this PR 5 failed / 43 passed (14 new passing tests, 0 new failures).
Commits
feat(desktop): edit project folders — move/redirect from UI— the move/redirect half.feat(desktop): add remove folder to edit-folders dialog— the remove toggle, store wiring, and combined Save flow that ships both move + remove in one submit.fix(desktop): remove duplicate project locale keys— removes duplicate object keys reported by review.fix(desktop): handle stale folder move backends— adds stale-backend translation, optimistic rollback coverage, and lint cleanup.User-visible behavior
Right-click an explicit project → Edit folders… → dialog lists each existing folder with its current path. Per row:
Hit Folders updated → staged removals run first, then staged moves, sequentially through the corresponding JSON-RPC methods. The sidebar tree retargets, primary_path updates where needed, and the dialog closes.
Related
add_folder/remove_folder/set_primarynaming; additive only (no schema migration, no removed API).Review follow-up
Addressed the automated review feedback in two atomic commits:
menuEditFolderskeys from ja / zh / zh-hant.projects.move_folderRPC into the existing stale-backend state/message and added rollback coverage.Verified with Desktop typecheck, targeted ESLint, and
projects.test.ts(14/14 passing).