Skip to content

Commit e117208

Browse files
authored
Command argument hints and change release engineer to a command (RooCodeInc#6333)
1 parent 9baac07 commit e117208

File tree

9 files changed

+271
-41
lines changed

9 files changed

+271
-41
lines changed

.roo/commands/release.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
description: "Create a new release of the Roo Code extension"
3+
argument-hint: patch | minor | major
4+
---
5+
6+
1. Identify the SHA corresponding to the most recent release using GitHub CLI: `gh release view --json tagName,targetCommitish,publishedAt`
7+
2. Analyze changes since the last release using: `gh pr list --state merged --json number,title,author,url,mergedAt,closingIssuesReferences --limit 1000 -q '[.[] | select(.mergedAt > "TIMESTAMP") | {number, title, author: .author.login, url, mergedAt, issues: .closingIssuesReferences}] | sort_by(.number)'`
8+
3. For each PR with linked issues, fetch the issue details to get the issue reporter: `gh issue view ISSUE_NUMBER --json number,author -q '{number, reporter: .author.login}'`
9+
4. Summarize the changes. If the user did not specify, ask them whether this should be a major, minor, or patch release.
10+
5. Create a changeset in .changeset/v[version].md instead of directly modifying package.json. The format is:
11+
12+
```
13+
---
14+
"roo-cline": patch|minor|major
15+
---
16+
[list of changes]
17+
```
18+
19+
- Always include contributor attribution using format: (thanks @username!)
20+
- For PRs that close issues, also include the issue number and reporter: "- Fix: Description (#123 by @reporter, PR by @contributor)"
21+
- For PRs without linked issues, use the standard format: "- Add support for feature (thanks @contributor!)"
22+
- Provide brief descriptions of each item to explain the change
23+
- Order the list from most important to least important
24+
- Example formats:
25+
- With issue: "- Fix: Resolve memory leak in extension (#456 by @issueReporter, PR by @prAuthor)"
26+
- Without issue: "- Add support for Gemini 2.5 Pro caching (thanks @contributor!)"
27+
- CRITICAL: Include EVERY SINGLE PR in the changeset - don't assume you know which ones are important. Count the total PRs to verify completeness and cross-reference the list to ensure nothing is missed.
28+
29+
6. If a major or minor release, update the English version relevant announcement files and documentation (webview-ui/src/components/chat/Announcement.tsx, README.md, and the `latestAnnouncementId` in src/core/webview/ClineProvider.ts)
30+
7. Ask the user to confirm the English version
31+
8. Use the new_task tool to create a subtask in `translate` mode with detailed instructions of which content needs to be translated into all supported languages
32+
9. Create a new branch for the release preparation: `git checkout -b release/v[version]`
33+
10. Commit and push the changeset file and any documentation updates to the repository: `git add . && git commit -m "chore: add changeset for v[version]" && git push origin release/v[version]`
34+
11. Create a pull request for the release: `gh pr create --title "Release v[version]" --body "Release preparation for v[version]. This PR includes the changeset and any necessary documentation updates." --base main --head release/v[version]`
35+
12. The GitHub Actions workflow will automatically:
36+
- Create a version bump PR when changesets are merged to main
37+
- Update the CHANGELOG.md with proper formatting
38+
- Publish the release when the version bump PR is merged

.roomodes

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -41,42 +41,6 @@ customModes:
4141
- mcp
4242
customInstructions: Focus on UI refinement, component creation, and adherence to design best-practices. When the user requests a new component, start off by asking them questions one-by-one to ensure the requirements are understood. Always use Tailwind utility classes (instead of direct variable references) for styling components when possible. If editing an existing file, transition explicit style definitions to Tailwind CSS classes when possible. Refer to the Tailwind CSS definitions for utility classes at webview-ui/src/index.css. Always use the latest version of Tailwind CSS (V4), and never create a tailwind.config.js file. Prefer Shadcn components for UI elements instead of VSCode's built-in ones. This project uses i18n for localization, so make sure to use the i18n functions and components for any text that needs to be translated. Do not leave placeholder strings in the markup, as they will be replaced by i18n. Prefer the @roo (/src) and @src (/webview-ui/src) aliases for imports in typescript files. Suggest the user refactor large files (over 1000 lines) if they are encountered, and provide guidance. Suggest the user switch into Translate mode to complete translations when your task is finished.
4343
source: project
44-
- slug: release-engineer
45-
name: 🚀 Release Engineer
46-
roleDefinition: You are Roo, a release engineer specialized in automating the release process for software projects. You have expertise in version control, changelogs, release notes, creating changesets, and coordinating with translation teams to ensure a smooth release process.
47-
whenToUse: Automate the release process for software projects.
48-
description: Automate the release process.
49-
customInstructions: |-
50-
When preparing a release:
51-
1. Identify the SHA corresponding to the most recent release using GitHub CLI: `gh release view --json tagName,targetCommitish,publishedAt`
52-
2. Analyze changes since the last release using: `gh pr list --state merged --json number,title,author,url,mergedAt,closingIssuesReferences --limit 1000 -q '[.[] | select(.mergedAt > "TIMESTAMP") | {number, title, author: .author.login, url, mergedAt, issues: .closingIssuesReferences}] | sort_by(.number)'`
53-
3. For each PR with linked issues, fetch the issue details to get the issue reporter: `gh issue view ISSUE_NUMBER --json number,author -q '{number, reporter: .author.login}'`
54-
4. Summarize the changes and ask the user whether this should be a major, minor, or patch release
55-
5. Create a changeset in .changeset/v[version].md instead of directly modifying package.json. The format is:
56-
```
57-
---
58-
"roo-cline": patch|minor|major
59-
---
60-
[list of changes]
61-
```
62-
- Always include contributor attribution using format: (thanks @username!) - For PRs that close issues, also include the issue number and reporter: "- Fix: Description (#123 by @reporter, PR by @contributor)" - For PRs without linked issues, use the standard format: "- Add support for feature (thanks @contributor!)" - Provide brief descriptions of each item to explain the change - Order the list from most important to least important - Example formats:
63-
- With issue: "- Fix: Resolve memory leak in extension (#456 by @issueReporter, PR by @prAuthor)"
64-
- Without issue: "- Add support for Gemini 2.5 Pro caching (thanks @contributor!)"
65-
- CRITICAL: Include EVERY SINGLE PR in the changeset - don't assume you know which ones are important. Count the total PRs to verify completeness and cross-reference the list to ensure nothing is missed.
66-
6. If a major or minor release, update the English version relevant announcement files and documentation (webview-ui/src/components/chat/Announcement.tsx, README.md, and the `latestAnnouncementId` in src/core/webview/ClineProvider.ts)
67-
7. Ask the user to confirm the English version
68-
8. Use the new_task tool to create a subtask in `translate` mode with detailed instructions of which content needs to be translated into all supported languages
69-
9. Create a new branch for the release preparation: `git checkout -b release/v[version]`
70-
10. Commit and push the changeset file and any documentation updates to the repository: `git add . && git commit -m "chore: add changeset for v[version]" && git push origin release/v[version]` 11. Create a pull request for the release: `gh pr create --title "Release v[version]" --body "Release preparation for v[version]. This PR includes the changeset and any necessary documentation updates." --base main --head release/v[version]` 12. The GitHub Actions workflow will automatically:
71-
- Create a version bump PR when changesets are merged to main
72-
- Update the CHANGELOG.md with proper formatting
73-
- Publish the release when the version bump PR is merged
74-
groups:
75-
- read
76-
- edit
77-
- command
78-
- browser
79-
source: project
8044
- slug: translate
8145
name: 🌐 Translate
8246
roleDefinition: You are Roo, a linguistic specialist focused on translating and managing localization files. Your responsibility is to help maintain and update translation files for the application, ensuring consistency and accuracy across all language resources.

src/core/webview/webviewMessageHandler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2375,6 +2375,7 @@ export const webviewMessageHandler = async (
23752375
source: command.source,
23762376
filePath: command.filePath,
23772377
description: command.description,
2378+
argumentHint: command.argumentHint,
23782379
}))
23792380

