diff --git a/docs/api-reference.md b/docs/api-reference.md index 38441dfd..0ee9aeb0 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -905,10 +905,10 @@ html and svg are the ones deliberately kept out. `POST /api/filesystem/mkdir` with `{ path, name, sessionId?, showHidden? }` makes one folder directly inside `path`, which must be a folder the path picker may browse, and answers `{ path }`. `name` is one path segment (letters, numbers, spaces and `_ - . ~`, not -starting with a dot or `~`). `400` for a cases folder (use Add Case → Create New with no -custom folder) or a `path` that is not a directory, `403` for a blocked tree or a `path` -outside the browse roots, `404` for a `path` that does not exist, `409` when the folder -already exists, and `422` when the mount under `path` does not answer. +starting with a dot or `~`). `400` for a cases folder (use Add Case → Create New or Clone +Repo with no custom folder) or a `path` that is not a directory, `403` for a blocked tree +or a `path` outside the browse roots, `404` for a `path` that does not exist, `409` when +the folder already exists, and `422` when the mount under `path` does not answer. ## Rendered HTML files diff --git a/docs/wiki/Working-With-Files.md b/docs/wiki/Working-With-Files.md index 02fcd0bb..85b438a7 100644 --- a/docs/wiki/Working-With-Files.md +++ b/docs/wiki/Working-With-Files.md @@ -192,7 +192,7 @@ In the **Link Existing**, **Create New** (custom folder) and **Clone Repo** (cus pickers, **+📁** (New folder) makes a folder inside the folder you are viewing and selects it. Names use letters, numbers, spaces and `_ - . ~` (not starting with a dot or `~`). The cases folder is refused, since every folder there is already a case: pick a folder outside -it, or make the case with **Add Case → Create New** (no custom folder). +it, or make the case with **Add Case → Create New** or **Clone Repo** (no custom folder). This is a separate file-serving surface from the viewer, with its own rules: it allowlists your home directory, the cases directory, and anything in `CODEMAN_FILE_PICKER_ROOTS`, and diff --git a/src/web/routes/file-routes.ts b/src/web/routes/file-routes.ts index bcc420d6..5024b94b 100644 --- a/src/web/routes/file-routes.ts +++ b/src/web/routes/file-routes.ts @@ -1567,8 +1567,9 @@ export function registerFileRoutes(app: FastifyInstance, ctx: SessionPort & Even // The cases directories (shared, and the caller's own in multi-user mode) are what GET /api/cases lists as // local cases: a folder made there is a case with no scaffold, which Link then refuses by name and which // deleting would take the linked folder with. A case there is made through Add Case > Create New with no - // custom folder. The refusal has to read right from both pickers that offer New folder: a Create New user - // is already on that tab, with the custom folder ticked. + // custom folder. The refusal has to read right from every picker that offers New folder: a Create New or + // Clone Repo user is already on that tab, with the custom folder ticked, and this is where Browse opens + // while the field is empty. const casesDirs = await Promise.all( [...new Set([CASES_DIR, resolveCasesDir(getAuthUser(req))])].map((dir) => fs.realpath(dir).catch(() => resolve(dir)) @@ -1578,7 +1579,7 @@ export function registerFileRoutes(app: FastifyInstance, ctx: SessionPort & Even reply.code(400); return createErrorResponse( ApiErrorCode.INVALID_INPUT, - 'This is a cases folder: every folder in it is a case. Pick a folder outside it, or make the case with Add Case > Create New (no custom folder).' + 'This is a cases folder: every folder in it is a case. Pick a folder outside it, or make the case with Add Case > Create New or Clone Repo (no custom folder).' ); } // Like browse, a file is not a place to create in. diff --git a/test/routes/filesystem-mkdir-routes.test.ts b/test/routes/filesystem-mkdir-routes.test.ts index d16146d0..050221c2 100644 --- a/test/routes/filesystem-mkdir-routes.test.ts +++ b/test/routes/filesystem-mkdir-routes.test.ts @@ -52,7 +52,8 @@ describe('POST /api/filesystem/mkdir', () => { const res = await mkdir({ path: casesDir, name: 'myproj' }); expect(res.statusCode).toBe(400); expect(res.json().error).toContain('Pick a folder outside it'); - expect(res.json().error).toContain('Add Case > Create New (no custom folder)'); + // Every picker offering New folder sees this, and Clone Repo opens here by default. + expect(res.json().error).toContain('Add Case > Create New or Clone Repo (no custom folder)'); expect(existsSync(join(casesDir, 'myproj'))).toBe(false); // A folder inside a case is fine. mkdirSync(join(casesDir, 'real-case'));