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.
Files changed (125) hide show
  1. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/PKG-INFO +41 -30
  2. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/README.md +39 -28
  3. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/pyproject.toml +2 -2
  4. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/__init__.py +1 -1
  5. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/cli.py +25 -15
  6. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/doctor_cmd.py +168 -26
  7. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/logs_cmd.py +3 -1
  8. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/opencode_cmd.py +145 -61
  9. nonoka_cli-0.2.12/src/nonoka_cli/commands/run_cmd.py +259 -0
  10. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/config/loader.py +30 -13
  11. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/config/models.py +2 -1
  12. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/agent_factory.py +24 -4
  13. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/operational_signals.py +51 -17
  14. nonoka_cli-0.2.12/src/nonoka_cli/safety/__init__.py +20 -0
  15. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/safety/preflight.py +13 -0
  16. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/tools/builtins/file_tools.py +12 -2
  17. nonoka_cli-0.2.12/tests/unit/commands/test_cli_entrypoint.py +42 -0
  18. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_doctor_cmd.py +66 -10
  19. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_logs_cmd.py +21 -0
  20. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_opencode_cmd.py +81 -4
  21. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_run_cmd.py +87 -4
  22. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/config/test_loader.py +15 -0
  23. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/config/test_models.py +11 -0
  24. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_agent_factory.py +27 -0
  25. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_operational_signals.py +97 -0
  26. nonoka_cli-0.2.12/tests/unit/safety/test_preflight.py +27 -0
  27. nonoka_cli-0.2.12/tests/unit/tools/test_file_tools.py +120 -0
  28. nonoka_cli-0.2.10/src/nonoka_cli/commands/run_cmd.py +0 -133
  29. nonoka_cli-0.2.10/src/nonoka_cli/safety/__init__.py +0 -6
  30. nonoka_cli-0.2.10/tests/unit/safety/test_preflight.py +0 -14
  31. nonoka_cli-0.2.10/tests/unit/tools/test_file_tools.py +0 -60
  32. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/.gitignore +0 -0
  33. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/LICENSE +0 -0
  34. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/NOTICE +0 -0
  35. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/__main__.py +0 -0
  36. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/benchmark/__init__.py +0 -0
  37. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/benchmark/harbor.py +0 -0
  38. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/benchmark/swe_bench.py +0 -0
  39. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/benchmark/swe_environment.py +0 -0
  40. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/benchmark/swe_runner.py +0 -0
  41. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/benchmark/watchdog.py +0 -0
  42. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/bridge/__init__.py +0 -0
  43. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/bridge/events.py +0 -0
  44. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/bridge/handler.py +0 -0
  45. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/bridge/nonoka_tools.py +0 -0
  46. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/bridge/protocol.py +0 -0
  47. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/bridge/server.py +0 -0
  48. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/__init__.py +0 -0
  49. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/benchmark_cmd.py +0 -0
  50. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/config_cmd.py +0 -0
  51. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/eval_cmd.py +0 -0
  52. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/plugin_cmd.py +0 -0
  53. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/commands/sessions_cmd.py +0 -0
  54. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/config/__init__.py +0 -0
  55. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/config/manager.py +0 -0
  56. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/__init__.py +0 -0
  57. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/context.py +0 -0
  58. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/context_trimmer.py +0 -0
  59. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/git_service.py +0 -0
  60. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/mcp_service.py +0 -0
  61. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/namespaces.py +0 -0
  62. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/orchestrator.py +0 -0
  63. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/plugin_manifest.py +0 -0
  64. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/plugin_manifest_converter.py +0 -0
  65. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/project_agents.py +0 -0
  66. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/prompt_builder.py +0 -0
  67. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/repo_map_service.py +0 -0
  68. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/run_evidence.py +0 -0
  69. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/runner_service.py +0 -0
  70. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/scorecard.py +0 -0
  71. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/semantic_cache.py +0 -0
  72. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/session_service.py +0 -0
  73. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/task_state.py +0 -0
  74. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/tool_output_policy.py +0 -0
  75. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/core/tool_service.py +0 -0
  76. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/mcp/__init__.py +0 -0
  77. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/mcp/manager.py +0 -0
  78. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/mcp/models.py +0 -0
  79. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/safety/sandbox.py +0 -0
  80. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/sessions/__init__.py +0 -0
  81. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/sessions/manager.py +0 -0
  82. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/sessions/models.py +0 -0
  83. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/skills/__init__.py +0 -0
  84. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/tools/__init__.py +0 -0
  85. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/tools/builtins/__init__.py +0 -0
  86. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/tools/builtins/agent_tools.py +0 -0
  87. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/tools/loader.py +0 -0
  88. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/utils/__init__.py +0 -0
  89. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/utils/errors.py +0 -0
  90. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/utils/logging.py +0 -0
  91. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/src/nonoka_cli/utils/trace_logger.py +0 -0
  92. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/conftest.py +0 -0
  93. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/fixtures/bridge_external_capabilities.json +0 -0
  94. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/integration/test_server_external_capabilities.py +0 -0
  95. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/integration/test_watchdog_process_group.py +0 -0
  96. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/benchmark/test_harbor.py +0 -0
  97. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/benchmark/test_swe_environment.py +0 -0
  98. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/benchmark/test_watchdog.py +0 -0
  99. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/bridge/test_events.py +0 -0
  100. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/bridge/test_handler.py +0 -0
  101. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/bridge/test_nonoka_tools.py +0 -0
  102. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/bridge/test_package.py +0 -0
  103. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/bridge/test_protocol.py +0 -0
  104. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/bridge/test_server.py +0 -0
  105. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_benchmark_cmd.py +0 -0
  106. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_config_cmd.py +0 -0
  107. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_eval_cmd.py +0 -0
  108. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_plugin_cmd.py +0 -0
  109. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/commands/test_swe_bench_cmd.py +0 -0
  110. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_context_trimmer.py +0 -0
  111. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_git_service.py +0 -0
  112. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_orchestrator.py +0 -0
  113. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_plugin_manifest.py +0 -0
  114. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_plugin_manifest_converter.py +0 -0
  115. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_project_agents.py +0 -0
  116. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_prompt_builder.py +0 -0
  117. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_repo_map_service.py +0 -0
  118. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_safety_policy.py +0 -0
  119. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_scorecard.py +0 -0
  120. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_semantic_cache.py +0 -0
  121. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_task_state.py +0 -0
  122. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/core/test_tool_output_policy.py +0 -0
  123. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/sessions/test_manager.py +0 -0
  124. {nonoka_cli-0.2.10 → nonoka_cli-0.2.12}/tests/unit/utils/test_logging.py +0 -0
  125. {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.10
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.7
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
- `nonoka-cli` runs as a stdio NDJSON bridge server (`nonoka-cli --server`) that
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-cli config init
60
+ nonoka config init
61
61
 
62
62
  # Or set it manually
63
63
  export DEEPSEEK_API_KEY=<your-key>
64
64
 
65
- nonoka-cli doctor
65
+ nonoka doctor
66
66
  nonoka
67
67
  ```
68
68
 
69
- `nonoka` is a shortcut for `nonoka-cli run` and starts the OpenCode TUI with the
70
- Nonoka backend. You can also use `nonoka run --message "<task>"` for one-shot
71
- CLI usage.
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-cli config init
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-cli config init --yes --model deepseek/deepseek-v4-pro
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-cli opencode init
111
+ nonoka init
112
112
 
113
113
  # User-level
114
- nonoka-cli opencode init --global
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.10
135
+ ✓ nonoka-cli 0.2.12
136
136
  ✓ Python 3.11
137
137
  ✓ opencode 1.18.2
138
- ✓ provider nonoka-opencode-provider@0.2.16
139
- ✓ nonoka framework 1.3.7
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-cli opencode init`
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-cli opencode init` generates two things:
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": ["bash", "-c", "nonoka-cli --server 2>/tmp/nonoka-server.log"],
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-cli opencode init` writes this automatically.
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-cli opencode init`, the
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-cli`'s own `cli.auto_approve` and `hitl.policy` settings only apply to
486
- standalone server / CLI mode. In OpenCode mode, permissions are governed by the
487
- `permission` block in `opencode.json`. To keep `nonoka.yaml` as the single
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` still auto-allows the core coding tools when no
499
- `permissions` block is present. For standalone mode, use `hitl.policy: auto`.
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 `nonoka-cli --server` automatically.
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 alongside OpenCode's
519
- native tools. Configure them in `~/.config/nonoka/config.yaml`:
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 `nonoka-cli --server` as a
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
- `nonoka-cli` runs as a stdio NDJSON bridge server (`nonoka-cli --server`) that
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-cli config init
31
+ nonoka config init
32
32
 
33
33
  # Or set it manually
34
34
  export DEEPSEEK_API_KEY=<your-key>
35
35
 
36
- nonoka-cli doctor
36
+ nonoka doctor
37
37
  nonoka
38
38
  ```
39
39
 
40
- `nonoka` is a shortcut for `nonoka-cli run` and starts the OpenCode TUI with the
41
- Nonoka backend. You can also use `nonoka run --message "<task>"` for one-shot
42
- CLI usage.
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-cli config init
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-cli config init --yes --model deepseek/deepseek-v4-pro
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-cli opencode init
82
+ nonoka init
83
83
 
84
84
  # User-level
85
- nonoka-cli opencode init --global
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.10
106
+ ✓ nonoka-cli 0.2.12
107
107
  ✓ Python 3.11
108
108
  ✓ opencode 1.18.2
109
- ✓ provider nonoka-opencode-provider@0.2.16
110
- ✓ nonoka framework 1.3.7
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-cli opencode init`
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-cli opencode init` generates two things:
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": ["bash", "-c", "nonoka-cli --server 2>/tmp/nonoka-server.log"],
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-cli opencode init` writes this automatically.
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-cli opencode init`, the
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-cli`'s own `cli.auto_approve` and `hitl.policy` settings only apply to
457
- standalone server / CLI mode. In OpenCode mode, permissions are governed by the
458
- `permission` block in `opencode.json`. To keep `nonoka.yaml` as the single
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` still auto-allows the core coding tools when no
470
- `permissions` block is present. For standalone mode, use `hitl.policy: auto`.
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 `nonoka-cli --server` automatically.
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 alongside OpenCode's
490
- native tools. Configure them in `~/.config/nonoka/config.yaml`:
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 `nonoka-cli --server` as a
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.10"
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.7",
23
+ "nonoka>=1.3.8",
24
24
  "structlog>=24.0.0",
25
25
  "pydantic>=2.0",
26
26
  "pyyaml>=6.0",
@@ -2,4 +2,4 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "0.2.10"
5
+ __version__ = "0.2.12"
@@ -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-cli",
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
- # Load .env files early so configuration and LLM providers see API keys.
120
- _load_env_files()
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