Add set_output tool (#205)
This commit is contained in:
committed by
pullfrog[bot]
parent
2daab6fc78
commit
c1f8247077
@@ -35,6 +35,10 @@ inputs:
|
|||||||
required: false
|
required: false
|
||||||
default: ${{ github.token }}
|
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:
|
runs:
|
||||||
using: "node24"
|
using: "node24"
|
||||||
main: "entry"
|
main: "entry"
|
||||||
|
|||||||
@@ -19763,7 +19763,7 @@ var require_core = __commonJS({
|
|||||||
Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
|
Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
|
||||||
}
|
}
|
||||||
exports.getBooleanInput = getBooleanInput;
|
exports.getBooleanInput = getBooleanInput;
|
||||||
function setOutput(name, value2) {
|
function setOutput2(name, value2) {
|
||||||
const filePath = process.env["GITHUB_OUTPUT"] || "";
|
const filePath = process.env["GITHUB_OUTPUT"] || "";
|
||||||
if (filePath) {
|
if (filePath) {
|
||||||
return (0, file_command_1.issueFileCommand)("OUTPUT", (0, file_command_1.prepareKeyValueMessage)(name, value2));
|
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);
|
process.stdout.write(os2.EOL);
|
||||||
(0, command_1.issueCommand)("set-output", { name }, (0, utils_1.toCommandValue)(value2));
|
(0, command_1.issueCommand)("set-output", { name }, (0, utils_1.toCommandValue)(value2));
|
||||||
}
|
}
|
||||||
exports.setOutput = setOutput;
|
exports.setOutput = setOutput2;
|
||||||
function setCommandEcho(enabled) {
|
function setCommandEcho(enabled) {
|
||||||
(0, command_1.issue)("echo", enabled ? "on" : "off");
|
(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
|
// mcp/pr.ts
|
||||||
var PullRequest = type({
|
var PullRequest = type({
|
||||||
title: type.string.describe("the title of the pull request"),
|
title: type.string.describe("the title of the pull request"),
|
||||||
@@ -143218,7 +143234,8 @@ async function startMcpHttpServer(ctx) {
|
|||||||
CreateBranchTool(ctx),
|
CreateBranchTool(ctx),
|
||||||
CommitFilesTool(ctx),
|
CommitFilesTool(ctx),
|
||||||
PushBranchTool(ctx),
|
PushBranchTool(ctx),
|
||||||
UploadFileTool(ctx)
|
UploadFileTool(ctx),
|
||||||
|
SetOutputTool(ctx)
|
||||||
];
|
];
|
||||||
if (ctx.payload.bash === "restricted") {
|
if (ctx.payload.bash === "restricted") {
|
||||||
tools.push(BashTool(ctx));
|
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) {
|
function resolveInstructions(ctx) {
|
||||||
const eventTitleBody = buildEventTitleBody(ctx.payload.event);
|
const eventTitleBody = buildEventTitleBody(ctx.payload.event);
|
||||||
const eventMetadata = buildEventMetadata(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)}
|
${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.
|
**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."
|
**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
|
// utils/run.ts
|
||||||
async function handleAgentResult(result) {
|
function handleAgentResult(result) {
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
@@ -162762,7 +162787,10 @@ async function main() {
|
|||||||
if (toolState.lastProgressBody) {
|
if (toolState.lastProgressBody) {
|
||||||
await writeSummary(toolState.lastProgressBody);
|
await writeSummary(toolState.lastProgressBody);
|
||||||
}
|
}
|
||||||
return handleAgentResult(result);
|
return {
|
||||||
|
...handleAgentResult(result),
|
||||||
|
result: toolState.output
|
||||||
|
};
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
var _error = _, _hasError = true;
|
var _error = _, _hasError = true;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -162796,6 +162824,9 @@ async function run() {
|
|||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
throw new Error(result.error || "Agent execution failed");
|
throw new Error(result.error || "Agent execution failed");
|
||||||
}
|
}
|
||||||
|
if (result.result) {
|
||||||
|
core5.setOutput("result", result.result);
|
||||||
|
}
|
||||||
} catch (error50) {
|
} catch (error50) {
|
||||||
const errorMessage = error50 instanceof Error ? error50.message : "Unknown error occurred";
|
const errorMessage = error50 instanceof Error ? error50.message : "Unknown error occurred";
|
||||||
core5.setFailed(`Action failed: ${errorMessage}`);
|
core5.setFailed(`Action failed: ${errorMessage}`);
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ async function run(): Promise<void> {
|
|||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
throw new Error(result.error || "Agent execution failed");
|
throw new Error(result.error || "Agent execution failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result.result) {
|
||||||
|
core.setOutput("result", result.result);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
||||||
core.setFailed(`Action failed: ${errorMessage}`);
|
core.setFailed(`Action failed: ${errorMessage}`);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export interface MainResult {
|
|||||||
success: boolean;
|
success: boolean;
|
||||||
output?: string | undefined;
|
output?: string | undefined;
|
||||||
error?: string | undefined;
|
error?: string | undefined;
|
||||||
|
result?: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function main(): Promise<MainResult> {
|
export async function main(): Promise<MainResult> {
|
||||||
@@ -153,7 +154,10 @@ export async function main(): Promise<MainResult> {
|
|||||||
await writeSummary(toolState.lastProgressBody);
|
await writeSummary(toolState.lastProgressBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleAgentResult(result);
|
return {
|
||||||
|
...handleAgentResult(result),
|
||||||
|
result: toolState.output,
|
||||||
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
|
|||||||
@@ -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
|
### other tools
|
||||||
|
|
||||||
see individual files for documentation on other tools:
|
see individual files for documentation on other tools:
|
||||||
- `comment.ts` - create, edit, and update comments
|
- `comment.ts` - create, edit, and update comments
|
||||||
- `issue.ts` - create issues
|
- `issue.ts` - create issues
|
||||||
|
- `output.ts` - set action output for workflow consumption
|
||||||
- `pr.ts` - create pull requests
|
- `pr.ts` - create pull requests
|
||||||
- `prInfo.ts` - get pull request information
|
- `prInfo.ts` - get pull request information
|
||||||
- `review.ts` - create pull request reviews
|
- `review.ts` - create pull request reviews
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ export interface ToolState {
|
|||||||
progressCommentId: number | null;
|
progressCommentId: number | null;
|
||||||
lastProgressBody?: string;
|
lastProgressBody?: string;
|
||||||
wasUpdated?: boolean;
|
wasUpdated?: boolean;
|
||||||
|
output?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
import type { ResolveRunResult } from "../utils/workflow.ts";
|
import type { ResolveRunResult } from "../utils/workflow.ts";
|
||||||
@@ -85,6 +86,7 @@ import { GetIssueCommentsTool } from "./issueComments.ts";
|
|||||||
import { GetIssueEventsTool } from "./issueEvents.ts";
|
import { GetIssueEventsTool } from "./issueEvents.ts";
|
||||||
import { IssueInfoTool } from "./issueInfo.ts";
|
import { IssueInfoTool } from "./issueInfo.ts";
|
||||||
import { AddLabelsTool } from "./labels.ts";
|
import { AddLabelsTool } from "./labels.ts";
|
||||||
|
import { SetOutputTool } from "./output.ts";
|
||||||
import { CreatePullRequestTool } from "./pr.ts";
|
import { CreatePullRequestTool } from "./pr.ts";
|
||||||
import { PullRequestInfoTool } from "./prInfo.ts";
|
import { PullRequestInfoTool } from "./prInfo.ts";
|
||||||
import { CreatePullRequestReviewTool } from "./review.ts";
|
import { CreatePullRequestReviewTool } from "./review.ts";
|
||||||
@@ -179,6 +181,7 @@ export async function startMcpHttpServer(
|
|||||||
CommitFilesTool(ctx),
|
CommitFilesTool(ctx),
|
||||||
PushBranchTool(ctx),
|
PushBranchTool(ctx),
|
||||||
UploadFileTool(ctx),
|
UploadFileTool(ctx),
|
||||||
|
SetOutputTool(ctx),
|
||||||
];
|
];
|
||||||
|
|
||||||
// only add BashTool when bash is "restricted"
|
// only add BashTool when bash is "restricted"
|
||||||
|
|||||||
@@ -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 {
|
export interface ResolvedInstructions {
|
||||||
full: string;
|
full: string;
|
||||||
system: string;
|
system: string;
|
||||||
@@ -187,6 +194,8 @@ Tool names may be formatted as \`(server name)/(tool name)\`, for example: \`${g
|
|||||||
|
|
||||||
${getShellInstructions(ctx.payload.bash)}
|
${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.
|
**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."
|
**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
@@ -2,7 +2,7 @@ import type { AgentResult } from "../agents/shared.ts";
|
|||||||
import type { MainResult } from "../main.ts";
|
import type { MainResult } from "../main.ts";
|
||||||
import { log } from "./cli.ts";
|
import { log } from "./cli.ts";
|
||||||
|
|
||||||
export async function handleAgentResult(result: AgentResult): Promise<MainResult> {
|
export function handleAgentResult(result: AgentResult): MainResult {
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user