Chapter 11: Polish & Introspection
Clean up tool implementations, add session compaction, self-analysis, and document known issues.
TUTOR WITH THEFOCUS.AI
Copy this prompt into Claude, ChatGPT, or any external AI assistant. It points the assistant to the course instructions and links it to your student profile to track your progress and customize observations.
You are not enrolled yet. Enroll to generate a Student ID to track lesson completions and store learning notes.
Chapter 11: Polish & Introspection
What We’re Building
The final chapter is about making everything work well together. We’ll:
- Clean up tool implementations
- Add response limits to prevent context overflow
- Implement session compaction (summarize old context)
- Add session introspection (analyze your own logs)
- Document known issues and future roadmap
Step 1: Clean Up Tools
Paste:
look through the tools implementations and make sure that it’s clean, and make sure that we are using ripgrep for searching
add a limit to the size of the response for search_files to make sure that it doesn’t return more than 50 lines. also add a 500 line limit to read_file and add some offset parameters to it. remove git commit command also
The Limit System
search_files — max 50 matches:
const lines = result.trim().split("\n");
if (lines.length > 50) {
return (
lines.slice(0, 50).join("\n") +
`\n... (and ${lines.length - 50} more matches)`
);
}
read_file — max 500 lines with start/limit:
const start = Math.max(0, startLine - 1);
const safeLimit = Math.min(limit, 500);
const end = Math.min(lines.length, start + safeLimit);
if (lines.length > end - start) {
result += `\n\n(Showing lines ${start + 1}-${end} of ${lines.length}. Use startLine and limit to see more.)`;
}
No Auto-Commit
The system prompt explicitly forbids auto-commit:
6. **No Auto-Commit**: NEVER automatically commit changes. You may use
'git_diff' to verify changes, but you must ask the user for confirmation
before committing, or wait for the user to commit manually.
Step 2: Session Compaction
As conversations grow, the context window fills up. Compaction summarizes the conversation history into a concise format, preserving essential context while freeing up tokens.
Paste:
Add session compaction — when the context gets too large, summarize the conversation history into a compact form that preserves the key decisions, code changes, and current state.
The compaction flow:
- Detect: When total tokens exceed ~80% of the context window
- Summarize: An agent (or subagent) reads the conversation and produces a summary
- Replace: Old messages are replaced with a single system message containing the summary
- Continue: The agent continues with fresh context
The summary format:
## Session Summary
### Current State
- Branch: main
- Last action: Completed tool refactoring
- Files modified: src/tools/files.ts, src/tools/search.ts
### Key Decisions
1. Use ripgrep for all text search
2. 50-line limit on search results
3. 500-line limit on file reads
### Pending Work
- Session logging needs subagent parent tracking
- TUI improvements pending
Step 3: Session Introspection
Paste:
add a session-analysis agent that reads the session logs, identifies patterns (which tools are used most, where errors occur, what costs the most), and generates a report with recommendations
The introspection agent:
- Reads
.session_logs/files - Counts tool usage, error rates, costs
- Identifies patterns: “You use
read_file3× more thansearch_files— consider searching first” - Generates recommendations: “Most errors happen in
replace_in_file— consider adding a preview step”
Step 4: The ask_user Tool
Paste:
We need a way for subagents to query the user for questions. Create a subagent who makes a poem about your name, and asks you first what your name is and then a follow up question about a color, and then writes a poem. Then lets test out the subagent to make sure that the main loop handles the handoff correctly, and knows what the final answer is.
The ask_user tool in src/tools/user.ts:
import { askQuestion } from "../lib/readline";
export async function askUser(
question: string,
agentName = "system",
): Promise<string> {
const answer = await askQuestion(`\n[${agentName}] ${question}\n> `);
return answer;
}
This shows labeled prompts like:
[poet] What is your name?
> Will
[poet] What is your favorite color?
> blue
[poem about Will and the color blue...]
Step 5: Known Issues & Roadmap
Things to Be Aware Of
- Subagent readline: Multiple subagents using
ask_usersimultaneously can be confusing. The current implementation serializes them. - Context overflow: Without compaction, long sessions degrade. Model behavior at the context limit varies — some get confused, some start repeating.
- Tool result truncation: Results over 1,000 characters are truncated in the console display but the full result goes to the LLM.
- Model selection: Gemini is cheaper but can be less precise than Opus for complex refactoring. Experiment to find your sweet spot.
Future Improvements
| Area | What Could Be Better |
|---|---|
| Streaming | Show partial LLM output as it generates (currently waits for full response) |
| MCP Integration | Connect to the Model Context Protocol ecosystem for reusable tools |
| Evaluation | Automated test suite that validates agent behavior doesn’t regress |
| Undo/Rollback | Git-based recovery from agent mistakes |
| OpenRouter Alternatives | Direct Anthropic/Google/OpenAI connections for provider-specific features |
Step 6: Final Verification
run the full test suite and lint everything
mise run check
Everything should pass:
✓ sanity.test.ts — all tests pass
✓ tools.test.ts — all tools tested
✓ agent.test.ts — agent loop verified
✓ api.test.ts — API client tested
✓ search.test.ts — ripgrep search tested
✓ web.test.ts — URL download tested
✓ tavily.test.ts — Tavily search tested
✓ subagent.test.ts — subagent spawning tested
Checked 24 files in 85ms. No errors.
The Complete Architecture
weekend-coding-agent/
├── src/
│ ├── index.ts # REPL, session setup, cost display
│ ├── agent.ts # runTurn loop, subagent runner
│ ├── lib/
│ │ ├── api.ts # OpenRouter client, model stats
│ │ ├── logger.ts # JSONL session logging
│ │ ├── readline.ts # Terminal I/O
│ │ └── types.ts # Message, CompletionResponse
│ ├── tools/
│ │ ├── index.ts # Tool definitions + dispatch
│ │ ├── files.ts # listFiles, readFile, writeFile, replaceInFile
│ │ ├── search.ts # ripgrep search
│ │ ├── system.ts # bash execution
│ │ ├── web.ts # URL → markdown
│ │ ├── tavily.ts # Web search
│ │ ├── git.ts # git diff
│ │ ├── user.ts # ask_user (human-in-the-loop)
│ │ └── types.ts # CommandExecutor, AgentRunner
│ └── prompts/
│ ├── index.ts # Prompt loader, skill loader
│ ├── default.md # Main coding agent
│ ├── code-map.md # Codebase documentation agent
│ ├── tech-researcher.md # Web research agent
│ ├── feature-planner.md # Feature planning agent
│ └── poet.md # Interactive poem agent
├── skills/
│ ├── big_text/SKILL.md # figlet ASCII art
│ ├── generate_image/SKILL.md # Image generation
│ └── generate_video/SKILL.md # Video generation
├── tests/ # Full test suite
├── .session_logs/ # JSONL recording
├── docs/ # Code maps
├── reports/ # Research reports
├── .env # API keys
├── mise.toml # Tasks and tools
└── biome.json # Linter config