apply_plan
Apply validated edit steps to a DOCX document in a single batch operation, ensuring all changes pass validation before implementation.
Instructions
Validate and apply a batch of edit steps (replace_text, insert_paragraph) to a document in one call. Validates all steps first; applies only if all pass. Accepts inline steps or a plan_file_path. Compatible with merge_plans output.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| file_path | Yes | Path to the DOCX file. | |
| steps | No | JSON array of edit steps. Each step needs step_id, operation, and operation-specific fields. | |
| plan_file_path | No | Path to a .json file containing an array of edit steps. Mutually exclusive with steps. |
Implementation Reference
- The main entry point for the `apply_plan` tool. It handles loading, normalizing, validating, and executing a sequence of steps (replace_text or insert_paragraph) on a DOCX file.
export async function applyPlan( manager: SessionManager, params: { file_path?: string; steps?: unknown[]; plan_file_path?: string; }, ): Promise<ToolResponse> { try { // Validate mutual exclusivity of steps and plan_file_path if (params.steps && params.plan_file_path) { return err( 'INVALID_PARAMS', 'Cannot provide both steps and plan_file_path. Use one or the other.', ); } if (!params.steps && !params.plan_file_path) { return err( 'INVALID_PARAMS', 'Must provide either steps (JSON array) or plan_file_path.', ); } // Load steps let rawSteps: unknown[]; if (params.plan_file_path) { const loaded = await loadStepsFromFile(params.plan_file_path); if (loaded.error) return loaded.error; rawSteps = loaded.steps; } else { rawSteps = params.steps!; } // Normalize steps const { steps, errors: normErrors } = normalizeSteps(rawSteps); if (normErrors.length > 0) { return err( 'NORMALIZATION_ERROR', `Step normalization failed with ${normErrors.length} error(s): ${normErrors.join('; ')}`, ); } if (steps.length === 0) { return err('EMPTY_PLAN', 'Plan contains no valid steps.'); } // Resolve session const resolved = await resolveSessionForTool(manager, params, { toolName: 'apply_plan' }); if (!resolved.ok) return resolved.response; const { session } = resolved; // Validation phase — check ALL steps before applying const validations = validateSteps(steps, session.doc); const overallValid = validations.every((v) => v.valid); if (!overallValid) { return { success: false, error: { code: 'VALIDATION_FAILED', message: `Plan validation failed: ${validations.filter((v) => !v.valid).length} of ${steps.length} step(s) have errors.`, hint: 'Fix the reported errors and resubmit.', }, overall_valid: false, steps: validations, }; } // Collect warnings const allWarnings = validations.flatMap((v) => v.warnings.map((w) => ({ step_id: v.step_id, warning: w }))); // Apply phase — execute steps sequentially const result = await executeSteps(manager, manager.normalizePath(session.originalPath), steps); if (result.failed_step_id !== undefined) { return { success: false, error: { code: 'APPLY_PARTIAL_FAILURE', message: `Plan execution stopped at step '${result.failed_step_id}' (index ${result.failed_step_index}).`, hint: 'Completed steps have already been applied. Reapply to original DOCX if rollback is needed.', }, file_path: manager.normalizePath(session.originalPath), completed_count: result.completed_step_ids.length, completed_step_ids: result.completed_step_ids, failed_step_id: result.failed_step_id, failed_step_index: result.failed_step_index, failed_step_error: result.failed_step_error, step_results: result.step_results, ...(allWarnings.length > 0 ? { warnings: allWarnings } : {}), }; } return ok({ file_path: manager.normalizePath(session.originalPath), edit_count: session.editCount, completed_count: result.completed_step_ids.length, completed_step_ids: result.completed_step_ids, step_results: result.step_results, ...(allWarnings.length > 0 ? { warnings: allWarnings } : {}), }); } catch (e: unknown) { return err('APPLY_PLAN_ERROR', `Failed to apply plan: ${errorMessage(e)}`); } }