{
  "@context": "https://schema.org",
  "@id": "https://aggr.aymericbeaumet.com/items/ampcode-com/2025-12-18-a-codebase-by-an-agent-for-an-agent/#webpage",
  "@type": [
    "WebPage",
    "ArchiveComponent"
  ],
  "articleBody": "Tim Culverhouse//Dec 18, 2025\n\nWhen I’m coding there are many decisions I have to make that affect my ability to navigate the codebase: what’s a good name for this function? Should I make a new file for this type or should it be in an existing file? What should the file be called? Should I create a new directory for this?\n\n![Agent stealing feather](https://ampcode.com/by-an-agent-for-an-agent/agent-stealing-feather.jpg)\n\nNot only do I have to decide these things, but I have to remember them. Every time I open Neovim I have to think about what past-Tim would’ve called this. Where would he have put it? I get hit with cognitive overhead on each side of the decision.\n\nThe good thing is that now I can offload nearly all of this to an agent.\n\nYes, *I know*, one shouldn’t, but hear me out. I did exactly that when I created the TUI framework behind the Amp CLI. The framework is a port of [my Zig TUI library](https://github.com/rockorager/libvaxis) into TypeScript and it’s 90% written by Amp. And, [as Thorsten](https://x.com/thorstenball/status/1962926086884401518) and many others on the team, have said: Amp *rips* when working with this framework on the CLI. It plows through most anything you can throw at it. So much so that the question of “why is Amp so good at working on our CLI?” has come up multiple times. I think I have the answer. I think the answer is that I offloaded decisions to the agent.\n\nThe thing is — I *do care* about the names of things and how the codebase is structured. Early on in the process of writing the framework I would often stop the agent — irritated by a change it made — and tell it to rename a function, move a file to a different directory, or put some class into its own file. I don’t enjoy making these decisions, but I still wanted to make them. There’s just some satisfaction from having things look nice in your codebase — so I would stop the agent and tell it “I know better”.\n\nBut I don’t. I am *not* absolutely right, it turns out.\n\nLet me give you an example. The Amp TUI framework uses a double-buffering approach toward updating the screen. We keep a front and a back screen — one representing the last frame, the other the next frame. Then we diff them, print the updates, and swap the screens. When porting the code from Zig to TypeScript, Amp named the function that swaps the screens `present()`. I didn’t like that and *I know better* so I renamed it to `swapScreens()`.\n\nBut as the port progressed, I saw Amp repeatedly trying to find a function called `present()`, not finding it, declaring “let me try something else”, and eventually finding the function under the name that had made sense to *me*.\n\nAmp spun its wheels because *I* had interjected with *my* opinions about code naming, structure, and layout. But apparently my opinions aren’t the most statistically likely. When the agent reached for the name `present()`, its hand was guided by training data and the context I had provided. But I pushed it away from that, against its instincts, so to say, and made the agent’s job harder the next time. Amp could no longer ask “what would past-Amp have called this?” and find the answer in its weights — it had to know what past-*Tim* would call this.\n\n![Orb writing code](https://ampcode.com/by-an-agent-for-an-agent/orb-writing-code.jpg)\n\nI saw this repeat a few times, and realized my mistake. I was interfering with its ability to navigate using its own thoughts. I made it go against its grain. I slowed it down, cost more tokens as it ran more loops, and, ultimately, caused frustration for *myself* when I saw that it couldn’t remember the name.\n\nSo I changed my approach. No more telling the agent how to name things or where to put them. The agent gets to decide.\n\nAnd it worked.\n\nOnce I took my hands off the wheel, the agent went faster and for longer. It chose names I wouldn’t have picked and file layouts that didn’t occur to me. It used more OOP patterns and classes than we have anywhere else in the codebase. Its use of generics and TypeScript primitives is *strange* when compared to the rest of our code.\n\nBut: it rips.\n\nThere was no denying that. Once it had finished the framework, it impressed us by being really, really good at *using* it — our own custom framework, mind you, that’s not documented anywhere and doesn’t fit into a single context window! With the framework it built, Amp can knock out things faster and more correctly than most of us. It knows how to add scrollbars to a new modal (we’re talking about a TUI here, remember!), it knows how the animation subsystem works, it knows how the keyboard shortcuts and click handlers work, and if there’s something missing in the framework, it adds it. “Oh, we don’t listen for this event. Let me fix that real quick.” And real quick it fixes it.\n\nUltimately I don’t know *why* it’s so good at using it, I can’t look inside its weights while it’s writing code, and I don’t trust its answers when I ask it about it, but here’s what I suspect.\n\nThe framework and its predecessor in Zig are inspired by Flutter. They have a Flutter-like API with Widgets and StatefulWidgets and Intents and Bindings and so on. And, I think, the agent recognizes that. It has enough Flutter in its weights. Even though Flutter is originally written in Dart, I think the agent recognized the Flutter in the Zig version and it can now recognize the Flutter in our codebase too and can look through the language and syntax and see the concepts at play.\n\nAnd the codebase *lets it do that*.\n\nAfter I gave it free rein over the codebase — letting it use the most *probable* names for functions and types, putting files where its “instincts” told it they should go — it built a framework for itself. A framework and a codebase in which things are exactly where a model that knows Flutter and that knows TypeScript — knows them in a probabilistic way — would first look for them. A codebase in which syntax and concepts strike a balance between what’s statistically likely and what actually compiles. A codebase in which the agent doesn’t have to second-guess what something is called and doesn’t have to wonder why something works the way it does. A codebase in equilibrium between what I want it to do, what the concepts require, and what the agent’s weights judge as the most likely way to achieve it.\n\nA codebase by an agent for an agent.\n\n![Cathedral orb codebase](https://ampcode.com/by-an-agent-for-an-agent/cathedral-orb-codebase.jpg)\n",
  "authors": [],
  "captured_at": "2026-10-03T23:09:50.514863041+00:00",
  "category": "programming",
  "content_html": "<p>Tim Culverhouse//Dec 18, 2025</p>\n<p>When I’m coding there are many decisions I have to make that affect my ability to navigate the codebase: what’s a good name for this function? Should I make a new file for this type or should it be in an existing file? What should the file be called? Should I create a new directory for this?</p>\n<p><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://ampcode.com/by-an-agent-for-an-agent/agent-stealing-feather.jpg\" alt=\"Agent stealing feather\" loading=\"eager\" decoding=\"async\" fetchpriority=\"high\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></p>\n<p>Not only do I have to decide these things, but I have to remember them. Every time I open Neovim I have to think about what past-Tim would’ve called this. Where would he have put it? I get hit with cognitive overhead on each side of the decision.</p>\n<p>The good thing is that now I can offload nearly all of this to an agent.</p>\n<p>Yes, <em>I know</em>, one shouldn’t, but hear me out. I did exactly that when I created the TUI framework behind the Amp CLI. The framework is a port of <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/rockorager/libvaxis\">my Zig TUI library</a> into TypeScript and it’s 90% written by Amp. And, <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://x.com/thorstenball/status/1962926086884401518\">as Thorsten</a> and many others on the team, have said: Amp <em>rips</em> when working with this framework on the CLI. It plows through most anything you can throw at it. So much so that the question of “why is Amp so good at working on our CLI?” has come up multiple times. I think I have the answer. I think the answer is that I offloaded decisions to the agent.</p>\n<p>The thing is — I <em>do care</em> about the names of things and how the codebase is structured. Early on in the process of writing the framework I would often stop the agent — irritated by a change it made — and tell it to rename a function, move a file to a different directory, or put some class into its own file. I don’t enjoy making these decisions, but I still wanted to make them. There’s just some satisfaction from having things look nice in your codebase — so I would stop the agent and tell it “I know better”.</p>\n<p>But I don’t. I am <em>not</em> absolutely right, it turns out.</p>\n<p>Let me give you an example. The Amp TUI framework uses a double-buffering approach toward updating the screen. We keep a front and a back screen — one representing the last frame, the other the next frame. Then we diff them, print the updates, and swap the screens. When porting the code from Zig to TypeScript, Amp named the function that swaps the screens <code>present()</code>. I didn’t like that and <em>I know better</em> so I renamed it to <code>swapScreens()</code>.</p>\n<p>But as the port progressed, I saw Amp repeatedly trying to find a function called <code>present()</code>, not finding it, declaring “let me try something else”, and eventually finding the function under the name that had made sense to <em>me</em>.</p>\n<p>Amp spun its wheels because <em>I</em> had interjected with <em>my</em> opinions about code naming, structure, and layout. But apparently my opinions aren’t the most statistically likely. When the agent reached for the name <code>present()</code>, its hand was guided by training data and the context I had provided. But I pushed it away from that, against its instincts, so to say, and made the agent’s job harder the next time. Amp could no longer ask “what would past-Amp have called this?” and find the answer in its weights — it had to know what past-<em>Tim</em> would call this.</p>\n<p><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://ampcode.com/by-an-agent-for-an-agent/orb-writing-code.jpg\" alt=\"Orb writing code\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></p>\n<p>I saw this repeat a few times, and realized my mistake. I was interfering with its ability to navigate using its own thoughts. I made it go against its grain. I slowed it down, cost more tokens as it ran more loops, and, ultimately, caused frustration for <em>myself</em> when I saw that it couldn’t remember the name.</p>\n<p>So I changed my approach. No more telling the agent how to name things or where to put them. The agent gets to decide.</p>\n<p>And it worked.</p>\n<p>Once I took my hands off the wheel, the agent went faster and for longer. It chose names I wouldn’t have picked and file layouts that didn’t occur to me. It used more OOP patterns and classes than we have anywhere else in the codebase. Its use of generics and TypeScript primitives is <em>strange</em> when compared to the rest of our code.</p>\n<p>But: it rips.</p>\n<p>There was no denying that. Once it had finished the framework, it impressed us by being really, really good at <em>using</em> it — our own custom framework, mind you, that’s not documented anywhere and doesn’t fit into a single context window! With the framework it built, Amp can knock out things faster and more correctly than most of us. It knows how to add scrollbars to a new modal (we’re talking about a TUI here, remember!), it knows how the animation subsystem works, it knows how the keyboard shortcuts and click handlers work, and if there’s something missing in the framework, it adds it. “Oh, we don’t listen for this event. Let me fix that real quick.” And real quick it fixes it.</p>\n<p>Ultimately I don’t know <em>why</em> it’s so good at using it, I can’t look inside its weights while it’s writing code, and I don’t trust its answers when I ask it about it, but here’s what I suspect.</p>\n<p>The framework and its predecessor in Zig are inspired by Flutter. They have a Flutter-like API with Widgets and StatefulWidgets and Intents and Bindings and so on. And, I think, the agent recognizes that. It has enough Flutter in its weights. Even though Flutter is originally written in Dart, I think the agent recognized the Flutter in the Zig version and it can now recognize the Flutter in our codebase too and can look through the language and syntax and see the concepts at play.</p>\n<p>And the codebase <em>lets it do that</em>.</p>\n<p>After I gave it free rein over the codebase — letting it use the most <em>probable</em> names for functions and types, putting files where its “instincts” told it they should go — it built a framework for itself. A framework and a codebase in which things are exactly where a model that knows Flutter and that knows TypeScript — knows them in a probabilistic way — would first look for them. A codebase in which syntax and concepts strike a balance between what’s statistically likely and what actually compiles. A codebase in which the agent doesn’t have to second-guess what something is called and doesn’t have to wonder why something works the way it does. A codebase in equilibrium between what I want it to do, what the concepts require, and what the agent’s weights judge as the most likely way to achieve it.</p>\n<p>A codebase by an agent for an agent.</p>\n<p><picture class=\"article-picture article-picture-fallback\" style=\"--image-ratio:16 / 9\"><img src=\"https://ampcode.com/by-an-agent-for-an-agent/cathedral-orb-codebase.jpg\" alt=\"Cathedral orb codebase\" loading=\"lazy\" decoding=\"async\" fetchpriority=\"low\" referrerpolicy=\"no-referrer\" class=\"progressive-image\"></picture></p>\n",
  "content_markdown": "Tim Culverhouse//Dec 18, 2025\n\nWhen I’m coding there are many decisions I have to make that affect my ability to navigate the codebase: what’s a good name for this function? Should I make a new file for this type or should it be in an existing file? What should the file be called? Should I create a new directory for this?\n\n![Agent stealing feather](https://ampcode.com/by-an-agent-for-an-agent/agent-stealing-feather.jpg)\n\nNot only do I have to decide these things, but I have to remember them. Every time I open Neovim I have to think about what past-Tim would’ve called this. Where would he have put it? I get hit with cognitive overhead on each side of the decision.\n\nThe good thing is that now I can offload nearly all of this to an agent.\n\nYes, *I know*, one shouldn’t, but hear me out. I did exactly that when I created the TUI framework behind the Amp CLI. The framework is a port of [my Zig TUI library](https://github.com/rockorager/libvaxis) into TypeScript and it’s 90% written by Amp. And, [as Thorsten](https://x.com/thorstenball/status/1962926086884401518) and many others on the team, have said: Amp *rips* when working with this framework on the CLI. It plows through most anything you can throw at it. So much so that the question of “why is Amp so good at working on our CLI?” has come up multiple times. I think I have the answer. I think the answer is that I offloaded decisions to the agent.\n\nThe thing is — I *do care* about the names of things and how the codebase is structured. Early on in the process of writing the framework I would often stop the agent — irritated by a change it made — and tell it to rename a function, move a file to a different directory, or put some class into its own file. I don’t enjoy making these decisions, but I still wanted to make them. There’s just some satisfaction from having things look nice in your codebase — so I would stop the agent and tell it “I know better”.\n\nBut I don’t. I am *not* absolutely right, it turns out.\n\nLet me give you an example. The Amp TUI framework uses a double-buffering approach toward updating the screen. We keep a front and a back screen — one representing the last frame, the other the next frame. Then we diff them, print the updates, and swap the screens. When porting the code from Zig to TypeScript, Amp named the function that swaps the screens `present()`. I didn’t like that and *I know better* so I renamed it to `swapScreens()`.\n\nBut as the port progressed, I saw Amp repeatedly trying to find a function called `present()`, not finding it, declaring “let me try something else”, and eventually finding the function under the name that had made sense to *me*.\n\nAmp spun its wheels because *I* had interjected with *my* opinions about code naming, structure, and layout. But apparently my opinions aren’t the most statistically likely. When the agent reached for the name `present()`, its hand was guided by training data and the context I had provided. But I pushed it away from that, against its instincts, so to say, and made the agent’s job harder the next time. Amp could no longer ask “what would past-Amp have called this?” and find the answer in its weights — it had to know what past-*Tim* would call this.\n\n![Orb writing code](https://ampcode.com/by-an-agent-for-an-agent/orb-writing-code.jpg)\n\nI saw this repeat a few times, and realized my mistake. I was interfering with its ability to navigate using its own thoughts. I made it go against its grain. I slowed it down, cost more tokens as it ran more loops, and, ultimately, caused frustration for *myself* when I saw that it couldn’t remember the name.\n\nSo I changed my approach. No more telling the agent how to name things or where to put them. The agent gets to decide.\n\nAnd it worked.\n\nOnce I took my hands off the wheel, the agent went faster and for longer. It chose names I wouldn’t have picked and file layouts that didn’t occur to me. It used more OOP patterns and classes than we have anywhere else in the codebase. Its use of generics and TypeScript primitives is *strange* when compared to the rest of our code.\n\nBut: it rips.\n\nThere was no denying that. Once it had finished the framework, it impressed us by being really, really good at *using* it — our own custom framework, mind you, that’s not documented anywhere and doesn’t fit into a single context window! With the framework it built, Amp can knock out things faster and more correctly than most of us. It knows how to add scrollbars to a new modal (we’re talking about a TUI here, remember!), it knows how the animation subsystem works, it knows how the keyboard shortcuts and click handlers work, and if there’s something missing in the framework, it adds it. “Oh, we don’t listen for this event. Let me fix that real quick.” And real quick it fixes it.\n\nUltimately I don’t know *why* it’s so good at using it, I can’t look inside its weights while it’s writing code, and I don’t trust its answers when I ask it about it, but here’s what I suspect.\n\nThe framework and its predecessor in Zig are inspired by Flutter. They have a Flutter-like API with Widgets and StatefulWidgets and Intents and Bindings and so on. And, I think, the agent recognizes that. It has enough Flutter in its weights. Even though Flutter is originally written in Dart, I think the agent recognized the Flutter in the Zig version and it can now recognize the Flutter in our codebase too and can look through the language and syntax and see the concepts at play.\n\nAnd the codebase *lets it do that*.\n\nAfter I gave it free rein over the codebase — letting it use the most *probable* names for functions and types, putting files where its “instincts” told it they should go — it built a framework for itself. A framework and a codebase in which things are exactly where a model that knows Flutter and that knows TypeScript — knows them in a probabilistic way — would first look for them. A codebase in which syntax and concepts strike a balance between what’s statistically likely and what actually compiles. A codebase in which the agent doesn’t have to second-guess what something is called and doesn’t have to wonder why something works the way it does. A codebase in equilibrium between what I want it to do, what the concepts require, and what the agent’s weights judge as the most likely way to achieve it.\n\nA codebase by an agent for an agent.\n\n![Cathedral orb codebase](https://ampcode.com/by-an-agent-for-an-agent/cathedral-orb-codebase.jpg)\n",
  "dateCreated": "2026-10-03T23:09:50.514863041+00:00",
  "date_created": "2025-12-18T00:00:00+00:00",
  "date_modified": "2025-12-18T00:00:00+00:00",
  "date_published": "2025-12-18T00:00:00+00:00",
  "description": "When Tim started to use Amp to build the TUI framework for our CLI, he often interrupted the agent. \"No, this function should be called that,\" he'd say, or \"this type should go into this file.\" But then he realized: wait, what I'm telling…",
  "external_url": "https://ampcode.com/by-an-agent-for-an-agent",
  "git": {
    "commit": "ebb0941c5c06a2ab2377342d607535262a3e969e",
    "path": "items/ampcode-com/2025/12/2025-12-18-a-codebase-by-an-agent-for-an-agent.md",
    "permalink": "https://github.com/aymericbeaumet/aggr-instance/blob/ebb0941c5c06a2ab2377342d607535262a3e969e/items/ampcode-com/2025/12/2025-12-18-a-codebase-by-an-agent-for-an-agent.md"
  },
  "id": "urn:aggr:item:e75140cc4e6df1e80d0d7f0c26be563b177304bb",
  "isBasedOn": {
    "@id": "https://ampcode.com/by-an-agent-for-an-agent"
  },
  "mainEntity": {
    "@id": "https://ampcode.com/by-an-agent-for-an-agent",
    "@type": "CreativeWork",
    "archivedAt": {
      "@id": "https://aggr.aymericbeaumet.com/items/ampcode-com/2025-12-18-a-codebase-by-an-agent-for-an-agent/#webpage"
    },
    "dateModified": "2025-12-18T00:00:00+00:00",
    "datePublished": "2025-12-18T00:00:00+00:00",
    "headline": "A Codebase by an Agent for an Agent",
    "timeRequired": "PT5M",
    "url": "https://ampcode.com/by-an-agent-for-an-agent",
    "wordCount": 1114
  },
  "name": "Archived snapshot: A Codebase by an Agent for an Agent",
  "preview": null,
  "reading_minutes": 5,
  "replicated_at": null,
  "source": "ampcode-com",
  "source_name": "Amp News",
  "summary": "When Tim started to use Amp to build the TUI framework for our CLI, he often interrupted the agent. \"No, this function should be called that,\" he'd say, or \"this type should go into this file.\" But then he realized: wait, what I'm telling the agent to do - it's not statistically likely. It's not what an agent would do. So he stopped and instead let the agent build the framework and the codebase for itself. And now the agent rips through it.",
  "tags": [],
  "temporalCoverage": "2026-10-03T23:09:50.514863041+00:00",
  "title": "A Codebase by an Agent for an Agent",
  "url": "https://aggr.aymericbeaumet.com/items/ampcode-com/2025-12-18-a-codebase-by-an-agent-for-an-agent/",
  "word_count": 1114
}