Skip to content

Commit f3658df

Browse files
committed
fix(skills): reconcile Agent Guild with current catalog
Signed-off-by: AgentTanuki <294486129+AgentTanuki@users.noreply.github.com>
2 parents d374634 + 6242eac commit f3658df

70 files changed

Lines changed: 6577 additions & 210 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

β€Ž.github/workflows/check-extensions.ymlβ€Ž

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ jobs:
5858
run: python scripts/sync_extensions.py --check
5959

6060
sync-sdk-skill:
61+
permissions:
62+
contents: read
6163
runs-on: ubuntu-latest
6264
steps:
6365
- name: Checkout repository
@@ -69,4 +71,6 @@ jobs:
6971
python-version: "3.12"
7072

7173
- name: Check SDK skill is up to date
74+
env:
75+
GITHUB_TOKEN: ${{ github.token }}
7276
run: python scripts/sync_openhands_sdk_skill.py --check
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
---
2+
name: PR Artifacts
3+
4+
on:
5+
workflow_dispatch: # Manual trigger for testing
6+
pull_request_target:
7+
types: [opened, synchronize, reopened, closed]
8+
branches: [main]
9+
pull_request_review:
10+
types: [submitted]
11+
12+
jobs:
13+
# Auto-remove .pr/ directory from same-repository PRs when approved.
14+
cleanup-on-approval:
15+
concurrency:
16+
group: cleanup-pr-artifacts-${{ github.event.pull_request.number }}
17+
cancel-in-progress: false
18+
if: >-
19+
github.event_name == 'pull_request_review' &&
20+
github.event.review.state == 'approved' &&
21+
github.event.pull_request.head.repo.full_name == github.repository
22+
runs-on: ubuntu-latest
23+
permissions:
24+
contents: write
25+
pull-requests: write
26+
steps:
27+
# Use PAT so the push triggers CI workflows that will complete and
28+
# satisfy branch protection. We can't use [skip ci] because GitHub Apps
29+
# can create stuck checks that block merging.
30+
- uses: actions/checkout@v7
31+
with:
32+
ref: ${{ github.event.pull_request.head.ref }}
33+
token: ${{ secrets.OPENHANDS_BOT_GITHUB_PAT_PUBLIC }}
34+
35+
- name: Remove .pr/ directory
36+
id: remove
37+
run: |
38+
if [ -d ".pr" ]; then
39+
git config user.name "allhands-bot"
40+
git config user.email "allhands-bot@users.noreply.github.com"
41+
git rm -rf .pr/
42+
git commit -m "chore: Remove PR-only artifacts [automated]"
43+
git push || {
44+
echo "::error::Failed to push cleanup commit. Check branch protection rules."
45+
exit 1
46+
}
47+
echo "removed=true" >> "$GITHUB_OUTPUT"
48+
echo "::notice::Removed .pr/ directory"
49+
else
50+
echo "removed=false" >> "$GITHUB_OUTPUT"
51+
echo "::notice::No .pr/ directory to remove"
52+
fi
53+
54+
- name: Update PR comment after cleanup
55+
if: steps.remove.outputs.removed == 'true'
56+
uses: actions/github-script@v9
57+
with:
58+
script: |
59+
const marker = '<!-- pr-artifacts-notice -->';
60+
const body = `${marker}
61+
βœ… **PR Artifacts Cleaned Up**
62+
63+
The \`.pr/\` directory has been automatically removed.
64+
`;
65+
66+
const comments = await github.paginate(
67+
github.rest.issues.listComments,
68+
{
69+
owner: context.repo.owner,
70+
repo: context.repo.repo,
71+
issue_number: context.issue.number,
72+
per_page: 100,
73+
},
74+
);
75+
76+
const existing = comments.find(c => c.body.includes(marker));
77+
if (existing) {
78+
await github.rest.issues.updateComment({
79+
owner: context.repo.owner,
80+
repo: context.repo.repo,
81+
comment_id: existing.id,
82+
body: body,
83+
});
84+
}
85+
86+
# Inspect the fork through the API; never check out untrusted code with a write token.
87+
check-pr-artifacts:
88+
if: >-
89+
github.event_name == 'pull_request_target' &&
90+
github.event.action != 'closed'
91+
runs-on: ubuntu-latest
92+
permissions:
93+
contents: read
94+
pull-requests: write
95+
steps:
96+
- name: Post or update PR comment
97+
uses: actions/github-script@v9
98+
with:
99+
script: |
100+
const marker = '<!-- pr-artifacts-notice -->';
101+
const pullRequest = context.payload.pull_request;
102+
const headRepository = pullRequest.head.repo;
103+
let exists = true;
104+
105+
try {
106+
await github.rest.repos.getContent({
107+
owner: headRepository.owner.login,
108+
repo: headRepository.name,
109+
path: '.pr',
110+
ref: pullRequest.head.sha,
111+
});
112+
} catch (error) {
113+
if (error.status === 404) {
114+
exists = false;
115+
} else {
116+
throw error;
117+
}
118+
}
119+
120+
const comments = await github.paginate(
121+
github.rest.issues.listComments,
122+
{
123+
owner: context.repo.owner,
124+
repo: context.repo.repo,
125+
issue_number: context.issue.number,
126+
per_page: 100,
127+
},
128+
);
129+
const existing = comments.find(c => c.body.includes(marker));
130+
131+
if (!exists) {
132+
if (existing) {
133+
await github.rest.issues.updateComment({
134+
owner: context.repo.owner,
135+
repo: context.repo.repo,
136+
comment_id: existing.id,
137+
body: `${marker}
138+
βœ… **PR Artifacts Cleaned Up**
139+
140+
The \`.pr/\` directory is no longer present.
141+
`,
142+
});
143+
}
144+
return;
145+
}
146+
147+
const isFork = headRepository.full_name !== context.payload.repository.full_name;
148+
const cleanup = isFork
149+
? 'Because this is a fork PR, the workflow will **open or update a cleanup PR against `main` after merge**.'
150+
: 'The directory will be **automatically removed when the PR is approved**.';
151+
const body = `${marker}
152+
πŸ“ **PR Artifacts Notice**
153+
154+
This PR contains a \`.pr/\` directory with temporary PR-specific documents. ${cleanup}
155+
`;
156+
157+
core.warning('.pr/ directory contains temporary PR-only artifacts');
158+
if (existing) {
159+
await github.rest.issues.updateComment({
160+
owner: context.repo.owner,
161+
repo: context.repo.repo,
162+
comment_id: existing.id,
163+
body: body,
164+
});
165+
} else {
166+
await github.rest.issues.createComment({
167+
owner: context.repo.owner,
168+
repo: context.repo.repo,
169+
issue_number: context.issue.number,
170+
body: body,
171+
});
172+
}
173+
174+
# Fork branches cannot be modified reliably. If artifacts reach the trusted base,
175+
# remove them through a pull request so branch protections remain enforced.
176+
cleanup-after-merge:
177+
concurrency:
178+
group: cleanup-pr-artifacts-${{ github.event.pull_request.base.ref }}
179+
cancel-in-progress: false
180+
if: >-
181+
github.event_name == 'pull_request_target' &&
182+
github.event.action == 'closed' &&
183+
github.event.pull_request.merged == true
184+
runs-on: ubuntu-latest
185+
permissions:
186+
contents: write
187+
pull-requests: write
188+
steps:
189+
- uses: actions/checkout@v7
190+
with:
191+
ref: ${{ github.event.pull_request.base.ref }}
192+
token: ${{ secrets.OPENHANDS_BOT_GITHUB_PAT_PUBLIC }}
193+
194+
- name: Create or update cleanup PR
195+
id: cleanup
196+
env:
197+
BASE_REF: ${{ github.event.pull_request.base.ref }}
198+
CLEANUP_BRANCH: automation/remove-pr-artifacts
199+
GH_TOKEN: ${{ secrets.OPENHANDS_BOT_GITHUB_PAT_PUBLIC }}
200+
run: |
201+
if [ ! -d ".pr" ]; then
202+
echo "created=false" >> "$GITHUB_OUTPUT"
203+
echo "::notice::No .pr/ directory to remove from $BASE_REF"
204+
exit 0
205+
fi
206+
207+
git config user.name "allhands-bot"
208+
git config user.email "allhands-bot@users.noreply.github.com"
209+
git fetch origin \
210+
"+refs/heads/$CLEANUP_BRANCH:refs/remotes/origin/$CLEANUP_BRANCH" || true
211+
git checkout -B "$CLEANUP_BRANCH"
212+
git rm -rf .pr/
213+
git commit \
214+
-m "chore: remove merged PR artifacts" \
215+
-m "Co-authored-by: openhands <openhands@all-hands.dev>"
216+
git push --force-with-lease origin "HEAD:$CLEANUP_BRANCH"
217+
218+
cleanup_pr_url=$(gh pr list \
219+
--base "$BASE_REF" \
220+
--head "$CLEANUP_BRANCH" \
221+
--state open \
222+
--json url \
223+
--jq '.[0].url')
224+
225+
if [ -z "$cleanup_pr_url" ]; then
226+
cleanup_pr_url=$(gh pr create \
227+
--base "$BASE_REF" \
228+
--head "$CLEANUP_BRANCH" \
229+
--title "chore: remove merged PR artifacts" \
230+
--body-file - <<EOF
231+
## Why
232+
233+
Temporary \`.pr/\` artifacts reached \`$BASE_REF\` through one or more merged PRs.
234+
235+
## Summary
236+
237+
- Remove the temporary \`.pr/\` directory from \`$BASE_REF\`.
238+
239+
## How to Test
240+
241+
- Confirm this PR only deletes files under \`.pr/\`.
242+
- Let the required checks validate the cleanup commit.
243+
244+
---
245+
246+
_This PR was automatically created by the PR Artifacts workflow._
247+
EOF
248+
)
249+
fi
250+
251+
echo "created=true" >> "$GITHUB_OUTPUT"
252+
echo "pr_url=$cleanup_pr_url" >> "$GITHUB_OUTPUT"
253+
echo "::notice::Cleanup PR ready: $cleanup_pr_url"
254+
255+
- name: Update source PR comment
256+
if: steps.cleanup.outputs.created == 'true'
257+
uses: actions/github-script@v9
258+
env:
259+
CLEANUP_PR_URL: ${{ steps.cleanup.outputs.pr_url }}
260+
with:
261+
script: |
262+
const marker = '<!-- pr-artifacts-notice -->';
263+
const body = `${marker}
264+
🧹 **PR Artifact Cleanup Queued**
265+
266+
The \`.pr/\` directory reached \`${context.payload.pull_request.base.ref}\` after merge. A cleanup PR has been opened or updated: ${process.env.CLEANUP_PR_URL}
267+
`;
268+
269+
const comments = await github.paginate(
270+
github.rest.issues.listComments,
271+
{
272+
owner: context.repo.owner,
273+
repo: context.repo.repo,
274+
issue_number: context.issue.number,
275+
per_page: 100,
276+
},
277+
);
278+
const existing = comments.find(c => c.body.includes(marker));
279+
280+
if (existing) {
281+
await github.rest.issues.updateComment({
282+
owner: context.repo.owner,
283+
repo: context.repo.repo,
284+
comment_id: existing.id,
285+
body: body,
286+
});
287+
} else {
288+
await github.rest.issues.createComment({
289+
owner: context.repo.owner,
290+
repo: context.repo.repo,
291+
issue_number: context.issue.number,
292+
body: body,
293+
});
294+
}

β€Ž.github/workflows/pr-description-check.ymlβ€Ž

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ jobs:
1717
validate-pr-description:
1818
name: Validate PR description
1919
# Draft PRs may still have incomplete descriptions; validate when review starts.
20-
# Release PRs are generated by the trusted prepare-release workflow without
20+
# Release and artifact-cleanup PRs are generated by trusted workflows without
2121
# the standard human-authored PR template.
2222
if: >-
2323
github.event.pull_request.draft == false &&
2424
github.event.pull_request.user.login != 'dependabot[bot]' &&
2525
!(github.event.pull_request.head.repo.full_name == github.repository &&
26-
startsWith(github.event.pull_request.head.ref, 'release-please-'))
26+
(startsWith(github.event.pull_request.head.ref, 'release-please-') ||
27+
(github.event.pull_request.base.ref == 'main' &&
28+
github.event.pull_request.head.ref == 'automation/remove-pr-artifacts')))
2729
runs-on: ubuntu-latest
2830
steps:
2931
- name: Checkout trusted workflow scripts
-107 KB
Binary file not shown.
-93.9 KB
Binary file not shown.
-78.9 KB
Binary file not shown.
-105 KB
Binary file not shown.
-99.5 KB
Binary file not shown.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "0.20.0"
2+
".": "0.21.0"
33
}

