Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ rulesync.local.jsonc
**/.kilo/skills/
**/.kilo/workflows/
**/.kilo/mcp.json
**/.kiloignore
**/.kilocodeignore
**/.kiro/steering/
**/.kiro/prompts/
**/.kiro/skills/
Expand Down
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@
"kimuson",
"kilo",
"Kilo",
"kilocodeignore",
"kiloignore",
"kilorules",
"kiro",
Expand Down
7 changes: 7 additions & 0 deletions docs/reference/file-formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ takt: # takt specific parameters (optional; emitted under .takt/facets/instructi
name: "renamed-stem" # (optional) override the emitted filename stem (no path separators or "..")
pi: # pi coding agent specific parameters (optional)
argument-hint: "[message]" # Hint shown in Pi's command palette
roo: # Roo Code specific parameters (optional)
mode: "architect" # (optional) mode slug to switch to before running the command body (e.g. "code", "architect")
---

target_pr = $ARGUMENTS
Expand Down Expand Up @@ -364,6 +366,11 @@ agentsskills: # for the Agent Skills standard target (optional; supports project
copilot: # for GitHub Copilot-specific parameters (optional)
license: MIT # (optional)
allowed-tools: "shell" # (optional) tools pre-approved without per-use confirmation
rovodev: # for Rovo Dev CLI-specific parameters (optional; Agent Skills standard)
allowed-tools: "grep bash" # (optional) space-separated string (a YAML list is also accepted)
license: MIT # (optional)
metadata: # (optional) free-form metadata
author: rulesync
zed: # for Zed-specific parameters (optional)
disable-model-invocation: true # (optional) prevent the model from auto-invoking this skill
cursor: # for Cursor-specific parameters (optional)
Expand Down
7 changes: 7 additions & 0 deletions skills/rulesync/file-formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ takt: # takt specific parameters (optional; emitted under .takt/facets/instructi
name: "renamed-stem" # (optional) override the emitted filename stem (no path separators or "..")
pi: # pi coding agent specific parameters (optional)
argument-hint: "[message]" # Hint shown in Pi's command palette
roo: # Roo Code specific parameters (optional)
mode: "architect" # (optional) mode slug to switch to before running the command body (e.g. "code", "architect")
---

target_pr = $ARGUMENTS
Expand Down Expand Up @@ -364,6 +366,11 @@ agentsskills: # for the Agent Skills standard target (optional; supports project
copilot: # for GitHub Copilot-specific parameters (optional)
license: MIT # (optional)
allowed-tools: "shell" # (optional) tools pre-approved without per-use confirmation
rovodev: # for Rovo Dev CLI-specific parameters (optional; Agent Skills standard)
allowed-tools: "grep bash" # (optional) space-separated string (a YAML list is also accepted)
license: MIT # (optional)
metadata: # (optional) free-form metadata
author: rulesync
zed: # for Zed-specific parameters (optional)
disable-model-invocation: true # (optional) prevent the model from auto-invoking this skill
cursor: # for Cursor-specific parameters (optional)
Expand Down
2 changes: 1 addition & 1 deletion src/cli/commands/gitignore-entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export const GITIGNORE_ENTRY_REGISTRY: ReadonlyArray<GitignoreEntryTag> = [
{ target: "kilo", feature: "skills", entry: "**/.kilo/skills/" },
{ target: "kilo", feature: "commands", entry: "**/.kilo/workflows/" },
{ target: "kilo", feature: "mcp", entry: "**/.kilo/mcp.json" },
{ target: "kilo", feature: "ignore", entry: "**/.kiloignore" },
{ target: "kilo", feature: "ignore", entry: "**/.kilocodeignore" },
// No `**/kilo.jsonc` entry: structurally identical to `opencode.jsonc` (no
// entry). The Kilo translator preserves non-permissions Kilo settings on
// round-trip, so the file is intended to be checked in by the user — adding
Expand Down
6 changes: 3 additions & 3 deletions src/e2e/e2e-ignore.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe("E2E: ignore", () => {
{ target: "geminicli", outputPath: ".geminiignore", format: "plaintext" as const },
{ target: "goose", outputPath: ".gooseignore", format: "plaintext" as const },
{ target: "cline", outputPath: ".clineignore", format: "plaintext" as const },
{ target: "kilo", outputPath: ".kiloignore", format: "plaintext" as const },
{ target: "kilo", outputPath: ".kilocodeignore", format: "plaintext" as const },
{ target: "roo", outputPath: ".rooignore", format: "plaintext" as const },
{ target: "qwencode", outputPath: ".geminiignore", format: "plaintext" as const },
{ target: "kiro", outputPath: ".aiignore", format: "plaintext" as const },
Expand Down Expand Up @@ -72,7 +72,7 @@ credentials/
{ target: "geminicli", orphanPath: ".geminiignore" },
{ target: "goose", orphanPath: ".gooseignore" },
{ target: "cline", orphanPath: ".clineignore" },
{ target: "kilo", orphanPath: ".kiloignore" },
{ target: "kilo", orphanPath: ".kilocodeignore" },
{ target: "roo", orphanPath: ".rooignore" },
{ target: "qwencode", orphanPath: ".geminiignore" },
{ target: "kiro", orphanPath: ".aiignore" },
Expand Down Expand Up @@ -136,7 +136,7 @@ describe("E2E: ignore (import)", () => {
{ target: "geminicli", sourcePath: ".geminiignore" },
{ target: "goose", sourcePath: ".gooseignore" },
{ target: "cline", sourcePath: ".clineignore" },
{ target: "kilo", sourcePath: ".kiloignore" },
{ target: "kilo", sourcePath: ".kilocodeignore" },
{ target: "roo", sourcePath: ".rooignore" },
{ target: "qwencode", sourcePath: ".geminiignore" },
{ target: "kiro", sourcePath: ".aiignore" },
Expand Down
44 changes: 44 additions & 0 deletions src/features/commands/roo-command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,36 @@ describe("RooCommand", () => {
expect(rulesyncCommand.getRelativeFilePath()).toBe("convert-test.md");
expect(rulesyncCommand.getOutputRoot()).toBe(".");
});

it("should carry the mode field through the roo section and round-trip back", () => {
const frontmatter: RooCommandFrontmatter = {
description: "Switches to architect mode",
mode: "architect",
};
const body = "Plan the change";

const rooCommand = new RooCommand({
outputRoot: "/test/base",
relativeDirPath: ".roo/commands",
relativeFilePath: "plan.md",
frontmatter,
body,
fileContent: stringifyFrontmatter(body, frontmatter),
});

const rulesyncCommand = rooCommand.toRulesyncCommand();
expect(rulesyncCommand.getFrontmatter()).toEqual({
targets: ["roo"],
description: "Switches to architect mode",
roo: { mode: "architect" },
});

const roundTripped = RooCommand.fromRulesyncCommand({ rulesyncCommand });
expect(roundTripped.getFrontmatter()).toEqual({
description: "Switches to architect mode",
mode: "architect",
});
});
});

describe("fromRulesyncCommand", () => {
Expand Down Expand Up @@ -444,6 +474,20 @@ This file has invalid frontmatter`;
expect(result.success).toBe(false);
});

it("should accept a mode slug and reject a non-string mode", () => {
const valid = RooCommandFrontmatterSchema.safeParse({
description: "d",
mode: "code",
});
expect(valid.success).toBe(true);
if (valid.success) {
expect(valid.data.mode).toBe("code");
}

const invalid = RooCommandFrontmatterSchema.safeParse({ description: "d", mode: 1 });
expect(invalid.success).toBe(false);
});

it("should reject frontmatter with invalid argument-hint type", () => {
const invalidFrontmatter = {
description: "Valid description",
Expand Down
3 changes: 3 additions & 0 deletions src/features/commands/roo-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import {
export const RooCommandFrontmatterSchema = z.looseObject({
description: z.optional(z.string()),
"argument-hint": optional(z.string()),
// Mode slug (e.g. "code", "architect") to switch to before running the command body.
// https://roocodeinc.github.io/Roo-Code/features/slash-commands
mode: z.optional(z.string()),
});

export type RooCommandFrontmatter = z.infer<typeof RooCommandFrontmatterSchema>;
Expand Down
Loading
Loading