apiforge-sdk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. apiforge_sdk-0.1.0/.gitignore +41 -0
  2. apiforge_sdk-0.1.0/Contributing.md +260 -0
  3. apiforge_sdk-0.1.0/LICENSE +21 -0
  4. apiforge_sdk-0.1.0/PKG-INFO +264 -0
  5. apiforge_sdk-0.1.0/README.md +203 -0
  6. apiforge_sdk-0.1.0/ROADMAP.md +135 -0
  7. apiforge_sdk-0.1.0/apiforge/__init__.py +40 -0
  8. apiforge_sdk-0.1.0/apiforge/cli/__init__.py +11 -0
  9. apiforge_sdk-0.1.0/apiforge/cli/main.py +197 -0
  10. apiforge_sdk-0.1.0/apiforge/core/__init__.py +29 -0
  11. apiforge_sdk-0.1.0/apiforge/core/client.py +255 -0
  12. apiforge_sdk-0.1.0/apiforge/core/config.py +62 -0
  13. apiforge_sdk-0.1.0/apiforge/core/credentials.py +259 -0
  14. apiforge_sdk-0.1.0/apiforge/core/discovery.py +105 -0
  15. apiforge_sdk-0.1.0/apiforge/core/exceptions.py +58 -0
  16. apiforge_sdk-0.1.0/apiforge/core/metadata.py +79 -0
  17. apiforge_sdk-0.1.0/apiforge/generators/__init__.py +78 -0
  18. apiforge_sdk-0.1.0/apiforge/mcp/__init__.py +12 -0
  19. apiforge_sdk-0.1.0/apiforge/mcp/adapter.py +92 -0
  20. apiforge_sdk-0.1.0/apiforge/mcp/generator.py +90 -0
  21. apiforge_sdk-0.1.0/apiforge/plugins/__init__.py +97 -0
  22. apiforge_sdk-0.1.0/apiforge/plugins/base.py +12 -0
  23. apiforge_sdk-0.1.0/apiforge/plugins/discord/__init__.py +88 -0
  24. apiforge_sdk-0.1.0/apiforge/plugins/github/__init__.py +122 -0
  25. apiforge_sdk-0.1.0/apiforge/plugins/notion/__init__.py +104 -0
  26. apiforge_sdk-0.1.0/docs/Architecture.md +270 -0
  27. apiforge_sdk-0.1.0/pyproject.toml +136 -0
  28. apiforge_sdk-0.1.0/tests/__init__.py +0 -0
  29. apiforge_sdk-0.1.0/tests/conftest.py +87 -0
  30. apiforge_sdk-0.1.0/tests/test_core/__init__.py +0 -0
  31. apiforge_sdk-0.1.0/tests/test_core/test_client.py +61 -0
  32. apiforge_sdk-0.1.0/tests/test_core/test_credentials.py +86 -0
  33. apiforge_sdk-0.1.0/tests/test_core/test_discovery.py +53 -0
  34. apiforge_sdk-0.1.0/tests/test_plugins/__init__.py +0 -0
  35. apiforge_sdk-0.1.0/tests/test_plugins/test_first_party.py +118 -0
