diff --git a/.agents/skills/issue/SKILL.md b/.agents/skills/issue/SKILL.md new file mode 100644 index 000000000..482d8cbc4 --- /dev/null +++ b/.agents/skills/issue/SKILL.md @@ -0,0 +1,212 @@ +--- +name: issue +description: Create GitHub issues with repo conventions, issue templates, audience control (internal/community), and issue type assignment. Also switches existing issues between internal and community. Use when user says "create an issue", "file a bug", "open an issue", "make this community", "switch issue to internal", or wants to create or manage issues. +--- + +# issue + +Create issues or switch the audience of existing issues, respecting repo conventions. + +## Prerequisites + +- `gh` CLI installed and authenticated. If missing, stop and tell the user. + +## Modes + +Detect mode from user input: + +- **Create** — no existing issue number referenced. Default mode. +- **Switch** — an existing issue number is referenced with an audience change intent (e.g., "make #42 community", "switch #42 to internal"). + +--- + +## Create Mode + +### 1. Read repo conventions + +Read these files in order, skipping any that don't exist: + +1. **`docs/agents/issue-tracker.md`** — determines which tracker and CLI to use. If missing, assume GitHub + `gh`. +2. **`docs/agents/issue-audience.md`** — audience labels and footers. If missing, offer to set it up (see [Setup Flow](#setup-flow)). +3. **`docs/agents/triage-labels.md`** — triage label vocabulary. If missing, skip triage labels entirely. +4. **`GLOSSARY.md`** — domain glossary for terminology. Use canonical terms in issue title and body. +5. **`.github/ISSUE_TEMPLATE/`** — parse frontmatter `type:` field from each template to determine available issue types. If no templates exist, fall back to: Bug, Feature, Task. + +### 2. Gather context + +If conversation context exists (e.g., from a prior /grill-with-docs, /triage, or discussion), draft the issue from that context. Otherwise, ask the user to describe the issue. + +### 3. Explore the codebase + +Scale exploration depth to the issue type: + +- **Task** — minimal. Skim relevant files to confirm terminology and add a sentence of context. +- **Bug / Spike** — moderate. Trace the relevant code path, check recent git history in the area, note related files or modules. +- **Feature / Epic** — light touch only. These are expected to go through /to-prd and /grill-with-docs for full scoping. Add enough context to orient a reader, not to scope the work. + +Use findings to enrich the issue body with technical context the user didn't provide. Use domain glossary terms from `GLOSSARY.md`. + +### 4. Determine issue type + +If the type is obvious from context, confirm it with the user. Otherwise, present the available types (derived from templates) and ask. + +**Important:** The issue type (Bug, Feature, Task, etc.) is NOT a label. It is set at creation time with `gh issue create --type ` (see step 11). Do not add the issue type as a label. When the user says "create a bug issue" or "file a bug", interpret "bug" as the issue type, not a label to apply. + +### 5. Check available labels + +Fetch the repo's label list: +```bash +gh label list --limit 100 +``` + +Select labels that are relevant to the issue based on the content, area of code, or category. Do NOT apply the issue type as a label (see step 4). Only apply labels that actually exist in the repo. + +### 6. Search for related issues + +Search open issues that might be related: +```bash +gh issue list --state open --search "" +``` + +If related issues exist, mention them in the draft body under a "Related Issues" section (e.g., `Related: #123, #456`). If a duplicate is found, flag it to the user before drafting. + +### 7. Ask about audience + +Always ask explicitly: + +> Is this issue internal (core team only) or open for community contribution? (default: internal) + +Never assume. Apply the label and footer from `docs/agents/issue-audience.md` based on the answer. + +### 8. Ask about parent issue + +If the user mentioned a parent issue, link it. Do NOT prompt for a parent — only pick it up if volunteered. + +### 9. Draft the issue + +Compose the issue using the corresponding issue template (if one exists for the chosen type). Fill in template sections from gathered context. Add: + +- The correct audience footer, wrapped in `` tags. GitHub does not render unknown HTML elements, so they act as invisible markers: + ``` + + + > [!IMPORTANT] + > Footer text here. + + + ``` + +**Redaction rules** — never include in title or body: +- Security vulnerabilities, CVEs, CWEs, or attack vectors +- Secrets, API keys, tokens +- PII + +If an `issue-internal` skill is available, invoke it now, before the preview in step 10, so the draft the user reviews has already been screened. + +### 10. Preview and confirm + +Show the user the full draft: +- Title +- Type +- Labels (audience + triage if applicable) +- Parent issue (if any) +- Body (rendered) + +Wait for explicit approval. If the user requests edits, apply them and re-preview. + +### 11. Publish + +Create the issue in one command, setting the type, parent, and any dependencies as flags (requires `gh` 2.94.0+): + +```bash +gh issue create --title "" --body "<body>" --label "<labels>" --type "<type>" \ + --parent <parent-number-or-url> \ + --blocked-by <numbers> --blocking <numbers> +``` + +`--type` sets the issue type, `--parent` links it as a sub-issue, and `--blocked-by`/`--blocking` mark dependencies (comma-separated numbers or URLs). Omit any flag you don't need. + +If the triage system is present and the issue type has a default triage state, apply the triage label. + +Print the issue URL when done. + +--- + +## Switch Mode + +Switch an existing issue's audience between internal and community. + +### 1. Read conventions + +Read `docs/agents/issue-audience.md`. If missing, offer to set it up. + +### 2. Determine current audience + +Check the issue's labels for the internal label defined in `issue-audience.md`. + +### 3. Confirm the switch + +Tell the user the current audience and ask to confirm the target: +> Issue #42 is currently **internal**. Switch to **community**? + +### 4. Update the footer + +Replace the footer in the issue body using this priority: + +1. **`<audience-footer>` tags** — if `<audience-footer>` and `</audience-footer>` exist, replace content between them. +2. **Legacy HTML comment markers** — if `<!-- audience-footer-start -->` and `<!-- audience-footer-end -->` exist, replace the entire block (including markers) with the new `<audience-footer>` format. +3. **Known footer text** — if the current footer text (from `issue-audience.md`) is found in the body, replace it and wrap with `<audience-footer>` tags. +4. **Append** — if neither is found, append the new footer wrapped in `<audience-footer>` tags. + +### 5. Update labels + +**Switching to internal:** +- Add the internal label +- Remove any signal labels defined in the community section of `issue-audience.md` + +**Switching to community:** +- Remove the internal label +- Add the primary signal label defined in the community section of `issue-audience.md` + +### 6. Apply changes + +```bash +gh issue edit <number> --body "<updated body>" --add-label "<label>" --remove-label "<label>" +``` + +Print the issue URL when done. + +--- + +## Setup Flow + +When `docs/agents/issue-audience.md` is missing, offer to create it with these defaults: + +```markdown +# Issue Audience + +How issues in this repo are marked for internal team use vs. open community contribution. + +## Internal + +- **Label:** `internal` +- **Footer:** + + > [!IMPORTANT] + > Internal only — this issue is maintained by the core team and is not accepting external contributions. + +## Community + +- **Signal labels:** `help-wanted` +- **Footer:** + + > [!IMPORTANT] + > If you are interested in working on this issue, please leave a comment. + + > [!TIP] + > Please use a 👍 reaction to provide a +1/vote. This helps the community and maintainers prioritize this request. +``` + +Present these defaults and ask: "Does this look right for your repo, or do you want to change any of the labels or footer text?" + +Apply edits, then write the file and continue with the original task. diff --git a/.agents/skills/pr/CREDITS.md b/.agents/skills/pr/CREDITS.md index 5314bda2f..ce491201f 100644 --- a/.agents/skills/pr/CREDITS.md +++ b/.agents/skills/pr/CREDITS.md @@ -1,3 +1,5 @@ # Credits -The **Summary** section's menu of visuals (pseudocode, call trees, component trees, file trees, Mermaid, diffs) and its placement guidance come from [Dex Horthy](https://github.com/dexhorthy)'s [`show-me`](https://github.com/humanlayer/humanlayer) skill, reproduced almost word for word and aimed at a diff instead of a live conversation. `pr` does not depend on `show-me` as a skill (it isn't part of this repo, and a hard dependency would break standalone installs), so the content is copied in rather than pointed at; this file is the attribution a dependency would otherwise have carried. +The template, its section guidance, and the layout of this skill come from [Matt Pocock](https://github.com/mattpocock)'s [`pr`](https://github.com/mattpocock/skills/blob/main/skills/engineering/pr/SKILL.md) skill, extended here with a publish workflow, related-issue linking, and language screening. + +The **Summary** section's menu of visuals (pseudocode, call trees, component trees, file trees, Mermaid, diffs) and its placement guidance come from [Dex Horthy](https://github.com/dexhorthy)'s [`show-me`](https://github.com/humanlayer/humanlayer) skill, by way of Matt's, reproduced almost word for word and aimed at a diff instead of a live conversation. `pr` does not depend on `show-me` as a skill (it isn't part of this repo, and a hard dependency would break standalone installs), so the content is copied in rather than pointed at; this file is the attribution a dependency would otherwise have carried. diff --git a/.agents/skills/pr/SKILL.md b/.agents/skills/pr/SKILL.md index 84dd4fb2f..bfe8bc647 100644 --- a/.agents/skills/pr/SKILL.md +++ b/.agents/skills/pr/SKILL.md @@ -14,12 +14,16 @@ Use this template for writing the PR body: ```markdown ## Summary +<one sentence on why> + <diagram, diff-sketch, or tree> ## Evidence -- **Before:** <screenshot/output/failing test run> - **After:** <screenshot/output/passing test run> +**Before:** <one line on the old behavior, or the failing test> +**After:** <screenshot, video, or passing test> + +**Verified live:** <what was exercised, against what> ## Merge Danger @@ -27,18 +31,25 @@ Use this template for writing the PR body: <optional: description> -**Blast Radius:** <one-word description> +**Impact:** <one-word description> <optional: potential ramifications of merge> + +## Related + +- Closes #123 +- Related to #456 ``` ## Sections -Skip all preambles and keep prose brief. Use the user's domain language from `GLOSSARY.md`. +Skip all preambles and keep prose brief. Use the domain language from `GLOSSARY.md` when present. Scale depth to diff size: a 3-line fix gets a sentence and a danger line, a 20-file feature gets the full set. Describe the **final diff** against the target branch, not the commit-by-commit journey. ### Summary -Pick the smallest view that makes the key point clear. +Open with one sentence on why: the problem or goal. If the session context cannot explain why, pause and ask the user. + +Then pick the smallest view that makes the key point clear. - Show logic or an algorithm as pseudocode: @@ -140,7 +151,7 @@ For a state or control-flow change: + invalidate cache ``` -- Show the whole block when most of it is new, when omitted context would hide ownership or order, or when the user needs a copyable target shape: +- Show the whole block when most of it is new, when omitted context would hide ownership or order, or when the reader needs a copyable target shape: ```ts function expandSkill(command: string): string { @@ -151,20 +162,96 @@ function expandSkill(command: string): string { #### Guidance -Place each visual next to the short text it supports. Keep only the calls, files, props, states, and boundaries needed to answer the user's current question or the options to resolve the current discussion point. +Place each visual next to the short text it supports. Keep only the calls, files, props, states, and boundaries a reviewer needs to follow the change. -You may use one of these, you may use several, it is unlikely you will use all of them. Use your judgement and don't overwhelm the user. +You may use one of these, you may use several, it is unlikely you will use all of them. Use your judgement and don't overwhelm the reader. ### Evidence -Concrete evidence that the change works. Show a before and after. +Concrete evidence that this change works. Before is one line on the old behavior. -Screenshots are S-tier - when the environment is set up for it and the change is visual. +- **Screenshots are S-tier.** When the diff changes anything a user can see in the UI, including behavior that shows through the UI, the After is a screenshot, or a video when the point is an interaction or motion. Done when every UI-visible change has an After. Capture them with the session's browser tooling; when there is none, ask the user for the files. See [references/evidence.md](references/evidence.md) for attaching them. +- **Execution-based evidence is A-tier.** Show the exact test that failed before and passes now, using pseudocode, or the console output that changed. +- **Verified live**: when this session exercised the change by hand, such as clicking through it in a browser or running it against a real database or service, name what was exercised and against what. Omit the line when nothing ran live. -Execution-based evidence is A-tier. Test results, console output. Show the exact test that now fails and passes, using pseudocode. +Evidence is specific to this change. CI reports the suite, lint, and typecheck. + +Omit the section when there is nothing observable to show, such as docs or a pure refactor. ### Merge Danger Describe whether it's a one-way or two-way door. You can walk back through two-way doors, but not one-way doors. A PR that is cheap to roll back is lower risk. Changes that involve destructive actions or hard-to-reverse decisions are one-way doors. -The blast radius is the potential impact or scope of the changes introduced by this PR. Consider all possibilities. Examples are layout shift, breakages for consumers, mobile responsiveness, etc. +The impact is the potential scope of the changes introduced by this PR. Consider all possibilities. Examples are layout shift, breakage for consumers, data or migration impact, mobile responsiveness. Add a line of ramifications only when they are not obvious. + +### Related + +The classified issues from step 3 as a bulleted list, required for GitHub's title expansion. Omit the section when there are none. + +### Redaction + +Never mention these in the title or body, Merge Danger included: + +- Security fixes, CVEs, CWEs, or how an issue is triggered. Frame impact as consumer breakage, data or migration impact, or rollback cost. +- Secrets, API keys, tokens. +- PII (personally identifiable information): names of real users, email addresses, phone numbers, physical addresses, IP addresses, account IDs, or any data that could identify a specific person. + +## Workflow + +Requires the `gh` CLI, installed and authenticated. If missing, stop and tell the user. + +### 1. Pre-flight checks + +1. **Target branch**: `gh repo view --json defaultBranchRef -q .defaultBranchRef.name`. Refuse if the current branch is the target branch. +2. **Uncommitted changes**: Run `git status --porcelain`. If dirty, show the user what's uncommitted and ask whether to commit or abort. +3. **Remote**: Use the branch's upstream tracking remote if set, otherwise default to `origin`. +4. **Branch name**: when `/language-rules` is available, screen the branch name with it now. The name goes public the moment step 2 runs and stays in the remote's reflog and in any notification sent after a delete. Rename with `git branch -m <new-name>` when it needs one. When an earlier run already pushed the flagged name, say so and let the user choose: delete the remote branch and push the new name, or accept that the old one stays. + +### 2. Push + +Push the branch to the remote. Use `git push -u <remote> HEAD`. + +### 3. Search backlog for related issues + +Determine the tracker from `docs/agents/issue-tracker.md` if present; otherwise use whatever issue tooling is available (e.g., `gh`, an MCP server). + +Gather issue references from two sources: + +1. **Explicit references** — scan commit messages and branch name for issue references (e.g., `#<number>`). +2. **Backlog search** — extract 2–4 keywords from the diff (changed module names, feature area, error being fixed) and search open issues. Run multiple searches if the diff spans unrelated areas. Include recently closed issues if the diff is a follow-up fix. + +**Completion criterion**: every issue that a reviewer would say "this is related" has been found. Err toward inclusion — a spurious "Related to" link is cheap; a missing "Closes" leaves an issue open. + +Classify each found issue: +- **Closes** — this PR fully resolves the issue. +- **Related to** — the PR touches the same area or partially addresses it. + +Discard issues that share only surface keywords but describe unrelated work. + +### 4. Write the PR + +**Title**: Generate from the diff — concise, imperative mood. Apply a conventional prefix only when `CONTRIBUTING.md` or recent merged PR titles use one. + +**Body**: the template above. Capture every screenshot and video now, saved outside the repo, since they upload in step 6. + +### 5. Screen + +When `/language-rules` is available, apply it to the title, body, and every screenshot and video. These are written from session context, so a ticket id, a doc name, or a system name can reach them without ever appearing in the code. The why sentence and the Related lines are the likeliest places. + +- Rewrite replaceable terms on the spot. +- A term quoted from the diff stays, since the text describes code that already carries it. Name it once as separate diff cleanup and keep publishing. +- When a reference is load-bearing and has no public form, show the user the sentence and ask. +- Open each screenshot, and the screens each video passes through, and check the browser chrome and page content for hostnames, account ids, aliases, and ticket ids. Retake or crop rather than attach. + +Show the final title and body, separated from your own text by horizontal rules, with any screening changes called out in one line. + +### 6. Publish + +Create the PR once, open for review, with the final body. Screenshots and video upload in this same call with `--attach`. + +```bash +gh pr create --title "<title>" --body "<body>" --base <target-branch> \ + --attach './after.png#After' +``` + +No labels, reviewers, or assignees. Print the PR URL when done. diff --git a/.agents/skills/pr/agents/openai.yaml b/.agents/skills/pr/agents/openai.yaml deleted file mode 100644 index 48286f48f..000000000 --- a/.agents/skills/pr/agents/openai.yaml +++ /dev/null @@ -1,3 +0,0 @@ -interface: - display_name: "PR" - short_description: "Write a PR body that's fast to review" diff --git a/.agents/skills/pr/references/evidence.md b/.agents/skills/pr/references/evidence.md new file mode 100644 index 000000000..e98da15cc --- /dev/null +++ b/.agents/skills/pr/references/evidence.md @@ -0,0 +1,60 @@ +# Evidence + +## Test evidence + +````markdown +**Before:** `skips the write when content is unchanged` fails + +```text +expected writeFile to be called 1 time, received 2 +``` + +**After:** the same test passes + +```text +✓ skips the write when content is unchanged (3 ms) +``` +```` + +## Screenshots and video + +`gh pr create --attach` uploads an image or video to GitHub and puts it in the body. No separate hosting, no committing images to the repo. + +- **Only on create.** `gh pr edit` has no `--attach`, so capture every screenshot before step 6. +- Up to 50 files per command. Images and video both work. Video renders as a player and has no alt text. +- Alt text follows the path after `#`: `--attach './login.png#The login error state'`. Without it the filename is used. +- Save files outside the repo, such as `$TMPDIR/pr-evidence/`, so they are never committed. + +Check the installed `gh` supports it: `gh pr create --help | grep -- --attach`. If it doesn't, write the Evidence section with a placeholder line and tell the user to drag the images into the PR by hand. + +### Append + +Attach without referencing the file in the body and `gh` appends the uploads to the end of the body. + +```bash +gh pr create --title "Fix login error state" --body "$BODY" \ + --attach './after.png#Login showing the error message' +``` + +### Place inline + +Reference the file in the body with a relative path and `gh` rewrites it to the uploaded asset in place. Use this to put each image under the After label in the Evidence section. + +```bash +BODY='## Evidence + +**Before:** login failed with no message. + +**After:** + +![Login showing the error message](./after.png)' + +gh pr create --title "Fix login error state" --body "$BODY" \ + --attach ./after.png +``` + +Alt text written in the body wins over alt text after `#`, so give it in one place. + +### Partial failure + +If some uploads fail, the PR is still created with the ones that worked. The command exits non-zero but still prints the PR URL. Capture the URL, do not retry `gh pr create` (it would fail on the existing PR), and tell the user which files are missing. diff --git a/.claude/skills/issue b/.claude/skills/issue new file mode 120000 index 000000000..53687b944 --- /dev/null +++ b/.claude/skills/issue @@ -0,0 +1 @@ +../../.agents/skills/issue \ No newline at end of file diff --git a/.kiro/skills/issue b/.kiro/skills/issue new file mode 120000 index 000000000..53687b944 --- /dev/null +++ b/.kiro/skills/issue @@ -0,0 +1 @@ +../../.agents/skills/issue \ No newline at end of file diff --git a/skills-lock.json b/skills-lock.json index ce8261066..d628c66ff 100644 --- a/skills-lock.json +++ b/skills-lock.json @@ -73,11 +73,15 @@ "skillPath": "skills/engineering/improve-codebase-architecture/SKILL.md", "computedHash": "ac2c94885f34ff8492dc12aa4543182f661f32ea1722b8d6b6c2c7045af33ae3" }, + "issue": { + "source": "../../../../Developer/skills/public", + "sourceType": "local", + "computedHash": "e59fcc3b78219dfe3c3948061c53274109888bfbc1eef15b382a2771f9e9e2e4" + }, "pr": { - "source": "mattpocock/skills", - "sourceType": "github", - "skillPath": "skills/engineering/pr/SKILL.md", - "computedHash": "22a15d4d469344b38e03eb0e95772478393975836289393b1bad3c0febcdc679" + "source": "../../../../Developer/skills/public", + "sourceType": "local", + "computedHash": "c95347e5d92574c80c51e20e44f33f1f60eea03e2c26f1aefb9d447062348632" }, "prototype": { "source": "mattpocock/skills",