β€ŽAGENTS.mdβ€Ž

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ This repository owns the public registry of reusable OpenHands skills, plugins,
1010

1111
Related repositories have distinct responsibilities:
1212

13-
- [`OpenHands/software-agent-sdk`](https://github.com/OpenHands/software-agent-sdk) owns the Python SDK, Agent Server, agent/tool behavior, conversations, workspaces, events, and canonical API.
14-
- [`OpenHands/typescript-client`](https://github.com/OpenHands/typescript-client) owns the browser-compatible typed Agent Server client.
13+
- [`OpenHands/software-agent-sdk`](https://github.com/OpenHands/software-agent-sdk) owns the Python SDK, Agent Server, agent/tool behavior, conversations, workspaces, events, canonical API, and browser-compatible TypeScript Agent Server client under `clients/typescript/`.
1514
- [`OpenHands/OpenHands`](https://github.com/OpenHands/OpenHands) owns Agent Canvas UI and local-stack orchestration.
1615
- [`OpenHands/automation`](https://github.com/OpenHands/automation) owns scheduling, webhooks, run history, dispatch, and sandbox lifecycle orchestration.
1716

18-
Put reusable skills, plugins, automations, and integrations here; put backend execution behavior in the SDK, typed API access in `typescript-client`, application UI in Agent Canvas, and scheduling/dispatch lifecycle code in `automation`. If a PR is opened in the wrong repository, explicitly recommend closing and moving it to the owning repository. PRs must follow this repository's applicable contribution and code-review guidance.
17+
Put reusable skills, plugins, automations, and integrations here; put backend execution behavior and typed API access in the SDK, application UI in Agent Canvas, and scheduling/dispatch lifecycle code in `automation`. If a PR is opened in the wrong repository, explicitly recommend closing and moving it to the owning repository. PRs must follow this repository's applicable contribution and code-review guidance.
1918

2019
## What this repo contains
2120

@@ -118,6 +117,31 @@ When editing or adding skills in this repo, follow these rules (and add new skil
118117
- `plugins/release-notes` now has a standalone validator at `plugins/release-notes/scripts/validate_release_notes.py`; it rebuilds the deterministic tag-range context, fails if a change bullet omits explicit PR/commit refs or matching author handles, and enforces full PR/author coverage by appending a compact `### πŸ”Ž Small Fixes/Internal Changes` appendix grouped by author when the agent omits lower-signal items. New contributor detection in `generate_release_notes.py` should use merged PR history for human authors (excluding bots) rather than commit-author lookup.
119118

120119

120+
## PR-specific documents (`.pr/`)
121+
122+
When working on a PR that requires design documents, live-test logs, development-only scripts, or other temporary artifacts that should **not** be merged to `main`, store them in a `.pr/` directory at the repository root.
123+
124+
```bash
125+
mkdir -p .pr
126+
127+
.pr/
128+
β”œβ”€β”€ design.md # Design decisions and architecture notes
129+
β”œβ”€β”€ analysis.md # Investigation or debugging notes
130+
└── notes.md # Any other PR-specific content
131+
```
132+
133+
The `PR Artifacts` workflow (`.github/workflows/pr-artifacts.yml`) owns the lifecycle of this directory:
134+
135+
1. **Notification**: When a PR contains `.pr/`, a single comment is posted to the PR conversation alerting reviewers.
136+
2. **Auto-cleanup on approval**: For same-repository PRs, the directory is automatically removed by a follow-up commit when the PR is approved.
137+
3. **Post-merge cleanup**: If artifacts reach `main`, including through a fork PR, the workflow opens or updates a cleanup PR against `main`.
138+
139+
Important notes:
140+
141+
- Do not put anything in `.pr/` that needs to be preserved.
142+
- The `.pr/` check is informational during development; it posts a notice rather than blocking the PR.
143+
- Cleanup PRs follow the normal review and required-check protections for `main`.
144+
121145
## CI / validation gotchas
122146

123147
- The test suite expects **every directory under `skills/`** to be listed in a marketplace. If you add a new skill (or rebase onto a main branch that added skills), update the appropriate marketplace file or CI will fail with `Skills missing from marketplace: [...]`.

0 commit comments

Comments
Β (0)