Commit e3d2504c3acd
dots/pi/agent/extensions/github/actions/issue.ts
@@ -18,6 +18,7 @@ import {
addSubIssue,
removeSubIssue,
approvalGate,
+ approvalGateWithBodyPreview,
buildModifyResult,
buildRejectResult,
} from "../utils";
@@ -389,7 +390,13 @@ export async function handleIssueComment(
// APPROVAL GATE
if (ctx.hasUI) {
const confirmMessage = buildCommentConfirmation("Issue", params.number, params.body);
- const approval = await approvalGate(ctx, `Comment on issue #${params.number}?`, confirmMessage);
+ const approval = await approvalGateWithBodyPreview(
+ ctx,
+ `Comment on issue #${params.number}?`,
+ confirmMessage,
+ `Issue #${params.number} Comment Preview (${params.body.length} chars):`,
+ params.body,
+ );
if (approval.outcome === "modify") {
ctx.ui.notify("Comment paused for modifications", "info");
return buildModifyResult("comment", { action: "issue-comment", issueNumber: params.number });
@@ -460,7 +467,15 @@ export async function handleIssueEdit(
// APPROVAL GATE
if (ctx.hasUI) {
const confirmMessage = `Issue: #${params.number}\n\nChanges:\n${changes.join("\n")}\n\nThis will modify the issue.`;
- const approval = await approvalGate(ctx, `Edit issue #${params.number}?`, confirmMessage);
+ const approval = params.body
+ ? await approvalGateWithBodyPreview(
+ ctx,
+ `Edit issue #${params.number}?`,
+ confirmMessage,
+ `Issue #${params.number} New Body Preview (${params.body.length} chars):`,
+ params.body,
+ )
+ : await approvalGate(ctx, `Edit issue #${params.number}?`, confirmMessage);
if (approval.outcome === "modify") {
ctx.ui.notify("Edit paused for modifications", "info");
return buildModifyResult("issue edit", { action: "issue-edit", issueNumber: params.number });
dots/pi/agent/extensions/github/actions/pr.ts
@@ -28,6 +28,7 @@ import {
resolveGitCwd,
findPRTemplate,
approvalGate,
+ approvalGateWithBodyPreview,
buildModifyResult,
buildRejectResult,
} from "../utils";
@@ -523,7 +524,15 @@ export async function handlePRReview(
// APPROVAL GATE
if (ctx.hasUI) {
const confirmMessage = buildReviewConfirmation(params);
- const approval = await approvalGate(ctx, `Submit review on PR #${params.number}?`, confirmMessage);
+ const approval = params.body
+ ? await approvalGateWithBodyPreview(
+ ctx,
+ `Submit review on PR #${params.number}?`,
+ confirmMessage,
+ `PR #${params.number} Review Body Preview (${params.body.length} chars):`,
+ params.body,
+ )
+ : await approvalGate(ctx, `Submit review on PR #${params.number}?`, confirmMessage);
if (approval.outcome === "modify") {
ctx.ui.notify("Review paused for modifications", "info");
return buildModifyResult("review", { action: "pr-review", prNumber: params.number });
@@ -602,7 +611,13 @@ export async function handlePRComment(
// APPROVAL GATE
if (ctx.hasUI) {
const confirmMessage = buildCommentConfirmation("PR", params.number, params.body);
- const approval = await approvalGate(ctx, `Comment on PR #${params.number}?`, confirmMessage);
+ const approval = await approvalGateWithBodyPreview(
+ ctx,
+ `Comment on PR #${params.number}?`,
+ confirmMessage,
+ `PR #${params.number} Comment Preview (${params.body.length} chars):`,
+ params.body,
+ );
if (approval.outcome === "modify") {
ctx.ui.notify("Comment paused for modifications", "info");
return buildModifyResult("comment", { action: "pr-comment", prNumber: params.number });
@@ -816,7 +831,14 @@ export async function handlePRLineComment(
// APPROVAL GATE
if (ctx.hasUI) {
const confirmMessage = buildLineCommentConfirmation(params.number, params.path, params.line, params.body, params.startLine);
- const approval = await approvalGate(ctx, `Add inline comment on PR #${params.number}?`, confirmMessage);
+ const range = params.startLine ? `${params.path}:${params.startLine}-${params.line}` : `${params.path}:${params.line}`;
+ const approval = await approvalGateWithBodyPreview(
+ ctx,
+ `Add inline comment on PR #${params.number}?`,
+ confirmMessage,
+ `PR #${params.number} Inline Comment Preview at ${range} (${params.body.length} chars):`,
+ params.body,
+ );
if (approval.outcome === "modify") {
ctx.ui.notify("Inline comment paused for modifications", "info");
return buildModifyResult("inline comment", { action: "pr-line-comment", prNumber: params.number });
@@ -1127,7 +1149,13 @@ export async function handlePRReviewEdit(
// APPROVAL GATE
if (ctx.hasUI) {
const confirmMessage = buildReviewEditConfirmation(params.number, params.reviewId, params.body);
- const approval = await approvalGate(ctx, `Edit review ${params.reviewId} on PR #${params.number}?`, confirmMessage);
+ const approval = await approvalGateWithBodyPreview(
+ ctx,
+ `Edit review ${params.reviewId} on PR #${params.number}?`,
+ confirmMessage,
+ `PR #${params.number} Review ${params.reviewId} New Body Preview (${params.body.length} chars):`,
+ params.body,
+ );
if (approval.outcome === "modify") {
ctx.ui.notify("Review edit paused for modifications", "info");
return buildModifyResult("review edit", { action: "pr-review-edit", prNumber: params.number, reviewId: params.reviewId });
@@ -1266,7 +1294,13 @@ export async function handlePRReviewCommentEdit(
// APPROVAL GATE
if (ctx.hasUI) {
const confirmMessage = buildReviewCommentEditConfirmation(params.commentId, params.body);
- const approval = await approvalGate(ctx, `Edit review comment ${params.commentId}?`, confirmMessage);
+ const approval = await approvalGateWithBodyPreview(
+ ctx,
+ `Edit review comment ${params.commentId}?`,
+ confirmMessage,
+ `Review Comment ${params.commentId} New Body Preview (${params.body.length} chars):`,
+ params.body,
+ );
if (approval.outcome === "modify") {
ctx.ui.notify("Comment edit paused for modifications", "info");
return buildModifyResult("review comment edit", { action: "pr-review-comment-edit", commentId: params.commentId });
dots/pi/agent/extensions/github/utils.ts
@@ -562,6 +562,52 @@ export async function approvalGate(
return result;
}
+/**
+ * Approval gate with body preview for actions that carry long text content.
+ *
+ * When `bodyText` exceeds 200 characters a fourth option โ
+ * "๐ Preview body first" โ is offered before the standard
+ * Accept / Modify / Reject flow. Choosing it opens an editor pane so the
+ * user can read the full content, then re-presents the confirmation dialog.
+ *
+ * @param ctx Extension context
+ * @param title Confirmation dialog title
+ * @param description Confirmation dialog description (with truncated preview)
+ * @param previewTitle Label shown at the top of the editor preview pane
+ * @param bodyText Full body text; only previewed when length > 200
+ */
+export async function approvalGateWithBodyPreview(
+ ctx: ExtensionContext,
+ title: string,
+ description: string,
+ previewTitle: string,
+ bodyText: string,
+): Promise<ApprovalResult> {
+ if (bodyText.length <= 200) {
+ return approvalGate(ctx, title, description);
+ }
+
+ const prompt = description ? `${title}\n\n${description}` : title;
+ const choice = await ctx.ui.select(prompt, [
+ "โ Accept",
+ "๐ Preview body first",
+ "โ Modify",
+ "โ Reject",
+ ]);
+
+ if (choice === undefined || choice === "โ Reject") return { outcome: "rejected" };
+ if (choice === "โ Modify") return { outcome: "modify" };
+
+ if (choice === "๐ Preview body first") {
+ await ctx.ui.editor(`${previewTitle}\n\n---\n\n`, bodyText);
+ // Re-present the standard three-way dialog after the preview
+ return approvalGate(ctx, title, description);
+ }
+
+ // "โ Accept"
+ return { outcome: "accepted" };
+}
+
/**
* Build a tool result for when the user wants modifications.
* The message clearly tells the LLM to ask the user what they want changed.