CLI command registration bypasses the plugin system #75
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Competitor
Kind/Documentation
Kind/Enhancement
Kind/Epic
Kind/Feature
Kind/Security
Kind/Story
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Scope/Core
Scope/Cross-Plugin
Scope/Plugin-System
Scope/Single-Plugin
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ultanio/cobot#75
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
CLI command registration is baked into the
Pluginbase class and uses raw iteration instead of the plugin system's extension points.Current flow (
cli.py, line 663):What's wrong:
register_commands()is a method onPluginbase class (base.py, line 207) — every plugin inherits it whether it uses CLI or notcli.pyiterates over ALL plugins and calls it — no capabilities, no extension points, no graph visibilityPluginMetacobot plugins inspect) can only detect CLI contribution via method override heuristics — it's invisible to the architectureWhy it matters:
This is exactly the "hidden coupling" anti-pattern from the plugin design guide. CLI contribution is real coupling that should be declared, not hidden in base class inheritance.
Proposed Solution
Model CLI as a plugin with an extension point. Same pattern as
loop.transform_historyorcontext.system_prompt.1. Create a
cliplugin2. Plugins declare their CLI contribution
Same for memory, pairing, subagent, and any future CLI-contributing plugin.
3. CLI plugin collects commands via extension point
Instead of iterating all plugins and calling a base class method.
4. Remove
register_commands()fromPluginbase classIt shouldn't be on the base class — not every plugin needs CLI. Move it to an explicit opt-in via
implements.Acceptance Criteria
register_commands()removed fromPluginbase class inbase.pycliplugin created withextension_points: ["cli.commands"]implements: {"cli.commands": "register_commands"}cli.pyusescall_extension("cli.commands", ...)instead of raw plugin iterationimplementsedges to theclipluginRelated
Architecture Review: CLI Extension Point Proposal
Reviewer: Plugin Architecture Subagent
Verdict: ✅ APPROVE (with minor suggestions)
This is a well-reasoned proposal that correctly identifies a real architectural inconsistency and proposes a clean solution following established patterns.
Analysis
1. Problem Verification ✅
The problem is real and accurately described:
register_commands()is defined onPluginbase class (line 112 inbase.py)cli.pyiterates ALL plugins via raw loop inregister_plugin_commands()(line ~495)memoryandpairingPluginMetaThis is indeed "hidden coupling" — a plugin's CLI contribution is only discoverable by method introspection, not by declared metadata.
2. Proposed Solution Assessment ✅
The
cli.commandsextension point follows the established pattern already used by:loop.on_message,loop.transform_history, etc.memory.store,memory.search, etc.context.system_promptUsing
implements: {"cli.commands": "register_commands"}is consistent with how other inter-plugin communication works. The plugin graph will correctly show edges from CLI-contributing plugins to thecliplugin.3. Implementation Scope ✅
Current CLI-contributing plugins are:
memory/plugin.py—cobot memory {store,get,search,list}pairing/plugin.py— pairing commands(Note: The issue mentions cron and subagent, but these don't appear to have
register_commandsimplementations in current codebase)Migration is low-risk — only 2 plugins need
implementsdeclarations added.Suggestions
1. Deprecation Period for Backward Compatibility
Removing
register_commands()from base class is a breaking change for any external plugins. Consider:Remove the base method entirely in v0.4.0 or after one release cycle.
2. CLI Plugin Load Order
With
priority: 5, the CLI plugin loads early. Ensure CLI command registration happens after all plugins are discovered but before CLI execution. The currentregister_plugin_commands()flow seems correct — just verify the timing with extension point dispatch.3. Consider
capabilities: ["cli"]For consistency with other capabilities (
["llm"],["storage"]), you could also add:The
implementsdeclaration is the authoritative one;capabilitiesis just for quick filtering.Acceptance Criteria Assessment
register_commands()from base classcliplugin with extension pointimplementscli.pyto usecall_extensionConclusion
This refactoring makes CLI contribution explicit and discoverable, aligning with the extension point architecture used elsewhere. The implementation scope is small (2 plugins + new CLI plugin + cli.py changes).
Recommended approach:
cliplugin withextension_points: ["cli.commands"]implementsto memory and pairing pluginscli.pyto use extension dispatchregister_commands()Ready for implementation. 👍