Skip to main content

nonoka-cli

OpenCode backend for the Nonoka Agent framework.

nonoka-cli runs as a stdio NDJSON bridge server (nonoka-cli --server) that talks to the nonoka-opencode-provider TypeScript package. When used inside OpenCode, Nonoka acts as the conversation/decision backend while OpenCode owns tool execution and human-in-the-loop (HITL) approval using its native tools.

Quick install

The easiest way to get nonoka + OpenCode is the one-line installer:

curl -fsSL https://nonoka.dev/install.sh | bash

The installer will:

  1. Check Python 3.10+ and Node/npm.
  2. Install or update OpenCode.
  3. Install nonoka-cli and the OpenCode provider.
  4. Generate ~/.config/nonoka/config.yaml and ~/.config/opencode/opencode.json.

After installing, configure your API key and run nonoka:

# Interactive: it will ask for your key and save it to ~/.config/nonoka/.env
nonoka-cli config init

# Or set it manually
export DEEPSEEK_API_KEY=<your-key>

nonoka-cli doctor
nonoka

nonoka is a shortcut for nonoka-cli run and starts the OpenCode TUI with the Nonoka backend. You can also use nonoka run --message "<task>" for one-shot CLI usage.

nonoka-cli automatically loads ~/.config/nonoka/.env and ./.env on startup, so you don't need to export every time if you save the key in .env.

To use uv instead of pip, or to run non-interactively, pass flags: curl -fsSL https://nonoka.dev/install.sh | bash -s -- --uv --yes.

Manual installation

# Install nonoka-cli
pip install nonoka-cli
# or with uv
uv pip install nonoka-cli

# Install the OpenCode provider globally so OpenCode can load it
npm install -g nonoka-opencode-provider

Quick start

  1. Create your nonoka config (it will ask for your API key and save it to ~/.config/nonoka/.env):
nonoka-cli config init

