Add set_output tool (#205)

This commit is contained in:
Mateusz Burzyński
2026-01-29 22:49:00 +00:00
committed by pullfrog[bot]
parent 2daab6fc78
commit c1f8247077
9 changed files with 116 additions and 7 deletions
+4
View File
@@ -35,6 +35,10 @@ inputs:
required: false
default: ${{ github.token }}
outputs:
result:
description: "It's set when the prompt explicitly requests it. It can be used to capture an actionable output for the next step in the workflow."
runs:
using: "node24"
main: "entry"
+36 -5
View File
@@ -19763,7 +19763,7 @@ var require_core = __commonJS({
Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
}
exports.getBooleanInput = getBooleanInput;
function setOutput(name, value2) {
function setOutput2(name, value2) {
const filePath = process.env["GITHUB_OUTPUT"] || "";
if (filePath) {
return (0, file_command_1.issueFileCommand)("OUTPUT", (0, file_command_1.prepareKeyValueMessage)(name, value2));
@@ -19771,7 +19771,7 @@ Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
process.stdout.write(os2.EOL);
(0, command_1.issueCommand)("set-output", { name }, (0, utils_1.toCommandValue)(value2));
}
exports.setOutput = setOutput;
exports.setOutput = setOutput2;
function setCommandEcho(enabled) {
(0, command_1.issue)("echo", enabled ? "on" : "off");
}
@@ -142448,6 +142448,22 @@ function AddLabelsTool(ctx) {
});
}
// mcp/output.ts
var SetOutputParams = type({
value: type.string.describe("the output value to expose as a GitHub Action output")
});
function SetOutputTool(ctx) {
return tool({
name: "set_output",
description: "Set the action output for consumption by subsequent workflow steps. The value will be available as the 'result' output of the action.",
parameters: SetOutputParams,
execute: execute(async (params) => {
ctx.toolState.output = params.value;
return { success: true };
})
});
}
// mcp/pr.ts
var PullRequest = type({
title: type.string.describe("the title of the pull request"),
@@ -143218,7 +143234,8 @@ async function startMcpHttpServer(ctx) {
CreateBranchTool(ctx),
CommitFilesTool(ctx),
PushBranchTool(ctx),
UploadFileTool(ctx)
UploadFileTool(ctx),
SetOutputTool(ctx)
];
if (ctx.payload.bash === "restricted") {
tools.push(BashTool(ctx));
@@ -162114,6 +162131,12 @@ function getShellInstructions(bash) {
}
}
}
function getStandaloneModeInstructions(trigger) {
if (trigger !== "unknown") {
return "";
}
return `**Standalone mode**: You are running as a step in a user-defined CI workflow. When you complete your task, call \`${ghPullfrogMcpName}/set_output\` with the main result of your work (generated content, summary of changes, analysis results, etc.). This makes it available as a GitHub Action output named \`result\` for subsequent workflow steps to consume.`;
}
function resolveInstructions(ctx) {
const eventTitleBody = buildEventTitleBody(ctx.payload.event);
const eventMetadata = buildEventMetadata(ctx.payload.event);
@@ -162172,6 +162195,8 @@ Tool names may be formatted as \`(server name)/(tool name)\`, for example: \`${g
${getShellInstructions(ctx.payload.bash)}
${getStandaloneModeInstructions(ctx.payload.event.trigger)}
**Command execution**: Never use \`sleep\` to wait for commands to complete. Commands run synchronously - when the bash tool returns, the command has finished.
**Commenting style**: When posting comments via ${ghPullfrogMcpName}, write as a professional team member would. Your final comments should be polished and actionable\u2014do not include intermediate reasoning like "I'll now look at the code" or "Let me respond to the question."
@@ -162411,7 +162436,7 @@ function resolvePayload(repoSettings) {
}
// utils/run.ts
async function handleAgentResult(result) {
function handleAgentResult(result) {
if (!result.success) {
return {
success: false,
@@ -162762,7 +162787,10 @@ async function main() {
if (toolState.lastProgressBody) {
await writeSummary(toolState.lastProgressBody);
}
return handleAgentResult(result);
return {
...handleAgentResult(result),
result: toolState.output
};
} catch (_) {
var _error = _, _hasError = true;
} finally {
@@ -162796,6 +162824,9 @@ async function run() {
if (!result.success) {
throw new Error(result.error || "Agent execution failed");
}
if (result.result) {
core5.setOutput("result", result.result);
}
} catch (error50) {
const errorMessage = error50 instanceof Error ? error50.message : "Unknown error occurred";
core5.setFailed(`Action failed: ${errorMessage}`);
+4
View File
@@ -15,6 +15,10 @@ async function run(): Promise<void> {
if (!result.success) {
throw new Error(result.error || "Agent execution failed");
}
if (result.result) {
core.setOutput("result", result.result);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
core.setFailed(`Action failed: ${errorMessage}`);
+5 -1
View File
@@ -25,6 +25,7 @@ export interface MainResult {
success: boolean;
output?: string | undefined;
error?: string | undefined;
result?: string | undefined;
}
export async function main(): Promise<MainResult> {
@@ -153,7 +154,10 @@ export async function main(): Promise<MainResult> {
await writeSummary(toolState.lastProgressBody);
}
return handleAgentResult(result);
return {
...handleAgentResult(result),
result: toolState.output,
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
log.error(errorMessage);
+34
View File
@@ -154,11 +154,45 @@ await mcp.call("gh_pullfrog/reply_to_review_comment", {
});
```
### output tools
#### `set_output`
set the action output for consumption by subsequent workflow steps. useful when pullfrog is used as a step in a user-defined CI workflow (e.g., generating release notes).
**parameters:**
- `value` (string): the output value to expose
**returns:**
- `success`: true on success
the value will be available as the `result` output of the action, accessible via `${{ steps.<step-id>.outputs.result }}`.
**example:**
```typescript
// when generating content for downstream consumption
await mcp.call("gh_pullfrog/set_output", {
value: "## Release Notes\n\n- Added new feature X\n- Fixed bug Y"
});
```
**usage in workflow:**
```yaml
- uses: pullfrog/pullfrog@v1
id: notes
with:
prompt: "Generate release notes for v2.0.0"
- uses: softprops/action-gh-release@v1
with:
body: ${{ steps.notes.outputs.result }}
```
### other tools
see individual files for documentation on other tools:
- `comment.ts` - create, edit, and update comments
- `issue.ts` - create issues
- `output.ts` - set action output for workflow consumption
- `pr.ts` - create pull requests
- `prInfo.ts` - get pull request information
- `review.ts` - create pull request reviews
+20
View File
@@ -0,0 +1,20 @@
import { type } from "arktype";
import type { ToolContext } from "./server.ts";
import { execute, tool } from "./shared.ts";
export const SetOutputParams = type({
value: type.string.describe("the output value to expose as a GitHub Action output"),
});
export function SetOutputTool(ctx: ToolContext) {
return tool({
name: "set_output",
description:
"Set the action output for consumption by subsequent workflow steps. The value will be available as the 'result' output of the action.",
parameters: SetOutputParams,
execute: execute(async (params) => {
ctx.toolState.output = params.value;
return { success: true };
}),
});
}
+3
View File
@@ -32,6 +32,7 @@ export interface ToolState {
progressCommentId: number | null;
lastProgressBody?: string;
wasUpdated?: boolean;
output?: string;
}
import type { ResolveRunResult } from "../utils/workflow.ts";
@@ -85,6 +86,7 @@ import { GetIssueCommentsTool } from "./issueComments.ts";
import { GetIssueEventsTool } from "./issueEvents.ts";
import { IssueInfoTool } from "./issueInfo.ts";
import { AddLabelsTool } from "./labels.ts";
import { SetOutputTool } from "./output.ts";
import { CreatePullRequestTool } from "./pr.ts";
import { PullRequestInfoTool } from "./prInfo.ts";
import { CreatePullRequestReviewTool } from "./review.ts";
@@ -179,6 +181,7 @@ export async function startMcpHttpServer(
CommitFilesTool(ctx),
PushBranchTool(ctx),
UploadFileTool(ctx),
SetOutputTool(ctx),
];
// only add BashTool when bash is "restricted"
+9
View File
@@ -100,6 +100,13 @@ function getShellInstructions(bash: ResolvedPayload["bash"]): string {
}
}
function getStandaloneModeInstructions(trigger: string): string {
if (trigger !== "unknown") {
return "";
}
return `**Standalone mode**: You are running as a step in a user-defined CI workflow. When you complete your task, call \`${ghPullfrogMcpName}/set_output\` with the main result of your work (generated content, summary of changes, analysis results, etc.). This makes it available as a GitHub Action output named \`result\` for subsequent workflow steps to consume.`;
}
export interface ResolvedInstructions {
full: string;
system: string;
@@ -187,6 +194,8 @@ Tool names may be formatted as \`(server name)/(tool name)\`, for example: \`${g
${getShellInstructions(ctx.payload.bash)}
${getStandaloneModeInstructions(ctx.payload.event.trigger)}
**Command execution**: Never use \`sleep\` to wait for commands to complete. Commands run synchronously - when the bash tool returns, the command has finished.
**Commenting style**: When posting comments via ${ghPullfrogMcpName}, write as a professional team member would. Your final comments should be polished and actionable—do not include intermediate reasoning like "I'll now look at the code" or "Let me respond to the question."
+1 -1
View File
@@ -2,7 +2,7 @@ import type { AgentResult } from "../agents/shared.ts";
import type { MainResult } from "../main.ts";
import { log } from "./cli.ts";
export async function handleAgentResult(result: AgentResult): Promise<MainResult> {
export function handleAgentResult(result: AgentResult): MainResult {
if (!result.success) {
return {
success: false,