Skip to content

Refactor adapters into true external adapter executables #45

@albatrosary

Description

@albatrosary

Summary

Currently, adapter-like features such as @github, @sh, and similar interfaces are still too closely tied to the main multi-ai-cli codebase.

I propose refactoring them into true external adapters so that the core application remains a simple router / dispatcher, while each adapter becomes an independent specialized tool.

This would make the architecture cleaner, easier to extend, and much faster to evolve without rebuilding the entire application for every new integration.


Motivation

As the number of external interfaces grows, the main binary should not accumulate provider-specific logic.

Instead, the core should focus on only a few responsibilities:

  1. Parse the command head such as @github.tree
  2. Resolve the adapter from configuration
  3. Forward the remaining arguments to the adapter executable
  4. Relay the adapter result back to the user

This design provides several practical advantages:

  • Cleaner core architecture
    The main application stays small and focused.

  • Independent adapter development
    Adapters can be developed, tested, and debugged separately.

  • Faster release cycle
    New adapter features can be shipped without rebuilding the whole application.

  • Better extensibility
    Users can register their own adapters through configuration.

  • Language/runtime freedom
    Adapters do not need to be implemented in the same language as the core.


Proposed architecture

Command example

multi-ai @github.tree --repo "ashiras/multi-ai-cli" -w out.md

Flow

[ User Command ]
$ multi-ai @github.tree --repo "ashiras/multi-ai-cli" -w out.md
        |
        v
+-----------------------------+
|     Main Engine / Router    |
|-----------------------------|
| parse command               |
| resolve adapter from config |
| launch adapter subprocess   |
| relay stdout / stderr       |
+-------------+---------------+
              |
              v
+-----------------------------+
|        Adapter Config       |
|-----------------------------|
| github -> ./bin/github-adapter
| sh     -> ./bin/shell-adapter
| figma  -> ./bin/figma-adapter
+-------------+---------------+
              |
              v
+-----------------------------+
|       github-adapter        |
|-----------------------------|
| parse adapter action        |
| validate arguments          |
| execute GitHub API logic    |
| write stdout / file         |
+-----------------------------+

Naming model

The command head should be treated as a routing key:

@<adapter>.<action>

Examples:

  • @github.tree
  • @github.file
  • @github.issue
  • @sh.run
  • @figma.export

The main engine should not interpret the domain meaning of these actions.
Their semantics belong entirely to the adapter.


Configuration example

A simple initial approach:

[ADAPTERS]
github = ./bin/github-adapter
sh = ./bin/shell-adapter
figma = ./bin/figma-adapter

Possible future extension:

[ADAPTERS]
github.cmd = ./bin/github-adapter
github.enabled = true
github.timeout_sec = 30

sh.cmd = ./bin/shell-adapter
sh.enabled = true
sh.timeout_sec = 300

Adapter contract (minimum)

The key to making this architecture stable is to define a minimal contract between the core and adapters.

Core → Adapter

The core passes:

  • adapter action
  • remaining CLI arguments
  • current environment
  • current working directory
  • stdin if needed

Adapter → Core

The adapter returns:

  • exit code
  • stdout
  • stderr

This means the first version can remain very simple and Unix-like.


Future extension: structured response mode

In addition to plain stdout/stderr, adapters could later support a structured output mode such as --json.

Example:

{
  "ok": true,
  "adapter": "github",
  "action": "tree",
  "content_type": "text/markdown",
  "content": "# Repository Tree\n...",
  "artifacts": [
    {
      "type": "file",
      "path": "out.md"
    }
  ]
}

This is not required for the first implementation, but it would make future integration much easier.


##. Design principles

1. Keep the core dumb

The main engine should be a router, not an expert for every external system.

2. Keep adapters independent

Each adapter should be executable and testable on its own.

3. Keep the contract stable

The more stable the adapter protocol is, the faster independent releases can move.

4. Prefer subprocess isolation

Using external executables keeps adapters language-agnostic and reduces coupling.


Expected benefits

  • Reduce complexity in the main application
  • Make adapter development independent from core development
  • Improve release speed for new integrations
  • Allow third-party/custom adapters in the future
  • Make testing and debugging easier
  • Reduce rebuild cost for every adapter change

Suggested implementation phases

Phase 1

Define the adapter routing syntax and minimal execution contract.

Phase 2

Refactor @github into an external adapter executable.

Phase 3

Refactor @sh and @figma to follow the same contract.

Phase 4

Support user-defined / third-party adapters via configuration.


Open questions

  • Should -w be handled by the adapter or by the core?
  • Should there be a reserved separator such as -- between core options and adapter options?
  • Do we want a structured --json contract from the beginning, or later?
  • How should adapter timeouts and failures be normalized?
  • Should adapter protocol versioning be introduced early (e.g. MULTI_AI_ADAPTER_API=1)?

Conclusion

This refactor would move multi-ai-cli toward a cleaner and more scalable architecture:

  • Core = router
  • Adapter = independent specialist

That separation should make the project easier to grow, easier to test, and much faster to release.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions