Add a Skill to create release notes (#9711)
diff --git a/.agents/skills/adding-release-notes/SKILL.md b/.agents/skills/adding-release-notes/SKILL.md
new file mode 100644
index 0000000..c47b3e6
--- /dev/null
+++ b/.agents/skills/adding-release-notes/SKILL.md
@@ -0,0 +1,87 @@
+---
+name: adding-release-notes
+description: Adds user-facing change descriptions to DevTools release notes. Use when documenting improvements, fixes, or new features in the NEXT_RELEASE_NOTES.md file.
+---
+
+# Adding Release Notes
+
+This skill helps automate adding release notes to `packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md`.
+
+## Workflow
+Copy this checklist into your response to track progress:
+
+```markdown
+Release Notes Progress:
+- [ ] Step 1: Formulate the entry (past tense)
+- [ ] Step 2: Find the PR number (if not already known)
+- [ ] Step 3: Determine the section (Inspector, Memory, etc.)
+- [ ] Step 4: Add the entry (use scripts/add_note.dart)
+- [ ] Step 5: Add images (if applicable)
+```
+
+## Guidelines
+
+### 1. Identify the PR Number
+If the PR number is unknown, use the following methods to find it:
+- **Local Branch**: Identify the branch name using `git branch` or `git log`. If the branch is pushed to origin, it often has a linked PR.
+- **GitHub CLI (`gh`)**: Use the GitHub CLI to find the PR associated with the current branch.
+ - **IMPORTANT**: Always use `PAGER=cat` to prevent `gh` from hanging in non-interactive terminals.
+ - Command: `PAGER=cat gh pr list --head <branch_name> --json number,title`
+- **Search by Change Description**: Search open PRs using keywords from your change title or description.
+ - Command: `PAGER=cat gh pr list --search "<keywords>" --limit 5`
+- **Web Search**: If CLI tools fail, use `search_web` to find the PR on GitHub:
+ - Query: `site:github.com/flutter/devtools "Add support for searching within the log details view"`
+
+### 2. Formulate the Entry
+- **Tense**: Always use **past tense** (e.g., "Added", "Improved", "Fixed").
+- **Punctuation**: Always end entries with a **period**.
+- **Template**: `* <Description>. [#<PR_NUMBER>](https://github.com/flutter/devtools/pull/<PR_NUMBER>)`
+- **Placeholder**: Use `TODO` if you have exhausted all search methods and the PR has not been created yet.
+- **Images**: If adding an image, indent it by two spaces to align with the bullet point, and ensure there is only one newline between the text and the image.
+ - Correct Format:
+ ```markdown
+ - Added support for XYZ. [#TODO](https://github.com/flutter/devtools/pull/TODO)
+ 
+ ```
+- **Examples**:
+ - `* Added support for XYZ. [#12345](https://github.com/flutter/devtools/pull/12345)`
+ - `* Fixed a crash in the ABC screen. [#67890](https://github.com/flutter/devtools/pull/67890)`
+
+### 3. User-Facing Changes Only
+- **Criteria**: Focus on **what** changed for the user, not **how** it was implemented.
+- **Avoid**: Technical details like "Implemented XYZ with a new controller", "Updated the build method", or naming internal classes.
+- **Example (Bad)**: `* Implemented log details search using SearchControllerMixin. [#TODO](https://github.com/flutter/devtools/pull/TODO)`
+- **Example (Good)**: `* Added search support to the log details view. [#TODO](https://github.com/flutter/devtools/pull/TODO)`
+
+### 4. Determine Section
+Match the change to the section in `NEXT_RELEASE_NOTES.md`:
+- `General updates`
+- `Inspector updates`
+- `Performance updates`
+- `CPU profiler updates`
+- `Memory updates`
+- `Debugger updates`
+- `Network profiler updates`
+- `Logging updates`
+- `App size tool updates`
+- `Deep links tool updates`
+- `VS Code sidebar updates`
+- `DevTools extension updates`
+- `Advanced developer mode updates`
+
+### 5. Add to NEXT_RELEASE_NOTES.md
+Use the provided utility script to insert the note safely. The script handles replacing the TODO placeholder if it's the first entry in that section.
+
+```bash
+dart .agents/skills/adding-release-notes/scripts/add_note.dart "Inspector updates" "Added XYZ support" TODO
+```
+
+### 6. Optional: Images
+Add images to `packages/devtools_app/release_notes/images/` and reference them:
+```markdown
+
+```
+**Constraint**: Use **dark mode** for screenshots.
+
+## Resources
+- [README.md](../../packages/devtools_app/release_notes/README.md): Official project guidance.
diff --git a/.agents/skills/adding-release-notes/scripts/add_note.dart b/.agents/skills/adding-release-notes/scripts/add_note.dart
new file mode 100644
index 0000000..42ca9f5
--- /dev/null
+++ b/.agents/skills/adding-release-notes/scripts/add_note.dart
@@ -0,0 +1,72 @@
+// Copyright 2019 The Flutter Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
+
+import 'dart:io';
+
+void main(List<String> args) {
+ if (args.length < 3) {
+ print('Usage: dart add_note.dart <section> <note> <pr_number>');
+ exit(1);
+ }
+
+ final section = args[0].trim();
+ final note = args[1].trim();
+ final pr = args[2].trim();
+
+ final prLink = pr == 'TODO'
+ ? '[TODO](https://github.com/flutter/devtools/pull/TODO)'
+ : '[#$pr](https://github.com/flutter/devtools/pull/$pr)';
+
+ final filePath = 'packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md';
+ final file = File(filePath);
+
+ if (!file.existsSync()) {
+ print('Error: $filePath not found.');
+ exit(1);
+ }
+
+ var content = file.readAsStringSync();
+
+ if (!content.contains('## $section')) {
+ print("Error: Section '$section' not found.");
+ exit(1);
+ }
+
+ final noteWithPeriod = note.endsWith('.') ? note : '$note.';
+ final newEntry = '- $noteWithPeriod $prLink\n';
+
+ // Check for TODO placeholder.
+ const todoText = 'TODO: Remove this section if there are not any updates.';
+ final todoPattern = RegExp(
+ '## ${RegExp.escape(section)}\\s*\\n\\s*${RegExp.escape(todoText)}\\s*\\n*',
+ );
+
+ if (todoPattern.hasMatch(content)) {
+ content = content.replaceFirst(todoPattern, '## $section\n\n$newEntry\n');
+ } else {
+ // Append to existing list in the section.
+ final sectionHeader = '## $section';
+ final sectionStart = content.indexOf(sectionHeader);
+
+ // Find the next section start or the end of the file.
+ var nextSectionStart = content.indexOf('\n## ', sectionStart + 1);
+ if (nextSectionStart == -1) {
+ nextSectionStart =
+ content.indexOf('\n# Full commit history', sectionStart + 1);
+ }
+ if (nextSectionStart == -1) {
+ nextSectionStart = content.length;
+ }
+
+ var sectionContent =
+ content.substring(sectionStart, nextSectionStart).trimRight();
+ sectionContent += '\n$newEntry';
+
+ content =
+ '${content.substring(0, sectionStart)}$sectionContent\n${content.substring(nextSectionStart).trimLeft()}';
+ }
+
+ file.writeAsStringSync(content);
+ print('Successfully added note to $section.');
+}
diff --git a/pubspec.lock b/pubspec.lock
index b0a61b8..e30fd8b 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -758,10 +758,10 @@
dependency: transitive
description:
name: source_gen
- sha256: "1d562a3c1f713904ebbed50d2760217fd8a51ca170ac4b05b0db490699dbac17"
+ sha256: adc962c96fffb2de1728ef396a995aaedcafbe635abdca13d2a987ce17e57751
url: "https://pub.dev"
source: hosted
- version: "4.2.0"
+ version: "4.2.1"
source_map_stack_trace:
dependency: transitive
description: