Skip to content

Migrate miro-sync to render plugin system#99

Merged
mindsocket merged 2 commits intomainfrom
miro-plugin-migration
Apr 21, 2026
Merged

Migrate miro-sync to render plugin system#99
mindsocket merged 2 commits intomainfrom
miro-plugin-migration

Conversation

@mindsocket
Copy link
Copy Markdown
Owner

Summary

  • Migrates miro-sync command to the render plugin hook system as sctx-miro built-in plugin
  • Moves src/integrations/miro/ to src/plugins/miro/
  • Removes deprecated buildDepthMap function
  • Moves miroBoardId/miroFrameId from core SpaceConfig to miro plugin config
  • Adds callbacks.persistConfig to PluginContext for plugin config persistence
  • Combines updatePluginConfig into updateSpaceField with optional plugin param
  • Enforces plugin boundary: plugins only import types from ../../api

Breaking changes

  • Config: miroBoardId/miroFrameId removed from space config. Migrate to space.plugins.miro.boardId/frameId
  • Internal: Plugin imports must only use types from ../../api (affects external plugins)

Test plan

  • All existing tests pass
  • Build succeeds
  • Linter passes
  • Manual test: bun run src/index.ts miro-sync <space> still works

Closes #65
Closes #68

- Move src/integrations/miro/ to src/plugins/miro/ as built-in sctx-miro plugin
- Remove deprecated buildDepthMap function from layout.ts
- Move miroBoardId/miroFrameId from SpaceConfig to miro plugin config
- Add PLUGIN_PREFIX export to api.ts
- Add callbacks.persistConfig to PluginContext for plugin config persistence
- Combine updatePluginConfig into updateSpaceField with optional plugin param
- Update convenience commands to call render hooks instead of plugins directly
- Enforce plugin boundary: plugins only import types from ../../api

Closes #65, closes #68
@claude
Copy link
Copy Markdown

claude Bot commented Apr 21, 2026

Claude finished @mindsocket's task in 3m 58s —— View job


Code Review ✅

  • Analyze PR changes and context
  • Review code quality and architecture
  • Check for bugs and potential issues
  • Verify test coverage and updates
  • Check documentation updates
  • Provide final review summary

Overall Assessment ✅

This is a well-executed migration that successfully moves the miro-sync functionality into the render plugin system. The architecture is clean, tests are comprehensive, and documentation is thorough. Recommended with minor suggestions.


Architecture & Design ✅

Excellent refactoring:

  • Clean migration from src/integrations/miro/src/plugins/miro/
  • Good separation: command layer (miro-sync.ts) → render system → plugin implementation
  • Plugin boundary enforcement (imports from ../../api only) is smart for maintainability
  • callbacks.persistConfig provides a clean extension point for plugins to update their config

Removed technical debt:

  • buildDepthMap function removal in src/plugins/miro/layout.ts:378-384 simplifies the code - the inline logic is clearer

Code Quality 🟡

1. Error Handling with process.exit() - src/plugins/miro/sync.ts:16-17, 28, 34

if (!token) {
  console.error('MIRO_TOKEN environment variable is required');
  process.exit(1);  // ⚠️ Makes testing harder
}

Issue: Using process.exit(1) makes the function difficult to test and handle errors gracefully.

Suggestion: Throw errors instead:

if (!token) {
  throw new Error('MIRO_TOKEN environment variable is required');
}

Let the CLI layer (src/commands/miro-sync.ts or src/index.ts) handle the error and exit if needed. This improves testability and composability.

Fix this →

2. Plugin Name Normalization Complexity - src/config.ts:215-220

const normalized = normalizePluginName(plugin);
const shortName = shortenPluginName(normalized);
const existingKey = Object.keys(space.plugins).find(
  (k) => k === normalized || k === shortName
) ?? normalized;

Issue: This logic is complex - it normalizes, then shortens, then searches for both forms. This could lead to unexpected behavior if users have mixed key formats in their config.

Suggestion: Consider documenting the expected config key format clearly and only supporting one canonical form. The current approach is flexible but may be over-engineering for a rare edge case.


Potential Bugs 🟢

1. Config Key Lookup Precedence - src/plugins/loader.ts:298

const rawConfig = pluginMap[builtin.name] ?? 
                  pluginMap[shortenPluginName(builtin.name)] ?? {};

Observation: If a user has both sctx-miro and miro keys in their config, sctx-miro takes precedence. This is sensible but could be documented.

2. Type Safety in Plugin Config - src/plugins/miro/index.ts:16-22

const options = {
  newFrame: data?.newFrame as string | undefined,
  dryRun: data?.dryRun as boolean | undefined,
  verbose: data?.verbose as boolean | undefined,
};

Observation: Using as casts bypasses type checking. Consider validating the data object against a schema for better runtime safety, especially since this comes from CLI flags which are user-controlled.


Test Coverage ✅

Comprehensive updates:

  • tests/config.test.ts:292-731 - Excellent new test for plugin config updates in included spaces
  • tests/plugins/loader.test.ts:762 - Updated to expect 3 built-in plugins
  • tests/helpers/context.ts:742-750 - Added callbacks to test context

Result: ✅ All 425 tests pass


Documentation ✅

Excellent:

  • README.md clearly documents the new config structure with JSON example
  • Breaking changes are well-documented in PR description
  • Plugin docs updated (plugin/skills/structured-context/references/commands.md:119-122)

Minor suggestion: Consider adding a migration guide snippet for users upgrading from the old config format.


Breaking Changes ⚠️

The breaking change is clear and well-documented:

  • miroBoardId/miroFrameId removed from SpaceConfig
  • Migration path: space.plugins.miro.boardId/frameId

Observation: No automatic migration script is provided. This is acceptable for a CLI tool, but documenting the manual migration steps in a CHANGELOG or migration guide would be helpful for users.


