(args, context)
| 586 | }, |
| 587 | |
| 588 | async execute(args, context) { |
| 589 | const invokingAgent = (context as { agent?: string }).agent; |
| 590 | if (shouldRejectSubmitPlanForAgent(invokingAgent, workflowOptions)) { |
| 591 | return `Plannotator is configured for plan-agent mode. submit_plan can only be called by: ${workflowOptions.planningAgents.join(", ")}. |
| 592 | |
| 593 | Use /plannotator-last or /plannotator-annotate for manual review, or set workflow to all-agents to allow broader submit_plan access.`; |
| 594 | } |
| 595 | |
| 596 | if (!args.edits || args.edits.length === 0) { |
| 597 | return "Error: No edits provided. Pass at least one edit with start and content."; |
| 598 | } |
| 599 | |
| 600 | // Read existing backing file (empty on first call) |
| 601 | const project = sanitizeTag(path.basename(ctx.directory)) || "_unknown"; |
| 602 | const backingPath = getPlanBackingPath(project); |
| 603 | const backingDir = path.dirname(backingPath); |
| 604 | mkdirSync(backingDir, { recursive: true }); |
| 605 | |
| 606 | let existingContent = ""; |
| 607 | if (existsSync(backingPath)) { |
| 608 | existingContent = readFileSync(backingPath, "utf-8"); |
| 609 | } |
| 610 | |
| 611 | // Validate and apply edits |
| 612 | const existingLines = existingContent ? existingContent.split("\n") : []; |
| 613 | |
| 614 | const validationError = validateEdits(existingLines, args.edits); |
| 615 | if (validationError) { |
| 616 | return `Error: ${validationError}`; |
| 617 | } |
| 618 | |
| 619 | let resultLines: string[]; |
| 620 | try { |
| 621 | resultLines = applyEdits(existingLines, args.edits); |
| 622 | } catch (err) { |
| 623 | return `Error applying edits: ${err instanceof Error ? err.message : String(err)}`; |
| 624 | } |
| 625 | |
| 626 | const planContent = resultLines.join("\n"); |
| 627 | if (planContent.length > MAX_PLAN_SIZE) { |
| 628 | return `Error: Plan content exceeds the maximum size of ${MAX_PLAN_SIZE / (1024 * 1024)}MB.`; |
| 629 | } |
| 630 | if (!planContent.trim()) { |
| 631 | return "Error: Plan content is empty after applying edits."; |
| 632 | } |
| 633 | |
| 634 | // Write backing file |
| 635 | writeFileSync(backingPath, planContent, "utf-8"); |
| 636 | |
| 637 | const timeoutSeconds = getPlanTimeoutSeconds(); |
| 638 | let result: OpenCodePlanReviewResult; |
| 639 | try { |
| 640 | result = await runPlanReview({ |
| 641 | client: ctx.client, |
| 642 | runtime: workflowOptions.runtime, |
| 643 | planContent, |
| 644 | sharingEnabled: await getSharingEnabled(), |
| 645 | shareBaseUrl: getShareBaseUrl(), |
nothing calls this directly
no test coverage detected