23802381
await provider.postMessageToWebview({

src/services/command/__tests__/frontmatter-commands.spec.ts

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ npm run build
4343
source: "project",
4444
filePath: path.join("/test/cwd", ".roo", "commands", "setup.md"),
4545
description: "Sets up the development environment",
46+
argumentHint: undefined,
4647
})
4748
})
4849

@@ -66,6 +67,7 @@ npm run build
6667
source: "project",
6768
filePath: path.join("/test/cwd", ".roo", "commands", "setup.md"),
6869
description: undefined,
70+
argumentHint: undefined,
6971
})
7072
})
7173

@@ -108,6 +110,7 @@ Command content here.`
108110
source: "project",
109111
filePath: path.join("/test/cwd", ".roo", "commands", "setup.md"),
110112
description: undefined,
113+
argumentHint: undefined,
111114
})
112115
})
113116

@@ -142,6 +145,7 @@ Global setup instructions.`
142145
source: "project",
143146
filePath: path.join("/test/cwd", ".roo", "commands", "setup.md"),
144147
description: "Project-specific setup",
148+
argumentHint: undefined,
145149
})
146150
})
147151

@@ -168,10 +172,118 @@ Global setup instructions.`
168172
source: "global",
169173
filePath: expect.stringContaining(path.join(".roo", "commands", "setup.md")),
170174
description: "Global setup command",
175+
argumentHint: undefined,
171176
})
172177
})
173178
})
174179

180+
describe("argument-hint functionality", () => {
181+
it("should load command with argument-hint from frontmatter", async () => {
182+
const commandContent = `---
183+
description: Create a new release of the Roo Code extension
184+
argument-hint: patch | minor | major
185+
---
186+
187+
# Release Command
188+
189+
Create a new release.`
190+
191+
mockFs.stat = vi.fn().mockResolvedValue({ isDirectory: () => true })
192+
mockFs.readFile = vi.fn().mockResolvedValue(commandContent)
193+
194+
const result = await getCommand("/test/cwd", "release")
195+
196+
expect(result).toEqual({
197+
name: "release",
198+
content: "# Release Command\n\nCreate a new release.",
199+
source: "project",
200+
filePath: path.join("/test/cwd", ".roo", "commands", "release.md"),
201+
description: "Create a new release of the Roo Code extension",
202+
argumentHint: "patch | minor | major",
203+
})
204+
})
205+
206+
it("should handle command with both description and argument-hint", async () => {
207+
const commandContent = `---
208+
description: Deploy application to environment
209+
argument-hint: staging | production
210+
author: DevOps Team
211+
---
212+
213+
# Deploy Command
214+
215+
Deploy the application.`
216+
217+
mockFs.stat = vi.fn().mockResolvedValue({ isDirectory: () => true })
218+
mockFs.readFile = vi.fn().mockResolvedValue(commandContent)
219+
220+
const result = await getCommand("/test/cwd", "deploy")
221+
222+
expect(result).toEqual({
223+
name: "deploy",
224+
content: "# Deploy Command\n\nDeploy the application.",
225+
source: "project",
226+
filePath: path.join("/test/cwd", ".roo", "commands", "deploy.md"),
227+
description: "Deploy application to environment",
228+
argumentHint: "staging | production",
229+
})
230+
})
231+
232+
it("should handle empty argument-hint in frontmatter", async () => {
233+
const commandContent = `---
234+
description: Test command
235+
argument-hint: ""
236+
---
237+
238+
# Test Command
239+
240+
Test content.`
241+
242+
mockFs.stat = vi.fn().mockResolvedValue({ isDirectory: () => true })
243+
mockFs.readFile = vi.fn().mockResolvedValue(commandContent)
244+
245+
const result = await getCommand("/test/cwd", "test")
246+
247+
expect(result?.argumentHint).toBeUndefined()
248+
})
249+
250+
it("should handle whitespace-only argument-hint in frontmatter", async () => {
251+
const commandContent = `---
252+
description: Test command
253+
argument-hint: " "
254+
---
255+
256+
# Test Command
257+
258+
Test content.`
259+
260+
mockFs.stat = vi.fn().mockResolvedValue({ isDirectory: () => true })
261+
mockFs.readFile = vi.fn().mockResolvedValue(commandContent)
262+
263+
const result = await getCommand("/test/cwd", "test")
264+
265+
expect(result?.argumentHint).toBeUndefined()
266+
})
267+
268+
it("should handle non-string argument-hint in frontmatter", async () => {
269+
const commandContent = `---
270+
description: Test command
271+
argument-hint: 123
272+
---
273+
274+
# Test Command
275+
276+
Test content.`
277+
278+
mockFs.stat = vi.fn().mockResolvedValue({ isDirectory: () => true })
279+
mockFs.readFile = vi.fn().mockResolvedValue(commandContent)
280+
281+
const result = await getCommand("/test/cwd", "test")
282+
283+
expect(result?.argumentHint).toBeUndefined()
284+
})
285+
})
286+
175287
describe("getCommands with frontmatter", () => {
176288
it("should load multiple commands with descriptions", async () => {
177289
const setupContent = `---
@@ -215,14 +327,62 @@ Build instructions without frontmatter.`
215327
expect.objectContaining({
216328
name: "setup",
217329
description: "Sets up the development environment",
330+
argumentHint: undefined,
218331
}),
219332
expect.objectContaining({
220333
name: "deploy",
221334
description: "Deploys the application to production",
335+
argumentHint: undefined,
222336
}),
223337
expect.objectContaining({
224338
name: "build",
225339
description: undefined,
340+
argumentHint: undefined,
341+
}),
342+
]),
343+
)
344+
})
345+
346+
it("should load multiple commands with argument hints", async () => {
347+
const releaseContent = `---
348+
description: Create a new release
349+
argument-hint: patch | minor | major
350+
---
351+
352+
# Release Command
353+
354+
Create a release.`
355+
356+
const deployContent = `---
357+
description: Deploy to environment
358+
argument-hint: staging | production
359+
---
360+
361+
# Deploy Command
362+
363+
Deploy the app.`
364+
365+
mockFs.stat = vi.fn().mockResolvedValue({ isDirectory: () => true })
366+
mockFs.readdir = vi.fn().mockResolvedValue([
367+
{ name: "release.md", isFile: () => true },
368+
{ name: "deploy.md", isFile: () => true },
369+
])
370+
mockFs.readFile = vi.fn().mockResolvedValueOnce(releaseContent).mockResolvedValueOnce(deployContent)
371+
372+
const result = await getCommands("/test/cwd")
373+
374+
expect(result).toHaveLength(2)
375+
expect(result).toEqual(
376+
expect.arrayContaining([
377+
expect.objectContaining({
378+
name: "release",
379+
description: "Create a new release",
380+
argumentHint: "patch | minor | major",
381+
}),
382+
expect.objectContaining({
383+
name: "deploy",
384+
description: "Deploy to environment",
385+
argumentHint: "staging | production",
226386
}),
227387
]),
228388
)

src/services/command/commands.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface Command {
99
source: "global" | "project"
1010
filePath: string
1111
description?: string
12+
argumentHint?: string
1213
}
1314

1415
/**
@@ -70,6 +71,7 @@ async function tryLoadCommand(
7071

7172
let parsed
7273
let description: string | undefined
74+
let argumentHint: string | undefined
7375
let commandContent: string
7476

7577
try {
@@ -79,10 +81,15 @@ async function tryLoadCommand(
7981
typeof parsed.data.description === "string" && parsed.data.description.trim()
8082
? parsed.data.description.trim()
8183
: undefined
84+
argumentHint =
85+
typeof parsed.data["argument-hint"] === "string" && parsed.data["argument-hint"].trim()
86+
? parsed.data["argument-hint"].trim()
87+
: undefined
8288
commandContent = parsed.content.trim()
8389
} catch (frontmatterError) {
8490
// If frontmatter parsing fails, treat the entire content as command content
8591
description = undefined
92+
argumentHint = undefined
8693
commandContent = content.trim()
8794
}
8895

@@ -92,6 +99,7 @@ async function tryLoadCommand(
9299
source,
93100
filePath,
94101
description,
102+
argumentHint,
95103
}
96104
} catch (error) {
97105
// File doesn't exist or can't be read
@@ -137,6 +145,7 @@ async function scanCommandDirectory(
137145

138146
let parsed
139147
let description: string | undefined
148+
let argumentHint: string | undefined
140149
let commandContent: string
141150

142151
try {
@@ -146,10 +155,15 @@ async function scanCommandDirectory(
146155
typeof parsed.data.description === "string" && parsed.data.description.trim()
147156
? parsed.data.description.trim()
148157
: undefined
158+
argumentHint =
159+
typeof parsed.data["argument-hint"] === "string" && parsed.data["argument-hint"].trim()
160+
? parsed.data["argument-hint"].trim()
161+
: undefined
149162
commandContent = parsed.content.trim()
150163
} catch (frontmatterError) {
151164
// If frontmatter parsing fails, treat the entire content as command content
152165
description = undefined
166+
argumentHint = undefined
153167
commandContent = content.trim()
154168
}
155169

@@ -161,6 +175,7 @@ async function scanCommandDirectory(
161175
source,
162176
filePath,
163177
description,
178+
argumentHint,
164179
})
165180
}
166181
} catch (error) {

src/shared/ExtensionMessage.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export interface Command {
2525
source: "global" | "project"
2626
filePath?: string
2727
description?: string
28+
argumentHint?: string
2829
}
2930

3031
// Type for marketplace installed metadata

0 commit comments

Comments
 (0)