smithery/bonny

logger-messages

Enforces active voice for logger messages and the Event Details API. Use when writing a new logger class or modifying message arrays in getInfo().

Installation

$ npx skills add smithery/bonny --skill logger-messages

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Allowed toolsRead, Grep, Glob

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 8,069 B
  • docs SUMMARY.md 326 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Logger Message Guidelines

Write clear, user-friendly messages for Simple History event logs.

Core Principle: Active Voice

Write as if someone is telling you what they just did.

✅ DO                          ❌ DON'T
─────────────────────────────────────────────
Activated plugin              Plugin was activated
Created menu                  Menu has been created
Updated settings              Settings were updated
Published post                Post has been published

In Logger Classes

public function getInfo() {
    return [
        'messages' => [
            'plugin_activated' => __( 'Activated plugin', 'simple-history' ),
            'plugin_deactivated' => __( 'Deactivated plugin', 'simple-history' ),
            'post_updated' => __( 'Updated post "{post_title}"', 'simple-history' ),
        ],
    ];
}

Message Key Uniqueness

Keys must be globally unique across all loggers (used as RFC 5424 MSGID).

// ✅ Good - descriptive prefix
'plugin_activated', 'theme_switched', 'user_logged_in'

// ❌ Bad - too generic
'activated', 'updated', 'deleted'

Verify uniqueness: grep -r "'your_key'" loggers/

Common Verbs

  • Create: Created, Added, Generated
  • Modify: Updated, Changed, Edited
  • Delete: Deleted, Removed, Trashed
  • Toggle: Activated, Deactivated, Enabled, Disabled

Avoid

  • ❌ "was [verb]" - passive
  • ❌ "has been [verb]" - passive
  • ❌ Technical jargon users won't understand

No Links Inside Message Text

The message body is a declarative sentence ("what happened?"). Action links (rendered below the message, see the action-links skill) are the canonical "what can I do?" affordance. Wrapping {post_title} (or any other interpolated token) in an <a> tag inside the template puts a CTA mid-sentence and competes with the action row.

Rule: Inline links inside message templates are permitted only when they point somewhere the action row cannot reach. If getactionlinks() already covers the destination (Edit, View, Revisions, the overview page, …), the message must be plain text.

// ❌ Don't — Edit/View action links already point to the post.
'post_updated' => __( 'Updated post "<a href="...">{post_title}</a>"', 'simple-history' ),

// ✅ Do — plain title, action row handles navigation.
'post_updated' => __( 'Updated post "{post_title}"', 'simple-history' ),

Deleted items: still plain text. A dead link is worse than no link — the overview action link (All pages, All plugins) is the right hand-off.

Legitimate exception: the link goes somewhere no action link can express (e.g. an arbitrary external reference). Then an inline link is additive, not redundant.

Not an urgent migration — apply opportunistically when touching a logger for other reasons.

Context Key Naming

Prefix all context keys with the entity name to avoid collisions and keep keys self-documenting.

// ✅ Good - prefixed with entity
'plugin_name', 'plugin_current_version', 'theme_new_version'
'site_health_status', 'site_health_label', 'site_health_badge_label'

// ❌ Bad - too generic
'test', 'label', 'status', 'name', 'version'

Event Details Output

Use the Event Details API for getlogrowdetailsoutput(). Never build raw HTML with SimpleHistoryLogitem__keyValueTable.

use Simple_History\Event_Details\Event_Details_Group;
use Simple_History\Event_Details\Event_Details_Group_Table_Formatter;
use Simple_History\Event_Details\Event_Details_Item;

public function get_log_row_details_output( $row ) {
    $group = new Event_Details_Group();
    $group->set_formatter( new Event_Details_Group_Table_Formatter() );
    $group->add_items(
        array(
            // Read value directly from context key.
            new Event_Details_Item( 'status', __( 'Status', 'simple-history' ) ),
            // Read new/prev pair from context (looks for key_new and key_prev).
            new Event_Details_Item( array( 'setting_name' ), __( 'Setting', 'simple-history' ) ),
        )
    );
    return $group;
}

Formatters:

  • EventDetailsGroupTableFormatter — key-value table (default)
  • EventDetailsGroupDiffTable_Formatter — before/after with diffs
  • EventDetailsGroupInlineFormatter — compact inline text

Manual values (when context keys don't match conventions):

( new Event_Details_Item( null, __( 'Label', 'simple-history' ) ) )
    ->set_new_value( $value )

See docs/architecture/event-details.md for full API reference.

RAW Formatters (Escape Hatch)

When the structured API can't express your output (images, HTML content, color swatches):

  • ItemRAWFormatter — Full custom HTML/JSON for an item (no name column)
  • ItemTableRowRAWFormatter — Table row with escaped name + raw HTML value
use Simple_History\Event_Details\Event_Details_Item_Table_Row_RAW_Formatter;

$raw_formatter = ( new Event_Details_Item_Table_Row_RAW_Formatter() )
    ->set_html_output( sprintf( '<a href="%1$s">%2$s</a>', esc_url( $url ), esc_html( $url ) ) )
    ->set_json_output( [ 'url' => $url ] );

$item = ( new Event_Details_Item( null, __( 'URL', 'simple-history' ) ) )
    ->set_formatter( $raw_formatter );

Use RAW formatters sparingly — only when no structured formatter fits.

Links Below Events: Use Action Links, Not Details

Navigational links (Edit, View, Preview) belong in getactionlinks(), not inside getlogrowdetailsoutput(). See the action-links skill.

Old loggers often embed <a> tags in the details table (e.g., "View/Edit" comment link, "View plugin info" thickbox). When migrating these loggers:

  1. Move navigational links to getactionlinks()
  2. Keep only informational data in Event Details

The only case for a link inside details is when the value itself is a URL (e.g., a plugin's homepage URL displayed as data). Use ItemTableRowRAWFormatter for that.

Migrating from Old HTML to Event Details

Many older loggers build HTML manually with SimpleHistoryLogitem__keyValueTable tables. When migrating:

Old pattern New approach
<table class='SimpleHistoryLogitem__keyValueTable'> with <tr>/<td> EventDetailsGroup + GroupTableFormatter
<ins> / <del> for changed values EventDetailsItem with set_values() (auto-generates ins/del)
<span class='SimpleHistoryLogitem__inlineDivided'> EventDetailsGroup + GroupInlineFormatter
Inline <a href> links to edit/view Move to getactionlinks()
Images, color swatches, shortcode output ItemRAWFormatter or ItemTableRowRAWFormatter
Standalone <p> text blocks (not key-value) GroupInlineFormatter with a single item, or RAW formatter

Value transforms (e.g., true → "Enabled", locale → display name): Transform in PHP, then pass to setnewvalue() / setprevvalue().

Conditional rows: Don't set values for items you want hidden — the container auto-removes empty items.

Detailed Resources

  • [examples.md](examples.md) - Extensive examples across all WordPress contexts
  • [integration.md](integration.md) - Complete logger class implementation