Engineering

How We Built Our Custom Knot CLI

We built an internal CLI that both engineers and AI agents use to query our systems. Here's how it works and what we learned.

Enea Kllomollari
Enea KllomollariTech Lead

Knot is a merchant connectivity platform. We update payment methods on file, retrieve SKU-level transaction data, manage subscriptions, and power agentic cart creation and checkout - all across hundreds of merchant integrations. Our team, like many, use Claude and Cursor extensively for writing code. However, when auditing data quality across integrations we faced an issue.

A coding agent can read your codebase all day, but it can't tell you why a six-month-old DoorDash order is missing tax information, or why a subtotal is 5x higher than the product price. Those answers live in production data, behind internal APIs.

We needed our agents to query the same systems we do. So we built knot-cli - an internal tool that both engineers and AI agents use to query our systems.

How we built it

Under the hood, knot-cli wraps several internal APIs, dashboards, and services behind a single interface.

An engineer or agent doesn't need to know which backend holds the transaction data, where the logs live, or how access requests get routed. They just call knot-cli and get what they need.

A human gets formatted tables, colored output, and interactive prompts. An agent passes --raw and gets JSON it can parse and act on. The commands are the same either way.

For example, looking up a transaction:

$ knot-cli txn 200014101174363
Transaction (production)
 
ID                 61551982
External ID        200014101174363
Status             DELIVERED
Amount             $43.84
Date               Dec 18, 2025
 
Merchant           Walmart
Customer           Acme Corp
Merchant Account   2768141
 
Products
 AVAWO Kids Case for Samsung Galaxy Tab A11+ Plus - $15.98 [DELIVERED]
 Schylling Baby Dino Jack in The Box - $24.99 [DELIVERED]
 
Shipping
  Jane Smith
  Providence, RI
  US
 
Price
  Subtotal:    $40.97
  Tax:         +$2.87
  Total:       $43.84

An agent runs the same command with --raw:

$ knot-cli txn 200014101174363 --raw
{
  "id": 61551982,
  "externalId": "200014101174363",
  "amount": 43.84,
  "currency": "$",
  "status": "DELIVERED",
  "date": "2025-12-18T12:35:01.000000Z",
  "merchant": { "name": "Walmart", "id": 45 },
  "customer": { "name": "Acme Corp", "id": 6 },
  "products": [
    {
      "name": "AVAWO Kids Case for Samsung Galaxy Tab A11+ Plus",
      "quantity": 1,
      "price": 15.98,
      "status": "DELIVERED"
    },
    {
      "name": "Schylling Baby Dino Jack in The Box",
      "quantity": 1,
      "price": 24.99,
      "status": "DELIVERED"
    }
  ],
  "shipping": {
    "name": "Jane Smith",
    "city": "Providence",
    "region": "RI",
    "country": "US"
  },
  "price": {
    "subTotal": 40.97,
    "total": 43.84,
    "adjustments": [{ "type": "TAX", "amount": 2.87 }]
  }
}

The agent then runs a /subscriptions-qa skill that validates the response against our data quality rules and product spec. If something's off, it writes the fix, adds unit tests with real fixtures from the response, and opens a PR.

The entire flow from knot-cli call to reviewable PR happens autonomously. A human only shows up when it's time to review.

Built on Bun

The CLI is TypeScript, compiled with Bun. We use Commander.js for the command framework, @inquirer/prompts for interactive flows (token setup, role selection, access requests), and chalk for terminal styling.

Bun's build --compile is what makes distribution painless - it produces standalone binaries for macOS and Linux with no runtime dependencies. An engineer installs it from a GitHub release. A Cursor cloud agent gets it baked into a remote environment image.

Interactive and non-interactive

The --raw flag works per-command. But when knot-cli runs inside an agent's environment, you want non-interactive behavior everywhere. KNOT_CLI_NON_INTERACTIVE does that - --raw becomes implicit, prompts fall back to CLI arguments, and exit codes mean something. Without it, an agent hangs the moment it hits a prompt it can't answer.

We designed the rest of the CLI with the same constraint. Every subcommand has --help with real examples - agents pattern-match off examples faster than they parse prose. Errors fail fast and show the correct invocation instead of waiting on missing input. Commands are idempotent, because agents retry constantly.

The command structure is predictable too. If an agent learns knot-cli txn, it can guess knot-cli sub and knot-cli merchant. Most of the work was making explicit what humans figure out implicitly.

How agents use it

The agent can check merchant details, pull anonymized subscription and transaction records, and chain these calls together to build a full picture. What used to require an engineer clicking through dashboards is now a sequence of CLI calls.

Data quality audits at scale. Hundreds of integrations means things can drift. We run agents that systematically check integration health through knot-cli, pulling anonymized records and verifying them against expected patterns.

The anonymization happens server-side, before data ever reaches the agent, so AI models never see PII. Agents flag anomalies; humans make the call on what to fix. At this point, most of our knot-cli usage comes from agents.

Incident triage. When an alert fires, an agent picks it up and pulls context through knot-cli - recent tasks, sample records, relevant details. It compares what's coming back now against what we saw last week and flags what changed.

By the time an engineer looks at the incident, there's a summary with what changed, when it started, and which records are affected. The engineer makes the call on what to fix, but the context-gathering is already done.

Auth that makes sense

Each agent authenticates with a personal token - the same kind every engineer uses. The token's permissions are scoped to exactly what the agent needs for its job. Every action is attributable to the person whose token it is. If an agent requests privileged access, that request shows up in the same audit log as a human request.

Cursor cloud agents and remote environments

When knot-cli runs inside Cursor cloud agents, it's in a remote environment spun up with everything pre-configured. The environment image has the CLI binary installed, the personal token injected via environment variables, and the full codebase checked out. The agent has everything it needs the moment it spins up.

We trigger these agents from Linear. A teammate files an issue, an agent picks it up, pulls the data it needs through knot-cli, and starts working. Once the environment is set up, onboarding a new agent is just a config change.

What worked for us

We gave our agents a CLI. Built it for humans and agents at the same time, authenticated with personal tokens, anonymized sensitive data server-side, and ran it all inside remote environments ready to go on spin-up. So far, this is the fastest path from "agents can write code" to "agents can actually help run the product."

We're hiring across the team. Come help us build what's next.