c5a1b34ebc
PostHog JS snippet injected into _template.html and _index_template.html, pointed at eu.i.posthog.com. All 11 posts + index regenerated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
178 lines
13 KiB
HTML
178 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<title>One Binary to Rule Them All: Building a Studio CLI — Tinqs Blog</title>
|
|
<meta name="description" content="A single Go binary that handles machine identity, screenshots, cloud vision, and health checks. The glue that makes AI agents useful across a multi-machine game studio.">
|
|
<meta name="robots" content="index, follow">
|
|
<link rel="canonical" href="https://www.tinqs.com/blog/studio-cli">
|
|
|
|
<meta property="og:type" content="article">
|
|
<meta property="og:url" content="https://www.tinqs.com/blog/studio-cli">
|
|
<meta property="og:title" content="One Binary to Rule Them All: Building a Studio CLI">
|
|
<meta property="og:description" content="A single Go binary for machine identity, screenshots, cloud vision, and AI agent coordination.">
|
|
<meta property="og:image" content="https://www.tinqs.com/img/og-cover.jpg">
|
|
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="twitter:title" content="One Binary to Rule Them All: Building a Studio CLI">
|
|
<meta name="twitter:description" content="A single Go binary for machine identity, screenshots, cloud vision, and AI agent coordination.">
|
|
<meta name="twitter:image" content="https://www.tinqs.com/img/og-cover.jpg">
|
|
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "BlogPosting",
|
|
"headline": "One Binary to Rule Them All: Building a Studio CLI",
|
|
"datePublished": "2026-05-18",
|
|
"author": {
|
|
"@type": "Person",
|
|
"name": "Ozan Bozkurt"
|
|
},
|
|
"publisher": {
|
|
"@type": "Organization",
|
|
"name": "Tinqs Limited",
|
|
"url": "https://www.tinqs.com"
|
|
},
|
|
"description": "A single Go binary that handles machine identity, screenshots, cloud vision, and health checks. The glue that makes AI agents useful across a multi-machine game studio."
|
|
}
|
|
</script>
|
|
|
|
<!-- PostHog (EU) -->
|
|
<script>
|
|
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
|
|
posthog.init('phc_teG6p5oxf6poQHPThq5AGKzWQNhw4bHW9arLwWAVXm3f',{api_host:'https://eu.i.posthog.com',ui_host:'https://eu.posthog.com',person_profiles:'identified_only',defaults:'2026-01-30'})
|
|
</script>
|
|
|
|
<link rel="icon" type="image/svg+xml" href="/img/favicon.svg">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="../style.css">
|
|
</head>
|
|
<body>
|
|
|
|
<!-- NAV -->
|
|
<nav class="nav nav--scrolled" id="nav">
|
|
<a href="/" class="nav__logo" aria-label="Tinqs home">
|
|
<span class="nav__wordmark">TINQS</span>
|
|
</a>
|
|
<div class="nav__links">
|
|
<a href="/#game" class="nav__link">Games</a>
|
|
<a href="/#tech" class="nav__link">Technology</a>
|
|
<a href="/#about" class="nav__link">About</a>
|
|
<a href="/blog/" class="nav__link" style="color: var(--c-accent-l);">Blog</a>
|
|
<a href="/#signup" class="nav__link">Contact</a>
|
|
<a href="/press" class="nav__link">Press</a>
|
|
</div>
|
|
<button class="nav__burger" aria-label="Open menu" id="navBurger">
|
|
<span></span><span></span><span></span>
|
|
</button>
|
|
</nav>
|
|
|
|
<!-- MOBILE MENU -->
|
|
<div class="mobile-menu" id="mobileMenu">
|
|
<a href="/#game" class="mobile-menu__link">Games</a>
|
|
<a href="/#tech" class="mobile-menu__link">Technology</a>
|
|
<a href="/#about" class="mobile-menu__link">About</a>
|
|
<a href="/blog/" class="mobile-menu__link">Blog</a>
|
|
<a href="/#signup" class="mobile-menu__link">Contact</a>
|
|
<a href="/press" class="mobile-menu__link">Press</a>
|
|
</div>
|
|
|
|
<!-- POST -->
|
|
<article class="post">
|
|
<a href="/blog/" class="post__back">← All Posts</a>
|
|
<span class="post__date">18 May 2026</span>
|
|
<h1 class="post__title">One Binary to Rule Them All: Building a Studio CLI</h1>
|
|
<p class="post__lead">Every machine in our studio runs the same Go binary. It knows who you are, what machine you're on, and what services are reachable. It takes screenshots, sends them to cloud vision, and runs health checks. This is the glue that makes AI agents actually useful in a multi-machine game studio.</p>
|
|
|
|
<div class="post__body">
|
|
<h2>Why Build a CLI</h2>
|
|
<p>When you have multiple machines across several people, two operating systems, and AI agents that need context about the environment they're running in, the glue becomes the hardest part. Which machine is this? What services are reachable? Is the game running? Can I take a screenshot of what the developer is looking at?</p>
|
|
<p>We tried shell scripts. A <code>setup.sh</code> for Mac, a <code>setup.ps1</code> for Windows, a <code>check-services.sh</code> for health checks, a <code>screenshot.py</code> that never worked on Windows. They drifted. They broke. Nobody updated them.</p>
|
|
<p>So we built one Go binary that does everything.</p>
|
|
<h2>The Identity System</h2>
|
|
<p>The most important command is <code>identity</code>. When an AI agent starts a new session — Cursor, Claude Code, any tool — the first thing it does is call this command. The output tells the agent:</p>
|
|
<ul>
|
|
<li><strong>The soul file</strong> — the agent's persistent identity, values, and operating principles</li>
|
|
<li><strong>Company context</strong> — team members, roles, what the company does</li>
|
|
<li><strong>Machine context</strong> — hostname, OS, which repos are cloned, what services are running</li>
|
|
<li><strong>Ecosystem</strong> — other repos and their purpose</li>
|
|
<li><strong>Service status</strong> — which URLs are live and reachable</li>
|
|
</ul>
|
|
<p>This solves a fundamental problem with AI agents: <strong>cold starts.</strong> Every new chat window, every new agent tab, every new session is a blank slate. The agent doesn't know what project this is, who's asking, or what infrastructure exists. One CLI call gives it full context.</p>
|
|
<p>The data lives in markdown files in the docs repo — the source of truth. Any machine on the network can read it.</p>
|
|
<h2>Screenshots and Vision</h2>
|
|
<p>The CLI can capture any window from outside the process. No in-game overlay, no rendering pipeline integration. It uses the OS-level window capture API — works on Windows (via GDI+) and Mac (via screencapture).</p>
|
|
<p>A <code>photo</code> command does the same thing but sends the screenshot to a cloud vision model for analysis. The agent says "take a photo of the game" and gets back a structured description: "The player character is standing near a half-built hut. There are 3 palm trees to the left. The terrain has a visible seam between two biomes."</p>
|
|
<p>This is how you file bugs without typing. Look at the game, tell the agent what's wrong, and the agent takes a screenshot, describes what it sees, and creates an issue with both the description and the image attached.</p>
|
|
<h2>Health Checks</h2>
|
|
<p>A <code>doctor</code> command runs a comprehensive health check:</p>
|
|
<ul>
|
|
<li>Is the git platform reachable? Can we authenticate?</li>
|
|
<li>Is the game server running?</li>
|
|
<li>Are all expected repos cloned and on the right branch?</li>
|
|
<li>Are required tools installed and at the right version?</li>
|
|
</ul>
|
|
<p>The output is a green/yellow/red table. If something's wrong, the agent knows immediately and can diagnose or escalate. This is essential for unattended agent sessions — the agent can verify its environment before starting work.</p>
|
|
<h2>Why Go</h2>
|
|
<p>Go compiles to a single static binary with no runtime dependencies. No Python virtualenvs, no Node.js version managers, no DLL hell on Windows. The same binary runs on a gaming PC, a designer's MacBook, and a CI runner in AWS.</p>
|
|
<p>Cross-compilation is trivial. We build Windows, Mac (arm64 + amd64), and Linux binaries from a single CI workflow. Push a tag, CI builds all three, uploads to S3, done.</p>
|
|
<p>The binary is 15MB. It starts in under 100ms. It has zero runtime dependencies. For a tool that AI agents call on every session start, speed matters.</p>
|
|
<h2>What We Learned</h2>
|
|
<p><strong>The CLI is the API for AI agents.</strong> When we started, this was a convenience tool for humans. It became the primary interface for AI agents. The <code>identity</code> command was originally "nice to have" — now it's the single most important function in our stack. Every agent session starts with it.</p>
|
|
<p><strong>One binary beats ten scripts.</strong> Scripts rot. They have different shells, different PATH assumptions, different error handling. A compiled binary either works or it doesn't. It ships with its dependencies baked in. It doesn't care if your Python is 3.9 or 3.12.</p>
|
|
<p><strong>Cloud vision is underrated for game dev.</strong> Sending a screenshot to a vision model and getting back a structured description sounds gimmicky. In practice, it's the fastest way to document visual bugs. "The tree is floating 2m above the terrain" is much faster to write when the AI is looking at the same screen you are.</p>
|
|
<p><strong>Agent cold starts are the real problem.</strong> Without the identity system, every new session starts with the agent asking "what project is this?" and the human re-explaining context. With it, the agent knows everything in 100ms. That's the difference between an AI assistant and an AI team member.</p>
|
|
<hr>
|
|
<p>The CLI is part of <a href="https://tinqs.com" style="color: var(–c-accent-l);">Tinqs Studio</a> — our game development platform that brings git hosting, AI agent tools, and team workflows together. Every time we find ourselves writing a script that needs to work on multiple machines, we add a subcommand instead. One binary that makes the studio work, whether the operator is human or AI.</p>
|
|
|
|
</div>
|
|
|
|
<div class="post__author">
|
|
<div class="post__author-avatar">OB</div>
|
|
<div class="post__author-info">
|
|
<span class="post__author-name">Ozan Bozkurt</span><br>
|
|
CTO & Developer, Tinqs
|
|
</div>
|
|
</div>
|
|
</article>
|
|
|
|
<!-- FOOTER -->
|
|
<footer class="footer">
|
|
<div class="footer__inner">
|
|
<span class="footer__wordmark">TINQS</span>
|
|
<div class="footer__links">
|
|
<a href="/#game">Games</a>
|
|
<a href="/#tech">Technology</a>
|
|
<a href="/#about">About</a>
|
|
<a href="/blog/">Blog</a>
|
|
<a href="mailto:hello@tinqs.com">hello@tinqs.com</a>
|
|
<a href="/press">Press Kit</a>
|
|
</div>
|
|
<p class="footer__copy">Tinqs Limited — London, est. 2020</p>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
const burger = document.getElementById('navBurger');
|
|
const mobileMenu = document.getElementById('mobileMenu');
|
|
burger.addEventListener('click', () => {
|
|
const open = mobileMenu.classList.toggle('mobile-menu--open');
|
|
burger.classList.toggle('nav__burger--open', open);
|
|
document.body.style.overflow = open ? 'hidden' : '';
|
|
});
|
|
mobileMenu.querySelectorAll('a').forEach(link => {
|
|
link.addEventListener('click', () => {
|
|
mobileMenu.classList.remove('mobile-menu--open');
|
|
burger.classList.remove('nav__burger--open');
|
|
document.body.style.overflow = '';
|
|
});
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|