Generic multi-AI orchestration platform: Playwright engine, 7 AI platforms, rate limiter, agent fallback (browser-use), comparator, solution-researcher, landscape-researcher skills
SaferSkills independently audited multai (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This skill is the entry point for all research and multi-AI workflows. It routes to the right specialist skill or, for arbitrary tasks, runs the engine directly and consolidates generically. Follow the phases below.
RUNTIME RULES — READ BEFORE ACTING
>
This skill operates in two modes depending on where it runs:
>
Code tab (Mac — Darwin): Use ONLY the Playwright engine (Phase 2). NEVER use Claude-in-Chrome MCP tools, computer-use tools, or any manual browser tools. They conflict with the engine and defeat parallel execution.
>
Cowork tab (Linux sandbox): The Playwright engine cannot run here. Use ONLY the Claude-in-Chrome MCP path (Phase 2-Cowork). The engine Python script will fail or produce empty sessions in this environment.
>
Phase 0a below detects the runtime. Follow its output to determine which path to take.
Run this before anything else to determine which execution path to use:
python3 - <<'EOF'
import sys, shutil, socket
runtime = "cowork"
if sys.platform != "linux":
runtime = "code-tab"
elif shutil.which("google-chrome") or shutil.which("chromium"):
runtime = "code-tab"
else:
try:
s = socket.create_connection(("localhost", 9222), timeout=1)
s.close()
runtime = "code-tab"
except OSError:
pass
print(f"RUNTIME: {runtime}")
EOFTell the user which runtime was detected before proceeding.
Identify the user's intent and announce your routing decision before acting.
Routing decision tree:
"landscape" / "market map" / "ecosystem" / "vendor landscape" / "market overview"
"competitive landscape" / "category survey" / "industry landscape" / "Gartner-style"
→ ROUTE: landscape-researcher skill (invoke its Phase 0 onward)
Product URL + research intent
OR specific product name + evaluate / benchmark / research / analyze
OR "competitive intelligence" / "capabilities report"
→ ROUTE: solution-researcher skill (invoke its Phase 0 onward)
"comparison matrix" / "add platform" / "update matrix" / "combo column"
"verify ticks" / "reorder matrix"
→ ROUTE: comparator skill (invoke its Phase 0 onward)
Everything else (arbitrary prompt, general question, multi-source analysis
without a specific product or landscape intent)
→ Direct multi-AI (continue to Phase 1 below)Follow-up detection:
"additionally ask them", "what did they say about X", "ask the same AIs"): → add --followup to the Phase 2 command (reuses open conversations)
→ omit --followup (engine will start new conversations in the same tabs)
Tell the user which route you've selected: "Routing to [skill name] — [brief reason]." Accept a user override: if they say "no, do X instead", re-route accordingly.
Before proceeding to Phase 1 / the specialist skill, display this data-sharing notice and immediately continue — do NOT wait for a response:
ℹ️ MultAI — sending to Claude.ai, ChatGPT, Microsoft Copilot, Perplexity, Grok, DeepSeek, and Google Gemini. Each service may retain your prompt per its own data policy. Type cancel at any time to abort.Invoking /multai is the user's consent. Proceed to Phase 1 immediately after displaying the notice. Only stop if the user explicitly says "cancel".
Skip this phase if you've routed to a specialist skill, or if runtime is Cowork (go to Phase 2-Cowork).
DEEP or REGULAR (default: REGULAR). **If the prompt text beginswith DEEP or REGULAR as its very first word/line (MultAI mode prefix), extract that word as the mode and strip it from the prompt text before writing to the temp file.** The cleaned prompt (without the prefix) is what gets sent to platforms.
If the prompt is inline text, extract any leading mode prefix, then write the cleaned prompt to a temp file:
cat > /tmp/orchestrator-prompt.md << 'PROMPT_EOF'
[PROMPT TEXT HERE — without the leading DEEP/REGULAR prefix word]
PROMPT_EOFCheck that the engine's virtual environment exists and is bootstrapped:
ls skills/orchestrator/engine/.venv/bin/python 2>/dev/null && echo "venv OK" || echo "venv MISSING"If the output is `venv MISSING`, stop and tell the user:
"The MultAI engine environment is not set up yet. Please run the one-time bootstrap from the repo root:
>
``bash bash setup.sh ``>
This installs Playwright, Chromium, and openpyxl into an isolated virtual environment. Run bash setup.sh --with-fallback to also install the browser-use agent. Once complete, re-invoke this skill."Do not proceed to Phase 2 until the venv exists.
Optional — enable browser-use fallback:
If ANTHROPIC_API_KEY (or GOOGLE_API_KEY) is set in the environment or .env file, two additional safety nets activate automatically:
Neither fallback is required — the engine runs without an API key.
cd <workspace-root>
python3 skills/orchestrator/engine/orchestrator.py \
--prompt-file <PROMPT_FILE_PATH> \
--mode [DEEP|REGULAR] \
--task-name "<Short Task Name>"Output goes to reports/<Short Task Name>/. The engine auto-collates all responses into reports/<task-name>/<task-name> - Raw AI Responses.md.
CLI options:
| Flag | Required | Description |
|---|---|---|
--prompt-file | Yes* | Path to prompt file |
--prompt | Yes* | Literal prompt text (mutually exclusive with --prompt-file) |
--task-name | Recommended | Short run label — output saved to reports/{task-name}/ |
--condensed-prompt | No | Condensed prompt text for constrained platforms |
--condensed-prompt-file | No | Path to condensed prompt file (alternative to --condensed-prompt) |
--mode | No | DEEP or REGULAR (default: REGULAR) |
--output-dir | No | Override output directory (ignored if --task-name is set) |
--platforms | No | Comma-separated platform names, or all (default: all) |
--chrome-profile | No | Chrome profile name (default: Default) |
--headless | No | Run headlessly (not recommended) |
--fresh | No | Force launch new Chrome instance |
--tier | No | Subscription tier: free or paid (default: free) |
--skip-rate-check | No | Bypass rate limit pre-flight checks |
--budget | No | Show rate limit budget summary and exit |
--stagger-delay | No | Seconds between platform launches (default: 5) |
--followup | No | Reuse existing conversations (same topic follow-up). Omit for new topics. |
Timeouts:
The engine tracks usage per platform and enforces conservative rate limits.
rate_limited status~/.chrome-playwright/rate-limit-state.jsonCheck budget before running:
python3 skills/orchestrator/engine/orchestrator.py --prompt "test" --budget --tier freeOnly follow this phase if Phase 0a detected `RUNTIME: cowork`. Skip if Code tab.
The Playwright engine cannot run in the Cowork sandbox. Use the Claude-in-Chrome MCP path instead: Claude controls the user's real Mac Chrome directly, where sessions are already authenticated.
mcp__Claude_in_Chrome__tabs_context_mcp(createIfEmpty=False)If the result contains "not connected" or similar failure:
MultAI requires Claude-in-Chrome to run in Cowork.
>
To continue: - Option A (Cowork): Open Chrome → ensure the Claude-in-Chrome extension is installed and signed in → retry this skill - Option B (Code tab): Switch to the Code tab where MultAI runs natively with full parallel execution across all 7 platforms simultaneously
>
The Code tab is recommended for best performance.
Stop here. Do not attempt the Playwright engine.
If connected: proceed to Step 2 and inform the user:
Running in Cowork mode — using Claude-in-Chrome path. Platforms will be queried sequentially (one at a time) rather than in parallel. Make sure you are signed into the AI platforms in Chrome.
Query each platform in order: Gemini → Claude.ai → ChatGPT → Copilot → Perplexity → Grok → DeepSeek
For each platform, use this pattern (adapt selectors from the table below):
a. Open or reuse a tab:
mcp__Claude_in_Chrome__tabs_create_mcp() # get tabId
mcp__Claude_in_Chrome__navigate(url=PLATFORM_URL, tabId=tabId)Wait ~3s for page load.
b. Check if signed in:
mcp__Claude_in_Chrome__get_page_text(tabId=tabId)If the page text contains a login signal (see table below), skip this platform and note needs_login.
c. Inject the prompt:
For contenteditable inputs:
mcp__Claude_in_Chrome__javascript_tool(action="javascript_exec", tabId=tabId, text="""
const el = document.querySelector('INPUT_SEL');
el.focus();
document.execCommand('selectAll');
document.execCommand('insertText', false, PROMPT_TEXT);
el.dispatchEvent(new Event('input', {bubbles: true}));
""")For textarea inputs:
mcp__Claude_in_Chrome__javascript_tool(action="javascript_exec", tabId=tabId, text="""
const el = document.querySelector('INPUT_SEL');
const nativeInput = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
nativeInput.call(el, PROMPT_TEXT);
el.dispatchEvent(new Event('input', {bubbles: true}));
""")d. Submit:
mcp__Claude_in_Chrome__javascript_tool(action="javascript_exec", tabId=tabId, text="""
document.querySelector('SUBMIT_SEL').click();
""")Or use mcp__Claude_in_Chrome__find + mcp__Claude_in_Chrome__form_input as fallbacks.
e. Wait and extract: Poll every 15s up to 10 minutes (REGULAR) / 20 minutes (DEEP):
mcp__Claude_in_Chrome__get_page_text(tabId=tabId)Declare complete when the page text has not grown for 2 consecutive polls.
| Platform | URL | Input selector | Input type | Submit selector | Login signals |
|---|---|---|---|---|---|
| Google Gemini | https://gemini.google.com | rich-textarea .ql-editor, div[contenteditable='true'][role='textbox'] | contenteditable | button[aria-label='Send message'] | "Sign in", "Log in" |
| Claude.ai | https://claude.ai/new | div[contenteditable='true'] | contenteditable | button[aria-label*='Send'] | "Sign in", "Continue with Google" |
| ChatGPT | https://chat.openai.com | #prompt-textarea | contenteditable | button[data-testid='send-button'] | "Log in", "Sign up" |
| Copilot | https://copilot.microsoft.com | textarea[placeholder*='Message'] | textarea | button[aria-label*='Submit'] | "Sign in", "Microsoft account" |
| Perplexity | https://www.perplexity.ai | textarea[placeholder*='Ask'], div[contenteditable='true'] | contenteditable | button[type='submit'] | "Log in", "Sign up" |
| Grok | https://grok.com | div[contenteditable='true'].ProseMirror, div[contenteditable='true'] | contenteditable | button[aria-label*='Send'] | "Log in", "Sign in" |
| DeepSeek | https://chat.deepseek.com | textarea#chat-input, textarea | textarea | button[aria-label*='Send'] | "Log in", "Sign in" |
Canonical selectors are also maintained in skills/orchestrator/engine/platforms/chrome_selectors.py.
After all platforms complete (or are skipped), write each response to: reports/<task-name>/<Platform>-raw-response.md
Then run collation manually:
python3 skills/orchestrator/engine/collate_responses.py reports/<task-name>/ "<Task Name>"Proceed to Phase 5 (Invoke Consolidator) using the collated archive.
After the engine completes, outputs are in reports/<task-name>/:
| File | Description |
|---|---|
status.json | Per-platform terminal status and metadata |
{Platform}-raw-response.md | Individual raw response per platform |
{task-name} - Raw AI Responses.md | Auto-generated archive — all responses collated |
Platform statuses: complete / partial / failed / timeout / rate_limited / needs_login
Login-needed platforms (`needs_login`): the engine automatically waits 90 seconds after the parallel run completes, printing a sign-in prompt for each affected platform. After the countdown, those platforms are retried. Inform the user of this if any platform shows needs_login in the output — they should watch the terminal and sign in during the countdown.
Genuinely failed platforms (`failed`): if ANTHROPIC_API_KEY is set, the engine will attempt a full browser-use agent run for each failed platform after the main run.
The engine automatically runs collate_responses.py at end of each run. To re-run collation manually on an existing output dir:
python3 skills/orchestrator/engine/collate_responses.py reports/<task-name>/ "<Task Name>"Skip this phase if you've routed to a specialist skill — they invoke the consolidator themselves.
Invoke the consolidator skill with:
reports/{task-name}/{task-name} - Raw AI Responses.mdThe consolidator will produce a generic synthesis covering: summary, consensus areas, disagreements, unique insights, gaps, and source reliability.
After each successful direct multi-AI run, append a run log entry and note any observations about the engine, rate limiting, or routing logic.
Scope boundary: Only update files inside skills/orchestrator/. Never modify other skills' files or the domain files.
<!-- Append new entries at the top of this section after each run -->
platforms/chatgpt.py, platforms/claude_ai.py, platforms/copilot.py, platforms/grok.py, platforms/gemini.py, platforms/perplexity.py, platforms/deepseek.py, platforms/base.py (8 files)< 1000, masked by UI chrome); DeepSeek marker scan echo-guard added (replaced blind rfind with full-scan + is_prompt_echo)check_rate_limit false-positive patterns tightened ("too many"→"too many requests", "try again"→"try again later"); _inject_exec_command deprecation-proofed (return-value check + _inject_clipboard_paste auto-fallback); ChatGPT DR coordinate extraction rewritten with proportional iframe offsets + text-selector verification for Copy menumake check 93/93 PASS after all rounds~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.