( runtime: Runtime, workspaceName: string, projectName: string, workspaceId: string )
| 117 | * ~/.mux/plans/{projectName}/{workspaceName}.md |
| 118 | */ |
| 119 | export async function readPlanFile( |
| 120 | runtime: Runtime, |
| 121 | workspaceName: string, |
| 122 | projectName: string, |
| 123 | workspaceId: string |
| 124 | ): Promise<ReadPlanResult> { |
| 125 | const muxHome = runtime.getMuxHome(); |
| 126 | const planPath = getPlanFilePath(workspaceName, projectName, muxHome); |
| 127 | // Legacy paths only used for non-Docker runtimes |
| 128 | const legacyPath = getLegacyPlanFilePath(workspaceId); |
| 129 | |
| 130 | // Resolve tilde to absolute path for client use (editor deep links, etc.) |
| 131 | // For local runtimes this expands ~ to /home/user; for SSH it resolves remotely |
| 132 | const resolvedPath = await runtime.resolvePath(planPath); |
| 133 | |
| 134 | // Try new path first |
| 135 | try { |
| 136 | const content = await readFileString(runtime, planPath); |
| 137 | return { content, exists: true, path: resolvedPath }; |
| 138 | } catch { |
| 139 | // Fall back to legacy path |
| 140 | try { |
| 141 | const content = await readFileString(runtime, legacyPath); |
| 142 | // Migrate: move to new location. |
| 143 | // Resolve paths first because shellQuote() intentionally prevents ~ expansion. |
| 144 | try { |
| 145 | const planDir = planPath.substring(0, planPath.lastIndexOf("/")); |
| 146 | const resolvedPlanDir = await runtime.resolvePath(planDir); |
| 147 | const resolvedLegacyPath = await runtime.resolvePath(legacyPath); |
| 148 | await execBuffered( |
| 149 | runtime, |
| 150 | `mkdir -p ${shellQuote(resolvedPlanDir)} && mv ${shellQuote(resolvedLegacyPath)} ${shellQuote(resolvedPath)}`, |
| 151 | { |
| 152 | cwd: "/tmp", |
| 153 | timeout: 5, |
| 154 | } |
| 155 | ); |
| 156 | } catch { |
| 157 | // Migration failed, but we have the content |
| 158 | } |
| 159 | return { content, exists: true, path: resolvedPath }; |
| 160 | } catch { |
| 161 | // File doesn't exist at either location |
| 162 | return { content: "", exists: false, path: resolvedPath }; |
| 163 | } |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | /** |
| 168 | * Check if a non-empty plan file exists for this workspace. |
no test coverage detected