Skip to contentStep 1: Create
Step 2: Create
Step 3: Create
Step 4: Create
Part 1: Scaffolding
Part 1 is still the structural foundation: a Node CLI entrypoint, Commander for argument parsing, and Ink for the terminal UI.
The difference now is that we are treating it as a real rebuild checkpoint.
By the end of this part, you should have a minimal interactive terminal app that matches protoagent-tutorial-again-part-1.
What you are building in this part
You are building the smallest useful shell of ProtoAgent:
- a TypeScript CLI package
- a compiled
dist/cli.jsentrypoint - a Commander-based command shell
- an Ink app with a message area and input box
No AI yet. No tools yet. Just the terminal shell the rest of the project will grow inside.
Starting point
Start from a fresh directory.
If you want to compare your result at the end, the target snapshot is:
protoagent-tutorial-again-part-1
Files to create
Create these files first:
package.jsontsconfig.jsonsrc/cli.tsxsrc/App.tsx
Step 1: Create package.json
Use a small Node + TypeScript CLI package with ESM enabled.
Key things to include:
"type": "module""bin": "dist/cli.js"- a
buildscript that runstsc - a
devscript that runstsx src/cli.tsx - Ink, Commander, React, and TypeScript dependencies
At this stage, the snapshot uses these package scripts:
json
{
"scripts": {
"build": "tsc",
"dev": "tsx src/cli.tsx",
"build:watch": "tsc --watch"
}
}And these runtime dependencies:
json
{
"dependencies": {
"@inkjs/ui": "^2.0.0",
"commander": "^14.0.3",
"ink": "^6.7.0",
"ink-big-text": "^2.0.0",
"react": "^19.1.1"
}
}Step 2: Create tsconfig.json
Keep the compiler setup simple and aligned with the staged snapshot.
The stage uses:
json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"jsx": "react",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}Later parts evolve this setup, but this is enough for the first stage.
Step 3: Create src/cli.tsx
This file does three things:
- reads the package version from
package.json - creates the Commander program
- renders the Ink app
The stage-1 CLI is intentionally tiny. It does not have subcommands yet. It just parses the base program and renders App.
Critical details to keep:
- use a
#!/usr/bin/env nodeshebang - read
package.jsonrelative to the built file location - import
Appfrom./App.js - call
render(<App options={options} />)
That last part is what gives you the clean split between CLI entrypoint and interactive UI.
Step 4: Create src/App.tsx
This first version of App is mostly a terminal shell.
It keeps:
- an array of submitted messages
- an
inputKeyso the input resets after submit - a
handleSubmit()function that appends the latest message
It renders:
- a
BigTexttitle - a short intro
- the message list
- an input box at the bottom
You do not need to overthink this part. The point is to establish the TUI layout that every later feature will build on.
The snapshot version uses a single-column Box with:
- a scroll-like content area on top
- a bordered input area on the bottom
What changed since the earliest version
The original scaffold really was just a chat-shaped terminal app. The current full app is much richer:
- the UI is session-aware
- slash commands are built in
- config can be edited inline or mid-session
- approvals are rendered inline in the same TUI
- the app can resume previous sessions by ID
But none of that changes the basic split you are establishing here.
Verification
Install dependencies and build the project:
bash
npm install && npm run build && node dist/cli.js --helpThen launch the dev version:
bash
npm run devIf it worked, you should see:
- the
ProtoAgenttitle rendered in the terminal - a text input prompt at the bottom
- submitted messages appear in the message area
Resulting snapshot
At the end of this part, your project should match:
protoagent-tutorial-again-part-1
Pitfalls
- forgetting
"type": "module"and then fighting ESM import issues - using the wrong JSX setting and getting Ink/React build errors
- importing
./App.tsxinstead of./App.jsfrom built code - reading
package.jsonfrom the wrong relative path after compilation
Core takeaway
Even in the current codebase, the architecture still starts with the same simple split:
- parse CLI arguments
- decide which Ink component to render
- keep the app stateful and interactive inside
App.tsx
That separation makes every later feature easier to layer on.