Thanks to visit codestin.com
Credit goes to github.com

Skip to content

mjdouglas/nanoagent

Repository files navigation

nanoagent

nanoagent is a small TypeScript coding agent built as a learning exercise. Milestone 10 adds a Pi-inspired native TUI on top of the existing runtime, stdio MCP support, slash commands, layered settings, and CLAUDE.md-style memory loading:

  • CLI
  • TUI
  • JSON-RPC over stdin/stdout

Requirements

  • Node.js 22+
  • pnpm via Corepack or a local install
  • Python 3 for PTY-backed TUI integration tests

Setup

corepack enable
corepack prepare [email protected] --activate
pnpm install

Create a .env file from .env.example and set ANTHROPIC_API_KEY before using the chat paths.

You can also copy nanoagent.policy.example.json to nanoagent.policy.json and adjust tool permission defaults or overrides.

You can copy nanoagent.hooks.example.json to nanoagent.hooks.json to enable sample hooks.

You can copy nanoagent.settings.example.json to nanoagent.settings.json and CLAUDE.example.md to CLAUDE.md to start using layered settings and project memory.

The same settings file can also define custom slash commands.

Commands

pnpm test
pnpm typecheck
pnpm nanoagent --help
pnpm nanoagent config
pnpm nanoagent chat "Explain this repository"
pnpm nanoagent chat --session "<session-id>" "Continue from the last exchange"
pnpm nanoagent --session "<session-id>" "Continue from the last exchange"
pnpm nanoagent-tui
echo '{"jsonrpc":"2.0","id":1,"method":"runtime.info"}' | pnpm nanoagent-rpc
echo '{"jsonrpc":"2.0","id":1,"method":"config.inspect"}' | pnpm nanoagent-rpc
echo '{"jsonrpc":"2.0","id":1,"method":"tools.list"}' | pnpm nanoagent-rpc
echo '{"jsonrpc":"2.0","id":1,"method":"session.create"}' | pnpm nanoagent-rpc
echo '{"jsonrpc":"2.0","id":1,"method":"session.resume","params":{"sessionId":"<session-id>"}}' | pnpm nanoagent-rpc
echo '{"jsonrpc":"2.0","id":1,"method":"message.send","params":{"prompt":"Say hello"}}' | pnpm nanoagent-rpc
echo '{"jsonrpc":"2.0","id":1,"method":"permission.respond","params":{"token":"...","decision":"allow"}}' | pnpm nanoagent-rpc
pnpm nanoagent:tui
echo '{"jsonrpc":"2.0","id":1,"method":"runtime.info"}' | pnpm nanoagent:rpc

Structure

  • src/agent/: shared runtime boundary, tool loop, approval flow, and session continuation
  • src/cli/: one-shot chat CLI entrypoint
  • src/commands/: shared slash command registry for CLI and TUI
  • src/mcp/: stdio MCP client, server lifecycle, and remote capability mapping
  • src/tui/: multi-turn native terminal UI with scrollback-first rendering
  • src/rpc/: JSON-RPC stdin/stdout transport
  • src/config/: .env loading and config parsing
  • src/config/: .env, layered settings, and CLAUDE.md memory loading
  • src/llm/: model client abstractions and Anthropic implementation
  • src/tools/: tool registry and execution for LS, Read, Write, and Bash
  • src/permissions/: permission policy loading and decisions
  • src/session/: on-disk session metadata, state, and transcript storage
  • src/hooks/: hook bus, config loading, and built-in hooks

Current State

Milestone 7 is intentionally focused:

  • all interfaces share the same bounded tool-capable runtime
  • the default model is Claude Sonnet 4.6 via claude-sonnet-4-6
  • the available tools are LS, Read, Write, and Bash
  • read-only tools are allowed by default
  • Write and Bash require approval by default
  • sessions are persisted under .nanoagent/sessions/<id>/
  • approval tokens now point at stored session state instead of serializing the full request state
  • lifecycle hooks can observe model calls, tool execution, permission decisions, and session boundaries
  • built-in sample hooks include console logging and file-backed event metrics
  • runtime settings can be layered from user, project, and local config files
  • CLAUDE.md-style memory is loaded and injected into the runtime system prompt
  • CLI and TUI share a slash command layer for built-ins and custom prompt shortcuts
  • config-driven stdio MCP servers can contribute tools, resources, and prompts
  • the TUI now shows live thinking status, tool calls, tool output, and inline approval prompts in a native terminal flow

Tool activity and permission decisions are returned from the runtime and surfaced by each interface. The RPC surface now includes tools.list, session.resume, and permission.respond in addition to runtime.info, session.create, and message.send. For MCP inspection, RPC also includes mcp.servers, mcp.resources.list, mcp.resources.read, mcp.prompts.list, and mcp.prompts.get.

