From 1b94ea1f1336ee383235a8bb1d381400ba9d16fa Mon Sep 17 00:00:00 2001 From: Craig Davison <5341072+davisonio@users.noreply.github.com> Date: Sun, 9 Aug 2026 17:11:01 +0100 Subject: [PATCH] fix(server): strip positions from file manager targets --- .../src/process/externalLauncher.test.ts | 47 +++++++++++++++++++ apps/server/src/process/externalLauncher.ts | 18 ++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/apps/server/src/process/externalLauncher.test.ts b/apps/server/src/process/externalLauncher.test.ts index 1ab6166e92a1..0fa195451cbc 100644 --- a/apps/server/src/process/externalLauncher.test.ts +++ b/apps/server/src/process/externalLauncher.test.ts @@ -94,6 +94,53 @@ it.effect("launches the default browser through the platform command", () => { ); }); +it.effect("strips file manager positions without changing existing paths", () => + Effect.gen(function* () { + const fileSystem = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const binDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-file-manager-" }); + const commandPath = path.join(binDir, "open"); + const targetPath = path.join(binDir, "CLAUDE.md"); + const existingColonPath = `${targetPath}:2024`; + const missingColonPath = path.join(binDir, "missing:2024"); + yield* fileSystem.writeFileString(commandPath, "#!/bin/sh\n"); + yield* fileSystem.chmod(commandPath, 0o755); + yield* fileSystem.writeFileString(targetPath, ""); + yield* fileSystem.writeFileString(existingColonPath, ""); + + const spawned: ChildProcess.StandardCommand[] = []; + yield* Effect.gen(function* () { + const launcher = yield* ExternalLauncher.ExternalLauncher; + for (const cwd of [targetPath, `${targetPath}:4`, `${targetPath}:12:4`]) { + yield* launcher.launchEditor({ editor: "file-manager", cwd }); + } + yield* launcher.launchEditor({ editor: "file-manager", cwd: existingColonPath }); + yield* launcher.launchEditor({ editor: "file-manager", cwd: missingColonPath }); + }).pipe( + Effect.provide( + testLayer({ + platform: "darwin", + env: { PATH: binDir }, + onSpawn: (command) => { + spawned.push(command); + }, + }), + ), + ); + + assert.deepEqual( + spawned.map(({ command, args }) => ({ command, args })), + [ + { command: "open", args: [targetPath] }, + { command: "open", args: [targetPath] }, + { command: "open", args: [targetPath] }, + { command: "open", args: [existingColonPath] }, + { command: "open", args: [missingColonPath] }, + ], + ); + }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), +); + it.effect("launches an installed editor with platform-safe arguments", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; diff --git a/apps/server/src/process/externalLauncher.ts b/apps/server/src/process/externalLauncher.ts index 8ec928f26fc3..814bb84839ce 100644 --- a/apps/server/src/process/externalLauncher.ts +++ b/apps/server/src/process/externalLauncher.ts @@ -124,6 +124,19 @@ function parseTargetPathAndPosition(target: string): Option.Option { + const parsedTarget = parseTargetPathAndPosition(target); + if (Option.isNone(parsedTarget)) return target; + + const fileSystem = yield* FileSystem.FileSystem; + if (yield* fileSystem.exists(target).pipe(Effect.orElseSucceed(() => false))) return target; + return (yield* fileSystem.exists(parsedTarget.value.path).pipe(Effect.orElseSucceed(() => false))) + ? parsedTarget.value.path + : target; +}); + function resolveCommandEditorArgs( editor: (typeof EDITORS)[number], target: string, @@ -376,11 +389,12 @@ const resolveEditorLaunch = Effect.fn("resolveEditorLaunch")(function* ( return yield* new ExternalLauncherUnsupportedEditorError({ editor: input.editor }); } + const target = yield* resolveFileManagerTarget(input.cwd); return { editor: editorDef.id, - target: input.cwd, + target, command: fileManagerCommandForPlatform(platform), - args: [input.cwd], + args: [target], }; });