nonoka-cli 0.2.10__tar.gz → 0.2.12__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/PKG-INFO +41 -30
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/README.md +39 -28
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/pyproject.toml +2 -2
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/__init__.py +1 -1
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/cli.py +25 -15
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/doctor_cmd.py +168 -26
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/logs_cmd.py +3 -1
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/opencode_cmd.py +145 -61
- nonoka_cli-0.2.12/src/nonoka_cli/commands/run_cmd.py +259 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/config/loader.py +30 -13
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/config/models.py +2 -1
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/agent_factory.py +24 -4
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/operational_signals.py +51 -17
- nonoka_cli-0.2.12/src/nonoka_cli/safety/__init__.py +20 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/safety/preflight.py +13 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/tools/builtins/file_tools.py +12 -2
- nonoka_cli-0.2.12/tests/unit/commands/test_cli_entrypoint.py +42 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_doctor_cmd.py +66 -10
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_logs_cmd.py +21 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_opencode_cmd.py +81 -4
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_run_cmd.py +87 -4
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/config/test_loader.py +15 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/config/test_models.py +11 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_agent_factory.py +27 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_operational_signals.py +97 -0
- nonoka_cli-0.2.12/tests/unit/safety/test_preflight.py +27 -0
- nonoka_cli-0.2.12/tests/unit/tools/test_file_tools.py +120 -0
- nonoka_cli-0.2.10/src/nonoka_cli/commands/run_cmd.py +0 -133
- nonoka_cli-0.2.10/src/nonoka_cli/safety/__init__.py +0 -6
- nonoka_cli-0.2.10/tests/unit/safety/test_preflight.py +0 -14
- nonoka_cli-0.2.10/tests/unit/tools/test_file_tools.py +0 -60
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/.gitignore +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/LICENSE +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/NOTICE +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/__main__.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/benchmark/__init__.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/benchmark/harbor.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/benchmark/swe_bench.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/benchmark/swe_environment.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/benchmark/swe_runner.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/benchmark/watchdog.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/bridge/__init__.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/bridge/events.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/bridge/handler.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/bridge/nonoka_tools.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/bridge/protocol.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/bridge/server.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/__init__.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/benchmark_cmd.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/config_cmd.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/eval_cmd.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/plugin_cmd.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/sessions_cmd.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/config/__init__.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/config/manager.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/__init__.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/context.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/context_trimmer.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/git_service.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/mcp_service.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/namespaces.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/orchestrator.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/plugin_manifest.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/plugin_manifest_converter.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/project_agents.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/prompt_builder.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/repo_map_service.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/run_evidence.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/runner_service.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/scorecard.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/semantic_cache.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/session_service.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/task_state.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/tool_output_policy.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/tool_service.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/mcp/__init__.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/mcp/manager.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/mcp/models.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/safety/sandbox.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/sessions/__init__.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/sessions/manager.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/sessions/models.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/skills/__init__.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/tools/__init__.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/tools/builtins/__init__.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/tools/builtins/agent_tools.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/tools/loader.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/utils/__init__.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/utils/errors.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/utils/logging.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/utils/trace_logger.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/conftest.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/fixtures/bridge_external_capabilities.json +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/integration/test_server_external_capabilities.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/integration/test_watchdog_process_group.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/benchmark/test_harbor.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/benchmark/test_swe_environment.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/benchmark/test_watchdog.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/bridge/test_events.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/bridge/test_handler.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/bridge/test_nonoka_tools.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/bridge/test_package.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/bridge/test_protocol.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/bridge/test_server.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_benchmark_cmd.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_config_cmd.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_eval_cmd.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_plugin_cmd.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_swe_bench_cmd.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_context_trimmer.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_git_service.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_orchestrator.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_plugin_manifest.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_plugin_manifest_converter.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_project_agents.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_prompt_builder.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_repo_map_service.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_safety_policy.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_scorecard.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_semantic_cache.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_task_state.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_tool_output_policy.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/sessions/test_manager.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/utils/test_logging.py +0 -0
- {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/utils/test_trace_logger.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nonoka-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.12
|
|
4
4
|
Summary: OpenCode backend for the Nonoka Agent framework
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
16
|
Requires-Python: >=3.10
|
|
17
|
-
Requires-Dist: nonoka>=1.3.
|
|
17
|
+
Requires-Dist: nonoka>=1.3.8
|
|
18
18
|
Requires-Dist: pydantic>=2.0
|
|
19
19
|
Requires-Dist: python-dotenv>=1.0
|
|
20
20
|
Requires-Dist: pyyaml>=6.0
|
|
@@ -33,7 +33,7 @@ English | [简体中文](README.zh-CN.md)
|
|
|
33
33
|
|
|
34
34
|
OpenCode backend for the [Nonoka](https://pypi.org/project/nonoka/) Agent framework.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Nonoka runs as a stdio NDJSON bridge server (`python -m nonoka_cli --server`) that
|
|
37
37
|
talks to the `nonoka-opencode-provider` TypeScript package. When used inside
|
|
38
38
|
OpenCode, Nonoka acts as the conversation/decision backend while OpenCode owns
|
|
39
39
|
tool execution and human-in-the-loop (HITL) approval using its native tools.
|
|
@@ -57,18 +57,18 @@ After installing, configure your API key and run `nonoka`:
|
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
# Interactive: it will ask for your key and save it to ~/.config/nonoka/.env
|
|
60
|
-
nonoka
|
|
60
|
+
nonoka config init
|
|
61
61
|
|
|
62
62
|
# Or set it manually
|
|
63
63
|
export DEEPSEEK_API_KEY=<your-key>
|
|
64
64
|
|
|
65
|
-
nonoka
|
|
65
|
+
nonoka doctor
|
|
66
66
|
nonoka
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
`nonoka` is
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
`nonoka` is the primary command. It starts the OpenCode TUI when invoked without
|
|
70
|
+
a subcommand, while `nonoka run --message "<task>"` provides one-shot CLI usage.
|
|
71
|
+
The legacy `nonoka-cli` executable remains available for compatibility.
|
|
72
72
|
|
|
73
73
|
`nonoka-cli` automatically loads `~/.config/nonoka/.env` and `./.env` on startup,
|
|
74
74
|
so you don't need to `export` every time if you save the key in `.env`.
|
|
@@ -94,24 +94,24 @@ npm install -g nonoka-opencode-provider
|
|
|
94
94
|
`~/.config/nonoka/.env`):
|
|
95
95
|
|
|
96
96
|
```bash
|
|
97
|
-
nonoka
|
|
97
|
+
nonoka config init
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
For scripted setups, use the non-interactive mode (you'll still need to set the
|
|
101
101
|
API key via `.env` or `export`):
|
|
102
102
|
|
|
103
103
|
```bash
|
|
104
|
-
nonoka
|
|
104
|
+
nonoka config init --yes --model deepseek/deepseek-v4-pro
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
2. Generate an OpenCode config in the current project or globally:
|
|
108
108
|
|
|
109
109
|
```bash
|
|
110
110
|
# Project-level
|
|
111
|
-
nonoka
|
|
111
|
+
nonoka init
|
|
112
112
|
|
|
113
113
|
# User-level
|
|
114
|
-
nonoka
|
|
114
|
+
nonoka init --global
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
3. Make sure your model API key is exported, then run:
|
|
@@ -132,11 +132,11 @@ Example output:
|
|
|
132
132
|
|
|
133
133
|
```
|
|
134
134
|
nonoka-cli doctor
|
|
135
|
-
✓ nonoka-cli 0.2.
|
|
135
|
+
✓ nonoka-cli 0.2.12
|
|
136
136
|
✓ Python 3.11
|
|
137
137
|
✓ opencode 1.18.2
|
|
138
|
-
✓ provider nonoka-opencode-provider@0.2.
|
|
139
|
-
✓ nonoka framework 1.3.
|
|
138
|
+
✓ provider nonoka-opencode-provider@0.2.17
|
|
139
|
+
✓ nonoka framework 1.3.8
|
|
140
140
|
✓ config ~/.config/nonoka/config.yaml
|
|
141
141
|
✓ API key DEEPSEEK_API_KEY set
|
|
142
142
|
✓ OpenCode provider config in /home/user/.config/opencode/opencode.json
|
|
@@ -403,7 +403,7 @@ similarity score without storing raw cache queries. `max_total_tokens` and
|
|
|
403
403
|
is unavailable, `fail_on_unknown_cost: true` terminates the task rather than
|
|
404
404
|
silently exceeding the cost budget.
|
|
405
405
|
|
|
406
|
-
### `nonoka
|
|
406
|
+
### `nonoka init`
|
|
407
407
|
|
|
408
408
|
Generate or merge an `opencode.json` in the current directory and create
|
|
409
409
|
`.opencode/agents/build.md` from your nonoka `system_prompt`. The generated
|
|
@@ -412,7 +412,8 @@ nonoka config path to the backend.
|
|
|
412
412
|
|
|
413
413
|
## OpenCode configuration
|
|
414
414
|
|
|
415
|
-
`nonoka
|
|
415
|
+
`nonoka init` generates two things. `nonoka opencode init` remains as a
|
|
416
|
+
backward-compatible, explicit spelling:
|
|
416
417
|
|
|
417
418
|
1. `opencode.json` in the current directory, which wires OpenCode to the
|
|
418
419
|
`nonoka-opencode-provider` package and sets HITL permissions.
|
|
@@ -429,7 +430,7 @@ A typical generated `opencode.json` looks like:
|
|
|
429
430
|
"npm": "nonoka-opencode-provider",
|
|
430
431
|
"name": "Nonoka",
|
|
431
432
|
"options": {
|
|
432
|
-
"serverCommand": ["
|
|
433
|
+
"serverCommand": ["/path/to/python", "-m", "nonoka_cli", "--server"],
|
|
433
434
|
"cwd": ".",
|
|
434
435
|
"configPath": "~/.config/nonoka/config.yaml"
|
|
435
436
|
},
|
|
@@ -463,12 +464,12 @@ A typical generated `opencode.json` looks like:
|
|
|
463
464
|
|
|
464
465
|
The `"tools": {"skill": false}` line disables OpenCode's native `skill:<name>`
|
|
465
466
|
tool so it does not collide with nonoka's `load_skill` / `skill__<name>__<tool>`
|
|
466
|
-
workflow. `nonoka
|
|
467
|
+
workflow. `nonoka init` writes this automatically.
|
|
467
468
|
|
|
468
469
|
## Prompt ownership
|
|
469
470
|
|
|
470
471
|
Nonoka owns the canonical system prompt via `system_prompt` in
|
|
471
|
-
`~/.config/nonoka/config.yaml`. When you run `nonoka
|
|
472
|
+
`~/.config/nonoka/config.yaml`. When you run `nonoka init`, the
|
|
472
473
|
command adapts that prompt and writes it to `.opencode/agents/build.md` so
|
|
473
474
|
OpenCode uses it for its primary agent. OpenCode-specific guidelines (tool
|
|
474
475
|
names, approval behavior, path conventions) are appended automatically; they are
|
|
@@ -482,10 +483,9 @@ When running inside OpenCode, HITL is handled by OpenCode itself. The generated
|
|
|
482
483
|
Nonoka forwards OpenCode's native tool definitions to the model, approval
|
|
483
484
|
dialogs render natively for `bash`, `read`, `write`, and `edit` operations.
|
|
484
485
|
|
|
485
|
-
`nonoka
|
|
486
|
-
|
|
487
|
-
`
|
|
488
|
-
source of truth, add a `permissions` block and re-run `nonoka-cli opencode init`:
|
|
486
|
+
In OpenCode mode, `nonoka init` derives both generated permission blocks from
|
|
487
|
+
`cli.auto_approve` and optional `permissions` overrides in `nonoka.yaml`. Add a
|
|
488
|
+
`permissions` block and re-run `nonoka init` to keep YAML as the source of truth:
|
|
489
489
|
|
|
490
490
|
```yaml
|
|
491
491
|
permissions:
|
|
@@ -495,8 +495,9 @@ permissions:
|
|
|
495
495
|
edit: ask
|
|
496
496
|
```
|
|
497
497
|
|
|
498
|
-
`cli.auto_approve: true`
|
|
499
|
-
`
|
|
498
|
+
`cli.auto_approve: true` auto-allows the core coding tools, including read-only
|
|
499
|
+
`glob` and `grep`, before explicit overrides are applied. For standalone mode,
|
|
500
|
+
`hitl.policy` still controls Nonoka-owned tool approval.
|
|
500
501
|
|
|
501
502
|
## External-tools mode
|
|
502
503
|
|
|
@@ -511,12 +512,14 @@ default. OpenCode sends its native tool list (e.g. `bash`, `read`, `write`,
|
|
|
511
512
|
`Runner.resume_external_tools()`.
|
|
512
513
|
|
|
513
514
|
To start external-tools mode, run OpenCode with the generated `opencode.json`;
|
|
514
|
-
the provider spawns `
|
|
515
|
+
the provider automatically spawns `python -m nonoka_cli --server` with the
|
|
516
|
+
interpreter that generated the project config.
|
|
515
517
|
|
|
516
518
|
## MCP and Skill support
|
|
517
519
|
|
|
518
|
-
nonoka-cli can merge MCP tools and lazy-loaded skills
|
|
519
|
-
native tools. Configure them in
|
|
520
|
+
nonoka-cli can merge custom Python tools, MCP tools, and lazy-loaded skills
|
|
521
|
+
alongside OpenCode's native tools. Configure them in
|
|
522
|
+
`~/.config/nonoka/config.yaml`:
|
|
520
523
|
|
|
521
524
|
```yaml
|
|
522
525
|
model: deepseek/deepseek-v4-pro
|
|
@@ -527,6 +530,9 @@ mcp_servers:
|
|
|
527
530
|
command: npx
|
|
528
531
|
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/docs"]
|
|
529
532
|
|
|
533
|
+
tool_paths:
|
|
534
|
+
- /home/user/.config/nonoka/tools
|
|
535
|
+
|
|
530
536
|
skills:
|
|
531
537
|
- code-review
|
|
532
538
|
- nextjs-best-practices
|
|
@@ -537,6 +543,10 @@ Store each skill at `.agents/skills/<name>/SKILL.md` in the project or at `~/.ag
|
|
|
537
543
|
- **MCP tools** are executed locally by nonoka-cli and are exposed with a
|
|
538
544
|
`mcp__<server>__<tool>` namespace prefix so they do not collide with OpenCode
|
|
539
545
|
native tools.
|
|
546
|
+
- **Custom Python tools** discovered from `tool_paths` are executed locally by
|
|
547
|
+
nonoka-cli and exposed as `custom__<tool>` in OpenCode mode. Built-in CLI
|
|
548
|
+
file/shell tools are omitted there because OpenCode already supplies native
|
|
549
|
+
equivalents. Standalone mode keeps the original unprefixed tool names.
|
|
540
550
|
- **Skills** use nonoka-agent's lazy `SkillRegistry`. Discovery reads names and descriptions without importing skill tools; enabled skill tools are resolved when the runtime catalog is built, while full guidance, its root directory, and bundled resource paths are loaded on-demand via `load_skill` and protected from normal context compaction. Skill tools are prefixed with `skill__<skill>__<tool>` in external-tools mode.
|
|
541
551
|
|
|
542
552
|
Both MCP tools and skill tools remain available in standalone mode without any
|
|
@@ -702,7 +712,8 @@ here because they affect the TUI/HITL experience but cannot be fixed inside
|
|
|
702
712
|
|
|
703
713
|
## Server logs and request traces
|
|
704
714
|
|
|
705
|
-
When running inside OpenCode, the provider spawns
|
|
715
|
+
When running inside OpenCode, the provider spawns the interpreter-pinned
|
|
716
|
+
`python -m nonoka_cli --server` command as a
|
|
706
717
|
long-lived NDJSON bridge. Server stderr is redirected by the provider to a
|
|
707
718
|
per-working-directory log file so it does not pollute OpenCode's TUI:
|
|
708
719
|
|
|
@@ -4,7 +4,7 @@ English | [简体中文](README.zh-CN.md)
|
|
|
4
4
|
|
|
5
5
|
OpenCode backend for the [Nonoka](https://pypi.org/project/nonoka/) Agent framework.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Nonoka runs as a stdio NDJSON bridge server (`python -m nonoka_cli --server`) that
|
|
8
8
|
talks to the `nonoka-opencode-provider` TypeScript package. When used inside
|
|
9
9
|
OpenCode, Nonoka acts as the conversation/decision backend while OpenCode owns
|
|
10
10
|
tool execution and human-in-the-loop (HITL) approval using its native tools.
|
|
@@ -28,18 +28,18 @@ After installing, configure your API key and run `nonoka`:
|
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
# Interactive: it will ask for your key and save it to ~/.config/nonoka/.env
|
|
31
|
-
nonoka
|
|
31
|
+
nonoka config init
|
|
32
32
|
|
|
33
33
|
# Or set it manually
|
|
34
34
|
export DEEPSEEK_API_KEY=<your-key>
|
|
35
35
|
|
|
36
|
-
nonoka
|
|
36
|
+
nonoka doctor
|
|
37
37
|
nonoka
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
`nonoka` is
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
`nonoka` is the primary command. It starts the OpenCode TUI when invoked without
|
|
41
|
+
a subcommand, while `nonoka run --message "<task>"` provides one-shot CLI usage.
|
|
42
|
+
The legacy `nonoka-cli` executable remains available for compatibility.
|
|
43
43
|
|
|
44
44
|
`nonoka-cli` automatically loads `~/.config/nonoka/.env` and `./.env` on startup,
|
|
45
45
|
so you don't need to `export` every time if you save the key in `.env`.
|
|
@@ -65,24 +65,24 @@ npm install -g nonoka-opencode-provider
|
|
|
65
65
|
`~/.config/nonoka/.env`):
|
|
66
66
|
|
|
67
67
|
```bash
|
|
68
|
-
nonoka
|
|
68
|
+
nonoka config init
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
For scripted setups, use the non-interactive mode (you'll still need to set the
|
|
72
72
|
API key via `.env` or `export`):
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
|
-
nonoka
|
|
75
|
+
nonoka config init --yes --model deepseek/deepseek-v4-pro
|
|
76
76
|
```
|
|
77
77
|
|
|
78
78
|
2. Generate an OpenCode config in the current project or globally:
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
81
|
# Project-level
|
|
82
|
-
nonoka
|
|
82
|
+
nonoka init
|
|
83
83
|
|
|
84
84
|
# User-level
|
|
85
|
-
nonoka
|
|
85
|
+
nonoka init --global
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
3. Make sure your model API key is exported, then run:
|
|
@@ -103,11 +103,11 @@ Example output:
|
|
|
103
103
|
|
|
104
104
|
```
|
|
105
105
|
nonoka-cli doctor
|
|
106
|
-
✓ nonoka-cli 0.2.
|
|
106
|
+
✓ nonoka-cli 0.2.12
|
|
107
107
|
✓ Python 3.11
|
|
108
108
|
✓ opencode 1.18.2
|
|
109
|
-
✓ provider nonoka-opencode-provider@0.2.
|
|
110
|
-
✓ nonoka framework 1.3.
|
|
109
|
+
✓ provider nonoka-opencode-provider@0.2.17
|
|
110
|
+
✓ nonoka framework 1.3.8
|
|
111
111
|
✓ config ~/.config/nonoka/config.yaml
|
|
112
112
|
✓ API key DEEPSEEK_API_KEY set
|
|
113
113
|
✓ OpenCode provider config in /home/user/.config/opencode/opencode.json
|
|
@@ -374,7 +374,7 @@ similarity score without storing raw cache queries. `max_total_tokens` and
|
|
|
374
374
|
is unavailable, `fail_on_unknown_cost: true` terminates the task rather than
|
|
375
375
|
silently exceeding the cost budget.
|
|
376
376
|
|
|
377
|
-
### `nonoka
|
|
377
|
+
### `nonoka init`
|
|
378
378
|
|
|
379
379
|
Generate or merge an `opencode.json` in the current directory and create
|
|
380
380
|
`.opencode/agents/build.md` from your nonoka `system_prompt`. The generated
|
|
@@ -383,7 +383,8 @@ nonoka config path to the backend.
|
|
|
383
383
|
|
|
384
384
|
## OpenCode configuration
|
|
385
385
|
|
|
386
|
-
`nonoka
|
|
386
|
+
`nonoka init` generates two things. `nonoka opencode init` remains as a
|
|
387
|
+
backward-compatible, explicit spelling:
|
|
387
388
|
|
|
388
389
|
1. `opencode.json` in the current directory, which wires OpenCode to the
|
|
389
390
|
`nonoka-opencode-provider` package and sets HITL permissions.
|
|
@@ -400,7 +401,7 @@ A typical generated `opencode.json` looks like:
|
|
|
400
401
|
"npm": "nonoka-opencode-provider",
|
|
401
402
|
"name": "Nonoka",
|
|
402
403
|
"options": {
|
|
403
|
-
"serverCommand": ["
|
|
404
|
+
"serverCommand": ["/path/to/python", "-m", "nonoka_cli", "--server"],
|
|
404
405
|
"cwd": ".",
|
|
405
406
|
"configPath": "~/.config/nonoka/config.yaml"
|
|
406
407
|
},
|
|
@@ -434,12 +435,12 @@ A typical generated `opencode.json` looks like:
|
|
|
434
435
|
|
|
435
436
|
The `"tools": {"skill": false}` line disables OpenCode's native `skill:<name>`
|
|
436
437
|
tool so it does not collide with nonoka's `load_skill` / `skill__<name>__<tool>`
|
|
437
|
-
workflow. `nonoka
|
|
438
|
+
workflow. `nonoka init` writes this automatically.
|
|
438
439
|
|
|
439
440
|
## Prompt ownership
|
|
440
441
|
|
|
441
442
|
Nonoka owns the canonical system prompt via `system_prompt` in
|
|
442
|
-
`~/.config/nonoka/config.yaml`. When you run `nonoka
|
|
443
|
+
`~/.config/nonoka/config.yaml`. When you run `nonoka init`, the
|
|
443
444
|
command adapts that prompt and writes it to `.opencode/agents/build.md` so
|
|
444
445
|
OpenCode uses it for its primary agent. OpenCode-specific guidelines (tool
|
|
445
446
|
names, approval behavior, path conventions) are appended automatically; they are
|
|
@@ -453,10 +454,9 @@ When running inside OpenCode, HITL is handled by OpenCode itself. The generated
|
|
|
453
454
|
Nonoka forwards OpenCode's native tool definitions to the model, approval
|
|
454
455
|
dialogs render natively for `bash`, `read`, `write`, and `edit` operations.
|
|
455
456
|
|
|
456
|
-
`nonoka
|
|
457
|
-
|
|
458
|
-
`
|
|
459
|
-
source of truth, add a `permissions` block and re-run `nonoka-cli opencode init`:
|
|
457
|
+
In OpenCode mode, `nonoka init` derives both generated permission blocks from
|
|
458
|
+
`cli.auto_approve` and optional `permissions` overrides in `nonoka.yaml`. Add a
|
|
459
|
+
`permissions` block and re-run `nonoka init` to keep YAML as the source of truth:
|
|
460
460
|
|
|
461
461
|
```yaml
|
|
462
462
|
permissions:
|
|
@@ -466,8 +466,9 @@ permissions:
|
|
|
466
466
|
edit: ask
|
|
467
467
|
```
|
|
468
468
|
|
|
469
|
-
`cli.auto_approve: true`
|
|
470
|
-
`
|
|
469
|
+
`cli.auto_approve: true` auto-allows the core coding tools, including read-only
|
|
470
|
+
`glob` and `grep`, before explicit overrides are applied. For standalone mode,
|
|
471
|
+
`hitl.policy` still controls Nonoka-owned tool approval.
|
|
471
472
|
|
|
472
473
|
## External-tools mode
|
|
473
474
|
|
|
@@ -482,12 +483,14 @@ default. OpenCode sends its native tool list (e.g. `bash`, `read`, `write`,
|
|
|
482
483
|
`Runner.resume_external_tools()`.
|
|
483
484
|
|
|
484
485
|
To start external-tools mode, run OpenCode with the generated `opencode.json`;
|
|
485
|
-
the provider spawns `
|
|
486
|
+
the provider automatically spawns `python -m nonoka_cli --server` with the
|
|
487
|
+
interpreter that generated the project config.
|
|
486
488
|
|
|
487
489
|
## MCP and Skill support
|
|
488
490
|
|
|
489
|
-
nonoka-cli can merge MCP tools and lazy-loaded skills
|
|
490
|
-
native tools. Configure them in
|
|
491
|
+
nonoka-cli can merge custom Python tools, MCP tools, and lazy-loaded skills
|
|
492
|
+
alongside OpenCode's native tools. Configure them in
|
|
493
|
+
`~/.config/nonoka/config.yaml`:
|
|
491
494
|
|
|
492
495
|
```yaml
|
|
493
496
|
model: deepseek/deepseek-v4-pro
|
|
@@ -498,6 +501,9 @@ mcp_servers:
|
|
|
498
501
|
command: npx
|
|
499
502
|
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/docs"]
|
|
500
503
|
|
|
504
|
+
tool_paths:
|
|
505
|
+
- /home/user/.config/nonoka/tools
|
|
506
|
+
|
|
501
507
|
skills:
|
|
502
508
|
- code-review
|
|
503
509
|
- nextjs-best-practices
|
|
@@ -508,6 +514,10 @@ Store each skill at `.agents/skills/<name>/SKILL.md` in the project or at `~/.ag
|
|
|
508
514
|
- **MCP tools** are executed locally by nonoka-cli and are exposed with a
|
|
509
515
|
`mcp__<server>__<tool>` namespace prefix so they do not collide with OpenCode
|
|
510
516
|
native tools.
|
|
517
|
+
- **Custom Python tools** discovered from `tool_paths` are executed locally by
|
|
518
|
+
nonoka-cli and exposed as `custom__<tool>` in OpenCode mode. Built-in CLI
|
|
519
|
+
file/shell tools are omitted there because OpenCode already supplies native
|
|
520
|
+
equivalents. Standalone mode keeps the original unprefixed tool names.
|
|
511
521
|
- **Skills** use nonoka-agent's lazy `SkillRegistry`. Discovery reads names and descriptions without importing skill tools; enabled skill tools are resolved when the runtime catalog is built, while full guidance, its root directory, and bundled resource paths are loaded on-demand via `load_skill` and protected from normal context compaction. Skill tools are prefixed with `skill__<skill>__<tool>` in external-tools mode.
|
|
512
522
|
|
|
513
523
|
Both MCP tools and skill tools remain available in standalone mode without any
|
|
@@ -673,7 +683,8 @@ here because they affect the TUI/HITL experience but cannot be fixed inside
|
|
|
673
683
|
|
|
674
684
|
## Server logs and request traces
|
|
675
685
|
|
|
676
|
-
When running inside OpenCode, the provider spawns
|
|
686
|
+
When running inside OpenCode, the provider spawns the interpreter-pinned
|
|
687
|
+
`python -m nonoka_cli --server` command as a
|
|
677
688
|
long-lived NDJSON bridge. Server stderr is redirected by the provider to a
|
|
678
689
|
per-working-directory log file so it does not pollute OpenCode's TUI:
|
|
679
690
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nonoka-cli"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.12"
|
|
8
8
|
description = "OpenCode backend for the Nonoka Agent framework"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -20,7 +20,7 @@ classifiers = [
|
|
|
20
20
|
"Programming Language :: Python :: 3.13",
|
|
21
21
|
]
|
|
22
22
|
dependencies = [
|
|
23
|
-
"nonoka>=1.3.
|
|
23
|
+
"nonoka>=1.3.8",
|
|
24
24
|
"structlog>=24.0.0",
|
|
25
25
|
"pydantic>=2.0",
|
|
26
26
|
"pyyaml>=6.0",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
+
import importlib.metadata
|
|
6
7
|
import logging
|
|
7
8
|
import os
|
|
8
9
|
import sys
|
|
@@ -42,6 +43,14 @@ from nonoka_cli.utils.logging import setup_logging
|
|
|
42
43
|
logger = structlog.get_logger("nonoka_cli.cli")
|
|
43
44
|
|
|
44
45
|
|
|
46
|
+
def _version() -> str:
|
|
47
|
+
"""Return the installed CLI version, including editable installs."""
|
|
48
|
+
try:
|
|
49
|
+
return importlib.metadata.version("nonoka-cli")
|
|
50
|
+
except importlib.metadata.PackageNotFoundError:
|
|
51
|
+
return "unknown"
|
|
52
|
+
|
|
53
|
+
|
|
45
54
|
def _load_env_files() -> None:
|
|
46
55
|
"""Load .env files so config env-var substitution works transparently.
|
|
47
56
|
|
|
@@ -61,9 +70,14 @@ def _load_env_files() -> None:
|
|
|
61
70
|
def _build_parser() -> argparse.ArgumentParser:
|
|
62
71
|
"""Build the argument parser."""
|
|
63
72
|
parser = argparse.ArgumentParser(
|
|
64
|
-
prog="nonoka
|
|
73
|
+
prog="nonoka",
|
|
65
74
|
description="Terminal frontend for the Nonoka Agent framework",
|
|
66
75
|
)
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"--version",
|
|
78
|
+
action="version",
|
|
79
|
+
version=f"%(prog)s {_version()}",
|
|
80
|
+
)
|
|
67
81
|
parser.add_argument(
|
|
68
82
|
"--config",
|
|
69
83
|
type=Path,
|
|
@@ -96,6 +110,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
96
110
|
subparsers = parser.add_subparsers(dest="command")
|
|
97
111
|
config_cmd.add_subparser(subparsers)
|
|
98
112
|
doctor_cmd.add_subparser(subparsers)
|
|
113
|
+
opencode_cmd.add_init_subparser(subparsers)
|
|
99
114
|
opencode_cmd.add_subparser(subparsers)
|
|
100
115
|
plugin_cmd.add_subparser(subparsers)
|
|
101
116
|
run_cmd.add_subparser(subparsers)
|
|
@@ -116,28 +131,23 @@ def main() -> int:
|
|
|
116
131
|
parser = _build_parser()
|
|
117
132
|
args = parser.parse_args()
|
|
118
133
|
|
|
119
|
-
|
|
120
|
-
|
|
134
|
+
log_level = (
|
|
135
|
+
logging.DEBUG if args.debug
|
|
136
|
+
else logging.INFO if args.verbose
|
|
137
|
+
else logging.WARNING
|
|
138
|
+
)
|
|
139
|
+
setup_logging(level=log_level, console=args.verbose or args.debug)
|
|
121
140
|
|
|
141
|
+
# Load .env files after logging is configured so the debug-only path message
|
|
142
|
+
# does not leak into normal command output.
|
|
143
|
+
_load_env_files()
|
|
122
144
|
if args.server:
|
|
123
145
|
return server_main(config_path=args.config, model=args.model)
|
|
124
146
|
|
|
125
147
|
if args.command and getattr(args, "func", None):
|
|
126
|
-
log_level = (
|
|
127
|
-
logging.DEBUG if args.debug
|
|
128
|
-
else logging.INFO if args.verbose
|
|
129
|
-
else logging.WARNING
|
|
130
|
-
)
|
|
131
|
-
setup_logging(level=log_level, console=args.verbose or args.debug)
|
|
132
148
|
return args.func(args)
|
|
133
149
|
|
|
134
150
|
# No subcommand given: launch the OpenCode TUI by default.
|
|
135
|
-
log_level = (
|
|
136
|
-
logging.DEBUG if args.debug
|
|
137
|
-
else logging.INFO if args.verbose
|
|
138
|
-
else logging.WARNING
|
|
139
|
-
)
|
|
140
|
-
setup_logging(level=log_level, console=args.verbose or args.debug)
|
|
141
151
|
return run_cmd.launch_tui(args)
|
|
142
152
|
|
|
143
153
|
|