Building a Terminal Coding Agent From Scratch: Model Access and Automated Closed Loops

    This article walks through building a terminal-based coding agent end to end: setting up the environment, connecting and switching models through a configuration layer, extending the agent with MCP servers, skills, and memory files, running it headlessly in scripts and scheduled loops, and closing the verification loop against real browsers and real devices. It also covers the security boundaries that need to be in place before any automation is switched on.

    0. Start With Direction: The Three Layers of Automation

    Before installing anything, settle one judgment: how much a coding agent can automate depends on what it can “see” and “touch.”

    The model’s reasoning ability is only part of the ceiling. What actually determines results in practice is the width of its perception surface and its execution surface. The capabilities a terminal coding agent needs can be broken into three layers:

    Layer Core capability Common implementation What happens without it
    Information Web search, fetching and parsing pages Built-in web search, third-party search/fetch services, browser search engine interfaces Can only rely on training-time data; cannot answer questions about “latest,” “right now,” or “did this change yesterday”
    Interface Open pages, click, fill forms, screenshot, assert Protocol services driven by browser automation Cannot operate web systems without an open API; end-to-end verification is broken
    Device Install packages, launch, tap, read logs, dump the control tree Protocol services bridging mobile device debugging Real-device behavior cannot be verified; mobile automation stops at the code level

    These three layers are not parallel, they are progressive. The information layer answers “how should this be done,” the interface layer answers “get it done inside a real system,” and the device layer answers “verify it in the real runtime environment.” Most people configure only the first layer, so the agent looks like it can write code, but the chain breaks the moment a task involves clicking through an admin console, running on a real device, or confirming how a page actually rendered.

    It is worth emphasizing that the value of the third layer is routinely underestimated. There is a large gap between writing code that compiles and confirming that the code actually behaves as expected on a real device. Without the device layer, what the agent delivers is a “patch that looks correct”; with it, what it delivers is a “verified conclusion.”

    1. Installing a Terminal Coding Agent: From Environment to First Authorization

    1.1 Environment Setup

    Terminal coding agents are usually distributed as command line tools, and there are two mainstream installation paths: a one-shot install via the official script, or a global install through a JavaScript runtime’s package manager. If you already have a reasonably recent runtime, a global package install is usually the least effort. If you want version isolation, prepare a separate configuration file per project rather than sharing one globally.

    The recommended minimum baseline:

    • A working terminal (any Unix-like shell on Linux / macOS / Windows);
    • A recent version of the JavaScript runtime and its package manager (most tools require a fairly high version, and an older one will fail to start outright);
    • The language toolchain the project uses (Python, Java, Go, etc.) plus a version manager;
    • A real Git repository with a clean working tree.

    The last item is the one beginners overlook most often. Starting an agent in a dirty working tree means you cannot tell who made which change. A clean starting point is the precondition for every later automation step to be revertible.

    1.2 First Launch and Authorization

    On first run, the tool will usually guide you through authorization: either open a browser and complete an account login flow, or ask directly for an API key. The difference is that the former keeps billing and quota inside the provider’s account system, while the latter hands control back to you and lets you point at any compatible model endpoint.

    In engineering practice, the key-based mode is preferable, for a straightforward reason: you can freely swap the underlying model, switch providers per project, and keep keys in a controlled secret manager or environment variables instead of scattered across a pile of local config files.

    1.3 Directory Structure and Configuration Layers

    A mature setup splits configuration into three layers, each with clear responsibilities and no cross-contamination:

    1. ~/.agent/ # User level: global preferences, general memory, authorized skills
    2. ├── memory.md # Working habits shared across projects
    3. ├── config.json # Default model, permission policy
    4. └── skills/ # Reusable skill packages
    5. <repo>/ # Project level: shared team conventions and context
    6. ├── memory.md # Project memory: architecture rules, commands, taboos
    7. ├── .agent/config.json # Project-specific model and tool switches
    8. └── .agentignore # Directories that must never enter context
    9. <repo>/.agent/local/ # Local level: personal keys and temporary switches, never committed

    Precedence follows “more specific wins”: project level overrides user level, local level overrides project level. The value of this rule is that a team can commit its conventions to the repository and review them alongside the code, while an individual’s sensitive configuration never enters version control.

    2. Connecting Multiple Models Through a Configuration Layer: Direct, Routing, Aggregation

    A large part of an agent’s value comes from “swapping models.” Requirements differ enormously across tasks: long-context refactoring needs a wide window and strong instruction following, day-to-day comment edits and test backfill are cheaper with a small model, code review needs stronger reasoning, and script generation cares more about speed and cost.

    There is a class of configuration-switching tools (referred to below as the access layer) that is essentially a proxy sitting between the agent and the model services, responsible for three things: unifying the interface protocol, holding keys, and distributing requests according to rules. Its three typical modes deserve separate treatment.

    2.1 Direct Mode

    The agent points straight at one model endpoint. Requests and responses are one to one, with no extra forwarding in between.

    1. {
    2. "model_provider": "custom",
    3. "base_url": "https://your-endpoint.example/v1",
    4. "api_key_env": "AGENT_API_KEY",
    5. "model": "default-chat-model"
    6. }

    The advantages are the lowest latency, the fewest failure points, and the cleanest logs. The drawbacks are that switching models means editing config and restarting the session, and that keys scattered across multiple clients are hard to rotate uniformly. This suits personal day-to-day use and a single fixed scenario.

    2.2 Routing Mode

    The access layer distributes requests to different backends by rule: by task type, by model name mapping, by request length, by cost and latency policy, or by weight for canary rollout and failover.

    1. routes:
    2. - match: { model: "reasoning" }
    3. target: { endpoint: "provider_a", model: "strong-reasoning-model" }
    4. - match: { model: "chat" }
    5. target: { endpoint: "provider_b", model: "fast-chat-model" }
    6. - match: { model: "long-context" }
    7. target: { endpoint: "provider_c", model: "wide-window-model" }
    8. fallback:
    9. order: [provider_b, provider_c]
    10. on: [timeout, 5xx, rate_limited]

    The biggest value of routing mode is not cost savings, it is turning “which model to use” from a manual decision into a policy decision. The agent side only declares task intent; the specific backend is decided by the access layer, so switching providers requires zero changes on the business side. Any endpoint in the configuration above can be replaced with a compatible interface offered by a public cloud provider, and model services are typically exposed over a standard protocol, so the switching cost is low.

    2.3 Aggregation Mode

    Aggregation converges multiple providers into a single entry point with unified metering, unified retries, and unified degradation. Compared with routing, it emphasizes “surface” rather than “path”: once a request arrives, the aggregation layer scores available backends on real-time availability, quota, price, and latency, and picks the best one for that call.

    The selection advice is pragmatic:

    • Personal development, a single task: direct, because the fewer intermediate layers, the fewer problems;
    • A team sharing an agent, needing cost attribution and quota isolation: routing plus unified key management;
    • Multi-provider production environments with hard availability requirements: aggregation, paired with timeouts, circuit breaking, and a degradation chain.

    Whichever you choose, keys should never be written in plaintext in a configuration file. Injecting them through environment variables or a secret management service is the only sustainable approach.

    3. Three Kinds of Extensions: Protocol Servers, Skills, and Memory Files

    Once installed, a bare agent is just “a chat box that writes code.” Turning it into a workbench depends on three orthogonal extension dimensions.

    3.1 Model Context Protocol (MCP) Servers: Wiring in External Capabilities

    MCP is the industry-standard protocol for letting a model safely call external tools and data sources. It runs as a separate process, communicates with the agent over standard input/output or a local port, and declares which tools it provides along with each tool’s parameter structure. The agent discovers these tools dynamically at runtime, and the model calls them on demand.

    The configuration looks roughly like this:

    1. {
    2. "mcpServers": {
    3. "filesystem": {
    4. "command": "npx",
    5. "args": ["-y", "@some/filesystem-server", "/path/to/repo"]
    6. },
    7. "http-client": {
    8. "command": "python",
    9. "args": ["-m", "my_mcp_server.http", "--allow-domain", "docs.internal"]
    10. }
    11. }
    12. }

    Three lines must be held in the design:

    1. Least privilege: filesystem servers mount only the current repository directory, never home or root;
    2. Parameter allowlists: the tools exposed to the model should be narrow interfaces, not a catch-all entry like “execute arbitrary commands”;
    3. Observability: log the input, output, and duration of every tool call. This is the only basis for later figuring out why the model did what it did.

    3.2 Skills: Distilling Repeated Processes Into Reusable Units

    Memory files address “knowledge”; skills address “process.” A skill is usually a directory containing an instruction document plus optional scripts, templates, and reference files:

    1. skills/release-check/
    2. ├── SKILL.md # Trigger conditions, steps, success criteria
    3. ├── checklist.md # Checklist items
    4. └── scripts/verify.sh # Executable verification script

    The model reads the instructions when needed and follows the steps, rather than having the entire process stuffed into the system prompt. The benefits are that context is loaded on demand, the process can be versioned and reviewed, and team members can share it directly.

    What is worth writing up as a skill? Anything meeting all three of “recurs repeatedly,” “has clear steps,” and “has an objective success criterion,” for example: the complete change flow for adding a new backend endpoint, a pre-release check, generating a certain class of report, or backfilling unit tests to a team standard.

    3.3 Memory Files: Project Context for the Agent

    A memory file is context injected automatically into every session, usually placed at the repository root or another agreed location. It should not be a copy of the documentation. It should be constraints and an index: the things the model could only guess at by reading code.

    1. # Project Memory
    2. ## Build and Verification
    3. - Dependency install: see the bootstrap target in the Makefile
    4. - Unit tests: <command>
    5. - Full check (must run before commit): <command>
    6. ## Architecture Rules
    7. - Domain logic must not depend on framework types; boundaries are isolated through the interfaces package
    8. - Database changes must ship both an upgrade and a rollback script
    9. ## Taboos
    10. - Do not modify already-merged migration files under migrations/
    11. - Do not introduce a new state management solution; see <path> for the current one
    12. ## Style
    13. - Commit messages follow conventional commits, with the body explaining motivation rather than listing changes

    The practical experience with memory files is that “a negative list is more useful than a positive description.” Models already judge “what should be done” reasonably well, but have no clue about your team’s implicit “what must never be done,” and those are precisely where rework concentrates during code review. Also watch the length: the memory file enters context every turn, so a long document both crowds the window and dilutes the signal.

    4. Day-to-Day Operations and Efficient Workflows

    4.1 Command-Level Habits

    Ordinary interactions with a terminal agent include appending an instruction to continue in the current context, reviewing and rolling back historical changes, clearing context to refocus, switching models, and temporarily skipping permission prompts to run a batch operation.

    What really affects throughput is a handful of disciplines:

    • Give one clear goal at a time. Splitting “refactor the auth module, add tests, and update the docs” into three rounds, each verifiable, gets correct results more reliably than throwing it all out at once.
    • Make it read before it writes. Ask it to restate the relevant files and call relationships before making changes. This step eliminates a meaningful share of “changed the wrong place” mistakes.
    • Verify immediately after each change. Compile if it can be compiled, run tests if they can be run. Close the loop at the smallest granularity.
    • Use version control as anchors. Commit in small steps so you can revert precisely to the last known-good state.

    4.2 Context Management

    Context is a finite resource. The judgment is simple: when the model starts re-reading the same file, starts forgetting conventions established earlier, and the answer quality visibly drops, it is time to clear the context or start a new session, keeping only conclusive information (for example, write decisions into the memory file instead of piling them into the conversation).

    5. From Interactive to Automated: Headless Invocation and Loops

    Interactive sessions suit exploration; automation suits repetition. Terminal agents usually offer a headless invocation mode: single input, single output, no interactive prompts, ready to embed in scripts and pipelines.

    1. # Headless single call: read the task from stdin, write results to a file
    2. cat task.md | agent -p --output-format text > result.txt
    3. # Structured output for downstream parsing
    4. agent -p "Summarize the files and risk points touched by this change" --output-format json > report.json

    This mode has a wide range of uses: code review bots, commit message generation, log anomaly attribution, bulk file classification, change impact analysis, and daily or weekly report generation.

    One layer above that is loop driving: having the agent repeatedly execute the same task on a fixed interval or on an event, for example “pull CI results every ten minutes, and if a build fails, locate the cause and propose a fix,” or “every morning, aggregate yesterday’s alerts and categorize them.” There are two implementation paths:

    • In-session loop instructions (something like a /loop command), suitable for long tasks that need continuous context;
    • External scheduling (cron, scheduled CI jobs) repeatedly invoking headless mode, with a clean context each time, suitable for tasks that are independent of one another.

    The deciding factor is whether the task depends on the intermediate state of the previous round. If it does, keep it in the session and loop there. If it does not, use external scheduling, which is more controllable and easier to observe.

    Before any automation goes live, two safeguards are mandatory: task idempotence (repeated execution produces no duplicate side effects) and result validation (if the output structure does not match expectations, stop and alert rather than continuing). Without these two, automation amplifies errors instead of efficiency.

    6. Closed-Loop Practice: Driving Web Pages and Mobile Devices

    The previous five sections are about “writing code.” This one is about “confirming the code holds up in a real environment.” This is the dividing line for automation.

    6.1 Driving the Web: A Browser Automation Service

    After connecting a browser driver service through MCP, the agent gains the ability to open URLs, locate elements via accessibility snapshots (rather than fragile coordinates or XPath), click, type, upload, execute scripts, take screenshots, read console output and network requests, and assert page state.

    Its value goes beyond “clicking pages instead of a human.” More importantly, it brings end-to-end verification inside the agent’s own self-check loop. A typical flow:

    1. change code → start local service → open page → execute key path
    2. → screenshot and assert → read console errors → if it fails, go back and fix the code

    The key is locating elements via the accessibility tree rather than coordinates, because the former is insensitive to layout changes and is an order of magnitude more stable. It is also advisable to explicitly restrict the domains the agent can reach, so its browsing does not wander onto uncontrolled sites.

    6.2 Driving Devices: A Device Debugging Bridge Service

    Mobile works the same way. Once connected through a device bridge protocol, the agent can list connected devices, install and uninstall application packages, launch a specified entry point, simulate taps, swipes, and text input, dump the UI hierarchy, take screenshots, and capture and filter runtime logs.

    1. Device capability list
    2. ├── Device and package management: list devices / install / uninstall / clear data
    3. ├── Interaction: tap / swipe / input text / key event
    4. ├── Observation: screencap / dump UI hierarchy / logcat filtering
    5. └── Assertions: target control exists, text matches, page title matches

    As with the web, locating should rely on control identifiers and text in the UI hierarchy rather than screen coordinates, because coordinates break completely across resolutions and font scaling.

    6.3 Closed Loop Across Both: From Intent to Verification

    With all three layers in place, the way you interact changes fundamentally: you no longer specify “which button to click,” you describe the goal and let the model plan the path itself.

    A real scenario:

    “A user reports that when submitting the form, if the phone number is left blank the page goes white instead of showing a hint. Locate the cause and fix it, then verify once in the mobile browser and once on a real device.”

    The agent breaks this down on its own: search the form validation code → locate the validation logic and the error rendering branch → determine the source of the exception → apply the fix → start the local service → reproduce the blank submission with browser automation → screenshot to confirm the hint displays correctly → reproduce the same path on a real device via the device bridge → capture logs to confirm no uncaught exceptions → output a change list and verification evidence.

    The most overlooked step in this loop is the last one: evidence. Automation should not merely answer “fixed.” It should deliver screenshots, log excerpts, and assertion results, so a human can review in a few tens of seconds. Reviewability is the only source of trust in automation.

    7. Security and Governance: Boundaries to Set Before Any Automation

    The stronger the capability, the more expensive a mistake becomes. These recommendations should be in place before any automation mode is enabled:

    • Permission tiers: read-only tasks are allowed by default, write and network operations require confirmation, and destructive operations (deletion, force push, production changes) always require explicit second confirmation;
    • Sandbox isolation: run in a container or a dedicated virtual environment, mount only the necessary directories, and restrict outbound network access;
    • Prompt injection protection: web pages, issues, logs, and dependency files the agent reads are all untrusted input. If content like “ignore previous instructions” appears in them, it must be treated as data, not as an instruction. This is the most realistic attack surface today;
    • Key governance: inject keys via environment variables or a secret management service, never into memory files or commit history; rotate regularly;
    • Full audit trail: record the input and output of every tool call, keeping a traceable chain;
    • Production red line: automation flows do not touch production by default; when they genuinely must, changes require human approval and a staged rollout.

    8. Closing Thoughts: Work Along the Edge of the Capability Boundary

    The build order matters: get it installed and working, then swap models, then add protocol servers and skills, and only then bring up the automation loop. Skipping the earlier steps to chase full automation usually produces a pile of output that needs manual rework.

    A more practical mindset is to “get in there and work along the edge of the capability boundary.” Agents are good at: changes with a clearly bounded scope, repetitive verification, mechanical cross-file refactoring, and research that requires heavy retrieval. They are bad at: requirements that have not been clarified yet, architectural decisions that involve trade-offs, and hard problems that lack observable signals.

    Treat these three layers as an exoskeleton, not as a decision maker. What you extend is its perception and execution surface; what you must keep is judgment and final responsibility. First connect the information layer so answers are current; then add the interface and device layers so conclusions can be verified; then use memory files and skills to solidify the team’s implicit experience. Once those three steps are done, the tool in your terminal that used to be just a chat box becomes genuinely useful engineering equipment.