Chapter 1
Setting Up Snikket
As a new user, you’ll get an invite link (that looks like https://chat.kunguru.net/invite/...).
- Open that link on a mobile device (Android or iOS)
- Do one of:
- Install the app if you haven’t yet with the link to your app store (note: Install through app store, not with the “install this web page” button in the browser.)
- Click the Open the App link if it’s already installed.
- Create your account. Your lowercase first name is a good username.
- Save the auto-generated password if you want to also set up a desktop app.
- Once your account is created, start a chat with kunguru@chat.kunguru.net
- You’ll have to click “disable encryption”, since the AI agent doesn’t do fancy E2E encryption (your connection is still encrypted, the messages just aren’t encrypted a second time).
Chatting with the Kunguru Agent
- The kunguru@chat.kunguru.net chat user is an LLM chatbot (Kimi K2.6 via the Moonshot coding plan API).
- It lives on the server.
- It can run commands, write (and run) programs, and has limited sudo access to configure nginx and request HTTPS certificates with certbot.
- That lets it manage web sites and web apps (The main kunguru.net site and any number of additional [subdomain].kunguru.net sites).
- Ask it about what skills and custom skills it has.
- Most of the stock skills that require external API keys aren’t set up.
The Server
- The kunguru.net server is a Debian Linux virtual server running hermes agent, nginx, and snikket.
- The kunguru@chat.kunguru.net hermes instance is running as the kunguru system user.
Building Web Apps with Hermes — A Human Guide
Why Version Control and Testing?
If you’ve ever made a change “quick, just one line” and then spent an hour fixing what it broke, this workflow is for you.
Version control (Git) is like a video game save system. It takes snapshots of your code at every step:
- Save before making a risky change.
- Load the last save if something breaks.
- Make a parallel save (a branch) to explore an idea — the
maincopy stays untouched. - Merge the branch back into
mainwhen the experiment is ready.
Testing is automatic checks after every change. Instead of clicking through your whole app to see if things still work, Hermes runs the checks for you and reports what passed and what failed.
The rest of this guide shows how both work together on your server. Nothing ships to your users unless it’s been snapshot, checked, and double-checked.
What This Is
You have multiple web applications running on one server. Each app has two versions:
- Live at
myapp.kunguru.net— what users see - Dev at
myapp-dev.kunguru.net— where experiments happen
Hermes manages both. You describe what you want in plain English. Hermes does the technical work and tells you what happened.
Why Two Versions?
| Live | Dev |
|---|---|
| Sacred. Users depend on it. | Disposable. Break it, no one cares. |
| Updates only after careful checking. | Updates constantly as you experiment. |
Runs from the main branch. | Runs from feature branches. |
Think of Dev as a rehearsal stage. Live is opening night.
Actually, there’s three copies. The third copy lives on the Github servers.
The Safety Nets
Three things prevent “I added a feature and broke everything”:
1. Branches (Parallel Timelines)
Before changing anything, Hermes creates a branch — a parallel copy of the code. Changes happen there first. The live app doesn’t see them until you approve.
What you say: “Hermes, add dark mode to the blog app.”
What happens: Hermes creates branch feat/dark-mode in the blog’s dev copy. Builds there. Live blog unchanged.
2. Automated Checks (The Double-Check)
Hermes runs tests before and after changes. Quick checks take seconds. Full checks run the full test suite and take a few minutes.
What you see:
Quick check: ✓ API up, ✓ homepage loads
Full check: ✓ 12 API tests, ✓ 8 page tests, ✓ 3 browser flows
If something breaks, Hermes stops and explains:
✗ Login test failed
Expected: reject bad password
Got: server error
I probably broke this adding validation. Fix, revert, or show me?
3. GitHub Safety Gate
Before merging to main, Hermes pushes the branch to GitHub. GitHub runs the same checks on a clean machine. If they pass, you see a green checkmark and a link to the PR. You review and click Merge when ready.
Why this matters: Sometimes things work on your server but fail everywhere else. GitHub catches this. Also, if you ever have a co-editor, the merge button is the moment you both agree “this is ready.”
What You Need to Know
| Concept | Plain English |
|---|---|
| Branch | Parallel copy of code for one feature. Safe to break. |
| Main | The official, permanent code. Only merge when ready. |
| Merge | Bring a branch into main. Point of no return. |
| Check / Test | Automatic verification that things work. |
| Deploy | Push the latest code to the live site so users see it. |
| Safety Gate | GitHub’s automatic double-check before merge. |
| PR (Pull Request) | GitHub page showing proposed changes, checks, and merge button. |
You don’t run commands. You just say yes/no, describe changes, and approve merges.
The Workflow: A Typical Feature
1. Start
You: “Hermes, I want user profiles on the blog app.”
Hermes: “I’ll create a branch feat/user-profiles in blog-dev. Live blog stays as-is. I’ll check in at each step.”
2. Build
Hermes works in blog-dev. After significant steps, it runs quick checks and reports:
“Database table added. Quick check passes. Moving to profile page.”
You can interrupt anytime: “Make the avatar round” or “Wait, don’t do that yet.”
3. Verify
Hermes: “Profile page built. Running full check to verify login, signup, search, and posts still work.”
[2 minutes later]
Hermes: “Full check passed. All existing features work. New profile feature works. Ready to push to GitHub for the safety gate?”
4. Safety Gate
Hermes pushes feat/user-profiles to GitHub. GitHub runs checks.
Hermes: “GitHub safety gate running… [link to checks]”
Option A — passes: “Safety gate passed. Here’s the PR: [link]. You can review and merge on GitHub.”
[You open the link, review, and click Merge.]
You: “Merged. Deploy it.”
Option B — fails: “Safety gate failed. Export test broke on clean machine but passed here. I forgot to include a config file. Fixing… [later] Re-running… Passed. Ready to merge?”
5. Merge & Deploy
Hermes: “Deploying to live blog… Done. Final quick check on live: ✓ all good. User profiles are live.”
Emergency Procedures
| Situation | What You Say |
|---|---|
| Live app broke after deploy | “Hermes, roll back the blog app to before user profiles.” |
| Dev branch is a mess | “Hermes, abandon this branch. Start over.” |
| I want to see what changed | “Hermes, show me what this branch changes.” |
| I want to try something risky | “Hermes, create a branch for this experiment.” |
| Someone else edited the same app | “Hermes, check if there are new changes on GitHub I need to pull.” |
Multiple Apps
Each app has its own dev copy, live copy, and GitHub repo. Hermes knows which is which.
You: “Hermes, fix the search on the store app.”
Hermes: “Working on store-dev. Store live untouched.”
No confusion. Each app is isolated.
If Multiple People Edit
If someone else pushes to GitHub, Hermes detects it:
Hermes: “New changes on main from GitHub. Should I pull them into dev before we start, or work on top of what we have?”
If you both edit the same file, Hermes resolves simple conflicts automatically. Complex conflicts, it asks you: “You and [person] both changed the login form. Their version keeps the old layout. Yours adds dark mode. Which should win?”
Summary
- Two copies per app: dev (experiments) and live (users)
- Branches: one feature, one branch, isolated until merged
- Checks: automatic verification before every merge
- GitHub: safety gate + merge button + collaboration readiness
- You: describe, approve, intervene when needed
- Hermes: execute, verify, explain, manage all tooling
Building Apps from Components — A Human Guide
The Loop
If you’ve ever built a working prototype, made one small change, and watched the whole thing break — and then couldn’t find what broke, and then started over from scratch — this is for you.
The pattern looks like this:
- Build a working prototype in pure HTML and JS.
- Add a feature or fix something.
- Something else breaks. You don’t know what.
- No automatic check tells you what’s wrong.
- You click around the app for a while, hunting.
- It would take longer to fix than to start over.
- So you start over.
- Repeat.
This isn’t a you problem. It’s a structure problem. When the whole app is one blob, every change can break any part of it, and there’s no fast way to find out what broke. The fix isn’t “be more careful.” The fix is to build the app differently — out of small, independent pieces, each with its own test.
The rest of this guide shows what those pieces are, why they make the loop disappear, and how Hermes builds apps this way.
A Different Way to Think About a Page
A web page isn’t one thing. It’s a collection of pieces sitting next to each other.
Look at almost any page:
- a header at the top
- a search box
- a button
- a list of items, each with a checkbox and some text
- a footer
Each of those is a piece. A component is one of those pieces. A page is a sandwich of components, not a single solid loaf.
What is a Component?
A component is a chunk of a web page with a single job.
It has:
- a look — how it appears
- behavior — what happens when you click, type, or hover
- a boundary — what’s inside it and what’s outside
The simplest example: a Button. One job — be a button. Looks like a button. Behaves like a button. Nothing else.
A slightly bigger one: a TodoItem. One job — show a single task. It shows the task’s text, a checkbox, and tells the rest of the page when the checkbox is clicked. Nothing else.
Components as Fillable Templates (Props)
You rarely want the button. You want this button with this label, this color, this size.
A component takes props — short for “properties” — which fill in its blanks. Think of props like filling out a form:
“Button: make me a red one, with text ‘Save’, and slightly bigger.”
That’s three props: color=red, text="Save", size=large. The Button component is the template; the props are the answers to its questions.
A real example from the TODO list: the TodoItem component takes the text of the task and a done flag as props. Same component, used dozens of times, each instance with different text and a different done flag. You don’t write a new component per item — you reuse the same one with different props.
Components That Remember (State)
Some pieces need to remember things between moments — what you typed, what’s checked, what’s in the list right now.
That short-term memory is called state. When a component’s state changes, the component redraws itself to show the new memory. Type a character in a search box → state changes → the box redraws with the new character. Check a checkbox → state changes → the item redraws as done.
A key distinction: state is the component’s short-term memory. It forgets when you reload the page. The database is the long-term memory — it remembers across reloads, across devices, across days. They are different things, and confusing them causes bugs.
In our TODO list:
- The list of items itself is state. When you add a new one, the list’s state changes and the page redraws to show it.
- Whether a particular item is done or not is a prop on
TodoItem— it gets passed in. (Where it lives long-term is a different concern; that’s the database’s job.)
You don’t need to know the mechanics of how state is tracked. Hermes handles that. You need to know that some components remember things, and that’s the piece that makes the page feel alive.
Why This Breaks the Loop
This is the heart of it. Compare two ways of building:
| Monolithic prototype | Component-based |
|---|---|
| One big HTML+JS blob. | Many small, named pieces. |
| Change anywhere → might break anywhere. | Change in a component → only that piece is at risk. |
| “Is it broken?” answered by clicking around. | “Is it broken?” answered by a test, in seconds. |
| Fixing means archaeology. | Fixing means the failing test points at the exact piece. |
| When it’s too broken, start over. | When one piece breaks, fix that piece, run its test, move on. |
The “blast radius” of any change goes from “the whole app” to “one component.” That’s the structural fix for the loop.
Styling That Goes Where It Belongs
The same locality applies to styling. Each component carries its own styles. They don’t leak to other components.
Hermes writes styles using Tailwind — a system of small class names that control spacing, color, size, borders, and so on. We don’t need to dive into the class names; you describe what you want, Hermes writes them.
This is why components and styling are exciting together: you can tweak the look of one piece — make a button more purple, give cards more breathing room — and the rest of the app stays put.
If you ever peek at a component’s code, the class attribute will look like alphabet soup:
class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg"
That’s normal. You don’t read it; you describe the look, Hermes writes the classes.
Hermes also keeps a small shared style config so colors and spacing stay consistent across components. You don’t need to think about it — but it means “more purple” gives the same purple on every page.
Tests as the Per-Component Safety Net
Each component has a small test: “given these props, the component should look like X.”
Tests run in seconds. When one fails, the failure message names the component and tells you exactly what was expected vs. what it got. Hermes runs them, reads the output, and tells you what passed and what failed. You don’t have to.
A test for TodoItem in plain English:
Given:
text="Buy milk",done=falseShould show: the text “Buy milk” and an unchecked box.Given:
text="Buy milk",done=trueShould show: the text “Buy milk” with a line through it, and a checked box.
A test for the page (TodoList):
Given: no items in the list Should show: an empty-state message (“Nothing yet — add your first todo”).
Given: two items in the list Should show: both items, plus the input.
If something does break, the failure is specific:
✗ TodoItem test failed
Expected: text "Buy milk" with line-through when done=true
Got: text "Buy milk" with no line-through
I probably broke this changing styles. Fix, revert, or show me?
The test name tells you the component. The message tells you what was wrong. You don’t hunt.
The TODO List, From Idea to Live
A full walkthrough. As we covered in the workflow guide, this all happens on a branch in your dev copy first. The live app stays untouched.
1. Plan
You: “Hermes, build me a TODO list page from components.”
Hermes: “I’ll plan the components first. Here’s the shape:
TodoList (the page)
├── TodoInput (text box + "Add" button)
└── TodoItem × N
├── checkbox
└── the task text
Three components. I’ll build them bottom-up, with tests, on branch feat/todo-list in your dev copy. OK to start?“
2. Build TodoItem (and style it)
Hermes: “Building TodoItem first. It takes text and done as props, renders a checkbox and the text, fires a callback when the checkbox is toggled. Writing its test now.”
TodoItem tests: ✓ done=false, ✓ done=true. Quick check: ✓.
You: “Hermes, when a todo is done, strike through the text and gray it out.”
Hermes: “Updating TodoItem styles… Done. Test still passes. Quick check: ✓.”
3. Build TodoInput
Hermes: “Building TodoInput next. A text box and an ‘Add’ button. The button is disabled when the box is empty. Writing its test.”
TodoInput tests: ✓ empty disables, ✓ text enables. Quick check: ✓.
4. Build TodoList
Hermes: “Now TodoList. It uses TodoInput and TodoItem. It holds the list of items in its state. When you click ‘Add’ in TodoInput, the list adds a new item. When you toggle a checkbox in TodoItem, the list marks that item done. Writing its tests.”
TodoList tests: ✓ empty, ✓ renders, ✓ add, ✓ toggle. Quick check: ✓.
5. Full Check
Hermes: “All three components built, tested, and assembled. Running full check to verify they all work together — empty state, adding, toggling, the styling.”
[~30 seconds later]
Full check: ✓ 12 component tests, ✓ add flow, ✓ toggle flow, ✓ styling.
Hermes: “Full check passed. Ready to push to GitHub for the safety gate?”
6. Safety Gate & Deploy
This is the same ritual as the workflow guide. Hermes pushes the branch to GitHub, the safety gate runs, you review the PR, click Merge, then say “Deploy it.” Done.
What You Say to Hermes
A small starter set. As in the workflow guide, you don’t run commands — you describe and approve.
| You want to… | You say… |
|---|---|
| Build a new page | “Hermes, build me a [page] from components.” |
| See the plan first | “Hermes, what components do I need for this?” |
| Add a missing test | “Hermes, add a test for the [component].” |
| Tweak a component | “Hermes, change [component] so [behavior].” |
| Style a piece | “Hermes, make this button more purple.” |
| Style a state | “Hermes, strike through TodoItem text when done.” |
| Re-space something | “Hermes, the cards feel cramped — give them more breathing room.” |
| Diagnose a failure | “Hermes, which test is failing and why?” |
| Revert a single piece | “Hermes, undo the change to [component].” |
| Start a new app | “Hermes, set up a new app using Vite, Vite-Express, React, and Tailwind.” |
| Check the stack | “Hermes, is this app using Vite, Vite-Express, React, and Tailwind?” |
| Add a missing tool | “Hermes, add Tailwind to this app.” |
When Things Break (the new normal)
The old normal: vague “something’s wrong,” long hunt, give up, start over.
The new normal: a test fails, the test name tells you which component, the message tells you what’s wrong, you (or Hermes) fix that one piece, the test turns green, you move on.
You don’t restart because no change is big enough to require it. Each component is small. Each change is small. The tests catch breaks at the size of a component, not at the size of the whole app.
What You Don’t Need to Know
You describe, you approve, you intervene when needed. You don’t:
- write React
- read JSX line-by-line
- run tests by hand — Hermes does
- choose which component owns which state — Hermes plans, you approve
- manage the build pipeline — it’s set up
Hermes handles the technical work. You focus on what the app should do and how it should feel.
The Stack
When Hermes builds an app, it uses four tools. You don’t operate any of them — but it’s useful to know they exist and roughly what they do, so you can ask good questions.
| Tool | What it is, in plain English |
|---|---|
| Vite | The dev server. Runs your app while you build it; refreshes the page when Hermes makes a change. |
| Vite-Express | The bridge. Lets the same app run in development (Vite) and on the live server (Express) without two codebases. |
| React | The component model. The “pieces” we covered throughout this doc are React components. |
| Tailwind | How Hermes writes styles. (Covered in the styling section above.) |
When you start a new app, make sure Hermes is using Vite, Vite-Express, React, and Tailwind for your app. If any of the four is missing, ask Hermes to add it before you start building components — switching mid-build is painful.
Summary
- Apps are collections of components.
- Props make a component a fillable template.
- State is the component’s short-term memory; the database is the long-term memory.
- Each component has its own test.
- Components + Tailwind: styling is fast, local, and low-risk.
- Changes stay local; tests catch breaks; you patch, not restart.
- The stack: Vite, Vite-Express, React, Tailwind. Confirm it’s in place before you start building.
- You describe and approve; Hermes builds, tests, and ships.