Adapter Spec
April 26, 2026 ยท View on GitHub
This is the reusable shape for connecting a coding agent to a local application.
Goal
Make a running creative app legible and mutable through shell commands, so a coding agent can use its existing tools to read context, search docs, compose a script, execute it, inspect errors, and iterate. Without a custom agent runtime, MCP servers, app takeover, headless batch or screen scraping. Use only the existing API capabilities of the software.
Folder Shape
adapters/<app>_adapter/
<app>_bridge.py
APP.md
README.md
examples/
context.<jsx|py|json>
docs/ optional
sources.md optional, authoritative reference links
All app adapters should use this folder shape. Root-level bridge files, if present, should only be backwards-compatible wrappers.
The examples/ directory is for reusable, checked-in reference examples and smoke tests. Agents must not use it as a scratch directory for task-specific generated scripts. One-off bridge scripts should be passed through --stdin or, when a file is necessary, written under a temp/scratch path such as .tmp/<app>/
or the OS temp directory and removed after use.
The root AGENTS.md is a small router to shared rules and app notes. Adapter AGENTS.md files, if present, should only point to shared rules and APP.md.
Bridge Contract
The bridge must:
- Accept script code through argv,
--stdin, or--file. - Return JSON on stdout for success.
- Return JSON on stderr and non-zero exit for failure.
- Connect to a running app by default (avoiding launching the app unless
--allow-launchis explicitly passed). - Avoid hard-coded install paths.
- If the bridge exposes local HTTP eval, require an unguessable per-session token read automatically by the shell bridge from a user-scoped session file.
Bridge commands should remain easy to identify as *_bridge.py Python processes. tools/cleanup_bridges.py relies on checked-in bridge script paths to list and stop stale bridge processes without touching unrelated Python work.
OS-Level Script Dispatch
If an app exposes an operating-system automation surface, prefer that before shipping an in-app extension. The bridge should still keep the same shell contract regardless of transport.
Common transports:
- Windows COM automation, such as Adobe
DoJavaScript, InDesignDoScript, or Microsoft Office object models. - macOS AppleScript dispatch into app scripting runtimes, such as Adobe
do javascriptor InDesigndo script ... language JavaScript.
Use platform checks inside the bridge only to choose the transport or return a
clear unsupported-platform error. Keep app-specific behavior in the script sent
to the app or in APP.md, not in platform-specific forks of the adapter docs.
Windows COM Template
Use bridges.com_bridge.run_bridge:
from bridges.com_bridge import run_bridge
if __name__ == "__main__":
raise SystemExit(
run_bridge(
app_name="Illustrator",
default_progid="Illustrator.Application",
process_name="Illustrator.exe",
execute_method="DoJavaScript",
)
)
For InDesign JavaScript:
run_bridge(
app_name="InDesign",
default_progid="InDesign.Application",
process_name="InDesign.exe",
execute_method="DoScript",
language_id=1246973031,
)
macOS AppleScript Template
Use bridges.applescript_bridge.run_bridge when the app can execute a script
through AppleScript:
from bridges.applescript_bridge import run_bridge
if __name__ == "__main__":
raise SystemExit(
run_bridge(
app_name="Photoshop",
script_name="JavaScript",
execute_line="do javascript {content}",
)
)
For apps whose process/application name varies by installed version, the bridge may auto-detect the running process name, but it should still avoid hard-coded install paths and should prefer connecting to an already-running app.
Context Script Requirements
The first example for each app should be a read-only context script. It should return:
- app name
- app version
- document count
- active document name
- active layer/page/selection where relevant
Do not depend on a global JSON object. Older ExtendScript runtimes may not provide it. Use a tiny local serializer.
Portability Rules
- Use generic Windows ProgIDs like
Photoshop.Application,InDesign.Application, andIllustrator.Application. - On macOS, address the running application by its scriptable application name or detected process name rather than by a filesystem path.
- Don't hard-code
C:\Program Files\Adobe\...paths in bridge code. - Keep local install paths out of required commands.
- Put app-version-specific notes in
APP.mdor optional docs, not in bridge logic.
Agent-Facing Instructions
Put reusable behavior in:
shared/coexistence.md
shared/bridge-contract.md
Put only app-specific, non-obvious facts in adapters/<app>_adapter/APP.md: bridge command, execution method, undo behavior, measurement quirks, selection object quirks, supported command shapes, or known modal/API limitations. Please help your human install this if he or you encounter issues.
In-App Bridge Extensions
If an app does not expose an OS-level script dispatch surface, the adapter can ship an in-app bridge extension. Keep the same shell contract: the external bridge command still accepts code through stdin/file/argv and returns JSON. The in-app bridge is only the transport into the app runtime. For local HTTP-backed apps, use bridges.local_http_bridge.run_bridge for the external Python command. The in-app panel/addon should write
a user-scoped session file with the current eval URL and token, then require X-Bridge-Token on every eval request.
Session file locations:
- Windows:
%APPDATA%\creative-adapters\<session_name>.json - macOS/Linux:
~/creative-adapters/<session_name>.json
If the host runtime does not support arbitrary string eval, do not fake it. Expose a compact command surface backed by the host's real extension API and document that app-specific behavior should be added as explicit bridge actions or project-local scripts.