For scripted setups, use the non-interactive mode (you'll still need to set the API key via .env or export):

nonoka-cli config init --yes --model deepseek-chat
  1. Generate an OpenCode config in the current project or globally:
# Project-level
nonoka-cli opencode init

# User-level
nonoka-cli opencode init --global
  1. Make sure your model API key is exported, then run:
nonoka

nonoka-cli doctor

Diagnose your installation and configuration:

nonoka-cli doctor

Example output:

nonoka-cli doctor
✓ nonoka-cli 0.2.5
✓ Python 3.11
✓ opencode 1.18.2
✓ provider nonoka-opencode-provider@0.2.12
✓ config ~/.config/nonoka/config.yaml
✓ API key DEEPSEEK_API_KEY set
✓ OpenCode provider config in /home/user/.config/opencode/opencode.json

If anything is wrong, doctor prints a remedy line. To also verify the LLM API key with a real (small) call, use:

nonoka-cli doctor --check-llm

Configuration

nonoka-cli config init

Interactive wizard that writes ~/.config/nonoka/config.yaml. It asks for a model identifier (e.g. deepseek-chat, openai/gpt-4o, ollama/llama3.3), a masked API key, and whether to save it to ~/.config/nonoka/.env (recommended), directly in config.yaml, or skip saving. It also asks for a system prompt and whether to auto-approve all tool calls.

Non-interactive example:

nonoka-cli config init --yes --model openai/gpt-4o

nonoka-cli config set <key> <value>

Update a single config value. Dotted keys are supported:

nonoka-cli config set model openai/gpt-4o
nonoka-cli config set cli.theme light
nonoka-cli config set hitl.dangerous_tools '["write_file", "execute_command"]'

nonoka-cli config show

Print the resolved configuration and its file path.

nonoka-cli opencode init

Generate or merge an opencode.json in the current directory and create .opencode/agents/build.md from your nonoka system_prompt. The generated config points OpenCode at the nonoka-opencode-provider package and passes the nonoka config path to the backend.

OpenCode configuration

nonoka-cli opencode init generates two things:

  1. opencode.json in the current directory, which wires OpenCode to the nonoka-opencode-provider package and sets HITL permissions.
  2. .opencode/agents/build.md, which contains the agent prompt.

A typical generated opencode.json looks like:

{
  "$schema": "https://opencode.ai/config.json",
  "model": "nonoka/default",
  "provider": {
    "nonoka": {
      "npm": "nonoka-opencode-provider",
      "name": "Nonoka",
      "options": {
        "serverCommand": ["bash", "-c", "nonoka-cli --server 2>/tmp/nonoka-server.log"],
        "cwd": ".",
        "configPath": "~/.config/nonoka/config.yaml"
      },
      "models": {
        "default": { "name": "Nonoka deepseek-chat" }
      }
    }
  },
  "permission": {
    "*": "ask",
    "bash": "ask",
    "edit": "ask",
    "write": "ask"
  },
  "agent": {
    "build": {
      "mode": "primary",
      "permission": {
        "*": "ask",
        "bash": "ask",
        "edit": "ask",
        "write": "ask"
      }
    }
  },
  "tools": {
    "skill": false
  }
}

The "tools": {"skill": false} line disables OpenCode's native skill:<name> tool so it does not collide with nonoka's load_skill / skill__<name>__<tool> workflow. nonoka-cli opencode init writes this automatically.

Prompt ownership

Nonoka owns the canonical system prompt via system_prompt in ~/.config/nonoka/config.yaml. When you run nonoka-cli opencode init, the command adapts that prompt and writes it to .opencode/agents/build.md so OpenCode uses it for its primary agent. OpenCode-specific guidelines (tool names, approval behavior, path conventions) are appended automatically; they are not mixed into Nonoka's core prompt, so the same config works for other frontends in the future.

Human-in-the-loop

When running inside OpenCode, HITL is handled by OpenCode itself. The generated opencode.json sets "*": "ask" so every tool requires approval. Because Nonoka forwards OpenCode's native tool definitions to the model, approval dialogs render natively for bash, read, write, and edit operations.

nonoka-cli's own cli.auto_approve and hitl.policy settings only apply to standalone server / CLI mode. In OpenCode mode, permissions are governed by the permission block in opencode.json. To keep nonoka.yaml as the single source of truth, add a permissions block and re-run nonoka-cli opencode init:

permissions:
  read: allow
  bash: ask
  write: ask
  edit: ask

cli.auto_approve: true still auto-allows the core coding tools when no permissions block is present. For standalone mode, use hitl.policy: auto.

External-tools mode

When nonoka-cli runs inside OpenCode, it operates in external-tools mode by default. OpenCode sends its native tool list (e.g. bash, read, write, edit, todowrite) to the provider; nonoka-cli registers them as ExternalCapability objects and lets OpenCode execute them. This means:

  • OpenCode owns tool execution, HITL approval, and TUI rendering.
  • nonoka owns decision-making: which tool to call, when, and with what arguments.
  • Tool results are returned by OpenCode and resumed via Runner.resume_external_tools().

To start external-tools mode, run OpenCode with the generated opencode.json; the provider spawns nonoka-cli --server automatically.

MCP and Skill support

nonoka-cli can merge MCP tools and lazy-loaded skills alongside OpenCode's native tools. Configure them in ~/.config/nonoka/config.yaml:

model: deepseek-chat

mcp_servers:
  filesystem:
    transport: stdio
    command: npx
    args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/docs"]

skills:
  - code-review
  - nextjs-best-practices
  • MCP tools are executed locally by nonoka-cli and are exposed with a mcp__<server>__<tool> namespace prefix so they do not collide with OpenCode native tools.
  • Skills use nonoka-agent's lazy SkillRegistry. Only names and descriptions are injected into the system prompt; full guidance is loaded on-demand via the load_skill tool. Skill tools are prefixed with skill__<skill>__<tool> in external-tools mode.

Both MCP tools and skill tools remain available in standalone mode without any prefixing.

Skill tool import paths

A skill file lists its tools with an import entry in YAML frontmatter:

---
name: greet
description: A simple greeting skill.
tools:
  - import: greet_tool:say_hello
---
When loaded, use the say_hello tool to greet the user by name.

greet_tool:say_hello is resolved by Python's normal import machinery, so the module must be importable from the project working directory (or from a directory on PYTHONPATH). Place the tool module next to your skill file or add the skill source directory to PYTHONPATH if you use a nested layout.

Avoiding OpenCode's native skill tool

OpenCode has its own skill:<name> syntax that conflicts with nonoka's skill__<name>__<tool> namespace and load_skill tool. The generated opencode.json disables the native skill tool with "tools": {"skill": false}. If you hand-write opencode.json, keep that setting so the model only uses nonoka-managed skills.

Git safety net

nonoka-cli can automatically create a git checkpoint before each file change and roll back on failure. Enable it in nonoka.yaml:

git:
  auto_checkpoint: true
  auto_commit: true

When enabled, the model can call git_checkpoint before dangerous operations and git_rollback to restore the last checkpoint if something goes wrong.

Repo map

For large repositories, nonoka-cli builds a lightweight symbol index and injects a repo map into the system prompt. This reduces blind file reads by giving the model a structural overview of classes, functions, and exports.

Configure it in nonoka.yaml:

repo_map:
  enabled: true
  max_tokens: 4000

The build_repo_map and search_repo_map tools let the model refresh or query the index on demand.

Sub-agent workflow

nonoka-cli can optionally expose two sub-agent tools that the main agent can call for complex tasks:

  • plan_task — delegates planning to a dedicated planner agent and returns a numbered, file-level execution plan.
  • review_changes — delegates final review to a dedicated reviewer agent and returns a structured review with issues, suggestions, and an approval flag.

Both are disabled by default. Enable them by setting their model in nonoka.yaml:

model: deepseek-chat
max_turns: 20

agents:
  planner:
    model: deepseek-chat
    system_prompt: "You are a planning agent..."
  reviewer:
    model: deepseek-chat
    system_prompt: "You are a senior code reviewer..."

max_turns at the top level controls the main executor agent. The planner/reviewer roles each have their own max_turns inside agents.<role>.

When enabled, these tools are injected into the main agent's tool list alongside OpenCode's native tools. The main agent decides when to call them; the planner/reviewer run inside their own short-lived nonoka agent invocation and return their results as tool output.

review_changes accepts an optional files argument. When the main agent passes file paths, the reviewer reads those files and prepends their contents to the review context automatically:

# In the conversation the model can call:
# review_changes({
#   "task": "Review the changes against the goal: add logging",
#   "context": "<diff or summary>",
#   "files": ["src/main.py", "src/utils.py"]
# })

Note: plan_task and review_changes used to live in nonoka-agent. They have been moved to nonoka-cli so that sub-agent configuration (model, system prompt, max turns) is controlled by the CLI config and can use different models from the main agent.

Plugin manifest

Projects can declare their own Nonoka plugins via .nonoka/plugin.json:

{
  "name": "my-plugin",
  "skills": ["code-review"],
  "agents": {
    "planner": { "system_prompt": "..." }
  },
  "mcpServers": {},
  "allowedTools": ["read", "edit", "bash"]
}

The manifest is merged with user-level config and converted into OpenCode's skill/permission format when nonoka-cli opencode init runs. See .nonoka/plugin.json.example for a full example.

Known limitations

These are current behaviors observed with OpenCode CLI 1.17.18. They are tracked here because they affect the TUI/HITL experience but cannot be fixed inside nonoka-cli or nonoka-opencode-provider.

  • OpenCode native skill tool conflicts with nonoka skills: the generated opencode.json disables it with "tools": {"skill": false}, and the adapter prompt tells the model to use only load_skill and skill__<name>__<tool>.
  • External directory rejection crashes OpenCode: the adapter now injects the current working directory into the system prompt and instructs the model to use paths relative to it, so requests outside the workspace are rare. If one still occurs and you select Reject, OpenCode may still exit. Keep requests scoped to the current working directory, or approve if the path is safe.
  • write is auto-approved inside the workspace: even with "*": "ask" in opencode.json, OpenCode does not show an approval dialog for write operations within the workspace root. bash, read, and edit do ask.
  • Code blocks render as plain indented text: OpenCode renders Python and other code as plain indented output rather than fenced code blocks with syntax highlighting. This is an OpenCode TUI rendering choice.
  • Short replies leave empty vertical space: the OpenCode TUI uses a flex layout, so short assistant replies appear at the top with visible empty space above the status bar. This is normal OpenCode layout behavior.
  • Model may skip tools for ambiguous requests: the adapter prompt mitigates this, but a vague request can still cause the model to answer directly instead of calling read/edit. Make file/tool requests explicit.

Server logs and request traces

When running inside OpenCode, the provider spawns nonoka-cli --server as a long-lived NDJSON bridge. Server stderr is redirected by the provider to a per-working-directory log file so it does not pollute OpenCode's TUI:

/tmp/nonoka-server-<cwd-hash>.log

In addition, nonoka-cli --server writes a structured NDJSON trace of every request and stream event for debugging:

/tmp/nonoka-trace/trace-YYYYMMDD.jsonl

You can override the trace directory with the NONOKA_TRACE_DIR environment variable.

Debug environment variables

Variable Effect
NONOKA_DEBUG=1 Emit debug NDJSON events from the bridge for every request and stream transition.
NONOKA_TRACE_DIR=/path Directory for NDJSON request/event traces (default: /tmp/nonoka-trace).
NONOKA_SERVER_LOG=/path Override the server stderr log path when running the bridge manually.

Development

# Install in editable mode
uv pip install -e .

# Run the bridge server
nonoka-cli --server --config ./nonoka.yaml

# Lint and test
uv run --no-sync ruff check .
uv run --no-sync pytest tests/unit

Project layout

src/nonoka_cli/
├── bridge/          # NDJSON protocol, request handler, server
├── commands/        # CLI subcommands (config, doctor, opencode)
├── config/          # YAML config loading and Pydantic models
├── core/            # Orchestrator, RunnerService, SessionService, ToolService,
│                    # MCPService, AgentFactory, prompt/context/task-state/output pruning,
│                    # git safety net, repo map, planning, and plugin manifest
│                    #   agent_factory.py              # Build nonoka Agent from CLI config
│                    #   prompt_builder.py             # System prompt assembly for OpenCode mode
│                    #   context_trimmer.py            # Turn-based context window trimming
│                    #   task_state.py                 # Local TODO state mirror
│                    #   tool_output_policy.py         # Tool output pruning / spill policy
│                    #   git_service.py                # Git checkpoint / rollback helpers
│                    #   repo_map_service.py           # Symbol index generation and search
│                    #   planning_service.py           # Planner sub-agent (AgentTool)
│                    #   review_service.py             # Reviewer sub-agent (AgentTool)
│                    #   plugin_manifest.py            # .nonoka/plugin.json loader
│                    #   plugin_manifest_converter.py  # OpenCode skill/permission conversion
├── mcp/             # MCP server lifecycle manager (thin wrapper around nonoka-agent)
├── sessions/        # Session metadata persistence
├── skills/          # Skill loading shim (delegates to nonoka-agent SkillRegistry)
├── tools/           # Built-in and local tool loader
└── utils/           # Errors, logging, trace logger

packages/nonoka-opencode-provider/  # TypeScript provider for OpenCode
install.sh                          # One-line installer

License and attribution

nonoka-cli and nonoka-opencode-provider are released under the MIT License.

The terminal TUI and OpenCode client/server architecture are provided by OpenCode (MIT License). The agent core is provided by the Nonoka framework.

See LICENSE and NOTICE for full details.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nonoka_cli-0.2.6.tar.gz (353.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nonoka_cli-0.2.6-py3-none-any.whl (96.6 kB view details)

Uploaded Python 3

File details

Details for the file nonoka_cli-0.2.6.tar.gz.

File metadata

  • Download URL: nonoka_cli-0.2.6.tar.gz
  • Upload date:
  • Size: 353.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for nonoka_cli-0.2.6.tar.gz
Algorithm Hash digest
SHA256 45e1903d4c6c22a6b7cc9ee20f29064d365cdf6e654f48ede371b924a3bfb06d
MD5 4598d3e0341fb9bb635715b9b3ea4f70
BLAKE2b-256 fc8939c43066f0efdd50eca7bb9274dc850836e0c717d27fb2af637a9ec68ab0

See more details on using hashes here.

File details

Details for the file nonoka_cli-0.2.6-py3-none-any.whl.

File metadata

  • Download URL: nonoka_cli-0.2.6-py3-none-any.whl
  • Upload date:
  • Size: 96.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for nonoka_cli-0.2.6-py3-none-any.whl
Algorithm Hash digest
SHA256 c923e1f6d5c32e8a7dad1720b4568b5f7898bc9b321df7d2829d0ad379829b99
MD5 2c542cbf6aa2856e1bb0f0a41da9ce85
BLAKE2b-256 77ef14bc9dd8276e520c23fe92710e1d930800d42552c9ba4ad668ee5ff43e4e

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.33

2 files

0.2.32

2 files

0.2.31

2 files

0.2.30

2 files

0.2.29

2 files

0.2.28

2 files

0.2.27

2 files

0.2.26

2 files

0.2.25

2 files

0.2.24

2 files

0.2.23

2 files

0.2.22

2 files

0.2.21

2 files

0.2.20

2 files

0.2.19

2 files

0.2.18

2 files

0.2.17

2 files

0.2.16

2 files

0.2.15

2 files

0.2.14

2 files

0.2.13

2 files

0.2.12

2 files

0.2.11

2 files

0.2.10

2 files

0.2.7

1 file

This release

0.2.6 This release

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page