Skip to content

feat(cases): create a new case in a custom folder - #535

Open
opticon454 wants to merge 2 commits into
Ark0N:masterfrom
opticon454:feat/case-custom-path
Open

opticon454 wants to merge 2 commits into
Ark0N:masterfrom
opticon454:feat/case-custom-path

Conversation

@opticon454

Copy link
Copy Markdown
Contributor

What

Create a new case in a folder of your choice. Add Case → Create New gets a 📁 Create in a custom folder option with a Browse… button: pick the parent folder, and the case folder is created inside it (preview line: Will create: <parent>/<name>), scaffolded like any other case (CLAUDE.md, src/, .claude/settings.local.json), and listed alongside the rest. Until now a new case always lived under ~/codeman-cases, and putting one elsewhere meant creating the folder by hand and using Link Existing.

POST /api/cases takes an optional path for the same thing (absolute, or starting with ~).

How it behaves

  • The case is registered as a linked case (linked-cases.json), so it lists, resolves and deletes like one: deleting unlinks and never removes files.
  • Judged before anything is written (src/web/case-path.ts, pure policy + fs checks, no side effects):
    • absolute, no .., none of the shell metacharacters a session working directory is rejected for (isValidWorkingDir), so a case this accepts is one a session can start in;
    • refused: system directories (/etc, /usr, /proc, ...), the home folder itself, Codeman's own data folder (any ~/.codeman*), credential/config trees (~/.ssh, ~/.aws, ~/.claude, ...), judged on the path as typed and its symlink-resolved form, so a link into /etc is no way around it;
    • the parent must exist (one folder is created, never a chain);
    • the folder must not exist or must be an empty directory. A folder with files is Link Existing's job (409, and the message says so), so this can never scaffold into someone's project; a symlink or plain file at the target is refused.
  • Admin only in multi-user mode (same bar as POST /api/cases/link: it writes outside the cases directory and into the shared, ownerless registry). The option is hidden for a non-admin there.
  • Atomic enough: a failure after the first write undoes what this call created: the whole folder if it created it, otherwise only the scaffold inside the empty folder you picked. The registry is re-read just before it is written, so a racing request can't clobber it.
  • Mutually exclusive with "Run in Docker" in the UI (a Docker case has its own workspace flow).
  • No path = exactly the old behaviour.

Tests

  • test/case-path.test.ts (39): the path policy on a real temp filesystem: expansion, every refusal above (including symlink-into-blocked, file-as-parent, non-empty folder, symlink target), names that merely contain a blocked word (/etcetera, ~/etc/app) are allowed, nothing is ever created.
  • test/routes/case-custom-path-routes.test.ts (17): real fs: scaffold + registry + listing at the custom path, ~ and empty-folder fill, nothing under codeman-cases, the 400/404/409 cases, duplicate name and already-a-case, name validation, rollback for both a new folder and a picked empty folder, no-path unchanged, multi-user 403 with nothing created / admin allowed.
  • test/case-custom-path.browser.test.ts (7, real Chromium + real server + real folders): field hidden until ticked, live preview, Docker exclusivity both ways, Browse opens the picker for directories only and fills the field, empty-field error, a real create (folder + scaffold + listed at that path + modal closes), the server's "Link Existing" reason shown with the folder untouched, state resets on reopen.
  • Full CI gate: typecheck, lint, format, public assets, catalogue and 8566 tests pass.

Merge order

Rebased onto 1.34.0 (the earlier config/test-suites.ts overlap with the merged PRs is resolved). Independent of my other open PRs apart from adjacent insertions in config/test-suites.ts and docs/api-reference.md, which will need a trivial rebase for whichever lands second. I will do that as they merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JrzFKEdBLwVfu6ev2ZscJS

opticon454 and others added 2 commits October 5, 2026 07:03
POST /api/cases takes an optional path; Add Case > Create New gets a 'Create in a
custom folder' option with Browse. The folder is created (or an empty one filled),
scaffolded like a normal case and registered as a linked case. System, home,
credential and Codeman folders are refused; a folder with files is Link Existing's
job; a failure after the first write undoes what this call created. Admin only in
multi-user mode, like Link Existing.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant