<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[LordK]]></title><description><![CDATA[Hardcore software engineering and architecture. Deep dives into building high-performance CLI tools, AI agents, and industrial systems from scratch.]]></description><link>https://lordk.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6a2e71691f9deb6b8eb7a795/1995abf3-c60f-4545-b0a4-644f01ec7ed1.png</url><title>LordK</title><link>https://lordk.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Mon, 14 Sep 2026 17:30:50 GMT</lastBuildDate><atom:link href="https://lordk.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Building a Coding Agent from Zero: High-Performance CLI Base for Agent Development]]></title><description><![CDATA[Suppose we intend to develop a CLI coding agent similar to Claude Code, named piclaude.
When discussing building such a terminal agent, most people’s first instinct is to dive into research on LangCha]]></description><link>https://lordk.hashnode.dev/cli-agent-architecture</link><guid isPermaLink="true">https://lordk.hashnode.dev/cli-agent-architecture</guid><category><![CDATA[software architecture]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[cli]]></category><category><![CDATA[ai agents]]></category><dc:creator><![CDATA[LordK]]></dc:creator><pubDate>Mon, 22 Jun 2026 15:07:34 GMT</pubDate><content:encoded><![CDATA[<p>Suppose we intend to develop a CLI coding agent similar to Claude Code, named piclaude.</p>
<p>When discussing building such a terminal agent, most people’s first instinct is to dive into research on LangChain, prompt engineering, or RAG mechanisms. However, in real-world engineering deployment, what most often ruins the user experience is rarely an insufficiently capable large language model—instead, it is issues like the tool failing to install, refusing to run, or lagging unbearably slowly.</p>
<p>As a pure command-line (CLI) utility, it runs directly on the user’s physical machine. Users may be working in Windows PowerShell, on an M3-series Mac, or on an outdated Linux server lacking support for advanced instruction sets.</p>
<p>If you simply write a basic Node.js script and publish it to npm, users will encounter a litany of hurdles during installation and execution: incompatible Node versions, global dependency conflicts, and agonizingly slow cold starts.</p>
<p>To resolve these pain points, we need to build an industrial-grade, high-performance CLI foundation for our agent. Today, drawing on real code examples, we will break down the architecture and implementation of this foundation step by step, starting from the most fundamental entry-point design.</p>
<h2>Chapter 1: Architectural Tradeoffs — What Do We Distribute to End Users?</h2>
<p>Before writing any code, we must address a fundamental architectural question: <strong>What exactly are we delivering to users?</strong></p>
<p>The most straightforward approach is distributing source code written purely in TypeScript/JavaScript, letting users run <code>npm install -g</code> for installation and executing the code locally via the Node.js interpreter. Yet this imposes a severe cognitive burden: users are forced to set up a specific Node.js environment. Furthermore, Node.js incurs module resolution and JIT compilation overhead on every launch—a fatal cold-start delay for a CLI tool intended for frequent, instant invocation.</p>
<p>At the opposite extreme, we could rewrite the entire project in Go or Rust and compile it into standalone binaries. While this eliminates performance and dependency concerns, it abandons the vast, mature Node.js AI ecosystem (nearly all major SDKs offer their most fully featured JavaScript/TypeScript variants).</p>
<p><strong>This presents an unavoidable tradeoff.</strong></p>
<p>Thus, our final design decision is: <strong>Write core logic in TypeScript to leverage the ecosystem’s advantages, then cross-compile the code into self-contained binaries bundling a full runtime during the build phase using tools like Bun.</strong></p>
<p>This approach removes the Node.js installation requirement for end users while delivering near-native C++ launch speeds. It does, however, introduce a new challenge: binaries built for different operating systems and CPU architectures are entirely distinct. How do we serve users the correct build for their machine?</p>
<h2>Chapter 2: Uncovering the Entrypoint — What Happens When a User Runs the Command?</h2>
<p>Imagine a user runs <code>npm install -g piclaude</code> in their terminal, then types <code>piclaude</code> and presses Enter. How does the system determine which program to execute?</p>
<p>The answer lies within the <code>bin</code> field of the project’s <code>package.json</code>:</p>
<pre><code class="language-json">{
  "name": "piclaude",
  "version": "0.0.0",
  "bin": {
    "piclaude": "./bin/piclaude.cjs"
  }
}
</code></pre>
<p>Notice that we do not point the <code>bin</code> entry directly to our precompiled native binary, but instead to a wrapper script: <code>./bin/piclaude.cjs</code>.</p>
<p><strong>Why add this seemingly redundant layer?</strong></p>
<p>This design is a workaround for inherent limitations in npm’s distribution model. Unlike Homebrew, npm lacks native, intelligent logic to download only binaries compatible with the user’s operating system during installation. If we bundled binaries for every platform (Windows, macOS, Linux; x64, arm64) into a single npm package, the bundle size would balloon to hundreds of megabytes—an unworkable footprint.</p>
<p>For this reason, the industry standard best practice is the router/wrapper proxy pattern.</p>
<p><code>./bin/piclaude.cjs</code> is not the agent that performs core work; it is an extremely lightweight dispatcher. Its sole responsibility is to detect the user’s OS environment the moment they hit Enter, locate the matching pre-downloaded native binary within <code>node_modules</code>, and hand over process control to that executable.</p>
<h2>Chapter 3: Intelligent Distribution &amp; Runtime — Sophisticated Environment Detection Logic</h2>
<p>Since <code>piclaude.cjs</code> handles dispatching, let us examine its internal workflow. To visualize this routing pipeline clearly, refer to the high-level distribution and execution architecture diagram below:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a2e71691f9deb6b8eb7a795/5d50b905-1d40-430b-9abd-25010fe72bd9.png" alt="" style="display:block;margin:0 auto" />

<p>With this architectural mental model established, we analyze the implementation logic of <code>bin/piclaude.cjs</code>. If you inspect the source code of modern Node.js tooling—including esbuild, SWC, Prisma, and even Tauri—you will observe they all adopt this advanced architectural pattern: <strong>utilizing a Node.js wrapper script to distribute and execute cross-platform precompiled native binaries.</strong></p>
<p>This design represents a systematic compromise balancing installation success rates, maximum runtime performance, and engineering flexibility. Combined with our codebase, its core advantages fall into four key categories:</p>
<h3>1. Eliminate Local Compilation Failures (Higher Installation Success Rate)</h3>
<p>If a CLI’s core logic is built using high-performance tooling (in our case, Bun-compiled native binaries), traditional workflows rely on <code>node-gyp</code> for local compilation during <code>npm install</code>. This routinely triggers wall-of-text errors when users lack Python or C++ compilers on their machines. The wrapper pattern shifts all compilation to cloud CI/CD pipelines, where we prebuild executables for every mainstream platform. The <code>.cjs</code> wrapper only performs intelligent routing at runtime and launches the matching binary directly, entirely eliminating unpredictability introduced by local build toolchains.</p>
<h3>2. Developer-Friendly Escape Hatches &amp; Transparent Process I/O</h3>
<p>Before running complex environment detection logic, the script includes two developer-focused overrides at its very top: it reads the <code>PICLADE_BIN_PATH</code> environment variable and checks for a local <code>.piclaude</code> cache file in the same directory. These shortcuts streamline local development workflows: after compiling new binaries, developers can test builds instantly by setting the environment variable, without manually copying artifacts into <code>node_modules</code>.</p>
<p>Once the target binary is located, the wrapper spawns the executable via <code>child_process.spawnSync</code> with <code>stdio: "inherit"</code>. This configuration forwards all native process output, terminal color formatting, and interactive prompts seamlessly to the host terminal—delivering an experience indistinguishable from running a pure native CLI utility directly.</p>
<h3>3. Precision Environment Detection &amp; Robust Fault Tolerance</h3>
<p>The centerpiece of this design is granular hardware and OS probing logic with comprehensive error recovery.</p>
<p>The script first runs basic detection for operating system and CPU architecture:</p>
<pre><code class="language-typescript">const platformMap = { darwin: "darwin", linux: "linux", win32: "windows" }
const archMap = { x64: "x64", arm64: "arm64", arm: "arm" }

let platform = platformMap[os.platform()] || os.platform()
let arch = archMap[os.arch()] || os.arch()
const base = "piclaude-" + platform + "-" + arch
</code></pre>
<p>This snippet appears deceptively simple: <code>os.platform()</code> and <code>os.arch()</code> generate a base package name such as <code>piclaude-linux-x64</code>. Yet most production failures stem from overlooked edge cases, leading to the third critical design pillar:</p>
<h4>Extreme Performance Tuning (AVX2 Instruction Set Detection) &amp; Musl Libc Compatibility</h4>
<p>Take a Linux x64 environment as an example. We cannot safely launch the default precompiled binary unconditionally. Agents like piclaude perform heavy numerical computation and model inference under the hood, and AVX2 vector instructions deliver dramatic performance gains on supported CPUs. Running AVX2-optimized binaries on legacy servers lacking the instruction set immediately triggers low-level <code>Illegal instruction</code> crashes.</p>
<p>To avoid this fatal error, our CLI foundation implements extensive low-level hardware probing logic:</p>
<pre><code class="language-typescript">function supportsAvx2() {
  if (arch !== "x64") return false
  if (platform === "linux") {
    try {
      return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
    } catch {
      return false
    }
  }

  if (platform === "darwin") {
    try {
      const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], {
        encoding: "utf8",
        timeout: 1500,
      })
      if (result.status !== 0) return false
      return (result.stdout || "").trim() === "1"
    } catch {
      return false
    }
  }

  if (platform === "windows") {
    const cmd =
      '(Add-Type -MemberDefinition "[DllImport(\\"kernel32.dll\\")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
      
    for (const exe of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
      try {
        const result = childProcess.spawnSync(exe, ["-NoProfile", "-NonInteractive", "-Command", cmd], {
          encoding: "utf8",
          timeout: 3000,
          windowsHide: true,
        })
        if (result.status !== 0) continue
        const out = (result.stdout || "").trim().toLowerCase()
        if (out === "true" || out === "1") return true
        if (out === "false" || out === "0") return false
      } catch {
        continue
      }
    }
    return false
  }
  return false
}
</code></pre>
<p>The implementation reads <code>/proc/cpuinfo</code> on Linux, invokes <code>sysctl</code> on macOS, and even executes embedded C# via PowerShell to call Windows system APIs for AVX2 checks. Separate logic also detects musl libc for lightweight Alpine Docker containers.</p>
<h3>4. Flexible Binary Lookup with Prioritized Fallback Chain</h3>
<p>Drawing on all detection results above, the script constructs an ordered array <code>names</code> listing binary variants by priority (e.g., attempting the AVX2-optimized build first, falling back to the baseline compatibility build if unavailable). It then traverses parent directories upward to scan nested <code>node_modules</code> folders, terminating the search immediately once a matching executable is found. This layered fallback system drastically improves tool robustness, ensuring compatibility with aging hardware and specialized container environments.</p>
<p>On the build side (<code>script/build.ts</code>), we align compilation pipelines with this dispatching logic, leveraging Bun to run a build matrix covering 12 platform-architecture combinations:</p>
<pre><code class="language-typescript">const allTargets = [
  { os: "linux", arch: "arm64" },
  { os: "linux", arch: "x64" },
  { os: "linux", arch: "x64", avx2: false }, // Baseline compatibility build
  { os: "linux", arch: "x64", abi: "musl" }, // Alpine container-compatible build
  // ... remaining platform targets
];

for (const item of targets) {
  const name = /* Generate package name e.g. piclaude-linux-x64-baseline */;
  await Bun.build({
    target: name.replace(pkg.name, "bun"),
    outfile: `dist/\({name}/bin/\){binName}`,
    // ... additional build configuration
  });
}
</code></pre>
<p>The core strength of this architecture is clear: <strong>all cross-platform complexity and environment adaptation overhead is resolved at compile time, with comprehensive fallback logic built into the distribution layer—never forcing end users to resolve compatibility issues at runtime.</strong> For software to function reliably across heterogeneous environments, its underlying runtime must feature robust self-adaptation capabilities.</p>
<h2>Chapter 4: High-Performance CLI Design Principles — Latency &amp; Lazy Evaluation</h2>
<p>Once the dispatcher successfully spawns the matching native binary, execution enters the true runtime entrypoint at <code>src/index.ts</code>. This introduces another classic architectural split: <strong>physical separation between the lightweight bootstrap entrypoint and the full-featured CLI core module.</strong></p>
<p>As an agent utility, the complete CLI entrypoint <code>cli.ts</code> pulls in an extensive dependency tree: large model tokenizers, local filesystem watchers, complex AST parsers, and more. If we naively import all heavy modules statically at the top of the entry file (standard practice for regular frontend projects), severe performance degradation follows.</p>
<p>Even a trivial command such as <code>piclaude --version</code> to print the version number would force the V8 engine to parse and compile every heavy dependency. An operation that should complete in 5ms would stretch to 500ms or longer, ruining the snappy responsiveness critical to CLI tooling.</p>
<p>High-performance architecture demands obsessive optimization of startup latency. To maximize speed, we implement three extreme optimization strategies within the primary entrypoint <code>index.ts</code>:</p>
<pre><code class="language-typescript">async function main(): Promise&lt;void&gt; {
  const args = process.argv.slice(2);

  // Fast-path for --version/-v: loads zero external modules
  if (args.length === 1 &amp;&amp; (args[0] === '--version' || args[0] === '-v' || args[0] === '-V')) {
    console.log(`${MACRO.VERSION} (pi-claude)`);
    return;
  }
  
  // Initialize startup profiler for all other command paths
  const {
    profileCheckpoint
  } = await import('../utils/startupProfiler.js');
  profileCheckpoint('cli_entry');
  
  profileCheckpoint('cli_before_main_import');

  const {
    main: cliMain
  } = await import('../main.js');
  profileCheckpoint('cli_after_main_import');
  await cliMain();
  profileCheckpoint('cli_after_main_complete');
}

