mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-15 14:23:02 -07:00
fix(core,cli): properly resolve nested paths and plan dirs in exit_plan_mode
- **exit_plan_mode.ts**: Fixed an issue where `path.basename` incorrectly stripped nested directories (e.g., `tracks/123/index.md` to `index.md`) by adopting `resolveAndValidatePlanPath`. - **UI Components**: Updated `ExitPlanModeDialog.tsx` and `planCommand.ts` to use `config.getPlansDir()` instead of `config.storage.getPlansDir()`. This ensures the UI respects active extension plan directories (like Conductor) rather than falling back to the default temporary directory. - **write-file.ts**: Updated to use `config.getPlansDir()` for consistency.
This commit is contained in:
@@ -82,7 +82,7 @@ export const planCommand: SlashCommand = {
|
||||
try {
|
||||
const content = await processSingleFileContent(
|
||||
approvedPlanPath,
|
||||
config.storage.getPlansDir(),
|
||||
config.getPlansDir(),
|
||||
config.getFileSystemService(),
|
||||
);
|
||||
const fileName = path.basename(approvedPlanPath);
|
||||
|
||||
@@ -84,7 +84,7 @@ function usePlanContent(planPath: string, config: Config): PlanContentState {
|
||||
try {
|
||||
const pathError = await validatePlanPath(
|
||||
planPath,
|
||||
config.storage.getPlansDir(),
|
||||
config.getPlansDir(),
|
||||
);
|
||||
if (ignore) return;
|
||||
if (pathError) {
|
||||
@@ -101,7 +101,7 @@ function usePlanContent(planPath: string, config: Config): PlanContentState {
|
||||
|
||||
const result = await processSingleFileContent(
|
||||
planPath,
|
||||
config.storage.getPlansDir(),
|
||||
config.getPlansDir(),
|
||||
config.getFileSystemService(),
|
||||
);
|
||||
|
||||
|
||||
@@ -63,20 +63,20 @@ export class ExitPlanModeTool extends BaseDeclarativeTool<
|
||||
if (!params.plan_filename || params.plan_filename.trim() === '') {
|
||||
return 'plan_filename is required.';
|
||||
}
|
||||
try {
|
||||
resolveAndValidatePlanPath(
|
||||
params.plan_filename,
|
||||
this.config.getPlansDir(),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message.startsWith('Security violation')) {
|
||||
return `Access denied: plan path (${path.join(
|
||||
this.config.getPlansDir(),
|
||||
params.plan_filename,
|
||||
)}) must be within the designated plans directory (${this.config.getPlansDir()}).`;
|
||||
}
|
||||
return e instanceof Error ? e.message : String(e);
|
||||
}
|
||||
try {
|
||||
resolveAndValidatePlanPath(
|
||||
params.plan_filename,
|
||||
this.config.getPlansDir(),
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.message.startsWith('Security violation')) {
|
||||
return `Access denied: plan path (${path.join(
|
||||
this.config.getPlansDir(),
|
||||
params.plan_filename,
|
||||
)}) must be within the designated plans directory (${this.config.getPlansDir()}).`;
|
||||
}
|
||||
return e instanceof Error ? e.message : String(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -184,8 +184,10 @@ export class ExitPlanModeInvocation extends BaseToolInvocation<
|
||||
* Note: Validation is done in validateToolParamValues, so this assumes the path is valid.
|
||||
*/
|
||||
private getResolvedPlanPath(): string {
|
||||
const safeFilename = path.basename(this.params.plan_filename);
|
||||
return path.join(this.config.getPlansDir(), safeFilename);
|
||||
return resolveAndValidatePlanPath(
|
||||
this.params.plan_filename,
|
||||
this.config.getPlansDir(),
|
||||
);
|
||||
}
|
||||
|
||||
async execute({ abortSignal: _signal }: ExecuteOptions): Promise<ToolResult> {
|
||||
|
||||
@@ -504,7 +504,7 @@ export class WriteFileTool
|
||||
try {
|
||||
resolvedPath = resolveAndValidatePlanPath(
|
||||
filePath,
|
||||
this.config.storage.getPlansDir(),
|
||||
this.config.getPlansDir(),
|
||||
);
|
||||
} catch (err) {
|
||||
return err instanceof Error ? err.message : String(err);
|
||||
|
||||
Reference in New Issue
Block a user