Skip to content

Add a managed event-hook utility for Rovo Dev config.yml #1008

Description

@williamthorsen

Problem

CodeAssembly needs to install eventHooks entries into Rovo Dev's ~/.rovodev/config.yml, but the file is user-curated YAML carrying comments and foreign eventHooks entries written by other tools. Because multiple tools register commands on the same events (which Rovo Dev runs in parallel), CodeAssembly's commands are scattered across several event arrays, interleaved with foreign commands. No utility exists to add, verify, or remove CodeAssembly-owned entries without disturbing foreign content.

Context

  • Foreign comments and unrelated config keys must survive every operation. Comments inside CodeAssembly-owned entries need not be preserved, since an owned entry is CodeAssembly's to replace or rebuild as a unit.
  • Ownership is per-entry, marked by a sentinel embedded in each entry and matched wherever the entry sits — across multiple event arrays, interleaved with foreign entries. The sentinel encoding (a token in the hook command, or a metadata field if Rovo Dev verifiably ignores unknown keys) is the consumer's (Emit session-lifecycle events via harness hooks #1005) to fix once it observes real Rovo Dev behavior; this utility takes the sentinel matcher as an injected argument and stays agnostic about what it marks. Comment-fence / contiguous-region sentinels are ruled out: owned entries are scattered and interleaved, so no fence can delimit ownership.
  • eventHooks.events items are {name, commands[]}. Whether Rovo Dev tolerates duplicate name items or requires merging into an existing item's commands list is the consumer's concern; the sentinel rides the entry either way.
  • Functions operate on a parsed yaml Document; the caller owns file IO. This matches the pure-transform split of the existing prompts-yml-region.ts.
  • Both sibling utilities (Add a managed hook-entry utility for Claude Code settings.json #1007 Claude, this one) expose the same ensure/check/remove contract so the harness wiring in Emit session-lifecycle events via harness hooks #1005 stays uniform. Shared contract shape, independent implementations. Named rovo-config-hooks.ts; a generic config.yml section-mutation layer is extracted only when a second consumer needs it.

Proposed solution

A library module packages/agents/src/lib/rovo-config-hooks.ts exposing three operations over a Document plus caller-supplied entries and a sentinel matcher:

  • ensure — idempotent insert-or-replace, creating missing structure (including a missing eventHooks section); a drifted sentinel-matching entry is replaced in place, not duplicated.
  • check — reports present / drifted / absent per supplied entry (drifted = a sentinel-matching entry whose content differs).
  • remove — deletes only sentinel-matching entries and prunes structure the removal emptied.

Mutation is via the comment-preserving yaml Document API (surgical: foreign entries, foreign comments, and unrelated keys are untouched). Whole-entry rebuild is a sanctioned fallback where an edit can't be expressed surgically.

Acceptance criteria

Must have

  • Ensure is idempotent: an immediate re-run is a no-op, and a drifted sentinel-matching entry is replaced rather than duplicated.
  • Foreign eventHooks entries, foreign comments, and unrelated config keys survive every operation unchanged.
  • Owned entries scattered across multiple event arrays and interleaved with foreign entries are each matched and mutated correctly.
  • Remove deletes only sentinel-matching entries and prunes structure emptied by the removal.
  • A Document carrying parse errors is never treated as writable; the module surfaces the parse failure to the caller rather than emitting a corrupted result.
  • The exposed contract shape matches the Claude utility's (Add a managed hook-entry utility for Claude Code settings.json #1007).
  • New behavior is covered by tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions