Skip to content

v16.22.0

Choose a tag to compare

@dyoshikawa dyoshikawa released this 04 Sep 02:15
· 282 commits to main since this release
Immutable release. Only release title and notes can be modified.
d2d2e5f

What's Changed

New Features

  • Crush is a new tool target for rules, ignore files and skills. --targets crush writes the root CRUSH.md (project) or ~/.config/crush/CRUSH.md (global); Crush has no modular rules directory, so non-root rules fold into the root file the same way they do for zcode and codexcli. The ignore feature writes a project-only .crushignore with gitignore-style patterns, and the skills feature discovers and generates standard Agent Skills under .crush/skills/ (project) and ~/.config/crush/skills/ (global), including user-invocable, disable-model-invocation and the license / compatibility / metadata packaging fields, which are normalized through the shared Agent Skills helpers. Every surface was verified against Crush's Go source (#2920, closes #2762).
  • CodeBuddy Code is a new rules target. --targets codebuddy writes the root CODEBUDDY.md memory file (also read from .codebuddy/CODEBUDDY.md, and ~/.codebuddy/CODEBUDDY.md in global scope), modular rules under .codebuddy/rules/*.md with optional description / paths / alwaysApply frontmatter in the Cursor style, and the personal CODEBUDDY.local.md local-root file that mirrors CLAUDE.local.md. An Always Apply rule no longer carries a redundant universal glob. Only the rules feature ships in this release; other features will follow separately (#2918, closes #2760).
  • Factory Droid's DESIGN.md design-guidelines channel is emitted. Factory Droid loads DESIGN.md as an always-on design-system / UX / visual-guidance file separate from AGENTS.md. A non-root rule can now opt in with a factorydroid: { channel: design } frontmatter block, which routes its body to DESIGN.md instead of AGENTS.md / .factory/rules/*.md; several opted-in rules concatenate in source order. The file round-trips through import and generate --delete, is excluded from the root file's reference list because Factory Droid loads it itself, is routed by directory rather than bare basename, and gets its .gitignore entry derived automatically. Project scope only, since Factory documents no home-directory equivalent (#2916, closes #2749).

Bug Fixes

  • AugmentCode shell permissions honor the all-tools * category and fail closed. .augment/settings.json was generated from the bash category alone, so {"*": {"rm -rf *": "deny"}, "bash": {"rm *": "allow"}} emitted a bare launch-process allow and an all-tools-only deny or ask produced nothing at all. The adapter now resolves shell rules through the shared all-tools-aware helper, so a * deny or ask withholds every bash allow it covers, an all-tools deny or ask is folded into every managed tool rather than only the ones with their own entries, the unsupported wildcard toolName: "*" entry is skipped and legacy copies of it are removed on regenerate, and non-shell all-tools effects that cannot be represented are reported with a warning (#2919, closes #2917).
  • Command-capable adapters honor the all-tools * category. Cursor, Codex CLI, Roo Code, Zed, OpenCode, Kiro and the other adapters that build shell command lists silently dropped * rules. They now route through new shared helpers so a * deny or ask correctly shadows the overlapping bash allows, an all-tools ask or deny is honored fully across every bash-derived adapter, and Roo Code warns about categories it cannot express instead of dropping them without a trace (#2905, closes #2831).
  • Hermes Agent's command_allowlist is fed from bash allows only, and restrictions that cover them are honored. The allowlist was built from the allow patterns of every canonical category, so {"read": {"secrets/**": "allow"}} auto-approved a shell pattern and {"*": {"*": "allow"}} auto-approved every command, while a * deny or ask, or a bash ask, never withheld the allows it covered. Only bash allows now reach the allowlist, all-tools deny / ask rules withhold the allows they cover (compared as globs in both directions), an all-tools allow is skipped with a warning, restrictions Hermes cannot express are reported instead of dropped, and import treats the allowlist as authoritative for bash allows only so a withheld or non-command allow survives a generate → import round trip (#2903, closes #2829).
  • Warp and Goose no longer create an empty global permissions file. When no permission rule mapped and the global file did not exist, generate created a settings.toml holding only [agents.profiles] or a permission.yaml holding three empty lists. Both adapters now skip creation for an empty payload, mirroring the earlier deepagents fix; an existing file is still rewritten so stale lists are cleared and unrelated keys survive (#2898, closes #2894).
  • Frontmatter cleanup is bounded against alias bombs, circular anchors and prototype keys. The recursive frontmatter cleanup had no size bound and no cycle detection, so a small file with a few levels of nested YAML aliases expanded into tens of megabytes (and exhausted the heap), a self-referencing anchor overflowed the stack, and a __proto__: block replaced the cleaned record's prototype, letting a hidden key such as allowed-tools surface through the loose schemas. One shared walker now charges every visited value, mapping key, nullish leaf and string character against explicit budgets, caps nesting depth, refuses the document with a clear file-prefixed error when a budget is exceeded, drops references back to an ancestor, keeps a body reparse from reintroducing the problem, and skips __proto__ / constructor / prototype keys on both the parse and stringify paths. Ordinary frontmatter is unaffected (#2906, closes #2751).
  • Hook adapters resolve native event names with own-property lookups. Translating a native event name back to the canonical one used a plain-object bracket read, so a config whose event key is an Object.prototype member such as toString resolved to an inherited function and the imported hook was filed under a stringified prototype member. A new own-property-only lookupOwn helper is used by the shared converter and by every per-adapter site (Copilot, Copilot CLI, Cursor, Deep Agents, Hermes Agent, Kimi Code, Kiro, Kiro IDE, Qwen Code, Reasonix, Vibe), on both the import and generate sides (#2904, closes #2757).
  • Undocumented AugmentCode hook keys survive a regenerate. The hooks object in .augment/settings.json was replaced wholesale, erasing hand-authored keys such as afterFileEdit, beforeShellExecution and a lowercase stop that Auggie recognizes but rulesync does not model. Keys that are not rulesync-owned native event names are now preserved before the patch is applied, following the pattern already used for toolPermissions (#2913, refs #2670).
  • generate warns when a root file deactivates Junie's import-only rule roots. Once .junie/AGENTS.md exists, Junie reads it exclusively and .junie/rules/*.md / .junie/playbook.md become unreachable; import already warned about this, generate did not. It now warns once per run, names the paths that stop being read, and points at rulesync import --targets junie as the way to carry the content over first; the global-mode path is covered too (#2909, closes #2744).
  • import warns when a fold target still has folded non-root rules. For collisionPolicy: "fold" targets such as codexcli and junie, importing the root file back while the original non-root rules remain in .rulesync/rules/ duplicated their content on the next generate, once per cycle, with nothing to say why. The import path now warns when non-root rules already target the fold tool. A follow-up made the check side-effect free so it no longer trips the generate-time "no root rule found" warning or the localRoot validation during import, and localRoot rules are excluded from the warning in global mode (#2910, closes #2743; #2914).
  • Skill packaging metadata shapes that tools tolerate are no longer rejected. The license, compatibility and metadata frontmatter fields were strictly typed for Kilo Code, OpenCode and deepagents, so a numeric license, array compatibility or scalar metadata aborted the whole import / generate run for that file, even though none of those tools validates the fields (confirmed against their sources). The three targets now accept any shape, matching the earlier Factory Droid fix; Kiro and Rovo Dev stay strict because their docs defer to the Agent Skills specification, which does pin the types. Kiro skill frontmatter also emits name / description first again when the kiro section carries other keys (#2911, closes #2750).
  • The orphan sweeps case-fold and Unicode-normalize path identity before classifying orphans. On a case-insensitive filesystem, renaming a rule or skill by case only (my-skillMy-Skill) made generate --delete classify the pre-existing spelling as an orphan and delete the file or directory it had just rewritten. Every sweep now compares the same case-folded, normalized identity used for cross-root de-duplication, while still deleting the original spelling (#2912, closes #2740).
  • Case-insensitive command collisions across import sources are resolved. Commands from a secondary import source (OpenCode's inline command block, or a shared root matched by basename) were de-duplicated by exact string, so commit.md and Commit.md both passed and the last copy written silently overwrote the other on macOS and Windows. The merge now goes through the shared ClaimedIdentities helper, the primary root keeps precedence, and a warning names which spelling won and where it came from, distinguishing same-source from cross-source collisions (#2907, closes #2741).
  • fetch skips Windows-foldable segments and reserved device names in every fetched path. The check for names that Win32 folds (trailing dot or space, NAME~1 short-name aliases) now covers every segment of every fetched path, and treats CON, PRN, AUX, NUL, COM1COM9 and LPT1LPT9 (any case, with or without an extension) as unsafe as well. A folded nested segment drops that file, keeps the skill from being pruned, and is reported with the segment quoted as is; a skill directory with such a name is dropped whole. Lookalikes such as console, com10 and data~2parser are still fetched (#2899, closes #2892).
  • The skill picker's whitespace reason survives a cut label. describeConfusableNames listed the "carries more whitespace than the row shows" reason after the lookalike reason, and the latter alone exceeded the note budget, so the whitespace warning was cut off entirely. It is now listed first, since it is the one reason a reader cannot check by eye (#2900, closes #2839).
  • fetch drops names that begin with a combining mark and notes stray enclosing marks. A skill name whose first character is a combining mark is drawn over the checkbox or the quote before it, so such names are now dropped as hidden and shown without the mark. An enclosing mark (\p{Me}) that is not part of a keycap sequence is reported with a new sixth reason, placed right after the whitespace reason; non-spacing marks are left alone so Devanagari, Arabic and Vietnamese names are not flagged (#2901, closes #2838).
  • watch sanitizes control characters in trigger-path log output. The Change detected: ... line rendered watch-event paths verbatim, so a file name carrying ANSI escapes or bidirectional overrides in the watched tree could forge what the log line displays. Both the relative and the absolute fallback path are now stripped before logging, consistent with the rest of the codebase (#2908, closes #2753).
  • Rovo Dev import follows the configured MCP config path. rulesync import --targets rovodev --features mcp read .rovodev/mcp.json even when mcp.mcpConfigPath pointed elsewhere. The pointer is now resolved for project-relative and home-anchored paths that stay within the import scope, and the default file is used with a warning when the pointer is unset, empty or escapes the scope (#2902, closes #2736).

Other Changes

  • The Codex CLI, Muse Code and Rovo Dev MCP adapters share one guarded getRawMcpServer accessor for unfiltered source entries, with own-property, prototype-pollution and malformed-map handling covered directly (#2878, closes #2794).
  • The five existing mcpConfigPath states in the Rovo Dev MCP adapter are classified by a pure function and handled by an exhaustive switch, preserving every outcome and message (#2881, closes #2795).
  • The Homebrew formula was updated to v16.21.0 (#2897).

Contributors

Full Changelog

v16.21.0...v16.22.0