// Avoid top-level synchronous side effects
void main();
</code></pre>
<p>We break down the engineering rationale behind each performance optimization below:</p>
<h3>Strategy 1: Pre-Entry Flag Interception</h3>
<p><code>index.ts</code> acts as a frontline request interceptor. When a command is invoked, it first checks for lightweight special flags (help text, version queries, etc.) before passing control to the full <code>cli.ts</code> module. This mirrors a building’s security checkpoint: trivial requests are resolved at the entrance without routing traffic into core business logic.</p>
<h3>Strategy 2: Full Dynamic Lazy Imports</h3>
<p>If the interceptor determines the full agent workflow is required, core modules are loaded exclusively via <code>await import("./xxxx.js")</code>. This lazy evaluation pattern eliminates unnecessary initialization of massive dependency trees. In codebases with hundreds of transitive dependencies, deferring imports can cut module evaluation overhead by hundreds of milliseconds. The program should never execute work irrelevant to the current execution context.</p>
<h3>Strategy 3: Zero-Dependency Fast Paths</h3>
<p>Notice the complete absence of top-level <code>import</code> statements at the start of the file. This guarantees instant execution for lightweight fast paths like <code>--version</code>.</p>
<p>You may question how the script retrieves its version number without importing <code>package.json</code>—reading the JSON file via <code>fs.readFile</code> would introduce unnecessary I/O latency. We eliminate this overhead through <strong>compile-time macro injection</strong>. During the earlier <code>build.ts</code> pipeline, Bun’s define configuration hardcodes the version literal directly into source code:</p>
<pre><code class="language-typescript">await Bun.build({
  entrypoints: ["./src/index.ts"],
  define: {
    'MACRO.VERSION': `'${pkg.version}'`
  },
})
</code></pre>
<p>Experienced developers will immediately spot a potential flaw: during local development workflows (e.g., running <code>bun run src/index.ts</code>), the build script never executes, leaving <code>MACRO.VERSION</code> undefined and throwing a <code>ReferenceError: MACRO is not defined</code>.</p>
<p>This explains the seemingly redundant global initialization logic at the absolute top of <code>src/index.ts</code>:</p>
<pre><code class="language-typescript">if (typeof MACRO === 'undefined') {
  (globalThis as any).MACRO = {
    VERSION: '0.0.0-dev',
  };
}
</code></pre>
<p>This elegant environment decoupling mechanism resolves the conflict seamlessly. In local development environments, the script detects the missing <code>MACRO</code> global and attaches a fallback dev version string to <code>globalThis</code>, enabling safe access to <code>MACRO.VERSION</code> throughout the codebase. During official production builds, Bun’s define system statically replaces every <code>MACRO.VERSION</code> reference with the real semantic version string as a raw literal.</p>
<p>The full execution flow when a user runs <code>piclaude -v</code> is therefore: the underlying dispatcher instantly boots the pure native binary → enters the zero-dependency bootstrap layer <code>index.ts</code> → performs an in-memory string comparison → prints the compile-time injected version literal → exits immediately. This extreme minimization of top-level synchronous side effects delivers industry-leading startup performance.</p>
<h2>Chapter 5: Full End-to-End Overview — Complete CLI Dispatcher Execution Flowchart</h2>
<p>To conclude this technical deep dive, we revisit the full lifecycle of the <code>bin/piclaude.cjs</code> proxy wrapper, integrating all previously covered logic: environment variable overrides, local cache file checks, low-level hardware probing, prioritized fallback binary lists, and recursive upward <code>node_modules</code> directory scanning. Together, these components form the complete execution pipeline of the frontend dispatcher script.</p>
<p>The end-to-end flowchart below visualizes this logic, serving both as a concrete code reference and a blueprint for industrial-grade CLI tooling handling every conceivable edge case across heterogeneous physical environments:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a2e71691f9deb6b8eb7a795/1b72b963-4de2-48cd-89ee-364192c52ebd.png" alt="" style="display:block;margin:0 auto" />

<h3>Closing Summary</h3>
<p>From a purely engineering-focused perspective, we have deconstructed the underlying infrastructure required to build a production-ready agent CLI:</p>
<ol>
<li><p>Balancing ecosystem compatibility and raw performance by writing core logic in TypeScript and compiling self-contained binaries via Bun;</p>
</li>
<li><p>Designing the <code>.cjs</code> router proxy layer to comply with npm’s native distribution limitations;</p>
</li>
<li><p>Implementing granular low-level hardware probing—AVX2 detection to avoid illegal instruction crashes on legacy servers, and musl libc detection for lightweight Alpine containers;</p>
</li>
<li><p>Splitting bootstrap and runtime entrypoints, paired with rigorous lazy-loading optimizations to minimize startup latency.</p>
</li>
</ol>
<p>This breakdown illustrates a core truth of software engineering: behind a single simple terminal command lies countless hours of engineering work addressing edge cases and refining user experience. That is the beauty of thoughtful architectural design.</p>
<h3>Translation Notes</h3>
<ol>
<li><p>Technical terminology follows universal industry standards for Node.js / Bun / CLI development (cold start, lazy evaluation, cross-compile, binary wrapper, AVX2 instruction set, musl libc, CI/CD, JIT compilation, AST, fallback chain, macro injection).</p>
</li>
<li><p>Mermaid flowchart syntax retained unmodified for direct code reuse.</p>
</li>
<li><p>Code snippets kept original with minor escaping fixes for English formatting consistency.</p>
</li>
<li><p>Compound Chinese technical phrasing split into natural, concise English technical sentences without losing precision.</p>
</li>
<li><p>Domain-specific metaphors (security checkpoint, dispatcher, battle map) translated with matching English technical analogies to preserve rhetorical tone.</p>
</li>
</ol>
]]></content:encoded></item></channel></rss>