Each persisted session directory contains:

  • metadata.json
  • state.json
  • transcript.jsonl

Tool Naming Convention

When nanoagent implements a tool that has a clear Claude Code equivalent, it should keep the Claude Code-style tool name and stay as close as practical to the same semantics. The current alignment target includes LS, Read, Write, and Bash.

Hook Configuration

Hooks are loaded from nanoagent.hooks.json if present.

The built-in hook types are:

  • console_logger: writes lifecycle events to stderr
  • file_metrics: writes per-event counters to a JSON file
  • shell_command: runs an arbitrary bash command when matching events fire

Example:

{
  "hooks": [
    {
      "type": "console_logger"
    },
    {
      "type": "file_metrics",
      "outputPath": ".nanoagent/hooks/metrics.json"
    },
    {
      "type": "shell_command",
      "command": "printf '%s\n' \"$NANOAGENT_HOOK_EVENT_NAME\" >> .nanoagent/hooks/events.log",
      "events": ["session.end", "permission.decision"]
    }
  ]
}

For shell_command hooks:

  • command is executed with /bin/bash -lc
  • events is optional; if omitted, the command runs for every lifecycle event
  • cwd is optional and defaults to the project root
  • timeoutMs is optional and defaults to 10000
  • the hook process receives NANOAGENT_HOOK_EVENT_NAME
  • the hook process receives NANOAGENT_HOOK_EVENT_JSON

Example event-specific shell hook:

{
  "type": "shell_command",
  "command": "printf '%s\n' \"$NANOAGENT_HOOK_EVENT_JSON\" >> .nanoagent/hooks/events.jsonl",
  "events": ["tool.execution.after"]
}

The current lifecycle event names are:

  • session.start
  • session.end
  • model.request.before
  • model.response.after
  • tool.execution.before
  • tool.execution.after
  • permission.decision

The package scripts run TypeScript directly through Node's --experimental-strip-types support so the basic interfaces can work without an additional runtime layer.

Layered Settings and Memory

nanoagent now resolves settings and memory in this order:

Settings files:

  • user: ~/.config/nanoagent/settings.json
  • project: ./nanoagent.settings.json
  • local: ./nanoagent.settings.local.json
  • environment variables override file settings

Memory files:

  • user: ~/.config/nanoagent/CLAUDE.md
  • project: ./CLAUDE.md
  • local: ./CLAUDE.local.md

Memory files are concatenated in that order and injected into the system prompt for new sessions.

You can inspect the effective config and loaded memory sources with:

  • nanoagent config
  • RPC config.inspect

Slash Commands

CLI and TUI both support slash commands.

Built-in commands:

  • /help
  • /config
  • /tools
  • /session

Custom slash commands can be defined in layered settings files:

{
  "slashCommands": {
    "review": {
      "description": "Review the current workspace changes for bugs and regressions.",
      "prompt": "Review the current workspace changes. Focus on bugs, regressions, and missing tests.\n\nAdditional focus:\n{{args}}"
    }
  }
}

{{args}} is replaced with the text after the slash command name. If a prompt does not include {{args}}, trailing arguments are appended automatically.

Examples:

  • nanoagent chat "/help"
  • nanoagent chat "/review auth and permissions"
  • in the TUI: /tools

TUI

The TUI is a scrollback-first terminal interface rather than a full-screen dashboard.

Key controls:

  • Enter: send the current prompt
  • Esc: clear the current prompt
  • Ctrl+C: quit
  • /help: show slash commands
  • /exit or /quit: leave the TUI

During a turn, the footer shows live status changes such as:

  • idle
  • thinking
  • calling a tool
  • waiting for approval
  • error

Tool calls, tool output, approval requests, and assistant replies are appended to terminal scrollback as compact blocks.

MCP

nanoagent supports config-driven MCP servers over stdio.

Example:

{
  "mcpServers": {
    "docs": {
      "transport": "stdio",
      "command": "node",
      "args": ["./scripts/docs-mcp-server.js"],
      "permissionKind": "read"
    }
  }
}

Notes:

  • only stdio MCP servers are supported in this milestone
  • MCP tools are mapped into the normal tool loop and obey the same permission flow
  • server failures are isolated; a broken MCP server should not crash the local runtime
  • remote MCP tools are surfaced to the model as mcp__<server>__<tool>

Useful RPC methods:

  • mcp.servers
  • mcp.resources.list
  • mcp.resources.read
  • mcp.prompts.list
  • mcp.prompts.get

About

A small coding agent

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors