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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ The tables below show whether each tool supports a given feature (✅ = supporte
| Cline | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ |
| Kilo Code | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Roo Code | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | |
| Rovodev (Atlassian) | ✅ | | ✅ | | ✅ | ✅ | | |
| Rovodev (Atlassian) | ✅ | | ✅ | | ✅ | ✅ | | |
| Takt | ✅ | | | ✅ | ✅ | ✅ | | |
| Vibe Code | ✅ | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ |
| Qwen Code | ✅ | ✅ | | | | | | ✅ |
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/file-formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -765,4 +765,6 @@ For Warp, this generates the `agent_mode_command_execution_allowlist` / `agent_m

For the Antigravity IDE, this generates `permissions.allow`, `permissions.ask`, and `permissions.deny` arrays in the committable workspace `.antigravity/settings.json` (**project mode only**). Antigravity 2.0 evaluates these `Deny > Ask > Allow` and uses `action(target)` entries; rulesync maps canonical categories onto the IDE action vocabulary: `read` → `read_file`, `edit`/`write` → `write_file`, `bash` → `command`, `webfetch`/`websearch` → `read_url`, `mcp` → `mcp` (the IDE-only `execute_url` / `unsandboxed` actions have no canonical equivalent and pass through verbatim). Because `edit`/`write` collapse to `write_file` and `webfetch`/`websearch` collapse to `read_url`, importing normalizes back to `write` / `webfetch` (a documented, lossy mapping). The `settings.json` file holds other workspace settings, so the `permissions` block is merged in place — entries for unmanaged actions are preserved — and the file is never deleted. The User-scope settings file is a platform-dependent VS-Code-style path outside rulesync's home-relative global model, so **global mode is not supported**; the workspace file is intended to be checked into git. See the [Antigravity permissions docs](https://antigravity.google/docs/permissions).

For Rovo Dev CLI, this generates the `toolPermissions` block of the global `~/.rovodev/config.yml` (**global mode only** — Rovo Dev has no project-scoped permissions file, mirroring the Rovodev MCP adapter). Rovo Dev's three levels (`allow`/`ask`/`deny`) are an exact 1:1 with rulesync's canonical actions, so action values pass through verbatim. The `bash` category maps the catch-all `*` pattern to `bash.default` and every other pattern to a `bash.commands[]` entry `{ command: <pattern as regex>, permission }` (Rovo Dev matches commands as regexes, so author `bash` patterns accordingly). The `read` category maps to the inspection tools (`open_files`, `expand_code_chunks`, `expand_folder`, `grep`) and `edit`/`write` to the mutation tools (`find_and_replace_code`, `create_file`, `delete_file`, `move_file`); because these per-tool keys hold a single level (no per-pattern rules), only the catch-all `*` of each category sets the level. Because `edit` and `write` both map onto the same mutation tools, a conflicting catch-all between them cannot be represented; `edit` takes precedence and a warning is logged. Non-catch-all `allow` paths in those categories are surfaced as `allowedExternalPaths` so explicit grants are not dropped; non-`allow` non-catch-all rules cannot be expressed per-path and are skipped with a warning. Categories without a clean Rovo Dev target (e.g. `webfetch`) are skipped with a warning. `config.yml` holds all of Rovo Dev's settings (`agent`, `sessions`, `mcp`, etc.), so the `toolPermissions` block is merged in place — every other top-level key (and any unmanaged keys inside `toolPermissions`) is preserved (values only — YAML comments and formatting in the existing file are not retained on rewrite) — and the file is never deleted. See the [Rovo Dev CLI settings](https://support.atlassian.com/rovo/docs/manage-rovo-dev-cli-settings/) and [tool permissions](https://support.atlassian.com/rovo/docs/use-tools-in-rovo-dev-cli/) docs.

> **Note: Interaction with ignore feature.** Both the ignore feature and the permissions feature can manage `Read` tool deny entries in `.claude/settings.json`. When both features configure the `Read` tool, the **permissions feature takes precedence** and a warning is emitted. If you only need to restrict file reads based on glob patterns, use the ignore feature (`.rulesync/.aiignore`). Use permissions only when you need fine-grained `allow`/`ask`/`deny` control over the `Read` tool.
2 changes: 1 addition & 1 deletion docs/reference/supported-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Rulesync supports both **generation** and **import** for All of the major AI cod
| Cline | cline | ✅ 🌏 | ✅ | 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | | ✅ |
| Kilo Code | kilo | ✅ 🌏 | ✅ | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 |
| Roo Code | roo | ✅ | ✅ | ✅ | ✅ | 🎮 | ✅ 🌏 | | |
| Rovodev (Atlassian) | rovodev | ✅ 🌏 | | 🌏 | | ✅ 🌏 | ✅ 🌏 | | |
| Rovodev (Atlassian) | rovodev | ✅ 🌏 | | 🌏 | | ✅ 🌏 | ✅ 🌏 | | 🌏 |
| Takt | takt | ✅ 🌏 | | | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | | |
| Vibe Code | vibe | ✅ 🌏 | ✅ | ✅ 🌏 | | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 |
| Qwen Code | qwencode | ✅ | ✅ | | | | | | ✅ 🌏 |
Expand Down
2 changes: 2 additions & 0 deletions skills/rulesync/file-formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -765,4 +765,6 @@ For Warp, this generates the `agent_mode_command_execution_allowlist` / `agent_m

For the Antigravity IDE, this generates `permissions.allow`, `permissions.ask`, and `permissions.deny` arrays in the committable workspace `.antigravity/settings.json` (**project mode only**). Antigravity 2.0 evaluates these `Deny > Ask > Allow` and uses `action(target)` entries; rulesync maps canonical categories onto the IDE action vocabulary: `read` → `read_file`, `edit`/`write` → `write_file`, `bash` → `command`, `webfetch`/`websearch` → `read_url`, `mcp` → `mcp` (the IDE-only `execute_url` / `unsandboxed` actions have no canonical equivalent and pass through verbatim). Because `edit`/`write` collapse to `write_file` and `webfetch`/`websearch` collapse to `read_url`, importing normalizes back to `write` / `webfetch` (a documented, lossy mapping). The `settings.json` file holds other workspace settings, so the `permissions` block is merged in place — entries for unmanaged actions are preserved — and the file is never deleted. The User-scope settings file is a platform-dependent VS-Code-style path outside rulesync's home-relative global model, so **global mode is not supported**; the workspace file is intended to be checked into git. See the [Antigravity permissions docs](https://antigravity.google/docs/permissions).

For Rovo Dev CLI, this generates the `toolPermissions` block of the global `~/.rovodev/config.yml` (**global mode only** — Rovo Dev has no project-scoped permissions file, mirroring the Rovodev MCP adapter). Rovo Dev's three levels (`allow`/`ask`/`deny`) are an exact 1:1 with rulesync's canonical actions, so action values pass through verbatim. The `bash` category maps the catch-all `*` pattern to `bash.default` and every other pattern to a `bash.commands[]` entry `{ command: <pattern as regex>, permission }` (Rovo Dev matches commands as regexes, so author `bash` patterns accordingly). The `read` category maps to the inspection tools (`open_files`, `expand_code_chunks`, `expand_folder`, `grep`) and `edit`/`write` to the mutation tools (`find_and_replace_code`, `create_file`, `delete_file`, `move_file`); because these per-tool keys hold a single level (no per-pattern rules), only the catch-all `*` of each category sets the level. Because `edit` and `write` both map onto the same mutation tools, a conflicting catch-all between them cannot be represented; `edit` takes precedence and a warning is logged. Non-catch-all `allow` paths in those categories are surfaced as `allowedExternalPaths` so explicit grants are not dropped; non-`allow` non-catch-all rules cannot be expressed per-path and are skipped with a warning. Categories without a clean Rovo Dev target (e.g. `webfetch`) are skipped with a warning. `config.yml` holds all of Rovo Dev's settings (`agent`, `sessions`, `mcp`, etc.), so the `toolPermissions` block is merged in place — every other top-level key (and any unmanaged keys inside `toolPermissions`) is preserved (values only — YAML comments and formatting in the existing file are not retained on rewrite) — and the file is never deleted. See the [Rovo Dev CLI settings](https://support.atlassian.com/rovo/docs/manage-rovo-dev-cli-settings/) and [tool permissions](https://support.atlassian.com/rovo/docs/use-tools-in-rovo-dev-cli/) docs.

> **Note: Interaction with ignore feature.** Both the ignore feature and the permissions feature can manage `Read` tool deny entries in `.claude/settings.json`. When both features configure the `Read` tool, the **permissions feature takes precedence** and a warning is emitted. If you only need to restrict file reads based on glob patterns, use the ignore feature (`.rulesync/.aiignore`). Use permissions only when you need fine-grained `allow`/`ask`/`deny` control over the `Read` tool.
2 changes: 1 addition & 1 deletion skills/rulesync/supported-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Rulesync supports both **generation** and **import** for All of the major AI cod
| Cline | cline | ✅ 🌏 | ✅ | 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | | ✅ |
| Kilo Code | kilo | ✅ 🌏 | ✅ | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 |
| Roo Code | roo | ✅ | ✅ | ✅ | ✅ | 🎮 | ✅ 🌏 | | |
| Rovodev (Atlassian) | rovodev | ✅ 🌏 | | 🌏 | | ✅ 🌏 | ✅ 🌏 | | |
| Rovodev (Atlassian) | rovodev | ✅ 🌏 | | 🌏 | | ✅ 🌏 | ✅ 🌏 | | 🌏 |
| Takt | takt | ✅ 🌏 | | | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | | |
| Vibe Code | vibe | ✅ 🌏 | ✅ | ✅ 🌏 | | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 | ✅ 🌏 |
| Qwen Code | qwencode | ✅ | ✅ | | | | | | ✅ 🌏 |
Expand Down
1 change: 1 addition & 0 deletions src/constants/rovodev-paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export const ROVODEV_MODULAR_RULES_DIR_PATH = join(ROVODEV_DIR, ".rulesync", "mo
export const ROVODEV_RULE_FILE_NAME = "AGENTS.md";
export const ROVODEV_LEGACY_RULE_FILE_NAME = "AGENTS.local.md";
export const ROVODEV_MCP_FILE_NAME = "mcp.json";
export const ROVODEV_CONFIG_FILE_NAME = "config.yml";
export const ROVODEV_AGENTS_SKILLS_DIR_PATH = join(".agents", "skills");
52 changes: 52 additions & 0 deletions src/e2e/e2e-permissions.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { join } from "node:path";

import { load } from "js-yaml";
import * as smolToml from "smol-toml";
import { describe, expect, it } from "vitest";

Expand Down Expand Up @@ -1351,6 +1352,57 @@ describe("E2E: permissions (global mode)", () => {
expect(bash.allow).toEqual(["git status"]);
expect(parsed.disabled_tools).toContain("write_file");
});

it("should generate rovodev permissions in home directory with --global", async () => {
const projectDir = getProjectDir();
const homeDir = getHomeDir();

await writeFileContent(
join(projectDir, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH),
JSON.stringify(
{
permission: {
bash: { "*": "ask", "git status": "allow", "rm -rf .*": "deny" },
read: { "*": "allow" },
edit: { "*": "deny" },
},
},
null,
2,
),
);

// Pre-seed config.yml with unrelated user settings to verify the
// non-destructive merge into ~/.rovodev/config.yml.
await writeFileContent(
join(homeDir, ".rovodev", "config.yml"),
"agent:\n model: claude\nsessions:\n retention: 30\n",
);

await runGenerate({
target: "rovodev",
features: "permissions",
global: true,
env: { HOME_DIR: homeDir },
});

const parsed = load(await readFileContent(join(homeDir, ".rovodev", "config.yml")));
const root = toTable(parsed);
const toolPermissions = toTable(root.toolPermissions);
const bash = toTable(toolPermissions.bash);
// `bash` catch-all -> bash.default; other patterns -> bash.commands.
expect(bash.default).toBe("ask");
expect(bash.commands).toEqual([
{ command: "git status", permission: "allow" },
{ command: "rm -rf .*", permission: "deny" },
]);
// `read` -> inspection tools, `edit` -> mutation tools.
expect(toolPermissions.open_files).toBe("allow");
expect(toolPermissions.create_file).toBe("deny");
// Unrelated user settings preserved by the non-destructive merge.
expect(toTable(root.agent).model).toBe("claude");
expect(toTable(root.sessions).retention).toBe(30);
});
});

type AugmentEntry = {
Expand Down
1 change: 1 addition & 0 deletions src/features/permissions/permissions-processor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ describe("PermissionsProcessor", () => {
"kilo",
"opencode",
"qwencode",
"rovodev",
"vibe",
"warp",
"zed",
Expand Down
17 changes: 17 additions & 0 deletions src/features/permissions/permissions-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { KiloPermissions } from "./kilo-permissions.js";
import { KiroPermissions } from "./kiro-permissions.js";
import { OpencodePermissions } from "./opencode-permissions.js";
import { QwencodePermissions } from "./qwencode-permissions.js";
import { RovodevPermissions } from "./rovodev-permissions.js";
import { RulesyncPermissions } from "./rulesync-permissions.js";
import type {
ToolPermissionsForDeletionParams,
Expand Down Expand Up @@ -50,6 +51,7 @@ const permissionsProcessorToolTargetTuple = [
"kiro-ide",
"opencode",
"qwencode",
"rovodev",
"vibe",
"warp",
"zed",
Expand Down Expand Up @@ -270,6 +272,21 @@ export const toolPermissionsFactories = new Map<
},
},
],
[
"rovodev",
{
class: RovodevPermissions,
meta: {
// Rovo Dev CLI reads tool permissions only from the global
// `~/.rovodev/config.yml` (`toolPermissions` block); there is no
// project-scoped Rovo Dev permissions file (mirrors the Rovodev MCP
// adapter).
supportsProject: false,
supportsGlobal: true,
supportsImport: true,
},
},
],
[
"vibe",
{
Expand Down
Loading