@@ -0,0 +1,41 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ *.egg
11
+ .eggs/
12
+
13
+ # Virtual environments
14
+ .venv/
15
+ venv/
16
+ env/
17
+
18
+ # Test / coverage artifacts
19
+ .pytest_cache/
20
+ .coverage
21
+ .coverage.*
22
+ htmlcov/
23
+ coverage.xml
24
+ .mypy_cache/
25
+ .ruff_cache/
26
+
27
+ # IDE
28
+ .idea/
29
+ .vscode/
30
+ *.swp
31
+ *.swo
32
+
33
+ # OS
34
+ .DS_Store
35
+ Thumbs.db
36
+
37
+ # Local config
38
+ .env
39
+ .env.local
40
+ config.local.toml
41
+ *.local.toml
@@ -0,0 +1,260 @@
1
+ # Contributing to APIForge
2
+
3
+ Thanks for your interest in improving APIForge! This document walks
4
+ you through the plugin development workflow, the codebase layout,
5
+ the conventions we use, and how to send a pull request.
6
+
7
+ ---
8
+
9
+ ## Code of conduct
10
+
11
+ Be respectful, assume good faith, and remember there is a person on
12
+ the other side of the screen. We follow the
13
+ [Contributor Covenant](https://www.contributor-covenant.org/).
14
+
15
+ ---
16
+
17
+ ## Setting up your environment
18
+
19
+ ```bash
20
+ git clone https://github.com/apiforge/apiforge.git
21
+ cd apiforge
22
+ python -m venv .venv
23
+ source .venv/bin/activate
24
+ pip install -e ".[dev,test]"
25
+ ```
26
+
27
+ Run the tests:
28
+
29
+ ```bash
30
+ pytest
31
+ ```
32
+
33
+ Lint:
34
+
35
+ ```bash
36
+ ruff check apiforge tests
37
+ ruff format --check apiforge tests
38
+ ```
39
+
40
+ Type-check:
41
+
42
+ ```bash
43
+ mypy apiforge
44
+ ```
45
+
46
+ All three checks run in CI; see `.github/workflows/ci.yml`.
47
+
48
+ ---
49
+
50
+ ## Code layout
51
+
52
+ ```
53
+ apiforge/
54
+ ├── core/ ← client, config, credentials, discovery, metadata
55
+ ├── plugins/ ← first-party plugins (GitHub, Discord, Notion)
56
+ ├── mcp/ ← MCP adapter base + generator (Phase 4)
57
+ ├── generators/ ← OpenAPI generator (Phase 2)
58
+ └── cli/ ← the `apiforge` command
59
+
60
+ tests/
61
+ ├── test_core/
62
+ └── test_plugins/
63
+ ```
64
+
65
+ The rule of thumb:
66
+
67
+ - Cross-cutting concerns go in `core/`.
68
+ - Each service has its own plugin directory under `plugins/`.
69
+ - Adapters and generators are interface-first today; they grow
70
+ alongside the plugins they serve.
71
+
72
+ ---
73
+
74
+ ## Adding a new plugin
75
+
76
+ The fastest path: copy an existing plugin (`apiforge/plugins/github/`)
77
+ and adapt it. The full checklist:
78
+
79
+ ### 1. Create the package
80
+
81
+ ```
82
+ apiforge/plugins/<name>/__init__.py
83
+ ```
84
+
85
+ For a standalone distribution, the directory name should be
86
+ `<name>.py` or a package. The class name must be unique.
87
+
88
+ ### 2. Define metadata
89
+
90
+ ```python
91
+ from apiforge.plugins.base import BasePlugin
92
+ from apiforge.core.metadata import AuthType, OperationMetadata, PluginMetadata
93
+
94
+
95
+ class StripePlugin(BasePlugin):
96
+ metadata = PluginMetadata(
97
+ name="stripe",
98
+ version="0.1.0",
99
+ description="Stripe payments API.",
100
+ auth_type=AuthType.API_KEY,
101
+ base_url="https://api.stripe.com/v1",
102
+ operations=(
103
+ OperationMetadata(
104
+ name="create_charge",
105
+ description="Charge a customer.",
106
+ parameters={"amount": "int", "currency": "str"},
107
+ ),
108
+ ),
109
+ )
110
+ ```
111
+
112
+ `PluginMetadata` is strict — extra fields are rejected. The fields
113
+ you should fill in:
114
+
115
+ - `name` — unique, lowercase, matches the entry-point name.
116
+ - `version` — semver.
117
+ - `description` — one sentence, used in `apiforge list`.
118
+ - `auth_type` — one of `token`, `api_key`, `oauth2`, `basic`, `none`.
119
+ - `base_url` — the upstream root; some plugins don't have one.
120
+ - `operations` — at minimum the operations you implement.
121
+ - `openapi_url` — if the upstream publishes a spec.
122
+
123
+ ### 3. Implement operations
124
+
125
+ ```python
126
+ async def create_charge(self, amount: int, currency: str) -> dict:
127
+ token = self.require_credential()
128
+ response = await self.http.post(
129
+ f"{self.metadata.base_url}/charges",
130
+ json={"amount": amount, "currency": currency},
131
+ headers={"Authorization": f"Bearer {token}"},
132
+ )
133
+ response.raise_for_status()
134
+ return response.json()
135
+ ```
136
+
137
+ Conventions:
138
+
139
+ - All operations are `async def`.
140
+ - Use `self.http`, not your own client — we share the pool.
141
+ - Use `self.require_credential()` if a credential is mandatory.
142
+ - Use `self.get_credential()` if it's optional (e.g. for read-only
143
+ endpoints).
144
+ - Don't catch `HTTPStatusError` — let it propagate so the CLI can
145
+ show the upstream status.
146
+
147
+ ### 4. Wire the entry point
148
+
149
+ In your `pyproject.toml`:
150
+
151
+ ```toml
152
+ [project.entry-points."apiforge.plugins"]
153
+ stripe = "apiforge_plugin_stripe:StripePlugin"
154
+ ```
155
+
156
+ If you're adding to APIForge itself, edit the top-level
157
+ `pyproject.toml` instead.
158
+
159
+ ### 5. Add tests
160
+
161
+ Cover at minimum:
162
+
163
+ - The plugin's happy path, with `httpx` mocked via `respx`.
164
+ - The credential-missing path, if your plugin requires one.
165
+ - Auth headers — assert the right `Authorization` header is sent.
166
+
167
+ See `tests/test_plugins/test_first_party.py` for examples.
168
+
169
+ ### 6. Document
170
+
171
+ Add a one-paragraph section to the README under "Plugins".
172
+ Reference the upstream API docs.
173
+
174
+ ---
175
+
176
+ ## Adding an MCP adapter (Phase 4 preview)
177
+
178
+ When the MCP generation machinery lands, your plugin will need to
179
+ declare an adapter:
180
+
181
+ ```python
182
+ from apiforge.mcp.adapter import BaseMCPAdapter
183
+
184
+
185
+ class StripeMCPAdapter(BaseMCPAdapter):
186
+ def tool_definitions(self) -> list[dict]:
187
+ return [
188
+ {
189
+ "name": "stripe__create_charge",
190
+ "description": "Charge a customer.",
191
+ "inputSchema": {
192
+ "type": "object",
193
+ "properties": {
194
+ "amount": {"type": "integer"},
195
+ "currency": {"type": "string"},
196
+ },
197
+ "required": ["amount", "currency"],
198
+ },
199
+ }
200
+ ]
201
+
202
+
203
+ class StripePlugin(BasePlugin):
204
+ metadata = PluginMetadata(name="stripe", version="0.1.0", ...)
205
+ MCP_ADAPTER_CLASS = StripeMCPAdapter
206
+ ```
207
+
208
+ The contract is stable today; the generator walks the registry
209
+ looking for `MCP_ADAPTER_CLASS`.
210
+
211
+ ---
212
+
213
+ ## Style
214
+
215
+ - **Python 3.12+** syntax — `type` aliases, `match`, generics.
216
+ - **Type hints everywhere** public. Internal helpers can use less
217
+ formal types where it helps readability.
218
+ - **Pydantic models** for any data that crosses a boundary (config,
219
+ metadata, request/response shapes).
220
+ - **Ruff** for linting and formatting. Run `ruff format` before
221
+ committing.
222
+ - **mypy --strict** is the type-check setting.
223
+
224
+ ---
225
+
226
+ ## Pull request checklist
227
+
228
+ - [ ] Tests pass locally (`pytest`)
229
+ - [ ] Ruff is clean (`ruff check apiforge tests`)
230
+ - [ ] mypy is clean (`mypy apiforge`)
231
+ - [ ] New code is type-hinted
232
+ - [ ] New public surface is documented (docstrings + README update
233
+ if user-facing)
234
+ - [ ] If you added a plugin, the entry point is wired in
235
+ `pyproject.toml`
236
+ - [ ] CHANGELOG entry (we'll add one once we're out of 0.0.x)
237
+
238
+ ---
239
+
240
+ ## Release process
241
+
242
+ We use `hatch` for builds. To cut a release:
243
+
244
+ ```bash
245
+ # Bump version, update CHANGELOG.
246
+ # Tag and push:
247
+ git tag -a v0.2.0 -m "v0.2.0"
248
+ git push origin v0.2.0
249
+ ```
250
+
251
+ CI builds the wheel and sdist; we publish from CI with PyPI trusted
252
+ publishing.
253
+
254
+ ---
255
+
256
+ ## Questions?
257
+
258
+ Open a GitHub Discussion. Bug reports go in Issues. Security
259
+ disclosures: see `SECURITY.md` (TODO — please open an issue for now
260
+ and we'll route it).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 APIForge Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,264 @@
1
+ Metadata-Version: 2.4
2
+ Name: apiforge-sdk
3
+ Version: 0.1.0
4
+ Summary: Unified Python interface for multiple APIs with plugin architecture
5
+ Project-URL: Homepage, https://github.com/apiforge/apiforge
6
+ Project-URL: Documentation, https://github.com/apiforge/apiforge/blob/main/README.md
7
+ Project-URL: Repository, https://github.com/apiforge/apiforge
8
+ Project-URL: Issues, https://github.com/apiforge/apiforge/issues
9
+ Project-URL: Changelog, https://github.com/apiforge/apiforge/releases
10
+ Author-email: APIForge Contributors <maintainers@apiforge.dev>
11
+ Maintainer-email: APIForge Contributors <maintainers@apiforge.dev>
12
+ License: MIT License
13
+
14
+ Copyright (c) 2026 APIForge Contributors
15
+
16
+ Permission is hereby granted, free of charge, to any person obtaining a copy
17
+ of this software and associated documentation files (the "Software"), to deal
18
+ in the Software without restriction, including without limitation the rights
19
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20
+ copies of the Software, and to permit persons to whom the Software is
21
+ furnished to do so, subject to the following conditions:
22
+
23
+ The above copyright notice and this permission notice shall be included in all
24
+ copies or substantial portions of the Software.
25
+
26
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32
+ SOFTWARE.
33
+ License-File: LICENSE
34
+ Keywords: api,discord,github,integration,mcp,notion,openapi,plugins,sdk
35
+ Classifier: Development Status :: 3 - Alpha
36
+ Classifier: Intended Audience :: Developers
37
+ Classifier: License :: OSI Approved :: MIT License
38
+ Classifier: Operating System :: OS Independent
39
+ Classifier: Programming Language :: Python :: 3
40
+ Classifier: Programming Language :: Python :: 3.12
41
+ Classifier: Programming Language :: Python :: 3.13
42
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
43
+ Classifier: Typing :: Typed
44
+ Requires-Python: >=3.12
45
+ Requires-Dist: click>=8.1.7
46
+ Requires-Dist: httpx>=0.27.0
47
+ Requires-Dist: keyring>=24.3.0
48
+ Requires-Dist: pydantic-settings>=2.2.0
49
+ Requires-Dist: pydantic>=2.6.0
50
+ Requires-Dist: rich>=13.7.0
51
+ Provides-Extra: dev
52
+ Requires-Dist: mypy>=1.8.0; extra == 'dev'
53
+ Requires-Dist: pre-commit>=3.6.0; extra == 'dev'
54
+ Requires-Dist: ruff>=0.3.0; extra == 'dev'
55
+ Provides-Extra: test
56
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'test'
57
+ Requires-Dist: pytest-cov>=4.1.0; extra == 'test'
58
+ Requires-Dist: pytest>=8.0.0; extra == 'test'
59
+ Requires-Dist: respx>=0.21.0; extra == 'test'
60
+ Description-Content-Type: text/markdown
61
+
62
+ # APIForge
63
+
64
+ > One Python interface for thousands of APIs.
65
+
66
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
67
+ [![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
68
+ [![CI](https://github.com/apiforge/apiforge/actions/workflows/ci.yml/badge.svg)](https://github.com/apiforge/apiforge/actions/workflows/ci.yml)
69
+
70
+ APIForge is a plugin-based Python framework that gives you a single,
71
+ consistent way to talk to many APIs. The first release ships with
72
+ plugins for **GitHub**, **Discord**, and **Notion**; the architecture
73
+ is designed to grow to thousands.
74
+
75
+ ```python
76
+ from apiforge import APIForge
77
+
78
+ client = APIForge()
79
+ client.configure(
80
+ github_token="ghp_...",
81
+ discord_token="...",
82
+ notion_token="...",
83
+ )
84
+
85
+ # All of these look the same — no separate SDK to learn per service.
86
+ user = client.github.get_user("octocat")
87
+ msg = client.discord.send_message(channel_id="...", content="hi")
88
+ pages = client.notion.list_pages()
89
+ ```
90
+
91
+ The same client also exposes a CLI:
92
+
93
+ ```bash
94
+ apiforge list
95
+ apiforge plugins
96
+ apiforge info github
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Why APIForge?
102
+
103
+ If you integrate with five services, you install five SDKs, learn
104
+ five authentication schemes, and maintain five upgrade cycles.
105
+ APIForge collapses that into one.
106
+
107
+ - **One import.** `from apiforge import APIForge`.
108
+ - **One auth surface.** Pass tokens once; they reach every plugin.
109
+ - **One plugin contract.** Adding a new service is a small, well-defined job.
110
+ - **One way to fail.** A small exception hierarchy that you can catch in one place.
111
+ - **One direction.** Async-first under the hood, with sync wrappers for ergonomics.
112
+
113
+ ---
114
+
115
+ ## Installation
116
+
117
+ ```bash
118
+ pip install apiforge
119
+ ```
120
+
121
+ The base package installs the three first-party plugins
122
+ (GitHub, Discord, Notion) automatically. Additional plugins are
123
+ distributed as separate packages, e.g.:
124
+
125
+ ```bash
126
+ pip install apiforge-plugin-stripe
127
+ ```
128
+
129
+ Each one registers itself via the standard `apiforge.plugins`
130
+ entry-point group — there's nothing to wire up in your code.
131
+
132
+ ---
133
+
134
+ ## Quickstart
135
+
136
+ ### Sync usage
137
+
138
+ The simplest path. Every method on a plugin is awaitable under the
139
+ hood, but the client exposes a transparent sync wrapper, so you don't
140
+ need an event loop for one-off scripts:
141
+
142
+ ```python
143
+ from apiforge import APIForge
144
+
145
+ client = APIForge()
146
+ client.configure(github_token="ghp_...")
147
+
148
+ user = client.github.get_user("octocat")
149
+ print(user["login"])
150
+ ```
151
+
152
+ ### Async usage
153
+
154
+ For servers, batched jobs, or anything that benefits from concurrency:
155
+
156
+ ```python
157
+ import asyncio
158
+ from apiforge import APIForge
159
+
160
+ async def main() -> None:
161
+ async with APIForge() as client:
162
+ client.configure(github_token="ghp_...")
163
+ user, repos = await asyncio.gather(
164
+ client.github.get_user("octocat"),
165
+ client.github.list_repos("octocat", limit=5),
166
+ )
167
+
168
+ asyncio.run(main())
169
+ ```
170
+
171
+ ### Credentials
172
+
173
+ APIForge looks for credentials in this order:
174
+
175
+ 1. **Explicit** — `client.configure(github_token="...")`
176
+ 2. **Environment** — `APIFORGE_GITHUB_TOKEN=...`
177
+ 3. **Keyring** — the OS secure store (`keyring` package)
178
+ 4. **Config file** — `~/.config/apiforge/credentials.toml`
179
+
180
+ ```bash
181
+ # Store a token in your OS keyring:
182
+ python -c "from apiforge import APIForge; APIForge().credentials.store('github', 'ghp_...')"
183
+ ```
184
+
185
+ ---
186
+
187
+ ## CLI
188
+
189
+ ```bash
190
+ apiforge list # all registered plugins
191
+ apiforge plugins # alias for `list`
192
+ apiforge info github # details on one plugin
193
+ apiforge info notion --json # machine-readable
194
+
195
+ # Coming in later phases:
196
+ apiforge generate openapi.yaml --name myplugin # Phase 2
197
+ apiforge install apiforge-plugin-stripe # Phase 5
198
+ apiforge update # Phase 5
199
+ ```
200
+
201
+ ---
202
+
203
+ ## Writing a Plugin
204
+
205
+ A plugin is a class that subclasses `BasePlugin` and declares a
206
+ `PluginMetadata`:
207
+
208
+ ```python
209
+ from apiforge.plugins.base import BasePlugin
210
+ from apiforge.core.metadata import AuthType, OperationMetadata, PluginMetadata
211
+
212
+
213
+ class StripePlugin(BasePlugin):
214
+ metadata = PluginMetadata(
215
+ name="stripe",
216
+ version="0.1.0",
217
+ description="Stripe payments API.",
218
+ auth_type=AuthType.API_KEY,
219
+ base_url="https://api.stripe.com/v1",
220
+ operations=(
221
+ OperationMetadata(name="create_charge", parameters={"amount": "int"}),
222
+ ),
223
+ )
224
+
225
+ async def setup(self) -> None:
226
+ ...
227
+
228
+ async def create_charge(self, amount: int) -> dict:
229
+ ...
230
+ ```
231
+
232
+ Then register it in your package's `pyproject.toml`:
233
+
234
+ ```toml
235
+ [project.entry-points."apiforge.plugins"]
236
+ stripe = "apiforge_plugin_stripe:StripePlugin"
237
+ ```
238
+
239
+ Once installed, `client.stripe.create_charge(...)` works out of the
240
+ box. See [Contributing.md](Contributing.md) for the full checklist.
241
+
242
+ ---
243
+
244
+ ## Project Status
245
+
246
+ APIForge is in **alpha**. The core framework, the credential
247
+ manager, the CLI, and the three first-party plugins are stable
248
+ enough for everyday use, but the API may still evolve.
249
+
250
+ See [ROADMAP.md](ROADMAP.md) for what's next.
251
+
252
+ ---
253
+
254
+ ## Documentation
255
+
256
+ - [Architecture.md](Architecture.md) — the design, the layers, the contracts.
257
+ - [Contributing.md](Contributing.md) — how to write and ship a plugin.
258
+ - [ROADMAP.md](ROADMAP.md) — Phase 1 through Phase 5.
259
+
260
+ ---
261
+
262
+ ## License
263
+
264
+ MIT — see [LICENSE](LICENSE).