mirror of
https://github.com/Z3Prover/z3
synced 2025-10-01 05:29:28 +00:00
update improvers
This commit is contained in:
parent
7268136bb6
commit
2364ea42ba
12 changed files with 295 additions and 228 deletions
83
.github/workflows/ask.lock.yml
generated
vendored
83
.github/workflows/ask.lock.yml
generated
vendored
|
@ -2,7 +2,7 @@
|
|||
# To update this file, edit the corresponding .md file and run:
|
||||
# gh aw compile
|
||||
#
|
||||
# Effective stop-time: 2025-09-19 10:32:53
|
||||
# Effective stop-time: 2025-09-19 12:19:14
|
||||
|
||||
name: "Question Answering Researcher"
|
||||
on:
|
||||
|
@ -594,7 +594,7 @@ jobs:
|
|||
main();
|
||||
- name: Setup Safe Outputs Collector MCP
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true}}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true}}"
|
||||
run: |
|
||||
mkdir -p /tmp/safe-outputs
|
||||
cat > /tmp/safe-outputs/mcp-server.cjs << 'EOF'
|
||||
|
@ -747,7 +747,7 @@ jobs:
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "add-issue-comment",
|
||||
name: "add-comment",
|
||||
description: "Add a comment to a GitHub issue or pull request",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
|
@ -854,7 +854,7 @@ jobs:
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "add-issue-labels",
|
||||
name: "add-labels",
|
||||
description: "Add labels to a GitHub issue or pull request",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
|
@ -1028,7 +1028,7 @@ jobs:
|
|||
- name: Setup MCPs
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true}}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true}}"
|
||||
run: |
|
||||
mkdir -p /tmp/mcp-config
|
||||
cat > /tmp/mcp-config/mcp-servers.json << 'EOF'
|
||||
|
@ -1066,7 +1066,7 @@ jobs:
|
|||
WORKFLOW_NAME="Question Answering Researcher"
|
||||
|
||||
# Check stop-time limit
|
||||
STOP_TIME="2025-09-19 10:32:53"
|
||||
STOP_TIME="2025-09-19 12:19:14"
|
||||
echo "Checking stop-time limit: $STOP_TIME"
|
||||
|
||||
# Convert stop time to epoch seconds
|
||||
|
@ -1157,7 +1157,7 @@ jobs:
|
|||
|
||||
**Adding a Comment to an Issue or Pull Request**
|
||||
|
||||
To add a comment to an issue or pull request, use the add-issue-comments tool from the safe-outputs MCP
|
||||
To add a comment to an issue or pull request, use the add-comments tool from the safe-outputs MCP
|
||||
|
||||
EOF
|
||||
- name: Print prompt to step summary
|
||||
|
@ -1327,7 +1327,7 @@ jobs:
|
|||
uses: actions/github-script@v8
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true}}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true}}"
|
||||
with:
|
||||
script: |
|
||||
async function main() {
|
||||
|
@ -1360,15 +1360,12 @@ jobs:
|
|||
let sanitized = content;
|
||||
// Neutralize @mentions to prevent unintended notifications
|
||||
sanitized = neutralizeMentions(sanitized);
|
||||
// Remove XML comments to prevent content hiding
|
||||
sanitized = removeXmlComments(sanitized);
|
||||
// Remove ANSI escape sequences BEFORE removing control characters
|
||||
sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
|
||||
// Remove control characters (except newlines and tabs)
|
||||
sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
|
||||
// XML character escaping
|
||||
sanitized = sanitized
|
||||
.replace(/&/g, "&") // Must be first to avoid double-escaping
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
// URI filtering - replace non-https protocols with "(redacted)"
|
||||
sanitized = sanitizeUrlProtocols(sanitized);
|
||||
// Domain filtering for HTTPS URIs
|
||||
|
@ -1388,8 +1385,7 @@ jobs:
|
|||
lines.slice(0, maxLines).join("\n") +
|
||||
"\n[Content truncated due to line count]";
|
||||
}
|
||||
// Remove ANSI escape sequences
|
||||
sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
|
||||
// ANSI escape sequences already removed earlier in the function
|
||||
// Neutralize common bot trigger phrases
|
||||
sanitized = neutralizeBotTriggers(sanitized);
|
||||
// Trim excessive whitespace
|
||||
|
@ -1401,10 +1397,12 @@ jobs:
|
|||
*/
|
||||
function sanitizeUrlDomains(s) {
|
||||
return s.replace(
|
||||
/\bhttps:\/\/([^\/\s\])}'"<>&\x00-\x1f]+)/gi,
|
||||
(match, domain) => {
|
||||
/\bhttps:\/\/[^\s\])}'"<>&\x00-\x1f,;]+/gi,
|
||||
(match) => {
|
||||
// Extract just the URL part after https://
|
||||
const urlAfterProtocol = match.slice(8); // Remove 'https://'
|
||||
// Extract the hostname part (before first slash, colon, or other delimiter)
|
||||
const hostname = domain.split(/[\/:\?#]/)[0].toLowerCase();
|
||||
const hostname = urlAfterProtocol.split(/[\/:\?#]/)[0].toLowerCase();
|
||||
// Check if this domain or any parent domain is in the allowlist
|
||||
const isAllowed = allowedDomains.some(allowedDomain => {
|
||||
const normalizedAllowed = allowedDomain.toLowerCase();
|
||||
|
@ -1423,9 +1421,10 @@ jobs:
|
|||
* @returns {string} The string with non-https protocols redacted
|
||||
*/
|
||||
function sanitizeUrlProtocols(s) {
|
||||
// Match both protocol:// and protocol: patterns
|
||||
// Match protocol:// patterns (URLs) and standalone protocol: patterns that look like URLs
|
||||
// Avoid matching command line flags like -v:10 or z3 -memory:high
|
||||
return s.replace(
|
||||
/\b(\w+):(?:\/\/)?[^\s\])}'"<>&\x00-\x1f]+/gi,
|
||||
/\b(\w+):\/\/[^\s\])}'"<>&\x00-\x1f]+/gi,
|
||||
(match, protocol) => {
|
||||
// Allow https (case insensitive), redact everything else
|
||||
return protocol.toLowerCase() === "https" ? match : "(redacted)";
|
||||
|
@ -1444,6 +1443,16 @@ jobs:
|
|||
(_m, p1, p2) => `${p1}\`@${p2}\``
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Removes XML comments to prevent content hiding
|
||||
* @param {string} s - The string to process
|
||||
* @returns {string} The string with XML comments removed
|
||||
*/
|
||||
function removeXmlComments(s) {
|
||||
// Remove XML/HTML comments including malformed ones that might be used to hide content
|
||||
// Matches: <!-- ... --> and <!--- ... --> and <!--- ... --!> variations
|
||||
return s.replace(/<!--[\s\S]*?-->/g, "").replace(/<!--[\s\S]*?--!>/g, "");
|
||||
}
|
||||
/**
|
||||
* Neutralizes bot trigger phrases by wrapping them in backticks
|
||||
* @param {string} s - The string to process
|
||||
|
@ -1477,13 +1486,13 @@ jobs:
|
|||
switch (itemType) {
|
||||
case "create-issue":
|
||||
return 1; // Only one issue allowed
|
||||
case "add-issue-comment":
|
||||
case "add-comment":
|
||||
return 1; // Only one comment allowed
|
||||
case "create-pull-request":
|
||||
return 1; // Only one pull request allowed
|
||||
case "create-pull-request-review-comment":
|
||||
return 10; // Default to 10 review comments allowed
|
||||
case "add-issue-labels":
|
||||
case "add-labels":
|
||||
return 5; // Only one labels operation allowed
|
||||
case "update-issue":
|
||||
return 1; // Only one issue update allowed
|
||||
|
@ -1698,10 +1707,10 @@ jobs:
|
|||
);
|
||||
}
|
||||
break;
|
||||
case "add-issue-comment":
|
||||
case "add-comment":
|
||||
if (!item.body || typeof item.body !== "string") {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-comment requires a 'body' string field`
|
||||
`Line ${i + 1}: add-comment requires a 'body' string field`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -1736,10 +1745,10 @@ jobs:
|
|||
);
|
||||
}
|
||||
break;
|
||||
case "add-issue-labels":
|
||||
case "add-labels":
|
||||
if (!item.labels || !Array.isArray(item.labels)) {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-labels requires a 'labels' array field`
|
||||
`Line ${i + 1}: add-labels requires a 'labels' array field`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -1749,7 +1758,7 @@ jobs:
|
|||
)
|
||||
) {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-labels labels array must contain only strings`
|
||||
`Line ${i + 1}: add-labels labels array must contain only strings`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -2671,11 +2680,11 @@ jobs:
|
|||
pull-requests: write
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
comment_id: ${{ steps.create_comment.outputs.comment_id }}
|
||||
comment_url: ${{ steps.create_comment.outputs.comment_url }}
|
||||
comment_id: ${{ steps.add_comment.outputs.comment_id }}
|
||||
comment_url: ${{ steps.add_comment.outputs.comment_url }}
|
||||
steps:
|
||||
- name: Add Issue Comment
|
||||
id: create_comment
|
||||
id: add_comment
|
||||
uses: actions/github-script@v8
|
||||
env:
|
||||
GITHUB_AW_AGENT_OUTPUT: ${{ needs.question-answering-researcher.outputs.output }}
|
||||
|
@ -2709,15 +2718,15 @@ jobs:
|
|||
core.info("No valid items found in agent output");
|
||||
return;
|
||||
}
|
||||
// Find all add-issue-comment items
|
||||
// Find all add-comment items
|
||||
const commentItems = validatedOutput.items.filter(
|
||||
/** @param {any} item */ item => item.type === "add-issue-comment"
|
||||
/** @param {any} item */ item => item.type === "add-comment"
|
||||
);
|
||||
if (commentItems.length === 0) {
|
||||
core.info("No add-issue-comment items found in agent output");
|
||||
core.info("No add-comment items found in agent output");
|
||||
return;
|
||||
}
|
||||
core.info(`Found ${commentItems.length} add-issue-comment item(s)`);
|
||||
core.info(`Found ${commentItems.length} add-comment item(s)`);
|
||||
// If in staged mode, emit step summary instead of creating comments
|
||||
if (isStaged) {
|
||||
let summaryContent = "## 🎭 Staged Mode: Add Comments Preview\n\n";
|
||||
|
@ -2761,7 +2770,7 @@ jobs:
|
|||
for (let i = 0; i < commentItems.length; i++) {
|
||||
const commentItem = commentItems[i];
|
||||
core.info(
|
||||
`Processing add-issue-comment item ${i + 1}/${commentItems.length}: bodyLength=${commentItem.body.length}`
|
||||
`Processing add-comment item ${i + 1}/${commentItems.length}: bodyLength=${commentItem.body.length}`
|
||||
);
|
||||
// Determine the issue/PR number and comment endpoint for this comment
|
||||
let issueNumber;
|
||||
|
|
2
.github/workflows/ask.md
vendored
2
.github/workflows/ask.md
vendored
|
@ -11,7 +11,7 @@ permissions: read-all
|
|||
network: defaults
|
||||
|
||||
safe-outputs:
|
||||
add-issue-comment:
|
||||
add-comment:
|
||||
|
||||
tools:
|
||||
web-fetch:
|
||||
|
|
83
.github/workflows/ci-doctor.lock.yml
generated
vendored
83
.github/workflows/ci-doctor.lock.yml
generated
vendored
|
@ -2,7 +2,7 @@
|
|||
# To update this file, edit the corresponding .md file and run:
|
||||
# gh aw compile
|
||||
#
|
||||
# Effective stop-time: 2025-09-19 10:32:53
|
||||
# Effective stop-time: 2025-09-19 12:19:14
|
||||
|
||||
name: "CI Failure Doctor"
|
||||
"on":
|
||||
|
@ -75,7 +75,7 @@ jobs:
|
|||
main();
|
||||
- name: Setup Safe Outputs Collector MCP
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true},\"create-issue\":true}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true},\"create-issue\":true}"
|
||||
run: |
|
||||
mkdir -p /tmp/safe-outputs
|
||||
cat > /tmp/safe-outputs/mcp-server.cjs << 'EOF'
|
||||
|
@ -228,7 +228,7 @@ jobs:
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "add-issue-comment",
|
||||
name: "add-comment",
|
||||
description: "Add a comment to a GitHub issue or pull request",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
|
@ -335,7 +335,7 @@ jobs:
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "add-issue-labels",
|
||||
name: "add-labels",
|
||||
description: "Add labels to a GitHub issue or pull request",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
|
@ -509,7 +509,7 @@ jobs:
|
|||
- name: Setup MCPs
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true},\"create-issue\":true}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true},\"create-issue\":true}"
|
||||
run: |
|
||||
mkdir -p /tmp/mcp-config
|
||||
cat > /tmp/mcp-config/mcp-servers.json << 'EOF'
|
||||
|
@ -547,7 +547,7 @@ jobs:
|
|||
WORKFLOW_NAME="CI Failure Doctor"
|
||||
|
||||
# Check stop-time limit
|
||||
STOP_TIME="2025-09-19 10:32:53"
|
||||
STOP_TIME="2025-09-19 12:19:14"
|
||||
echo "Checking stop-time limit: $STOP_TIME"
|
||||
|
||||
# Convert stop time to epoch seconds
|
||||
|
@ -770,7 +770,7 @@ jobs:
|
|||
|
||||
**Adding a Comment to an Issue or Pull Request**
|
||||
|
||||
To add a comment to an issue or pull request, use the add-issue-comments tool from the safe-outputs MCP
|
||||
To add a comment to an issue or pull request, use the add-comments tool from the safe-outputs MCP
|
||||
|
||||
**Creating an Issue**
|
||||
|
||||
|
@ -941,7 +941,7 @@ jobs:
|
|||
uses: actions/github-script@v8
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true},\"create-issue\":true}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true},\"create-issue\":true}"
|
||||
with:
|
||||
script: |
|
||||
async function main() {
|
||||
|
@ -974,15 +974,12 @@ jobs:
|
|||
let sanitized = content;
|
||||
// Neutralize @mentions to prevent unintended notifications
|
||||
sanitized = neutralizeMentions(sanitized);
|
||||
// Remove XML comments to prevent content hiding
|
||||
sanitized = removeXmlComments(sanitized);
|
||||
// Remove ANSI escape sequences BEFORE removing control characters
|
||||
sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
|
||||
// Remove control characters (except newlines and tabs)
|
||||
sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
|
||||
// XML character escaping
|
||||
sanitized = sanitized
|
||||
.replace(/&/g, "&") // Must be first to avoid double-escaping
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
// URI filtering - replace non-https protocols with "(redacted)"
|
||||
sanitized = sanitizeUrlProtocols(sanitized);
|
||||
// Domain filtering for HTTPS URIs
|
||||
|
@ -1002,8 +999,7 @@ jobs:
|
|||
lines.slice(0, maxLines).join("\n") +
|
||||
"\n[Content truncated due to line count]";
|
||||
}
|
||||
// Remove ANSI escape sequences
|
||||
sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
|
||||
// ANSI escape sequences already removed earlier in the function
|
||||
// Neutralize common bot trigger phrases
|
||||
sanitized = neutralizeBotTriggers(sanitized);
|
||||
// Trim excessive whitespace
|
||||
|
@ -1015,10 +1011,12 @@ jobs:
|
|||
*/
|
||||
function sanitizeUrlDomains(s) {
|
||||
return s.replace(
|
||||
/\bhttps:\/\/([^\/\s\])}'"<>&\x00-\x1f]+)/gi,
|
||||
(match, domain) => {
|
||||
/\bhttps:\/\/[^\s\])}'"<>&\x00-\x1f,;]+/gi,
|
||||
(match) => {
|
||||
// Extract just the URL part after https://
|
||||
const urlAfterProtocol = match.slice(8); // Remove 'https://'
|
||||
// Extract the hostname part (before first slash, colon, or other delimiter)
|
||||
const hostname = domain.split(/[\/:\?#]/)[0].toLowerCase();
|
||||
const hostname = urlAfterProtocol.split(/[\/:\?#]/)[0].toLowerCase();
|
||||
// Check if this domain or any parent domain is in the allowlist
|
||||
const isAllowed = allowedDomains.some(allowedDomain => {
|
||||
const normalizedAllowed = allowedDomain.toLowerCase();
|
||||
|
@ -1037,9 +1035,10 @@ jobs:
|
|||
* @returns {string} The string with non-https protocols redacted
|
||||
*/
|
||||
function sanitizeUrlProtocols(s) {
|
||||
// Match both protocol:// and protocol: patterns
|
||||
// Match protocol:// patterns (URLs) and standalone protocol: patterns that look like URLs
|
||||
// Avoid matching command line flags like -v:10 or z3 -memory:high
|
||||
return s.replace(
|
||||
/\b(\w+):(?:\/\/)?[^\s\])}'"<>&\x00-\x1f]+/gi,
|
||||
/\b(\w+):\/\/[^\s\])}'"<>&\x00-\x1f]+/gi,
|
||||
(match, protocol) => {
|
||||
// Allow https (case insensitive), redact everything else
|
||||
return protocol.toLowerCase() === "https" ? match : "(redacted)";
|
||||
|
@ -1058,6 +1057,16 @@ jobs:
|
|||
(_m, p1, p2) => `${p1}\`@${p2}\``
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Removes XML comments to prevent content hiding
|
||||
* @param {string} s - The string to process
|
||||
* @returns {string} The string with XML comments removed
|
||||
*/
|
||||
function removeXmlComments(s) {
|
||||
// Remove XML/HTML comments including malformed ones that might be used to hide content
|
||||
// Matches: <!-- ... --> and <!--- ... --> and <!--- ... --!> variations
|
||||
return s.replace(/<!--[\s\S]*?-->/g, "").replace(/<!--[\s\S]*?--!>/g, "");
|
||||
}
|
||||
/**
|
||||
* Neutralizes bot trigger phrases by wrapping them in backticks
|
||||
* @param {string} s - The string to process
|
||||
|
@ -1091,13 +1100,13 @@ jobs:
|
|||
switch (itemType) {
|
||||
case "create-issue":
|
||||
return 1; // Only one issue allowed
|
||||
case "add-issue-comment":
|
||||
case "add-comment":
|
||||
return 1; // Only one comment allowed
|
||||
case "create-pull-request":
|
||||
return 1; // Only one pull request allowed
|
||||
case "create-pull-request-review-comment":
|
||||
return 10; // Default to 10 review comments allowed
|
||||
case "add-issue-labels":
|
||||
case "add-labels":
|
||||
return 5; // Only one labels operation allowed
|
||||
case "update-issue":
|
||||
return 1; // Only one issue update allowed
|
||||
|
@ -1312,10 +1321,10 @@ jobs:
|
|||
);
|
||||
}
|
||||
break;
|
||||
case "add-issue-comment":
|
||||
case "add-comment":
|
||||
if (!item.body || typeof item.body !== "string") {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-comment requires a 'body' string field`
|
||||
`Line ${i + 1}: add-comment requires a 'body' string field`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -1350,10 +1359,10 @@ jobs:
|
|||
);
|
||||
}
|
||||
break;
|
||||
case "add-issue-labels":
|
||||
case "add-labels":
|
||||
if (!item.labels || !Array.isArray(item.labels)) {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-labels requires a 'labels' array field`
|
||||
`Line ${i + 1}: add-labels requires a 'labels' array field`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -1363,7 +1372,7 @@ jobs:
|
|||
)
|
||||
) {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-labels labels array must contain only strings`
|
||||
`Line ${i + 1}: add-labels labels array must contain only strings`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -2480,11 +2489,11 @@ jobs:
|
|||
pull-requests: write
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
comment_id: ${{ steps.create_comment.outputs.comment_id }}
|
||||
comment_url: ${{ steps.create_comment.outputs.comment_url }}
|
||||
comment_id: ${{ steps.add_comment.outputs.comment_id }}
|
||||
comment_url: ${{ steps.add_comment.outputs.comment_url }}
|
||||
steps:
|
||||
- name: Add Issue Comment
|
||||
id: create_comment
|
||||
id: add_comment
|
||||
uses: actions/github-script@v8
|
||||
env:
|
||||
GITHUB_AW_AGENT_OUTPUT: ${{ needs.ci-failure-doctor.outputs.output }}
|
||||
|
@ -2518,15 +2527,15 @@ jobs:
|
|||
core.info("No valid items found in agent output");
|
||||
return;
|
||||
}
|
||||
// Find all add-issue-comment items
|
||||
// Find all add-comment items
|
||||
const commentItems = validatedOutput.items.filter(
|
||||
/** @param {any} item */ item => item.type === "add-issue-comment"
|
||||
/** @param {any} item */ item => item.type === "add-comment"
|
||||
);
|
||||
if (commentItems.length === 0) {
|
||||
core.info("No add-issue-comment items found in agent output");
|
||||
core.info("No add-comment items found in agent output");
|
||||
return;
|
||||
}
|
||||
core.info(`Found ${commentItems.length} add-issue-comment item(s)`);
|
||||
core.info(`Found ${commentItems.length} add-comment item(s)`);
|
||||
// If in staged mode, emit step summary instead of creating comments
|
||||
if (isStaged) {
|
||||
let summaryContent = "## 🎭 Staged Mode: Add Comments Preview\n\n";
|
||||
|
@ -2570,7 +2579,7 @@ jobs:
|
|||
for (let i = 0; i < commentItems.length; i++) {
|
||||
const commentItem = commentItems[i];
|
||||
core.info(
|
||||
`Processing add-issue-comment item ${i + 1}/${commentItems.length}: bodyLength=${commentItem.body.length}`
|
||||
`Processing add-comment item ${i + 1}/${commentItems.length}: bodyLength=${commentItem.body.length}`
|
||||
);
|
||||
// Determine the issue/PR number and comment endpoint for this comment
|
||||
let issueNumber;
|
||||
|
|
2
.github/workflows/ci-doctor.md
vendored
2
.github/workflows/ci-doctor.md
vendored
|
@ -18,7 +18,7 @@ network: defaults
|
|||
safe-outputs:
|
||||
create-issue:
|
||||
title-prefix: "${{ github.workflow }}"
|
||||
add-issue-comment:
|
||||
add-comment:
|
||||
|
||||
tools:
|
||||
web-fetch:
|
||||
|
|
86
.github/workflows/daily-backlog-burner.lock.yml
generated
vendored
86
.github/workflows/daily-backlog-burner.lock.yml
generated
vendored
|
@ -2,7 +2,7 @@
|
|||
# To update this file, edit the corresponding .md file and run:
|
||||
# gh aw compile
|
||||
#
|
||||
# Effective stop-time: 2025-09-19 10:32:53
|
||||
# Effective stop-time: 2025-09-19 12:19:14
|
||||
|
||||
name: "Daily Backlog Burner"
|
||||
"on":
|
||||
|
@ -55,7 +55,7 @@ jobs:
|
|||
main();
|
||||
- name: Setup Safe Outputs Collector MCP
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}"
|
||||
run: |
|
||||
mkdir -p /tmp/safe-outputs
|
||||
cat > /tmp/safe-outputs/mcp-server.cjs << 'EOF'
|
||||
|
@ -208,7 +208,7 @@ jobs:
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "add-issue-comment",
|
||||
name: "add-comment",
|
||||
description: "Add a comment to a GitHub issue or pull request",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
|
@ -315,7 +315,7 @@ jobs:
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "add-issue-labels",
|
||||
name: "add-labels",
|
||||
description: "Add labels to a GitHub issue or pull request",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
|
@ -489,7 +489,7 @@ jobs:
|
|||
- name: Setup MCPs
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}"
|
||||
run: |
|
||||
mkdir -p /tmp/mcp-config
|
||||
cat > /tmp/mcp-config/mcp-servers.json << 'EOF'
|
||||
|
@ -527,7 +527,7 @@ jobs:
|
|||
WORKFLOW_NAME="Daily Backlog Burner"
|
||||
|
||||
# Check stop-time limit
|
||||
STOP_TIME="2025-09-19 10:32:53"
|
||||
STOP_TIME="2025-09-19 12:19:14"
|
||||
echo "Checking stop-time limit: $STOP_TIME"
|
||||
|
||||
# Convert stop time to epoch seconds
|
||||
|
@ -668,7 +668,7 @@ jobs:
|
|||
|
||||
**Adding a Comment to an Issue or Pull Request**
|
||||
|
||||
To add a comment to an issue or pull request, use the add-issue-comments tool from the safe-outputs MCP
|
||||
To add a comment to an issue or pull request, use the add-comments tool from the safe-outputs MCP
|
||||
|
||||
**Creating an Issue**
|
||||
|
||||
|
@ -854,7 +854,7 @@ jobs:
|
|||
uses: actions/github-script@v8
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}"
|
||||
with:
|
||||
script: |
|
||||
async function main() {
|
||||
|
@ -887,15 +887,12 @@ jobs:
|
|||
let sanitized = content;
|
||||
// Neutralize @mentions to prevent unintended notifications
|
||||
sanitized = neutralizeMentions(sanitized);
|
||||
// Remove XML comments to prevent content hiding
|
||||
sanitized = removeXmlComments(sanitized);
|
||||
// Remove ANSI escape sequences BEFORE removing control characters
|
||||
sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
|
||||
// Remove control characters (except newlines and tabs)
|
||||
sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
|
||||
// XML character escaping
|
||||
sanitized = sanitized
|
||||
.replace(/&/g, "&") // Must be first to avoid double-escaping
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
// URI filtering - replace non-https protocols with "(redacted)"
|
||||
sanitized = sanitizeUrlProtocols(sanitized);
|
||||
// Domain filtering for HTTPS URIs
|
||||
|
@ -915,8 +912,7 @@ jobs:
|
|||
lines.slice(0, maxLines).join("\n") +
|
||||
"\n[Content truncated due to line count]";
|
||||
}
|
||||
// Remove ANSI escape sequences
|
||||
sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
|
||||
// ANSI escape sequences already removed earlier in the function
|
||||
// Neutralize common bot trigger phrases
|
||||
sanitized = neutralizeBotTriggers(sanitized);
|
||||
// Trim excessive whitespace
|
||||
|
@ -928,10 +924,12 @@ jobs:
|
|||
*/
|
||||
function sanitizeUrlDomains(s) {
|
||||
return s.replace(
|
||||
/\bhttps:\/\/([^\/\s\])}'"<>&\x00-\x1f]+)/gi,
|
||||
(match, domain) => {
|
||||
/\bhttps:\/\/[^\s\])}'"<>&\x00-\x1f,;]+/gi,
|
||||
(match) => {
|
||||
// Extract just the URL part after https://
|
||||
const urlAfterProtocol = match.slice(8); // Remove 'https://'
|
||||
// Extract the hostname part (before first slash, colon, or other delimiter)
|
||||
const hostname = domain.split(/[\/:\?#]/)[0].toLowerCase();
|
||||
const hostname = urlAfterProtocol.split(/[\/:\?#]/)[0].toLowerCase();
|
||||
// Check if this domain or any parent domain is in the allowlist
|
||||
const isAllowed = allowedDomains.some(allowedDomain => {
|
||||
const normalizedAllowed = allowedDomain.toLowerCase();
|
||||
|
@ -950,9 +948,10 @@ jobs:
|
|||
* @returns {string} The string with non-https protocols redacted
|
||||
*/
|
||||
function sanitizeUrlProtocols(s) {
|
||||
// Match both protocol:// and protocol: patterns
|
||||
// Match protocol:// patterns (URLs) and standalone protocol: patterns that look like URLs
|
||||
// Avoid matching command line flags like -v:10 or z3 -memory:high
|
||||
return s.replace(
|
||||
/\b(\w+):(?:\/\/)?[^\s\])}'"<>&\x00-\x1f]+/gi,
|
||||
/\b(\w+):\/\/[^\s\])}'"<>&\x00-\x1f]+/gi,
|
||||
(match, protocol) => {
|
||||
// Allow https (case insensitive), redact everything else
|
||||
return protocol.toLowerCase() === "https" ? match : "(redacted)";
|
||||
|
@ -971,6 +970,16 @@ jobs:
|
|||
(_m, p1, p2) => `${p1}\`@${p2}\``
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Removes XML comments to prevent content hiding
|
||||
* @param {string} s - The string to process
|
||||
* @returns {string} The string with XML comments removed
|
||||
*/
|
||||
function removeXmlComments(s) {
|
||||
// Remove XML/HTML comments including malformed ones that might be used to hide content
|
||||
// Matches: <!-- ... --> and <!--- ... --> and <!--- ... --!> variations
|
||||
return s.replace(/<!--[\s\S]*?-->/g, "").replace(/<!--[\s\S]*?--!>/g, "");
|
||||
}
|
||||
/**
|
||||
* Neutralizes bot trigger phrases by wrapping them in backticks
|
||||
* @param {string} s - The string to process
|
||||
|
@ -1004,13 +1013,13 @@ jobs:
|
|||
switch (itemType) {
|
||||
case "create-issue":
|
||||
return 1; // Only one issue allowed
|
||||
case "add-issue-comment":
|
||||
case "add-comment":
|
||||
return 1; // Only one comment allowed
|
||||
case "create-pull-request":
|
||||
return 1; // Only one pull request allowed
|
||||
case "create-pull-request-review-comment":
|
||||
return 10; // Default to 10 review comments allowed
|
||||
case "add-issue-labels":
|
||||
case "add-labels":
|
||||
return 5; // Only one labels operation allowed
|
||||
case "update-issue":
|
||||
return 1; // Only one issue update allowed
|
||||
|
@ -1225,10 +1234,10 @@ jobs:
|
|||
);
|
||||
}
|
||||
break;
|
||||
case "add-issue-comment":
|
||||
case "add-comment":
|
||||
if (!item.body || typeof item.body !== "string") {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-comment requires a 'body' string field`
|
||||
`Line ${i + 1}: add-comment requires a 'body' string field`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -1263,10 +1272,10 @@ jobs:
|
|||
);
|
||||
}
|
||||
break;
|
||||
case "add-issue-labels":
|
||||
case "add-labels":
|
||||
if (!item.labels || !Array.isArray(item.labels)) {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-labels requires a 'labels' array field`
|
||||
`Line ${i + 1}: add-labels requires a 'labels' array field`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -1276,7 +1285,7 @@ jobs:
|
|||
)
|
||||
) {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-labels labels array must contain only strings`
|
||||
`Line ${i + 1}: add-labels labels array must contain only strings`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -2406,6 +2415,7 @@ jobs:
|
|||
GITHUB_AW_AGENT_OUTPUT: ${{ needs.daily-backlog-burner.outputs.output }}
|
||||
GITHUB_AW_ISSUE_TITLE_PREFIX: "${{ github.workflow }}"
|
||||
with:
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
script: |
|
||||
async function main() {
|
||||
// Check if we're in staged mode
|
||||
|
@ -2595,16 +2605,17 @@ jobs:
|
|||
pull-requests: write
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
comment_id: ${{ steps.create_comment.outputs.comment_id }}
|
||||
comment_url: ${{ steps.create_comment.outputs.comment_url }}
|
||||
comment_id: ${{ steps.add_comment.outputs.comment_id }}
|
||||
comment_url: ${{ steps.add_comment.outputs.comment_url }}
|
||||
steps:
|
||||
- name: Add Issue Comment
|
||||
id: create_comment
|
||||
id: add_comment
|
||||
uses: actions/github-script@v8
|
||||
env:
|
||||
GITHUB_AW_AGENT_OUTPUT: ${{ needs.daily-backlog-burner.outputs.output }}
|
||||
GITHUB_AW_COMMENT_TARGET: "*"
|
||||
with:
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
script: |
|
||||
async function main() {
|
||||
// Check if we're in staged mode
|
||||
|
@ -2634,15 +2645,15 @@ jobs:
|
|||
core.info("No valid items found in agent output");
|
||||
return;
|
||||
}
|
||||
// Find all add-issue-comment items
|
||||
// Find all add-comment items
|
||||
const commentItems = validatedOutput.items.filter(
|
||||
/** @param {any} item */ item => item.type === "add-issue-comment"
|
||||
/** @param {any} item */ item => item.type === "add-comment"
|
||||
);
|
||||
if (commentItems.length === 0) {
|
||||
core.info("No add-issue-comment items found in agent output");
|
||||
core.info("No add-comment items found in agent output");
|
||||
return;
|
||||
}
|
||||
core.info(`Found ${commentItems.length} add-issue-comment item(s)`);
|
||||
core.info(`Found ${commentItems.length} add-comment item(s)`);
|
||||
// If in staged mode, emit step summary instead of creating comments
|
||||
if (isStaged) {
|
||||
let summaryContent = "## 🎭 Staged Mode: Add Comments Preview\n\n";
|
||||
|
@ -2686,7 +2697,7 @@ jobs:
|
|||
for (let i = 0; i < commentItems.length; i++) {
|
||||
const commentItem = commentItems[i];
|
||||
core.info(
|
||||
`Processing add-issue-comment item ${i + 1}/${commentItems.length}: bodyLength=${commentItem.body.length}`
|
||||
`Processing add-comment item ${i + 1}/${commentItems.length}: bodyLength=${commentItem.body.length}`
|
||||
);
|
||||
// Determine the issue/PR number and comment endpoint for this comment
|
||||
let issueNumber;
|
||||
|
@ -2827,6 +2838,7 @@ jobs:
|
|||
GITHUB_AW_PR_DRAFT: "true"
|
||||
GITHUB_AW_PR_IF_NO_CHANGES: "warn"
|
||||
with:
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
script: |
|
||||
/** @type {typeof import("fs")} */
|
||||
const fs = require("fs");
|
||||
|
|
3
.github/workflows/daily-backlog-burner.md
vendored
3
.github/workflows/daily-backlog-burner.md
vendored
|
@ -14,11 +14,12 @@ safe-outputs:
|
|||
create-issue:
|
||||
title-prefix: "${{ github.workflow }}"
|
||||
max: 3
|
||||
add-issue-comment:
|
||||
add-comment:
|
||||
target: "*" # all issues and PRs
|
||||
max: 3
|
||||
create-pull-request:
|
||||
draft: true
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
|
||||
tools:
|
||||
web-fetch:
|
||||
|
|
86
.github/workflows/daily-perf-improver.lock.yml
generated
vendored
86
.github/workflows/daily-perf-improver.lock.yml
generated
vendored
|
@ -2,7 +2,7 @@
|
|||
# To update this file, edit the corresponding .md file and run:
|
||||
# gh aw compile
|
||||
#
|
||||
# Effective stop-time: 2025-09-19 10:32:53
|
||||
# Effective stop-time: 2025-09-19 12:19:14
|
||||
|
||||
name: "Daily Perf Improver"
|
||||
"on":
|
||||
|
@ -69,7 +69,7 @@ jobs:
|
|||
main();
|
||||
- name: Setup Safe Outputs Collector MCP
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}"
|
||||
run: |
|
||||
mkdir -p /tmp/safe-outputs
|
||||
cat > /tmp/safe-outputs/mcp-server.cjs << 'EOF'
|
||||
|
@ -222,7 +222,7 @@ jobs:
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "add-issue-comment",
|
||||
name: "add-comment",
|
||||
description: "Add a comment to a GitHub issue or pull request",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
|
@ -329,7 +329,7 @@ jobs:
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "add-issue-labels",
|
||||
name: "add-labels",
|
||||
description: "Add labels to a GitHub issue or pull request",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
|
@ -503,7 +503,7 @@ jobs:
|
|||
- name: Setup MCPs
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}"
|
||||
run: |
|
||||
mkdir -p /tmp/mcp-config
|
||||
cat > /tmp/mcp-config/mcp-servers.json << 'EOF'
|
||||
|
@ -541,7 +541,7 @@ jobs:
|
|||
WORKFLOW_NAME="Daily Perf Improver"
|
||||
|
||||
# Check stop-time limit
|
||||
STOP_TIME="2025-09-19 10:32:53"
|
||||
STOP_TIME="2025-09-19 12:19:14"
|
||||
echo "Checking stop-time limit: $STOP_TIME"
|
||||
|
||||
# Convert stop time to epoch seconds
|
||||
|
@ -743,7 +743,7 @@ jobs:
|
|||
|
||||
**Adding a Comment to an Issue or Pull Request**
|
||||
|
||||
To add a comment to an issue or pull request, use the add-issue-comments tool from the safe-outputs MCP
|
||||
To add a comment to an issue or pull request, use the add-comments tool from the safe-outputs MCP
|
||||
|
||||
**Creating an Issue**
|
||||
|
||||
|
@ -929,7 +929,7 @@ jobs:
|
|||
uses: actions/github-script@v8
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true}"
|
||||
with:
|
||||
script: |
|
||||
async function main() {
|
||||
|
@ -962,15 +962,12 @@ jobs:
|
|||
let sanitized = content;
|
||||
// Neutralize @mentions to prevent unintended notifications
|
||||
sanitized = neutralizeMentions(sanitized);
|
||||
// Remove XML comments to prevent content hiding
|
||||
sanitized = removeXmlComments(sanitized);
|
||||
// Remove ANSI escape sequences BEFORE removing control characters
|
||||
sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
|
||||
// Remove control characters (except newlines and tabs)
|
||||
sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
|
||||
// XML character escaping
|
||||
sanitized = sanitized
|
||||
.replace(/&/g, "&") // Must be first to avoid double-escaping
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
// URI filtering - replace non-https protocols with "(redacted)"
|
||||
sanitized = sanitizeUrlProtocols(sanitized);
|
||||
// Domain filtering for HTTPS URIs
|
||||
|
@ -990,8 +987,7 @@ jobs:
|
|||
lines.slice(0, maxLines).join("\n") +
|
||||
"\n[Content truncated due to line count]";
|
||||
}
|
||||
// Remove ANSI escape sequences
|
||||
sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
|
||||
// ANSI escape sequences already removed earlier in the function
|
||||
// Neutralize common bot trigger phrases
|
||||
sanitized = neutralizeBotTriggers(sanitized);
|
||||
// Trim excessive whitespace
|
||||
|
@ -1003,10 +999,12 @@ jobs:
|
|||
*/
|
||||
function sanitizeUrlDomains(s) {
|
||||
return s.replace(
|
||||
/\bhttps:\/\/([^\/\s\])}'"<>&\x00-\x1f]+)/gi,
|
||||
(match, domain) => {
|
||||
/\bhttps:\/\/[^\s\])}'"<>&\x00-\x1f,;]+/gi,
|
||||
(match) => {
|
||||
// Extract just the URL part after https://
|
||||
const urlAfterProtocol = match.slice(8); // Remove 'https://'
|
||||
// Extract the hostname part (before first slash, colon, or other delimiter)
|
||||
const hostname = domain.split(/[\/:\?#]/)[0].toLowerCase();
|
||||
const hostname = urlAfterProtocol.split(/[\/:\?#]/)[0].toLowerCase();
|
||||
// Check if this domain or any parent domain is in the allowlist
|
||||
const isAllowed = allowedDomains.some(allowedDomain => {
|
||||
const normalizedAllowed = allowedDomain.toLowerCase();
|
||||
|
@ -1025,9 +1023,10 @@ jobs:
|
|||
* @returns {string} The string with non-https protocols redacted
|
||||
*/
|
||||
function sanitizeUrlProtocols(s) {
|
||||
// Match both protocol:// and protocol: patterns
|
||||
// Match protocol:// patterns (URLs) and standalone protocol: patterns that look like URLs
|
||||
// Avoid matching command line flags like -v:10 or z3 -memory:high
|
||||
return s.replace(
|
||||
/\b(\w+):(?:\/\/)?[^\s\])}'"<>&\x00-\x1f]+/gi,
|
||||
/\b(\w+):\/\/[^\s\])}'"<>&\x00-\x1f]+/gi,
|
||||
(match, protocol) => {
|
||||
// Allow https (case insensitive), redact everything else
|
||||
return protocol.toLowerCase() === "https" ? match : "(redacted)";
|
||||
|
@ -1046,6 +1045,16 @@ jobs:
|
|||
(_m, p1, p2) => `${p1}\`@${p2}\``
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Removes XML comments to prevent content hiding
|
||||
* @param {string} s - The string to process
|
||||
* @returns {string} The string with XML comments removed
|
||||
*/
|
||||
function removeXmlComments(s) {
|
||||
// Remove XML/HTML comments including malformed ones that might be used to hide content
|
||||
// Matches: <!-- ... --> and <!--- ... --> and <!--- ... --!> variations
|
||||
return s.replace(/<!--[\s\S]*?-->/g, "").replace(/<!--[\s\S]*?--!>/g, "");
|
||||
}
|
||||
/**
|
||||
* Neutralizes bot trigger phrases by wrapping them in backticks
|
||||
* @param {string} s - The string to process
|
||||
|
@ -1079,13 +1088,13 @@ jobs:
|
|||
switch (itemType) {
|
||||
case "create-issue":
|
||||
return 1; // Only one issue allowed
|
||||
case "add-issue-comment":
|
||||
case "add-comment":
|
||||
return 1; // Only one comment allowed
|
||||
case "create-pull-request":
|
||||
return 1; // Only one pull request allowed
|
||||
case "create-pull-request-review-comment":
|
||||
return 10; // Default to 10 review comments allowed
|
||||
case "add-issue-labels":
|
||||
case "add-labels":
|
||||
return 5; // Only one labels operation allowed
|
||||
case "update-issue":
|
||||
return 1; // Only one issue update allowed
|
||||
|
@ -1300,10 +1309,10 @@ jobs:
|
|||
);
|
||||
}
|
||||
break;
|
||||
case "add-issue-comment":
|
||||
case "add-comment":
|
||||
if (!item.body || typeof item.body !== "string") {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-comment requires a 'body' string field`
|
||||
`Line ${i + 1}: add-comment requires a 'body' string field`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -1338,10 +1347,10 @@ jobs:
|
|||
);
|
||||
}
|
||||
break;
|
||||
case "add-issue-labels":
|
||||
case "add-labels":
|
||||
if (!item.labels || !Array.isArray(item.labels)) {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-labels requires a 'labels' array field`
|
||||
`Line ${i + 1}: add-labels requires a 'labels' array field`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -1351,7 +1360,7 @@ jobs:
|
|||
)
|
||||
) {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-labels labels array must contain only strings`
|
||||
`Line ${i + 1}: add-labels labels array must contain only strings`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -2481,6 +2490,7 @@ jobs:
|
|||
GITHUB_AW_AGENT_OUTPUT: ${{ needs.daily-perf-improver.outputs.output }}
|
||||
GITHUB_AW_ISSUE_TITLE_PREFIX: "${{ github.workflow }}"
|
||||
with:
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
script: |
|
||||
async function main() {
|
||||
// Check if we're in staged mode
|
||||
|
@ -2670,16 +2680,17 @@ jobs:
|
|||
pull-requests: write
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
comment_id: ${{ steps.create_comment.outputs.comment_id }}
|
||||
comment_url: ${{ steps.create_comment.outputs.comment_url }}
|
||||
comment_id: ${{ steps.add_comment.outputs.comment_id }}
|
||||
comment_url: ${{ steps.add_comment.outputs.comment_url }}
|
||||
steps:
|
||||
- name: Add Issue Comment
|
||||
id: create_comment
|
||||
id: add_comment
|
||||
uses: actions/github-script@v8
|
||||
env:
|
||||
GITHUB_AW_AGENT_OUTPUT: ${{ needs.daily-perf-improver.outputs.output }}
|
||||
GITHUB_AW_COMMENT_TARGET: "*"
|
||||
with:
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
script: |
|
||||
async function main() {
|
||||
// Check if we're in staged mode
|
||||
|
@ -2709,15 +2720,15 @@ jobs:
|
|||
core.info("No valid items found in agent output");
|
||||
return;
|
||||
}
|
||||
// Find all add-issue-comment items
|
||||
// Find all add-comment items
|
||||
const commentItems = validatedOutput.items.filter(
|
||||
/** @param {any} item */ item => item.type === "add-issue-comment"
|
||||
/** @param {any} item */ item => item.type === "add-comment"
|
||||
);
|
||||
if (commentItems.length === 0) {
|
||||
core.info("No add-issue-comment items found in agent output");
|
||||
core.info("No add-comment items found in agent output");
|
||||
return;
|
||||
}
|
||||
core.info(`Found ${commentItems.length} add-issue-comment item(s)`);
|
||||
core.info(`Found ${commentItems.length} add-comment item(s)`);
|
||||
// If in staged mode, emit step summary instead of creating comments
|
||||
if (isStaged) {
|
||||
let summaryContent = "## 🎭 Staged Mode: Add Comments Preview\n\n";
|
||||
|
@ -2761,7 +2772,7 @@ jobs:
|
|||
for (let i = 0; i < commentItems.length; i++) {
|
||||
const commentItem = commentItems[i];
|
||||
core.info(
|
||||
`Processing add-issue-comment item ${i + 1}/${commentItems.length}: bodyLength=${commentItem.body.length}`
|
||||
`Processing add-comment item ${i + 1}/${commentItems.length}: bodyLength=${commentItem.body.length}`
|
||||
);
|
||||
// Determine the issue/PR number and comment endpoint for this comment
|
||||
let issueNumber;
|
||||
|
@ -2902,6 +2913,7 @@ jobs:
|
|||
GITHUB_AW_PR_DRAFT: "true"
|
||||
GITHUB_AW_PR_IF_NO_CHANGES: "warn"
|
||||
with:
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
script: |
|
||||
/** @type {typeof import("fs")} */
|
||||
const fs = require("fs");
|
||||
|
|
3
.github/workflows/daily-perf-improver.md
vendored
3
.github/workflows/daily-perf-improver.md
vendored
|
@ -16,10 +16,11 @@ safe-outputs:
|
|||
create-issue:
|
||||
title-prefix: "${{ github.workflow }}"
|
||||
max: 5
|
||||
add-issue-comment:
|
||||
add-comment:
|
||||
target: "*" # can add a comment to any one single issue or pull request
|
||||
create-pull-request:
|
||||
draft: true
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
|
||||
tools:
|
||||
web-fetch:
|
||||
|
|
87
.github/workflows/daily-test-improver.lock.yml
generated
vendored
87
.github/workflows/daily-test-improver.lock.yml
generated
vendored
|
@ -2,7 +2,7 @@
|
|||
# To update this file, edit the corresponding .md file and run:
|
||||
# gh aw compile
|
||||
#
|
||||
# Effective stop-time: 2025-09-19 10:32:53
|
||||
# Effective stop-time: 2025-09-19 12:19:15
|
||||
|
||||
name: "Daily Test Coverage Improver"
|
||||
"on":
|
||||
|
@ -69,7 +69,7 @@ jobs:
|
|||
main();
|
||||
- name: Setup Safe Outputs Collector MCP
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true,\"update-issue\":true}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true,\"update-issue\":true}"
|
||||
run: |
|
||||
mkdir -p /tmp/safe-outputs
|
||||
cat > /tmp/safe-outputs/mcp-server.cjs << 'EOF'
|
||||
|
@ -222,7 +222,7 @@ jobs:
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "add-issue-comment",
|
||||
name: "add-comment",
|
||||
description: "Add a comment to a GitHub issue or pull request",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
|
@ -329,7 +329,7 @@ jobs:
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "add-issue-labels",
|
||||
name: "add-labels",
|
||||
description: "Add labels to a GitHub issue or pull request",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
|
@ -503,7 +503,7 @@ jobs:
|
|||
- name: Setup MCPs
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true,\"update-issue\":true}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true,\"update-issue\":true}"
|
||||
run: |
|
||||
mkdir -p /tmp/mcp-config
|
||||
cat > /tmp/mcp-config/mcp-servers.json << 'EOF'
|
||||
|
@ -541,7 +541,7 @@ jobs:
|
|||
WORKFLOW_NAME="Daily Test Coverage Improver"
|
||||
|
||||
# Check stop-time limit
|
||||
STOP_TIME="2025-09-19 10:32:53"
|
||||
STOP_TIME="2025-09-19 12:19:15"
|
||||
echo "Checking stop-time limit: $STOP_TIME"
|
||||
|
||||
# Convert stop time to epoch seconds
|
||||
|
@ -714,7 +714,7 @@ jobs:
|
|||
|
||||
**Adding a Comment to an Issue or Pull Request**
|
||||
|
||||
To add a comment to an issue or pull request, use the add-issue-comments tool from the safe-outputs MCP
|
||||
To add a comment to an issue or pull request, use the add-comments tool from the safe-outputs MCP
|
||||
|
||||
**Creating an Issue**
|
||||
|
||||
|
@ -904,7 +904,7 @@ jobs:
|
|||
uses: actions/github-script@v8
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true,\"update-issue\":true}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true,\"target\":\"*\"},\"create-issue\":true,\"create-pull-request\":true,\"update-issue\":true}"
|
||||
with:
|
||||
script: |
|
||||
async function main() {
|
||||
|
@ -937,15 +937,12 @@ jobs:
|
|||
let sanitized = content;
|
||||
// Neutralize @mentions to prevent unintended notifications
|
||||
sanitized = neutralizeMentions(sanitized);
|
||||
// Remove XML comments to prevent content hiding
|
||||
sanitized = removeXmlComments(sanitized);
|
||||
// Remove ANSI escape sequences BEFORE removing control characters
|
||||
sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
|
||||
// Remove control characters (except newlines and tabs)
|
||||
sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
|
||||
// XML character escaping
|
||||
sanitized = sanitized
|
||||
.replace(/&/g, "&") // Must be first to avoid double-escaping
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
// URI filtering - replace non-https protocols with "(redacted)"
|
||||
sanitized = sanitizeUrlProtocols(sanitized);
|
||||
// Domain filtering for HTTPS URIs
|
||||
|
@ -965,8 +962,7 @@ jobs:
|
|||
lines.slice(0, maxLines).join("\n") +
|
||||
"\n[Content truncated due to line count]";
|
||||
}
|
||||
// Remove ANSI escape sequences
|
||||
sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
|
||||
// ANSI escape sequences already removed earlier in the function
|
||||
// Neutralize common bot trigger phrases
|
||||
sanitized = neutralizeBotTriggers(sanitized);
|
||||
// Trim excessive whitespace
|
||||
|
@ -978,10 +974,12 @@ jobs:
|
|||
*/
|
||||
function sanitizeUrlDomains(s) {
|
||||
return s.replace(
|
||||
/\bhttps:\/\/([^\/\s\])}'"<>&\x00-\x1f]+)/gi,
|
||||
(match, domain) => {
|
||||
/\bhttps:\/\/[^\s\])}'"<>&\x00-\x1f,;]+/gi,
|
||||
(match) => {
|
||||
// Extract just the URL part after https://
|
||||
const urlAfterProtocol = match.slice(8); // Remove 'https://'
|
||||
// Extract the hostname part (before first slash, colon, or other delimiter)
|
||||
const hostname = domain.split(/[\/:\?#]/)[0].toLowerCase();
|
||||
const hostname = urlAfterProtocol.split(/[\/:\?#]/)[0].toLowerCase();
|
||||
// Check if this domain or any parent domain is in the allowlist
|
||||
const isAllowed = allowedDomains.some(allowedDomain => {
|
||||
const normalizedAllowed = allowedDomain.toLowerCase();
|
||||
|
@ -1000,9 +998,10 @@ jobs:
|
|||
* @returns {string} The string with non-https protocols redacted
|
||||
*/
|
||||
function sanitizeUrlProtocols(s) {
|
||||
// Match both protocol:// and protocol: patterns
|
||||
// Match protocol:// patterns (URLs) and standalone protocol: patterns that look like URLs
|
||||
// Avoid matching command line flags like -v:10 or z3 -memory:high
|
||||
return s.replace(
|
||||
/\b(\w+):(?:\/\/)?[^\s\])}'"<>&\x00-\x1f]+/gi,
|
||||
/\b(\w+):\/\/[^\s\])}'"<>&\x00-\x1f]+/gi,
|
||||
(match, protocol) => {
|
||||
// Allow https (case insensitive), redact everything else
|
||||
return protocol.toLowerCase() === "https" ? match : "(redacted)";
|
||||
|
@ -1021,6 +1020,16 @@ jobs:
|
|||
(_m, p1, p2) => `${p1}\`@${p2}\``
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Removes XML comments to prevent content hiding
|
||||
* @param {string} s - The string to process
|
||||
* @returns {string} The string with XML comments removed
|
||||
*/
|
||||
function removeXmlComments(s) {
|
||||
// Remove XML/HTML comments including malformed ones that might be used to hide content
|
||||
// Matches: <!-- ... --> and <!--- ... --> and <!--- ... --!> variations
|
||||
return s.replace(/<!--[\s\S]*?-->/g, "").replace(/<!--[\s\S]*?--!>/g, "");
|
||||
}
|
||||
/**
|
||||
* Neutralizes bot trigger phrases by wrapping them in backticks
|
||||
* @param {string} s - The string to process
|
||||
|
@ -1054,13 +1063,13 @@ jobs:
|
|||
switch (itemType) {
|
||||
case "create-issue":
|
||||
return 1; // Only one issue allowed
|
||||
case "add-issue-comment":
|
||||
case "add-comment":
|
||||
return 1; // Only one comment allowed
|
||||
case "create-pull-request":
|
||||
return 1; // Only one pull request allowed
|
||||
case "create-pull-request-review-comment":
|
||||
return 10; // Default to 10 review comments allowed
|
||||
case "add-issue-labels":
|
||||
case "add-labels":
|
||||
return 5; // Only one labels operation allowed
|
||||
case "update-issue":
|
||||
return 1; // Only one issue update allowed
|
||||
|
@ -1275,10 +1284,10 @@ jobs:
|
|||
);
|
||||
}
|
||||
break;
|
||||
case "add-issue-comment":
|
||||
case "add-comment":
|
||||
if (!item.body || typeof item.body !== "string") {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-comment requires a 'body' string field`
|
||||
`Line ${i + 1}: add-comment requires a 'body' string field`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -1313,10 +1322,10 @@ jobs:
|
|||
);
|
||||
}
|
||||
break;
|
||||
case "add-issue-labels":
|
||||
case "add-labels":
|
||||
if (!item.labels || !Array.isArray(item.labels)) {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-labels requires a 'labels' array field`
|
||||
`Line ${i + 1}: add-labels requires a 'labels' array field`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -1326,7 +1335,7 @@ jobs:
|
|||
)
|
||||
) {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-labels labels array must contain only strings`
|
||||
`Line ${i + 1}: add-labels labels array must contain only strings`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -2456,6 +2465,7 @@ jobs:
|
|||
GITHUB_AW_AGENT_OUTPUT: ${{ needs.daily-test-coverage-improver.outputs.output }}
|
||||
GITHUB_AW_ISSUE_TITLE_PREFIX: "${{ github.workflow }}"
|
||||
with:
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
script: |
|
||||
async function main() {
|
||||
// Check if we're in staged mode
|
||||
|
@ -2645,16 +2655,17 @@ jobs:
|
|||
pull-requests: write
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
comment_id: ${{ steps.create_comment.outputs.comment_id }}
|
||||
comment_url: ${{ steps.create_comment.outputs.comment_url }}
|
||||
comment_id: ${{ steps.add_comment.outputs.comment_id }}
|
||||
comment_url: ${{ steps.add_comment.outputs.comment_url }}
|
||||
steps:
|
||||
- name: Add Issue Comment
|
||||
id: create_comment
|
||||
id: add_comment
|
||||
uses: actions/github-script@v8
|
||||
env:
|
||||
GITHUB_AW_AGENT_OUTPUT: ${{ needs.daily-test-coverage-improver.outputs.output }}
|
||||
GITHUB_AW_COMMENT_TARGET: "*"
|
||||
with:
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
script: |
|
||||
async function main() {
|
||||
// Check if we're in staged mode
|
||||
|
@ -2684,15 +2695,15 @@ jobs:
|
|||
core.info("No valid items found in agent output");
|
||||
return;
|
||||
}
|
||||
// Find all add-issue-comment items
|
||||
// Find all add-comment items
|
||||
const commentItems = validatedOutput.items.filter(
|
||||
/** @param {any} item */ item => item.type === "add-issue-comment"
|
||||
/** @param {any} item */ item => item.type === "add-comment"
|
||||
);
|
||||
if (commentItems.length === 0) {
|
||||
core.info("No add-issue-comment items found in agent output");
|
||||
core.info("No add-comment items found in agent output");
|
||||
return;
|
||||
}
|
||||
core.info(`Found ${commentItems.length} add-issue-comment item(s)`);
|
||||
core.info(`Found ${commentItems.length} add-comment item(s)`);
|
||||
// If in staged mode, emit step summary instead of creating comments
|
||||
if (isStaged) {
|
||||
let summaryContent = "## 🎭 Staged Mode: Add Comments Preview\n\n";
|
||||
|
@ -2736,7 +2747,7 @@ jobs:
|
|||
for (let i = 0; i < commentItems.length; i++) {
|
||||
const commentItem = commentItems[i];
|
||||
core.info(
|
||||
`Processing add-issue-comment item ${i + 1}/${commentItems.length}: bodyLength=${commentItem.body.length}`
|
||||
`Processing add-comment item ${i + 1}/${commentItems.length}: bodyLength=${commentItem.body.length}`
|
||||
);
|
||||
// Determine the issue/PR number and comment endpoint for this comment
|
||||
let issueNumber;
|
||||
|
@ -2877,6 +2888,7 @@ jobs:
|
|||
GITHUB_AW_PR_DRAFT: "true"
|
||||
GITHUB_AW_PR_IF_NO_CHANGES: "warn"
|
||||
with:
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
script: |
|
||||
/** @type {typeof import("fs")} */
|
||||
const fs = require("fs");
|
||||
|
@ -3197,6 +3209,7 @@ jobs:
|
|||
GITHUB_AW_UPDATE_BODY: true
|
||||
GITHUB_AW_UPDATE_TARGET: "*"
|
||||
with:
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
script: |
|
||||
async function main() {
|
||||
// Check if we're in staged mode
|
||||
|
|
3
.github/workflows/daily-test-improver.md
vendored
3
.github/workflows/daily-test-improver.md
vendored
|
@ -19,10 +19,11 @@ safe-outputs:
|
|||
target: "*" # one single issue
|
||||
body: # can update the issue title/body only
|
||||
title: # can update the issue title/body only
|
||||
add-issue-comment:
|
||||
add-comment:
|
||||
target: "*" # can add a comment to any one single issue or pull request
|
||||
create-pull-request: # can create a pull request
|
||||
draft: true
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
|
||||
tools:
|
||||
web-fetch:
|
||||
|
|
83
.github/workflows/pr-fix.lock.yml
generated
vendored
83
.github/workflows/pr-fix.lock.yml
generated
vendored
|
@ -2,7 +2,7 @@
|
|||
# To update this file, edit the corresponding .md file and run:
|
||||
# gh aw compile
|
||||
#
|
||||
# Effective stop-time: 2025-09-19 10:32:53
|
||||
# Effective stop-time: 2025-09-19 12:19:15
|
||||
|
||||
name: "PR Fix"
|
||||
on:
|
||||
|
@ -599,7 +599,7 @@ jobs:
|
|||
main();
|
||||
- name: Setup Safe Outputs Collector MCP
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true},\"create-issue\":true,\"push-to-pr-branch\":{\"enabled\":true}}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true},\"create-issue\":true,\"push-to-pr-branch\":{\"enabled\":true}}"
|
||||
run: |
|
||||
mkdir -p /tmp/safe-outputs
|
||||
cat > /tmp/safe-outputs/mcp-server.cjs << 'EOF'
|
||||
|
@ -752,7 +752,7 @@ jobs:
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "add-issue-comment",
|
||||
name: "add-comment",
|
||||
description: "Add a comment to a GitHub issue or pull request",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
|
@ -859,7 +859,7 @@ jobs:
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "add-issue-labels",
|
||||
name: "add-labels",
|
||||
description: "Add labels to a GitHub issue or pull request",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
|
@ -1033,7 +1033,7 @@ jobs:
|
|||
- name: Setup MCPs
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true},\"create-issue\":true,\"push-to-pr-branch\":{\"enabled\":true}}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true},\"create-issue\":true,\"push-to-pr-branch\":{\"enabled\":true}}"
|
||||
run: |
|
||||
mkdir -p /tmp/mcp-config
|
||||
cat > /tmp/mcp-config/mcp-servers.json << 'EOF'
|
||||
|
@ -1071,7 +1071,7 @@ jobs:
|
|||
WORKFLOW_NAME="PR Fix"
|
||||
|
||||
# Check stop-time limit
|
||||
STOP_TIME="2025-09-19 10:32:53"
|
||||
STOP_TIME="2025-09-19 12:19:15"
|
||||
echo "Checking stop-time limit: $STOP_TIME"
|
||||
|
||||
# Convert stop time to epoch seconds
|
||||
|
@ -1174,7 +1174,7 @@ jobs:
|
|||
|
||||
**Adding a Comment to an Issue or Pull Request**
|
||||
|
||||
To add a comment to an issue or pull request, use the add-issue-comments tool from the safe-outputs MCP
|
||||
To add a comment to an issue or pull request, use the add-comments tool from the safe-outputs MCP
|
||||
|
||||
**Creating an Issue**
|
||||
|
||||
|
@ -1358,7 +1358,7 @@ jobs:
|
|||
uses: actions/github-script@v8
|
||||
env:
|
||||
GITHUB_AW_SAFE_OUTPUTS: ${{ env.GITHUB_AW_SAFE_OUTPUTS }}
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-issue-comment\":{\"enabled\":true},\"create-issue\":true,\"push-to-pr-branch\":{\"enabled\":true}}"
|
||||
GITHUB_AW_SAFE_OUTPUTS_CONFIG: "{\"add-comment\":{\"enabled\":true},\"create-issue\":true,\"push-to-pr-branch\":{\"enabled\":true}}"
|
||||
with:
|
||||
script: |
|
||||
async function main() {
|
||||
|
@ -1391,15 +1391,12 @@ jobs:
|
|||
let sanitized = content;
|
||||
// Neutralize @mentions to prevent unintended notifications
|
||||
sanitized = neutralizeMentions(sanitized);
|
||||
// Remove XML comments to prevent content hiding
|
||||
sanitized = removeXmlComments(sanitized);
|
||||
// Remove ANSI escape sequences BEFORE removing control characters
|
||||
sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
|
||||
// Remove control characters (except newlines and tabs)
|
||||
sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
|
||||
// XML character escaping
|
||||
sanitized = sanitized
|
||||
.replace(/&/g, "&") // Must be first to avoid double-escaping
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
// URI filtering - replace non-https protocols with "(redacted)"
|
||||
sanitized = sanitizeUrlProtocols(sanitized);
|
||||
// Domain filtering for HTTPS URIs
|
||||
|
@ -1419,8 +1416,7 @@ jobs:
|
|||
lines.slice(0, maxLines).join("\n") +
|
||||
"\n[Content truncated due to line count]";
|
||||
}
|
||||
// Remove ANSI escape sequences
|
||||
sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
|
||||
// ANSI escape sequences already removed earlier in the function
|
||||
// Neutralize common bot trigger phrases
|
||||
sanitized = neutralizeBotTriggers(sanitized);
|
||||
// Trim excessive whitespace
|
||||
|
@ -1432,10 +1428,12 @@ jobs:
|
|||
*/
|
||||
function sanitizeUrlDomains(s) {
|
||||
return s.replace(
|
||||
/\bhttps:\/\/([^\/\s\])}'"<>&\x00-\x1f]+)/gi,
|
||||
(match, domain) => {
|
||||
/\bhttps:\/\/[^\s\])}'"<>&\x00-\x1f,;]+/gi,
|
||||
(match) => {
|
||||
// Extract just the URL part after https://
|
||||
const urlAfterProtocol = match.slice(8); // Remove 'https://'
|
||||
// Extract the hostname part (before first slash, colon, or other delimiter)
|
||||
const hostname = domain.split(/[\/:\?#]/)[0].toLowerCase();
|
||||
const hostname = urlAfterProtocol.split(/[\/:\?#]/)[0].toLowerCase();
|
||||
// Check if this domain or any parent domain is in the allowlist
|
||||
const isAllowed = allowedDomains.some(allowedDomain => {
|
||||
const normalizedAllowed = allowedDomain.toLowerCase();
|
||||
|
@ -1454,9 +1452,10 @@ jobs:
|
|||
* @returns {string} The string with non-https protocols redacted
|
||||
*/
|
||||
function sanitizeUrlProtocols(s) {
|
||||
// Match both protocol:// and protocol: patterns
|
||||
// Match protocol:// patterns (URLs) and standalone protocol: patterns that look like URLs
|
||||
// Avoid matching command line flags like -v:10 or z3 -memory:high
|
||||
return s.replace(
|
||||
/\b(\w+):(?:\/\/)?[^\s\])}'"<>&\x00-\x1f]+/gi,
|
||||
/\b(\w+):\/\/[^\s\])}'"<>&\x00-\x1f]+/gi,
|
||||
(match, protocol) => {
|
||||
// Allow https (case insensitive), redact everything else
|
||||
return protocol.toLowerCase() === "https" ? match : "(redacted)";
|
||||
|
@ -1475,6 +1474,16 @@ jobs:
|
|||
(_m, p1, p2) => `${p1}\`@${p2}\``
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Removes XML comments to prevent content hiding
|
||||
* @param {string} s - The string to process
|
||||
* @returns {string} The string with XML comments removed
|
||||
*/
|
||||
function removeXmlComments(s) {
|
||||
// Remove XML/HTML comments including malformed ones that might be used to hide content
|
||||
// Matches: <!-- ... --> and <!--- ... --> and <!--- ... --!> variations
|
||||
return s.replace(/<!--[\s\S]*?-->/g, "").replace(/<!--[\s\S]*?--!>/g, "");
|
||||
}
|
||||
/**
|
||||
* Neutralizes bot trigger phrases by wrapping them in backticks
|
||||
* @param {string} s - The string to process
|
||||
|
@ -1508,13 +1517,13 @@ jobs:
|
|||
switch (itemType) {
|
||||
case "create-issue":
|
||||
return 1; // Only one issue allowed
|
||||
case "add-issue-comment":
|
||||
case "add-comment":
|
||||
return 1; // Only one comment allowed
|
||||
case "create-pull-request":
|
||||
return 1; // Only one pull request allowed
|
||||
case "create-pull-request-review-comment":
|
||||
return 10; // Default to 10 review comments allowed
|
||||
case "add-issue-labels":
|
||||
case "add-labels":
|
||||
return 5; // Only one labels operation allowed
|
||||
case "update-issue":
|
||||
return 1; // Only one issue update allowed
|
||||
|
@ -1729,10 +1738,10 @@ jobs:
|
|||
);
|
||||
}
|
||||
break;
|
||||
case "add-issue-comment":
|
||||
case "add-comment":
|
||||
if (!item.body || typeof item.body !== "string") {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-comment requires a 'body' string field`
|
||||
`Line ${i + 1}: add-comment requires a 'body' string field`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -1767,10 +1776,10 @@ jobs:
|
|||
);
|
||||
}
|
||||
break;
|
||||
case "add-issue-labels":
|
||||
case "add-labels":
|
||||
if (!item.labels || !Array.isArray(item.labels)) {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-labels requires a 'labels' array field`
|
||||
`Line ${i + 1}: add-labels requires a 'labels' array field`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -1780,7 +1789,7 @@ jobs:
|
|||
)
|
||||
) {
|
||||
errors.push(
|
||||
`Line ${i + 1}: add-issue-labels labels array must contain only strings`
|
||||
`Line ${i + 1}: add-labels labels array must contain only strings`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
@ -3019,11 +3028,11 @@ jobs:
|
|||
pull-requests: write
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
comment_id: ${{ steps.create_comment.outputs.comment_id }}
|
||||
comment_url: ${{ steps.create_comment.outputs.comment_url }}
|
||||
comment_id: ${{ steps.add_comment.outputs.comment_id }}
|
||||
comment_url: ${{ steps.add_comment.outputs.comment_url }}
|
||||
steps:
|
||||
- name: Add Issue Comment
|
||||
id: create_comment
|
||||
id: add_comment
|
||||
uses: actions/github-script@v8
|
||||
env:
|
||||
GITHUB_AW_AGENT_OUTPUT: ${{ needs.pr-fix.outputs.output }}
|
||||
|
@ -3058,15 +3067,15 @@ jobs:
|
|||
core.info("No valid items found in agent output");
|
||||
return;
|
||||
}
|
||||
// Find all add-issue-comment items
|
||||
// Find all add-comment items
|
||||
const commentItems = validatedOutput.items.filter(
|
||||
/** @param {any} item */ item => item.type === "add-issue-comment"
|
||||
/** @param {any} item */ item => item.type === "add-comment"
|
||||
);
|
||||
if (commentItems.length === 0) {
|
||||
core.info("No add-issue-comment items found in agent output");
|
||||
core.info("No add-comment items found in agent output");
|
||||
return;
|
||||
}
|
||||
core.info(`Found ${commentItems.length} add-issue-comment item(s)`);
|
||||
core.info(`Found ${commentItems.length} add-comment item(s)`);
|
||||
// If in staged mode, emit step summary instead of creating comments
|
||||
if (isStaged) {
|
||||
let summaryContent = "## 🎭 Staged Mode: Add Comments Preview\n\n";
|
||||
|
@ -3110,7 +3119,7 @@ jobs:
|
|||
for (let i = 0; i < commentItems.length; i++) {
|
||||
const commentItem = commentItems[i];
|
||||
core.info(
|
||||
`Processing add-issue-comment item ${i + 1}/${commentItems.length}: bodyLength=${commentItem.body.length}`
|
||||
`Processing add-comment item ${i + 1}/${commentItems.length}: bodyLength=${commentItem.body.length}`
|
||||
);
|
||||
// Determine the issue/PR number and comment endpoint for this comment
|
||||
let issueNumber;
|
||||
|
|
2
.github/workflows/pr-fix.md
vendored
2
.github/workflows/pr-fix.md
vendored
|
@ -14,7 +14,7 @@ safe-outputs:
|
|||
push-to-pr-branch:
|
||||
create-issue:
|
||||
title-prefix: "${{ github.workflow }}"
|
||||
add-issue-comment:
|
||||
add-comment:
|
||||
github-token: ${{ secrets.DSYME_GH_TOKEN}}
|
||||
|
||||
tools:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue