@plainconceptsplatform/workflows 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/catalog-installation.d.ts +12 -0
  2. package/dist/catalog-installation.js +81 -0
  3. package/dist/catalog-installation.test.d.ts +1 -0
  4. package/dist/catalog-installation.test.js +85 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.js +57 -0
  7. package/dist/repository-inspection.d.ts +23 -0
  8. package/dist/repository-inspection.js +91 -0
  9. package/dist/repository-state.d.ts +18 -0
  10. package/dist/repository-state.js +77 -0
  11. package/dist/repository-state.test.d.ts +1 -0
  12. package/dist/repository-state.test.js +96 -0
  13. package/dist/workflow-catalog.d.ts +17 -0
  14. package/dist/workflow-catalog.js +39 -0
  15. package/dist/workflow-catalog.test.d.ts +1 -0
  16. package/dist/workflow-catalog.test.js +14 -0
  17. package/loops/actions/add-issue-labels/action.yml +29 -0
  18. package/loops/actions/agent-output.cjs +16 -0
  19. package/loops/actions/apply-agent-bundle/action.yml +23 -0
  20. package/loops/actions/apply-agent-bundle/apply-bundle.sh +45 -0
  21. package/loops/actions/apply-agent-comments/action.yml +41 -0
  22. package/loops/actions/apply-agent-labels/action.yml +54 -0
  23. package/loops/actions/apply-agent-output/action.yml +107 -0
  24. package/loops/actions/audit-close/action.yml +103 -0
  25. package/loops/actions/classify-route/action.yml +90 -0
  26. package/loops/actions/classify-route/classify-route.sh +172 -0
  27. package/loops/actions/cleanup-artifacts/action.yml +64 -0
  28. package/loops/actions/close-agent-issues/action.yml +42 -0
  29. package/loops/actions/create-agent-issues/action.yml +51 -0
  30. package/loops/actions/create-issue-comment/action.yml +28 -0
  31. package/loops/actions/download-agent-output/action.yml +52 -0
  32. package/loops/actions/identify-gate-subject/action.yml +96 -0
  33. package/loops/actions/link-pr-to-issue/action.yml +39 -0
  34. package/loops/actions/list-open-issues/action.yml +32 -0
  35. package/loops/actions/load-issue-context/action.yml +42 -0
  36. package/loops/actions/merge-agent-pr/action.yml +35 -0
  37. package/loops/actions/push-agent-branch/action.yml +44 -0
  38. package/loops/actions/remove-issue-labels/action.yml +34 -0
  39. package/loops/actions/stale-recovery/action.yml +287 -0
  40. package/loops/actions/update-agent-issues/action.yml +57 -0
  41. package/loops/actions/validate-refine-output/action.yml +43 -0
  42. package/loops/actions/validate-refine-output/validate-refine-output.sh +46 -0
  43. package/loops/actions/verify-composite-actions/action.yml +8 -0
  44. package/loops/actions/verify-composite-actions/verify-composite-actions.sh +50 -0
  45. package/loops/actions/verify-refine-output/action.yml +8 -0
  46. package/loops/actions/verify-refine-output/verify-refine-output.sh +65 -0
  47. package/loops/actions/verify-route-matrix/action.yml +8 -0
  48. package/loops/actions/verify-route-matrix/verify-route-matrix.sh +212 -0
  49. package/loops/aw/repo-config.md +12 -0
  50. package/loops/scripts/compile-agent-workflows.mjs +13 -0
  51. package/loops/workflows/agent-apply-review.md +372 -0
  52. package/loops/workflows/agent-audit.md +197 -0
  53. package/loops/workflows/agent-direct.md +362 -0
  54. package/loops/workflows/agent-implement.md +329 -0
  55. package/loops/workflows/agent-merge-gate.md +470 -0
  56. package/loops/workflows/agent-propose.md +342 -0
  57. package/loops/workflows/agent-refine.md +338 -0
  58. package/loops/workflows/shared/opencode-ci.md +26 -0
  59. package/loops/workflows/shared/platform-defaults.md +14 -0
  60. package/loops/workflows/work-router.yml +391 -0
  61. package/package.json +28 -0
@@ -0,0 +1,64 @@
1
+ name: Clean up artifacts
2
+ description: Delete workflow artifacts older than the configured retention period.
3
+ inputs:
4
+ token:
5
+ description: GitHub token with actions write access.
6
+ required: true
7
+ artifact-retention-days:
8
+ description: Retention period in days. Invalid or empty values use 3.
9
+ required: false
10
+ default: ''
11
+ runs:
12
+ using: composite
13
+ steps:
14
+ - name: Delete expired artifacts
15
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
16
+ env:
17
+ ARTIFACT_RETENTION_DAYS: ${{ inputs.artifact-retention-days }}
18
+ with:
19
+ github-token: ${{ inputs.token }}
20
+ script: |
21
+ const parsedDays = Number.parseInt(process.env.ARTIFACT_RETENTION_DAYS, 10);
22
+ const days = Number.isInteger(parsedDays) && parsedDays > 0 ? parsedDays : 3;
23
+ const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
24
+
25
+ core.info(`Retention: ${days} days (cutoff ${cutoff.toISOString()})`);
26
+
27
+ const artifacts = await github.paginate(github.rest.actions.listArtifactsForRepo, {
28
+ ...context.repo,
29
+ per_page: 100,
30
+ });
31
+
32
+ const expired = artifacts.filter((artifact) => new Date(artifact.created_at) < cutoff);
33
+
34
+ let deleted = 0;
35
+ const failed = [];
36
+
37
+ for (const artifact of expired) {
38
+ try {
39
+ await github.rest.actions.deleteArtifact({
40
+ ...context.repo,
41
+ artifact_id: artifact.id,
42
+ });
43
+
44
+ core.info(`Deleted ${artifact.name} (created ${artifact.created_at})`);
45
+ deleted += 1;
46
+ } catch (error) {
47
+ core.warning(`Could not delete ${artifact.name} (${artifact.id}): ${error.message}`);
48
+ failed.push(artifact.name);
49
+ }
50
+ }
51
+
52
+ await core.summary
53
+ .addHeading('Artifact cleanup', 2)
54
+ .addList([
55
+ `Retention: ${days} days (cutoff ${cutoff.toISOString()})`,
56
+ `Deleted: ${deleted}`,
57
+ `Failed to delete: ${failed.length}`,
58
+ `Within retention: ${artifacts.length - expired.length}`,
59
+ ])
60
+ .write();
61
+
62
+ if (failed.length > 0) {
63
+ core.setFailed(`${failed.length} artifact(s) could not be deleted: ${failed.join(', ')}`);
64
+ }
@@ -0,0 +1,42 @@
1
+ name: Close agent issues
2
+ description: Close every close_issue item from agent_output.json.
3
+ inputs:
4
+ output-file:
5
+ description: Path to agent_output.json.
6
+ required: true
7
+ token:
8
+ description: GitHub token with issues:write.
9
+ required: true
10
+ runs:
11
+ using: composite
12
+ steps:
13
+ - name: Close issues
14
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
15
+ env:
16
+ AGENT_OUTPUT_LIB: ${{ github.action_path }}/../agent-output.cjs
17
+ OUTPUT_FILE: ${{ inputs.output-file }}
18
+ with:
19
+ github-token: ${{ inputs.token }}
20
+ script: |
21
+ const { readAgentItems } = require(process.env.AGENT_OUTPUT_LIB);
22
+ const items = readAgentItems(process.env.OUTPUT_FILE, 'close_issue');
23
+
24
+ let closed = 0;
25
+
26
+ for (const item of items) {
27
+ if (!item.item_number) {
28
+ core.warning(`close_issue item has no item_number, skipping: ${JSON.stringify(item)}`);
29
+ continue;
30
+ }
31
+
32
+ await github.rest.issues.update({
33
+ ...context.repo,
34
+ issue_number: Number(item.item_number),
35
+ state: 'closed',
36
+ state_reason: item.state_reason === 'not_planned' ? 'not_planned' : 'completed',
37
+ });
38
+
39
+ closed += 1;
40
+ }
41
+
42
+ core.info(`Closed ${closed} issue(s).`);
@@ -0,0 +1,51 @@
1
+ name: Create agent issues
2
+ description: Create every create_issue item from agent_output.json.
3
+ inputs:
4
+ output-file:
5
+ description: Path to agent_output.json.
6
+ required: true
7
+ token:
8
+ description: GitHub token with issues:write.
9
+ required: true
10
+ outputs:
11
+ created-count:
12
+ description: Number of issues created.
13
+ value: ${{ steps.create.outputs.created-count }}
14
+ first-issue-number:
15
+ description: Issue number of the first issue created, or empty.
16
+ value: ${{ steps.create.outputs.first-issue-number }}
17
+ runs:
18
+ using: composite
19
+ steps:
20
+ - name: Create issues
21
+ id: create
22
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
23
+ env:
24
+ AGENT_OUTPUT_LIB: ${{ github.action_path }}/../agent-output.cjs
25
+ OUTPUT_FILE: ${{ inputs.output-file }}
26
+ with:
27
+ github-token: ${{ inputs.token }}
28
+ script: |
29
+ const { readAgentItems } = require(process.env.AGENT_OUTPUT_LIB);
30
+ const items = readAgentItems(process.env.OUTPUT_FILE, 'create_issue');
31
+
32
+ const created = [];
33
+
34
+ for (const item of items) {
35
+ if (!item.title) {
36
+ core.warning(`create_issue item has no title, skipping: ${JSON.stringify(item)}`);
37
+ continue;
38
+ }
39
+
40
+ const { data } = await github.rest.issues.create({
41
+ ...context.repo,
42
+ title: item.title,
43
+ body: item.body ?? '',
44
+ });
45
+
46
+ created.push(data.number);
47
+ core.info(`Created #${data.number}: ${item.title}`);
48
+ }
49
+
50
+ core.setOutput('created-count', String(created.length));
51
+ core.setOutput('first-issue-number', created.length > 0 ? String(created[0]) : '');
@@ -0,0 +1,28 @@
1
+ name: Create issue comment
2
+ description: Create a comment on an issue or pull request.
3
+ inputs:
4
+ token:
5
+ description: GitHub token used by github-script.
6
+ required: true
7
+ issue-number:
8
+ description: Issue or pull request number.
9
+ required: true
10
+ body:
11
+ description: Complete Markdown comment body.
12
+ required: true
13
+ runs:
14
+ using: composite
15
+ steps:
16
+ - name: Create comment
17
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
18
+ env:
19
+ ISSUE_NUMBER: ${{ inputs.issue-number }}
20
+ BODY: ${{ inputs.body }}
21
+ with:
22
+ github-token: ${{ inputs.token }}
23
+ script: |
24
+ await github.rest.issues.createComment({
25
+ ...context.repo,
26
+ issue_number: Number(process.env.ISSUE_NUMBER),
27
+ body: process.env.BODY,
28
+ });
@@ -0,0 +1,52 @@
1
+ name: Download agent output
2
+ description: Download the agent artifact and expose the output JSON and bundle paths.
3
+ inputs:
4
+ artifact-name:
5
+ description: >-
6
+ Artifact name to download. gh-aw prefixes it with the value it derives from the
7
+ workflow's inputs, so a workflow_call worker must pass the activation job's
8
+ artifact_prefix output followed by "agent", not the bare name.
9
+ required: true
10
+ outputs:
11
+ output-file:
12
+ description: Path to agent_output.json, or empty if not found.
13
+ value: ${{ steps.download.outputs.output-file }}
14
+ bundle-file:
15
+ description: Path to the git bundle, or empty if not found.
16
+ value: ${{ steps.download.outputs.bundle-file }}
17
+ item-count:
18
+ description: Number of items in the agent output.
19
+ value: ${{ steps.download.outputs.item-count }}
20
+ runs:
21
+ using: composite
22
+ steps:
23
+ - name: Download agent artifact
24
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
25
+ with:
26
+ name: ${{ inputs.artifact-name }}
27
+ path: agent-artifact
28
+ - name: Locate output and bundle
29
+ id: download
30
+ shell: bash
31
+ env:
32
+ ARTIFACT_NAME: ${{ inputs.artifact-name }}
33
+ run: |
34
+ set -euo pipefail
35
+ OUTPUT_FILE=""
36
+ BUNDLE_FILE=""
37
+ ITEM_COUNT="0"
38
+
39
+ if [ -f "agent-artifact/agent_output.json" ]; then
40
+ OUTPUT_FILE="agent-artifact/agent_output.json"
41
+ ITEM_COUNT=$(jq '.items | length' "$OUTPUT_FILE")
42
+ BUNDLE_FILE=$(ls agent-artifact/aw-*.bundle 2>/dev/null | head -1 || true)
43
+ else
44
+ echo "::error::Artifact '$ARTIFACT_NAME' contained no agent_output.json. Nothing the agent proposed will be applied."
45
+ exit 1
46
+ fi
47
+
48
+ echo "Artifact '$ARTIFACT_NAME' carries $ITEM_COUNT item(s)."
49
+
50
+ echo "output-file=$OUTPUT_FILE" >> "$GITHUB_OUTPUT"
51
+ echo "bundle-file=$BUNDLE_FILE" >> "$GITHUB_OUTPUT"
52
+ echo "item-count=$ITEM_COUNT" >> "$GITHUB_OUTPUT"
@@ -0,0 +1,96 @@
1
+ name: Identify gate subject
2
+ description: Confirm the pull request is one we authored, resolve the issue it closes, and check that issue carries the required label.
3
+ inputs:
4
+ token:
5
+ description: GitHub token with issues and pull-requests read access.
6
+ required: true
7
+ pr-number:
8
+ description: Pull request number to gate.
9
+ required: true
10
+ ci-conclusion:
11
+ description: Conclusion reported by the CI run that triggered this gate.
12
+ required: true
13
+ linked-issue:
14
+ description: Issue the pull request closes, when the router already resolved it.
15
+ required: false
16
+ default: ''
17
+ require-label:
18
+ description: Label the closing issue must carry. Empty disables the check.
19
+ required: false
20
+ default: ''
21
+ outputs:
22
+ found:
23
+ description: Whether a valid gate subject was found.
24
+ value: ${{ steps.identify.outputs.found }}
25
+ pr:
26
+ description: Pull request number.
27
+ value: ${{ steps.identify.outputs.pr }}
28
+ issue:
29
+ description: Issue number the pull request closes.
30
+ value: ${{ steps.identify.outputs.issue }}
31
+ conclusion:
32
+ description: CI conclusion, passed through unchanged.
33
+ value: ${{ steps.identify.outputs.conclusion }}
34
+ runs:
35
+ using: composite
36
+ steps:
37
+ - name: Identify pull request and closing issue
38
+ id: identify
39
+ shell: bash
40
+ env:
41
+ GH_TOKEN: ${{ inputs.token }}
42
+ REPO: ${{ github.repository }}
43
+ PR_NUMBER: ${{ inputs.pr-number }}
44
+ CONCLUSION: ${{ inputs.ci-conclusion }}
45
+ LINKED_ISSUE: ${{ inputs.linked-issue }}
46
+ REQUIRE_LABEL: ${{ inputs.require-label }}
47
+ run: |
48
+ set -euo pipefail
49
+
50
+ none() {
51
+ echo "found=false" >> "$GITHUB_OUTPUT"
52
+ echo "$1"
53
+ exit 0
54
+ }
55
+
56
+ pr="$(gh pr view "$PR_NUMBER" --repo "$REPO" \
57
+ --json state,author,closingIssuesReferences,body)"
58
+
59
+ [ "$(jq -r '.state' <<<"$pr")" = "OPEN" ] || none "PR #$PR_NUMBER is not open"
60
+
61
+ # Use GitHub's bot flag because App login formats differ across APIs.
62
+ if [ "$(jq -r '.author.is_bot' <<<"$pr")" != "true" ]; then
63
+ none "PR #$PR_NUMBER was authored by $(jq -r '.author.login' <<<"$pr"), a human."
64
+ fi
65
+
66
+ issue="$LINKED_ISSUE"
67
+
68
+ if [ -z "$issue" ]; then
69
+ issue="$(jq -r '.closingIssuesReferences[0].number // empty' <<<"$pr")"
70
+ fi
71
+
72
+ if [ -z "$issue" ]; then
73
+ issue="$(jq -r '.body // ""' <<<"$pr" |
74
+ grep -oiE '(close[sd]?|fixe?[sd]?|resolve[sd]?) +#[0-9]+' |
75
+ grep -oE '[0-9]+' | head -n 1 || true)"
76
+ fi
77
+
78
+ [ -n "$issue" ] || none "PR #$PR_NUMBER does not close a numbered issue"
79
+
80
+ if [ -n "$REQUIRE_LABEL" ]; then
81
+ labels="$(gh issue view "$issue" --repo "$REPO" --json labels \
82
+ --jq '[.labels[].name]')"
83
+
84
+ if ! jq -e --arg name "$REQUIRE_LABEL" 'index($name)' >/dev/null <<<"$labels"; then
85
+ none "issue #$issue does not carry $REQUIRE_LABEL (has: $(jq -r 'join(", ")' <<<"$labels"))"
86
+ fi
87
+ fi
88
+
89
+ {
90
+ echo "found=true"
91
+ echo "pr=$PR_NUMBER"
92
+ echo "issue=$issue"
93
+ echo "conclusion=$CONCLUSION"
94
+ } >> "$GITHUB_OUTPUT"
95
+
96
+ echo "PR #$PR_NUMBER closes #$issue; CI concluded $CONCLUSION"
@@ -0,0 +1,39 @@
1
+ name: Link pull request to issue
2
+ description: Ensure the pull request body carries a closing reference to its source issue.
3
+ inputs:
4
+ token:
5
+ description: GitHub token with pull-requests write access.
6
+ required: true
7
+ pr-number:
8
+ description: Pull request number.
9
+ required: true
10
+ issue-number:
11
+ description: Issue number the pull request should close.
12
+ required: true
13
+ runs:
14
+ using: composite
15
+ steps:
16
+ - name: Ensure a closing reference
17
+ shell: bash
18
+ env:
19
+ GH_TOKEN: ${{ inputs.token }}
20
+ REPO: ${{ github.repository }}
21
+ PR_NUMBER: ${{ inputs.pr-number }}
22
+ ISSUE_NUMBER: ${{ inputs.issue-number }}
23
+ run: |
24
+ set -euo pipefail
25
+
26
+ pr="$(gh pr view "$PR_NUMBER" --repo "$REPO" --json closingIssuesReferences,body)"
27
+
28
+ if [ "$(jq '.closingIssuesReferences | length' <<<"$pr")" -gt 0 ]; then
29
+ echo "PR #$PR_NUMBER already closes an issue."
30
+ exit 0
31
+ fi
32
+
33
+ # A closing reference can only be created from the pull request body; there is no
34
+ # REST or GraphQL mutation for it.
35
+ body="$(jq -r '.body // ""' <<<"$pr")"
36
+ printf '%s\n\nCloses #%s\n' "$body" "$ISSUE_NUMBER" > /tmp/pr-body.md
37
+
38
+ gh pr edit "$PR_NUMBER" --repo "$REPO" --body-file /tmp/pr-body.md
39
+ echo "Added 'Closes #$ISSUE_NUMBER' to PR #$PR_NUMBER."
@@ -0,0 +1,32 @@
1
+ name: List open issues
2
+ description: |
3
+ Writes all open issues (number, title, labels) to `/tmp/gh-aw/agent/open-issues.json`
4
+ so the agent can check for semantic duplicates without spending turns discovering them.
5
+ inputs:
6
+ token:
7
+ description: GitHub token with issues:read permission.
8
+ required: true
9
+ repo:
10
+ description: Repository in `owner/name` format.
11
+ required: true
12
+ output-path:
13
+ description: Path to write the JSON output.
14
+ required: false
15
+ default: /tmp/gh-aw/agent/open-issues.json
16
+ runs:
17
+ using: composite
18
+ steps:
19
+ - name: List open issues
20
+ shell: bash
21
+ env:
22
+ GH_TOKEN: ${{ inputs.token }}
23
+ REPO: ${{ inputs.repo }}
24
+ OUTPUT_PATH: ${{ inputs.output-path }}
25
+ run: |
26
+ set -euo pipefail
27
+ mkdir -p "$(dirname "$OUTPUT_PATH")"
28
+ gh issue list --repo "$REPO" --state open --limit 500 \
29
+ --json number,title,labels \
30
+ --jq '[.[] | {number, title, labels: [.labels[].name]}]' \
31
+ > "$OUTPUT_PATH"
32
+ echo "Wrote $(jq 'length' "$OUTPUT_PATH") open issues to $OUTPUT_PATH"
@@ -0,0 +1,42 @@
1
+ name: Load issue context
2
+ description: Write issue metadata and comment context to a JSON file.
3
+ inputs:
4
+ token:
5
+ description: GitHub token used by github-script.
6
+ required: true
7
+ issue-number:
8
+ description: Issue number to load.
9
+ required: true
10
+ output-path:
11
+ description: Absolute path where context JSON is written.
12
+ required: true
13
+ runs:
14
+ using: composite
15
+ steps:
16
+ - name: Load issue and comments
17
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
18
+ env:
19
+ ISSUE_NUMBER: ${{ inputs.issue-number }}
20
+ OUTPUT_PATH: ${{ inputs.output-path }}
21
+ with:
22
+ github-token: ${{ inputs.token }}
23
+ script: |
24
+ const fs = require('fs');
25
+ const issueNumber = Number(process.env.ISSUE_NUMBER);
26
+ const issue = await github.rest.issues.get({ ...context.repo, issue_number: issueNumber });
27
+ const comments = await github.paginate(github.rest.issues.listComments, {
28
+ ...context.repo, issue_number: issueNumber, per_page: 100,
29
+ });
30
+
31
+ fs.mkdirSync(require('path').dirname(process.env.OUTPUT_PATH), { recursive: true });
32
+ fs.writeFileSync(process.env.OUTPUT_PATH, JSON.stringify({
33
+ number: issue.data.number,
34
+ title: issue.data.title,
35
+ body: issue.data.body,
36
+ labels: issue.data.labels.map(({ name }) => name),
37
+ comments: comments.map(({ user, author_association, body }) => ({
38
+ author: user.login,
39
+ association: author_association,
40
+ body,
41
+ })),
42
+ }, null, 2));
@@ -0,0 +1,35 @@
1
+ name: Merge agent pull request
2
+ description: Read merge_pull_request from agent_output.json and merge the PR via gh.
3
+ inputs:
4
+ output-file:
5
+ description: Path to agent_output.json.
6
+ required: true
7
+ token:
8
+ description: GitHub token with pull-requests:write and contents:write.
9
+ required: true
10
+ repo:
11
+ description: Target repository in owner/name format.
12
+ required: false
13
+ default: ${{ github.repository }}
14
+ runs:
15
+ using: composite
16
+ steps:
17
+ - name: Merge pull request
18
+ shell: bash
19
+ env:
20
+ GH_TOKEN: ${{ inputs.token }}
21
+ OUTPUT_FILE: ${{ inputs.output-file }}
22
+ REPO: ${{ inputs.repo }}
23
+ run: |
24
+ set -euo pipefail
25
+ [ -f "$OUTPUT_FILE" ] || exit 0
26
+
27
+ jq -c '.items[] | select(.type == "merge_pull_request")' "$OUTPUT_FILE" | while read -r item; do
28
+ PR_NUM=$(echo "$item" | jq -er '.pr_number // .item_number // empty')
29
+ [[ "$PR_NUM" =~ ^[1-9][0-9]*$ ]] || { echo "::error::Invalid pull request number: $PR_NUM"; exit 1; }
30
+
31
+ HEAD_SHA=$(gh pr view "$PR_NUM" --repo "$REPO" --json headRefOid --jq '.headRefOid')
32
+ [[ "$HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]] || { echo "::error::Invalid pull request head SHA for #$PR_NUM"; exit 1; }
33
+
34
+ gh pr merge "$PR_NUM" --repo "$REPO" --squash --match-head-commit "$HEAD_SHA"
35
+ done
@@ -0,0 +1,44 @@
1
+ name: Push agent branch
2
+ description: Read push_to_pull_request_branch from agent_output.json and fast-forward from its git bundle.
3
+ inputs:
4
+ output-file:
5
+ description: Path to agent_output.json.
6
+ required: true
7
+ bundle-file:
8
+ description: Path to the git bundle from the agent artifact.
9
+ required: false
10
+ default: ''
11
+ token:
12
+ description: GitHub token with contents:write.
13
+ required: true
14
+ runs:
15
+ using: composite
16
+ steps:
17
+ - name: Configure git credentials
18
+ shell: bash
19
+ env:
20
+ GH_TOKEN: ${{ inputs.token }}
21
+ run: |
22
+ git config --global url."https://x-access-token:${GH_TOKEN}@github.com/".insteadOf "https://github.com/"
23
+ - name: Read pull request branch
24
+ id: request
25
+ shell: bash
26
+ env:
27
+ GH_TOKEN: ${{ inputs.token }}
28
+ OUTPUT_FILE: ${{ inputs.output-file }}
29
+ BUNDLE_FILE: ${{ inputs.bundle-file }}
30
+ run: |
31
+ set -euo pipefail
32
+ [ -f "$OUTPUT_FILE" ] || exit 0
33
+
34
+ ITEM=$(jq -c '.items[] | select(.type == "push_to_pull_request_branch")' "$OUTPUT_FILE")
35
+ [ -n "$ITEM" ] || exit 0
36
+
37
+ BRANCH=$(echo "$ITEM" | jq -r '.branch')
38
+ echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
39
+ - name: Apply bundle to pull request branch
40
+ if: steps.request.outputs.branch != '' && inputs.bundle-file != ''
41
+ uses: ./.github/actions/apply-agent-bundle
42
+ with:
43
+ bundle-file: ${{ inputs.bundle-file }}
44
+ target-branch: ${{ steps.request.outputs.branch }}
@@ -0,0 +1,34 @@
1
+ name: Remove issue labels
2
+ description: Remove one or more labels from an issue or pull request.
3
+ inputs:
4
+ token:
5
+ description: GitHub token used by github-script.
6
+ required: true
7
+ issue-number:
8
+ description: Issue or pull request number.
9
+ required: true
10
+ labels:
11
+ description: Newline-delimited labels to remove.
12
+ required: true
13
+ runs:
14
+ using: composite
15
+ steps:
16
+ - name: Remove labels
17
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
18
+ env:
19
+ ISSUE_NUMBER: ${{ inputs.issue-number }}
20
+ LABELS: ${{ inputs.labels }}
21
+ with:
22
+ github-token: ${{ inputs.token }}
23
+ script: |
24
+ for (const label of process.env.LABELS.split(/\r?\n/).filter(Boolean)) {
25
+ try {
26
+ await github.rest.issues.removeLabel({
27
+ ...context.repo,
28
+ issue_number: Number(process.env.ISSUE_NUMBER),
29
+ name: label,
30
+ });
31
+ } catch (error) {
32
+ if (error.status !== 404) throw error;
33
+ }
34
+ }