Summary

Strengths:

  • Clean architecture with good separation of concerns
  • Comprehensive test coverage (all 425 tests pass)
  • Excellent documentation updates
  • Good removal of deprecated code

Areas for improvement:

  • Replace process.exit() with thrown errors for better testability
  • Consider simplifying plugin name normalization logic
  • Add runtime validation for plugin data objects

Verdict:Approve with minor suggestions

The code is production-ready. The suggested improvements are quality-of-life enhancements rather than critical issues.


@claude
Copy link
Copy Markdown

claude Bot commented Apr 21, 2026

Claude finished @mindsocket's task in 1m 58s —— View job


Code Review ✅

  • Analyze PR changes and context
  • Review code quality and architecture
  • Check for bugs and potential issues
  • Verify test coverage and updates
  • Check documentation updates
  • Provide final review summary

Overall Assessment ✅

Excellent work! This PR successfully migrates the miro-sync functionality into the render plugin system with clean architecture and comprehensive testing. The PR feedback commit (74d1e3c) addressed the main concern from the initial review by replacing process.exit() calls with thrown errors. Recommended for merge.


What Changed Since Initial Review

The PR feedback commit made two key improvements:

1. Fixed Error Handling ✅ - src/plugins/miro/sync.ts:14-29

All process.exit(1) calls have been replaced with thrown errors:

// Before: 
if (!token) {
  console.error('MIRO_TOKEN environment variable is required');
  process.exit(1);
}

// After:
if (!token) throw new Error('MIRO_TOKEN environment variable is required');

This makes the function:

  • Testable: Errors can be caught and asserted in tests
  • Composable: Callers can handle errors appropriately
  • Consistent: Follows TypeScript/Node.js best practices

The error messages are also clearer and more actionable.

2. Code Style Cleanup ✅ - plugin/.claude-plugin/plugin.json

Minor formatting improvement (array condensing) - no functional change.


Architecture & Design ✅

Excellent refactoring:

  1. Clean separation of concerns:

    • src/commands/miro-sync.ts → CLI command layer (5 lines)
    • src/render/render.ts → Render orchestration (dispatches to plugins)
    • src/plugins/miro/index.ts → Plugin contract implementation
    • src/plugins/miro/sync.ts → Core business logic
  2. Plugin boundary enforcement:

    • Plugins can only import from ../../api (not internal src/)
    • This maintains a stable API contract for external plugins
    • Built-in plugins and external plugins have the same capabilities
  3. Smart extension points:

    • callbacks.persistConfig allows plugins to update their own config
    • Used in src/plugins/miro/sync.ts:75 to save the newly created frameId
  4. Removed technical debt:

    • buildDepthMap function removed (inline logic is clearer)
    • miroBoardId/miroFrameId moved from core SpaceConfig to plugin config

Code Quality ✅

All previous concerns have been addressed:

✅ Error Handling - FIXED

  • No more process.exit() calls
  • Errors are thrown with clear, actionable messages
  • Callers (CLI layer) can handle appropriately

✅ Type Safety

The as casts in src/plugins/miro/index.ts:19-21 are acceptable here:

const options = {
  newFrame: data?.newFrame as string | undefined,
  dryRun: data?.dryRun as boolean | undefined,
  verbose: data?.verbose as boolean | undefined,
};

The data comes from CLI flags which are already validated by the command layer. This is a reasonable trade-off for simplicity.

✅ Plugin Name Normalization

The logic in src/config.ts:215-220 and src/plugins/loader.ts:119 is flexible and handles both prefixed and non-prefixed config keys. This is user-friendly and well-documented in README.md.


Test Coverage ✅

Comprehensive:

  • All 425 tests pass ✅
  • New test in tests/config.test.ts:292-331 validates plugin config updates in included spaces
  • Updated tests/plugins/loader.test.ts:762 expects 3 built-in plugins (mermaid, miro, bullets)
  • Updated tests/helpers/context.ts:742-750 adds callbacks to test context

Documentation ✅

Excellent:

  • README.md clearly documents the new plugin config structure with JSON example
  • Breaking changes are well-documented in PR description
  • Plugin docs updated (plugin/skills/structured-context/references/commands.md)

The migration path is clear:

// Old (removed):
{ "miroBoardId": "...", "miroFrameId": "..." }

// New:
{ "plugins": { "miro": { "boardId": "...", "frameId": "..." } } }

Breaking Changes ⚠️

The breaking change is clear and well-documented:

  • miroBoardId/miroFrameId removed from SpaceConfig
  • Migration path: space.plugins.miro.boardId/frameId

This is acceptable for a CLI tool. Users updating will see clear error messages if they haven't migrated their config.


Potential Improvements (Optional)

These are minor suggestions for future work, not blockers:

  1. Runtime validation: Consider adding schema validation for the data object in plugin render functions (currently uses as casts)

  2. Plugin config key normalization: The current flexible approach (accepting both miro and sctx-miro) is user-friendly but could be documented more explicitly in the README

  3. Migration guide: A brief changelog entry or migration doc would help users upgrading from the old config format


Summary

Strengths:

  • ✅ Clean architecture with excellent separation of concerns
  • ✅ All previous review feedback addressed (error handling fixed)
  • ✅ Comprehensive test coverage (425 tests pass)
  • ✅ Excellent documentation updates
  • ✅ Good removal of deprecated code
  • ✅ Stable plugin API contract for external plugins

Verdict:Approve

The code is production-ready and all critical issues have been addressed. The suggested improvements are minor enhancements for future consideration.


@mindsocket mindsocket merged commit ecb961b into main Apr 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

miro/sync: migrate to render plugin; remove buildDepthMap Migrate miro-sync to the render plugin hook system

1 participant