anthropic-screws-opencode.ts

4 min read Original article ↗
#!/usr/bin/env bun /** * ============================================================ * PROOF: Anthropic is specifically blocking "OpenCode" * in Claude Code OAuth system prompts * ============================================================ * * Video covering this script here: https://www.youtube.com/watch?v=G9YX6StP2-M * * This script demonstrates that Anthropic has specifically blocked * the phrase "You are OpenCode" in system prompts when using Claude's * OAuth tokens (Pro/Max subscription), while allowing other identity * statements like "You are Cursor", "You are Pi", etc. * * OpenCode (https://opencode.ai) is an open-source AI coding assistant. * * SETUP: * mkdir test-anthropic && cd test-anthropic * bun init -y * bun add @openauthjs/openauth * # Copy this file as opencode.ts * bun opencode.ts * * REQUIREMENTS: * - Bun runtime (https://bun.sh) * - Claude Pro or Max subscription * * HOW IT WORKS: * 1. Authenticates via OAuth using your Claude Pro/Max account * 2. Tests various system prompts against the Claude API * 3. Shows which prompts are blocked vs allowed * * FINDINGS: * - First system block MUST be exactly: * "You are Claude Code, Anthropic's official CLI for Claude." * - Second block can contain almost anything EXCEPT: * "You are OpenCode" or "You are opencode" * - Other identity statements work fine (Cursor, Pi, Droid, etc.) * * This appears to be targeted blocking of a specific competitor. */ import * as readline from "node:readline"; import { generatePKCE } from "@openauthjs/openauth/pkce"; const CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e"; const CC_HEADER = "You are Claude Code, Anthropic's official CLI for Claude."; const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); function prompt(question: string): Promise<string> { return new Promise((resolve) => { rl.question(question, (answer) => { resolve(answer); }); }); } // ============ OAuth Functions ============ async function authorize() { const pkce = await generatePKCE(); const url = new URL("https://claude.ai/oauth/authorize"); url.searchParams.set("code", "true"); url.searchParams.set("client_id", CLIENT_ID); url.searchParams.set("response_type", "code"); url.searchParams.set("redirect_uri", "https://console.anthropic.com/oauth/code/callback"); url.searchParams.set("scope", "org:create_api_key user:profile user:inference"); url.searchParams.set("code_challenge", pkce.challenge); url.searchParams.set("code_challenge_method", "S256"); url.searchParams.set("state", pkce.verifier); return { url: url.toString(), verifier: pkce.verifier }; } async function exchange(code: string, verifier: string) { const splits = code.split("#"); const result = await fetch("https://console.anthropic.com/v1/oauth/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ code: splits[0], state: splits[1], grant_type: "authorization_code", client_id: CLIENT_ID, redirect_uri: "https://console.anthropic.com/oauth/code/callback", code_verifier: verifier, }), }); if (!result.ok) return null; const json = await result.json(); return json.access_token; } // ============ Test Functions ============ interface TestResult { name: string; secondBlock: string; success: boolean; } async function runTest(accessToken: string, name: string, secondBlock: string): Promise<TestResult> { process.stdout.write(` ${name}... `); const response = await fetch("https://api.anthropic.com/v1/messages?beta=true", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${accessToken}`, "anthropic-version": "2023-06-01", "anthropic-beta": "oauth-2025-04-20,interleaved-thinking-2025-05-14,claude-code-20250219", "user-agent": "claude-cli/2.1.2 (external, cli)", }, body: JSON.stringify({ model: "claude-sonnet-4-20250514", max_tokens: 1024, system: [ { type: "text", text: CC_HEADER }, { type: "text", text: secondBlock }, ], tools: [{ name: "mcp_test", description: "Test tool", input_schema: { type: "object", properties: {} }, }], messages: [{ role: "user", content: "Say hello in 5 words." }], }), }); if (!response.ok) { console.log("FAIL"); return { name, secondBlock, success: false }; } await response.json(); console.log("PASS"); return { name, secondBlock, success: true }; } async function runTests(accessToken: string) { console.log("\n" + "=".repeat(50)); console.log("SYSTEM PROMPT VALIDATION TESTS"); console.log("=".repeat(50)); console.log(`\nFirst block (required): "${CC_HEADER}"`); console.log("\nTesting second block content:\n"); const tests = [ // These FAIL - "You are OpenCode" pattern is blocked { name: "You are OpenCode.", block: "You are OpenCode." }, { name: "You are opencode.", block: "You are opencode." }, // These PASS - other identity statements work fine { name: "You are Cursor.", block: "You are Cursor." }, { name: "You are Pi.", block: "You are Pi." }, { name: "You are Droid.", block: "You are Droid." }, ]; const results: TestResult[] = []; for (const test of tests) { const result = await runTest(accessToken, test.name, test.block); results.push(result); await new Promise((r) => setTimeout(r, 300)); } // Summary console.log("\n" + "=".repeat(50)); console.log("RESULTS"); console.log("=".repeat(50)); const passed = results.filter((r) => r.success); const failed = results.filter((r) => !r.success); console.log(`\nPassed: ${passed.length} | Failed: ${failed.length}`); if (failed.length > 0) { console.log("\nBLOCKED (cannot use in 2nd system block):"); failed.forEach((r) => console.log(` - "${r.secondBlock}"`)); } if (passed.length > 0) { console.log("\nALLOWED:"); passed.forEach((r) => console.log(` - "${r.secondBlock}"`)); } console.log("\n" + "=".repeat(50)); console.log("CONCLUSION: Anthropic specifically blocks 'You are OpenCode'"); console.log(" but allows other identity statements."); console.log("=".repeat(50) + "\n"); } // ============ Main ============ async function main() { const args = process.argv.slice(2); console.log("\n" + "=".repeat(50)); console.log(" ANTHROPIC OAUTH SYSTEM PROMPT TESTER"); console.log("=".repeat(50)); let accessToken = args[0]; if (!accessToken) { console.log("\nNo token provided. Starting OAuth flow...\n"); const { url, verifier } = await authorize(); console.log("1. Open this URL in your browser:\n"); console.log(url); console.log("\n2. Authorize and copy the code shown.\n"); const code = await prompt("3. Paste the code here: "); console.log("\nExchanging code for token..."); accessToken = await exchange(code.trim(), verifier); if (!accessToken) { console.error("\nFailed to get access token!"); process.exit(1); } console.log("Got access token!\n"); } await runTests(accessToken); rl.close(); } main().catch(console.error).finally(() => rl.close());