MCPcopy
hub / github.com/coder/mux / ProjectService

Class ProjectService

src/node/services/projectService.ts:370–1434  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

368}
369
370export class ProjectService {
371 private readonly fileCompletionsCache = new Map<string, FileCompletionsCacheEntry>();
372 private directoryPicker?: (initialPath?: string | null) => Promise<string | null>;
373 private readonly sshPromptService: SshPromptService | undefined;
374 private workspaceService?: WorkspaceRemover;
375
376 constructor(
377 private readonly config: Config,
378 sshPromptService?: SshPromptService
379 ) {
380 this.sshPromptService = sshPromptService;
381 }
382
383 setWorkspaceService(workspaceService: WorkspaceRemover): void {
384 this.workspaceService = workspaceService;
385 }
386
387 setDirectoryPicker(picker: (initialPath?: string | null) => Promise<string | null>) {
388 this.directoryPicker = picker;
389 }
390
391 async pickDirectory(initialPath?: string | null): Promise<string | null> {
392 if (!this.directoryPicker) return null;
393 return this.directoryPicker(initialPath ?? null);
394 }
395
396 async create(
397 projectPath: string
398 ): Promise<Result<{ projectConfig: ProjectConfig; normalizedPath: string }>> {
399 try {
400 // Validate input
401 if (!projectPath || projectPath.trim().length === 0) {
402 return Err("Project path cannot be empty");
403 }
404
405 // Resolve the path:
406 // - Bare names like "my-project" → ~/.mux/projects/my-project
407 // - Paths with ~ → expand to home directory
408 // - Absolute/relative paths → resolve normally
409 const isBareProjectName =
410 projectPath.length > 0 &&
411 !projectPath.includes("/") &&
412 !projectPath.includes("\\") &&
413 !projectPath.startsWith("~");
414
415 const config = this.config.loadConfigOrDefault();
416 let normalizedPath: string;
417 if (isBareProjectName) {
418 // Bare project name - put in default projects directory
419 const parentDir = resolveProjectParentDir(undefined, config.defaultProjectDir);
420 normalizedPath = path.join(parentDir, projectPath);
421 } else if (
422 projectPath === "~" ||
423 projectPath.startsWith("~/") ||
424 projectPath.startsWith("~\\")
425 ) {
426 // Tilde expansion - uses expandTilde to respect MUX_ROOT for ~/.mux paths
427 normalizedPath = path.resolve(expandTilde(projectPath));

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected