nonoka-cli 0.2.4__tar.gz → 0.2.6__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.4 → nonoka_cli-0.2.6}/.gitignore +4 -1
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/PKG-INFO +11 -7
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/README.md +10 -6
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/pyproject.toml +2 -1
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/bridge/server.py +1 -3
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/cli.py +4 -5
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/commands/opencode_cmd.py +3 -2
- nonoka_cli-0.2.6/src/nonoka_cli/commands/run_cmd.py +110 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/config/models.py +3 -9
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/agent_factory.py +6 -22
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/orchestrator.py +1 -26
- nonoka_cli-0.2.6/src/nonoka_cli/core/planning_service.py +89 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/prompt_builder.py +4 -13
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/tools/builtins/agent_tools.py +3 -3
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/commands/test_opencode_cmd.py +0 -25
- nonoka_cli-0.2.6/tests/unit/commands/test_run_cmd.py +113 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/config/test_models.py +0 -27
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/core/test_agent_factory.py +0 -46
- nonoka_cli-0.2.6/tests/unit/core/test_orchestrator.py +98 -0
- nonoka_cli-0.2.6/tests/unit/core/test_planning_service.py +40 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/uv.lock +214 -9
- nonoka_cli-0.2.4/.agents/skills/nonoka-tui-testing/SKILL.md +0 -413
- nonoka_cli-0.2.4/.agents/skills/nonoka-tui-testing/references/checklist.md +0 -48
- nonoka_cli-0.2.4/.agents/skills/nonoka-tui-testing/scripts/analyze-timeline.py +0 -100
- nonoka_cli-0.2.4/.agents/skills/nonoka-tui-testing/scripts/capture-screenshots.py +0 -191
- nonoka_cli-0.2.4/.agents/skills/nonoka-tui-testing/scripts/setup-sandbox.sh +0 -237
- nonoka_cli-0.2.4/.agents/skills/tui-testing/SKILL.md +0 -60
- nonoka_cli-0.2.4/.agents/skills/tui-testing/scripts/opencode_tui.py +0 -288
- nonoka_cli-0.2.4/.nonoka/repo_map.jsonl +0 -84
- nonoka_cli-0.2.4/docs/external-mcp-skill-design.md +0 -542
- nonoka_cli-0.2.4/docs/nonoka-cli-complex-task-evaluation.md +0 -254
- nonoka_cli-0.2.4/docs/nonoka-cli-refactor-design.md +0 -735
- nonoka_cli-0.2.4/docs/subagent-refactor-design.md +0 -162
- nonoka_cli-0.2.4/docs/subagent-refactor-plan.md +0 -134
- nonoka_cli-0.2.4/src/nonoka_cli/core/planning_service.py +0 -192
- nonoka_cli-0.2.4/src/nonoka_cli/core/review_service.py +0 -293
- nonoka_cli-0.2.4/src/nonoka_cli/core/subagent_service.py +0 -184
- nonoka_cli-0.2.4/tests/unit/core/test_orchestrator.py +0 -205
- nonoka_cli-0.2.4/tests/unit/core/test_planning_service.py +0 -174
- nonoka_cli-0.2.4/tests/unit/core/test_review_service.py +0 -251
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/.nonoka/plugin.json.example +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/LICENSE +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/NOTICE +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/TODO.md +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/install.sh +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/nonoka.yaml +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/nonoka.yaml.example +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/packages/nonoka-opencode-provider/.gitignore +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/packages/nonoka-opencode-provider/LICENSE +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/packages/nonoka-opencode-provider/README.md +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/packages/nonoka-opencode-provider/bun.lock +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/packages/nonoka-opencode-provider/package.json +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/packages/nonoka-opencode-provider/src/index.ts +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/packages/nonoka-opencode-provider/src/nonoka-language-model.ts +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/packages/nonoka-opencode-provider/src/protocol.ts +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/packages/nonoka-opencode-provider/src/stream.ts +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/packages/nonoka-opencode-provider/tests/language-model.test.ts +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/packages/nonoka-opencode-provider/tests/protocol.test.ts +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/packages/nonoka-opencode-provider/tests/stream.test.ts +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/packages/nonoka-opencode-provider/tsconfig.json +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/scripts/quick-install.sh +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/__init__.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/__main__.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/bridge/__init__.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/bridge/events.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/bridge/handler.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/bridge/protocol.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/commands/__init__.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/commands/config_cmd.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/commands/doctor_cmd.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/commands/plugin_cmd.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/config/__init__.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/config/loader.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/config/manager.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/__init__.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/context.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/context_trimmer.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/git_service.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/mcp_service.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/namespaces.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/plugin_manifest.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/plugin_manifest_converter.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/repo_map_service.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/runner_service.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/session_service.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/task_state.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/tool_output_policy.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/core/tool_service.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/mcp/__init__.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/mcp/manager.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/mcp/models.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/sessions/__init__.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/sessions/manager.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/sessions/models.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/skills/__init__.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/tools/__init__.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/tools/builtins/__init__.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/tools/builtins/file_tools.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/tools/loader.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/utils/__init__.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/utils/errors.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/utils/logging.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/src/nonoka_cli/utils/trace_logger.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/conftest.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/integration/test_server_external_capabilities.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/bridge/test_events.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/bridge/test_handler.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/bridge/test_protocol.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/commands/test_config_cmd.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/commands/test_doctor_cmd.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/config/test_loader.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/core/test_context_trimmer.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/core/test_git_service.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/core/test_plugin_manifest.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/core/test_plugin_manifest_converter.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/core/test_repo_map_service.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/core/test_task_state.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/core/test_tool_output_policy.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/tests/unit/sessions/test_manager.py +0 -0
- {nonoka_cli-0.2.4 → nonoka_cli-0.2.6}/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.6
|
|
4
4
|
Summary: OpenCode backend for the Nonoka Agent framework
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -51,7 +51,7 @@ The installer will:
|
|
|
51
51
|
3. Install `nonoka-cli` and the OpenCode provider.
|
|
52
52
|
4. Generate `~/.config/nonoka/config.yaml` and `~/.config/opencode/opencode.json`.
|
|
53
53
|
|
|
54
|
-
After installing, configure your API key and run `
|
|
54
|
+
After installing, configure your API key and run `nonoka`:
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
57
|
# Interactive: it will ask for your key and save it to ~/.config/nonoka/.env
|
|
@@ -61,9 +61,13 @@ nonoka-cli config init
|
|
|
61
61
|
export DEEPSEEK_API_KEY=<your-key>
|
|
62
62
|
|
|
63
63
|
nonoka-cli doctor
|
|
64
|
-
|
|
64
|
+
nonoka
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
`nonoka` is a shortcut for `nonoka-cli run` and starts the OpenCode TUI with the
|
|
68
|
+
Nonoka backend. You can also use `nonoka run --message "<task>"` for one-shot
|
|
69
|
+
CLI usage.
|
|
70
|
+
|
|
67
71
|
`nonoka-cli` automatically loads `~/.config/nonoka/.env` and `./.env` on startup,
|
|
68
72
|
so you don't need to `export` every time if you save the key in `.env`.
|
|
69
73
|
|
|
@@ -111,7 +115,7 @@ nonoka-cli opencode init --global
|
|
|
111
115
|
3. Make sure your model API key is exported, then run:
|
|
112
116
|
|
|
113
117
|
```bash
|
|
114
|
-
|
|
118
|
+
nonoka
|
|
115
119
|
```
|
|
116
120
|
|
|
117
121
|
## `nonoka-cli doctor`
|
|
@@ -126,10 +130,10 @@ Example output:
|
|
|
126
130
|
|
|
127
131
|
```
|
|
128
132
|
nonoka-cli doctor
|
|
129
|
-
✓ nonoka-cli 0.2.
|
|
133
|
+
✓ nonoka-cli 0.2.5
|
|
130
134
|
✓ Python 3.11
|
|
131
|
-
✓ opencode 1.
|
|
132
|
-
✓ provider nonoka-opencode-provider@0.2.
|
|
135
|
+
✓ opencode 1.18.2
|
|
136
|
+
✓ provider nonoka-opencode-provider@0.2.12
|
|
133
137
|
✓ config ~/.config/nonoka/config.yaml
|
|
134
138
|
✓ API key DEEPSEEK_API_KEY set
|
|
135
139
|
✓ OpenCode provider config in /home/user/.config/opencode/opencode.json
|
|
@@ -22,7 +22,7 @@ The installer will:
|
|
|
22
22
|
3. Install `nonoka-cli` and the OpenCode provider.
|
|
23
23
|
4. Generate `~/.config/nonoka/config.yaml` and `~/.config/opencode/opencode.json`.
|
|
24
24
|
|
|
25
|
-
After installing, configure your API key and run `
|
|
25
|
+
After installing, configure your API key and run `nonoka`:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
28
|
# Interactive: it will ask for your key and save it to ~/.config/nonoka/.env
|
|
@@ -32,9 +32,13 @@ nonoka-cli config init
|
|
|
32
32
|
export DEEPSEEK_API_KEY=<your-key>
|
|
33
33
|
|
|
34
34
|
nonoka-cli doctor
|
|
35
|
-
|
|
35
|
+
nonoka
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
`nonoka` is a shortcut for `nonoka-cli run` and starts the OpenCode TUI with the
|
|
39
|
+
Nonoka backend. You can also use `nonoka run --message "<task>"` for one-shot
|
|
40
|
+
CLI usage.
|
|
41
|
+
|
|
38
42
|
`nonoka-cli` automatically loads `~/.config/nonoka/.env` and `./.env` on startup,
|
|
39
43
|
so you don't need to `export` every time if you save the key in `.env`.
|
|
40
44
|
|
|
@@ -82,7 +86,7 @@ nonoka-cli opencode init --global
|
|
|
82
86
|
3. Make sure your model API key is exported, then run:
|
|
83
87
|
|
|
84
88
|
```bash
|
|
85
|
-
|
|
89
|
+
nonoka
|
|
86
90
|
```
|
|
87
91
|
|
|
88
92
|
## `nonoka-cli doctor`
|
|
@@ -97,10 +101,10 @@ Example output:
|
|
|
97
101
|
|
|
98
102
|
```
|
|
99
103
|
nonoka-cli doctor
|
|
100
|
-
✓ nonoka-cli 0.2.
|
|
104
|
+
✓ nonoka-cli 0.2.5
|
|
101
105
|
✓ Python 3.11
|
|
102
|
-
✓ opencode 1.
|
|
103
|
-
✓ provider nonoka-opencode-provider@0.2.
|
|
106
|
+
✓ opencode 1.18.2
|
|
107
|
+
✓ provider nonoka-opencode-provider@0.2.12
|
|
104
108
|
✓ config ~/.config/nonoka/config.yaml
|
|
105
109
|
✓ API key DEEPSEEK_API_KEY set
|
|
106
110
|
✓ OpenCode provider config in /home/user/.config/opencode/opencode.json
|
|
@@ -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.6"
|
|
8
8
|
description = "OpenCode backend for the Nonoka Agent framework"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -29,6 +29,7 @@ dependencies = [
|
|
|
29
29
|
|
|
30
30
|
[project.scripts]
|
|
31
31
|
nonoka-cli = "nonoka_cli.cli:main"
|
|
32
|
+
nonoka = "nonoka_cli.cli:main"
|
|
32
33
|
|
|
33
34
|
[project.optional-dependencies]
|
|
34
35
|
dev = [
|
|
@@ -138,9 +138,7 @@ async def _async_main(
|
|
|
138
138
|
) -> int:
|
|
139
139
|
"""Set up async stdin/stdout and run the bridge server."""
|
|
140
140
|
loop = asyncio.get_event_loop()
|
|
141
|
-
|
|
142
|
-
# well above the default 64 KiB StreamReader limit. Bump it to 10 MiB.
|
|
143
|
-
stdin = asyncio.StreamReader(limit=10 * 1024 * 1024)
|
|
141
|
+
stdin = asyncio.StreamReader()
|
|
144
142
|
await loop.connect_read_pipe(lambda: asyncio.StreamReaderProtocol(stdin), sys.stdin)
|
|
145
143
|
|
|
146
144
|
w_transport, w_protocol = await loop.connect_write_pipe(
|
|
@@ -26,7 +26,7 @@ import structlog
|
|
|
26
26
|
from dotenv import load_dotenv
|
|
27
27
|
|
|
28
28
|
from nonoka_cli.bridge.server import main as server_main
|
|
29
|
-
from nonoka_cli.commands import config_cmd, doctor_cmd, opencode_cmd, plugin_cmd
|
|
29
|
+
from nonoka_cli.commands import config_cmd, doctor_cmd, opencode_cmd, plugin_cmd, run_cmd
|
|
30
30
|
from nonoka_cli.utils.logging import setup_logging
|
|
31
31
|
|
|
32
32
|
logger = structlog.get_logger("nonoka_cli.cli")
|
|
@@ -88,6 +88,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
88
88
|
doctor_cmd.add_subparser(subparsers)
|
|
89
89
|
opencode_cmd.add_subparser(subparsers)
|
|
90
90
|
plugin_cmd.add_subparser(subparsers)
|
|
91
|
+
run_cmd.add_subparser(subparsers)
|
|
91
92
|
|
|
92
93
|
return parser
|
|
93
94
|
|
|
@@ -116,16 +117,14 @@ def main() -> int:
|
|
|
116
117
|
setup_logging(level=log_level, console=args.verbose or args.debug)
|
|
117
118
|
return args.func(args)
|
|
118
119
|
|
|
120
|
+
# No subcommand given: launch the OpenCode TUI by default.
|
|
119
121
|
log_level = (
|
|
120
122
|
logging.DEBUG if args.debug
|
|
121
123
|
else logging.INFO if args.verbose
|
|
122
124
|
else logging.WARNING
|
|
123
125
|
)
|
|
124
126
|
setup_logging(level=log_level, console=args.verbose or args.debug)
|
|
125
|
-
|
|
126
|
-
print("nonoka-cli: use --server to start the OpenCode backend.", file=sys.stderr)
|
|
127
|
-
parser.print_help(sys.stderr)
|
|
128
|
-
return 0
|
|
127
|
+
return run_cmd.launch_tui(args)
|
|
129
128
|
|
|
130
129
|
|
|
131
130
|
if __name__ == "__main__":
|
|
@@ -54,8 +54,9 @@ def _build_opencode_permission(config: CLIConfig) -> dict[str, str]:
|
|
|
54
54
|
permission[tool] = action
|
|
55
55
|
|
|
56
56
|
# nonoka.yaml permissions take precedence over auto_approve defaults.
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
user_permissions = getattr(config, "permissions", None)
|
|
58
|
+
if user_permissions:
|
|
59
|
+
permission.update(user_permissions)
|
|
59
60
|
return permission
|
|
60
61
|
|
|
61
62
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Launch the OpenCode TUI from nonoka-cli."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from nonoka_cli.commands.opencode_cmd import cmd_init
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _has_opencode() -> bool:
|
|
16
|
+
"""Return True if the opencode binary is available on PATH."""
|
|
17
|
+
return shutil.which("opencode") is not None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _has_opencode_config(cwd: Path) -> bool:
|
|
21
|
+
"""Return True if the directory already has an opencode.json."""
|
|
22
|
+
return (cwd / "opencode.json").exists()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _ensure_opencode_config(args: argparse.Namespace, cwd: Path) -> int:
|
|
26
|
+
"""Initialize opencode.json if it is missing.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
0 if the config exists or was created successfully, otherwise an error code.
|
|
30
|
+
"""
|
|
31
|
+
if _has_opencode_config(cwd):
|
|
32
|
+
return 0
|
|
33
|
+
|
|
34
|
+
print(f"No opencode.json found in {cwd}. Running 'nonoka-cli opencode init'...", file=sys.stderr)
|
|
35
|
+
|
|
36
|
+
init_args = argparse.Namespace(
|
|
37
|
+
config=getattr(args, "config", None),
|
|
38
|
+
cwd=str(cwd),
|
|
39
|
+
global_=False,
|
|
40
|
+
yes=True,
|
|
41
|
+
command="opencode",
|
|
42
|
+
opencode_command="init",
|
|
43
|
+
)
|
|
44
|
+
return cmd_init(init_args)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def launch_tui(args: argparse.Namespace) -> int:
|
|
48
|
+
"""Launch the OpenCode TUI in the requested directory.
|
|
49
|
+
|
|
50
|
+
If ``args.message`` is provided, run OpenCode in one-shot mode with
|
|
51
|
+
``opencode run --auto <message>``. Otherwise start the interactive TUI.
|
|
52
|
+
"""
|
|
53
|
+
cwd = Path(getattr(args, "cwd", ".")).expanduser().resolve()
|
|
54
|
+
|
|
55
|
+
if not _has_opencode():
|
|
56
|
+
print("Error: opencode is not installed.", file=sys.stderr)
|
|
57
|
+
print("Install it with: npm install -g opencode", file=sys.stderr)
|
|
58
|
+
return 1
|
|
59
|
+
|
|
60
|
+
ret = _ensure_opencode_config(args, cwd)
|
|
61
|
+
if ret != 0:
|
|
62
|
+
return ret
|
|
63
|
+
|
|
64
|
+
message = getattr(args, "message", None)
|
|
65
|
+
if message:
|
|
66
|
+
cmd = ["opencode", "run", "--auto", message]
|
|
67
|
+
else:
|
|
68
|
+
cmd = ["opencode", str(cwd)]
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
result = subprocess.run(cmd, cwd=cwd)
|
|
72
|
+
return result.returncode
|
|
73
|
+
except KeyboardInterrupt:
|
|
74
|
+
return 130
|
|
75
|
+
except FileNotFoundError:
|
|
76
|
+
print("Error: opencode disappeared during launch.", file=sys.stderr)
|
|
77
|
+
return 1
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def cmd_run(args: argparse.Namespace) -> int:
|
|
81
|
+
"""Entry point for the ``nonoka-cli run`` subcommand."""
|
|
82
|
+
return launch_tui(args)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def add_subparser(subparsers: Any) -> None:
|
|
86
|
+
"""Register the ``run`` subcommand."""
|
|
87
|
+
run_parser = subparsers.add_parser(
|
|
88
|
+
"run",
|
|
89
|
+
help="Launch the OpenCode TUI (default when no subcommand is given)",
|
|
90
|
+
)
|
|
91
|
+
run_parser.add_argument(
|
|
92
|
+
"--config",
|
|
93
|
+
dest="config",
|
|
94
|
+
type=Path,
|
|
95
|
+
default=None,
|
|
96
|
+
help="Path to the nonoka config file (default: ~/.config/nonoka/config.yaml)",
|
|
97
|
+
)
|
|
98
|
+
run_parser.add_argument(
|
|
99
|
+
"--cwd",
|
|
100
|
+
dest="cwd",
|
|
101
|
+
default=".",
|
|
102
|
+
help="Directory to start OpenCode in (default: current directory)",
|
|
103
|
+
)
|
|
104
|
+
run_parser.add_argument(
|
|
105
|
+
"--message", "-m",
|
|
106
|
+
dest="message",
|
|
107
|
+
default=None,
|
|
108
|
+
help="Run a single message and exit instead of starting the interactive TUI",
|
|
109
|
+
)
|
|
110
|
+
run_parser.set_defaults(func=cmd_run)
|
|
@@ -111,20 +111,16 @@ class RepoMapConfig(BaseModel):
|
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
class AgentRoleConfig(BaseModel):
|
|
114
|
-
"""Configuration for a single agent role (planner /
|
|
114
|
+
"""Configuration for a single agent role (planner / executor)."""
|
|
115
115
|
model: str = ""
|
|
116
116
|
max_turns: int = 5
|
|
117
117
|
system_prompt: str = ""
|
|
118
118
|
|
|
119
119
|
|
|
120
120
|
class AgentsConfig(BaseModel):
|
|
121
|
-
"""Multi-agent role configuration.
|
|
122
|
-
|
|
123
|
-
The ``executor`` role has been removed: the main agent is the executor, and
|
|
124
|
-
its ``max_turns`` is configured at the top level of ``CLIConfig``.
|
|
125
|
-
"""
|
|
121
|
+
"""Multi-agent role configuration."""
|
|
126
122
|
planner: AgentRoleConfig = Field(default_factory=AgentRoleConfig)
|
|
127
|
-
|
|
123
|
+
executor: AgentRoleConfig = Field(default_factory=AgentRoleConfig)
|
|
128
124
|
|
|
129
125
|
|
|
130
126
|
class PluginConfig(BaseModel):
|
|
@@ -146,11 +142,9 @@ class CLIConfig(BaseModel):
|
|
|
146
142
|
Matches the expected structure of ~/.config/nonoka/config.yaml.
|
|
147
143
|
"""
|
|
148
144
|
model: str = "gpt-4o"
|
|
149
|
-
max_turns: int = 20
|
|
150
145
|
system_prompt: str = ""
|
|
151
146
|
api_key: str = ""
|
|
152
147
|
base_url: str = ""
|
|
153
|
-
permissions: dict[str, str] = Field(default_factory=dict)
|
|
154
148
|
mcp_servers: dict[str, MCPServerConfigModel] = Field(default_factory=dict)
|
|
155
149
|
tool_paths: list[Path] = Field(default_factory=list)
|
|
156
150
|
skills: list[str] = Field(default_factory=list)
|
|
@@ -26,6 +26,7 @@ from nonoka_cli.config.models import CLIConfig
|
|
|
26
26
|
from nonoka_cli.core.namespaces import (
|
|
27
27
|
PrefixedCapability,
|
|
28
28
|
mcp_tool_name,
|
|
29
|
+
sanitize_tool_name,
|
|
29
30
|
skill_tool_name,
|
|
30
31
|
)
|
|
31
32
|
from nonoka_cli.core.prompt_builder import SystemPromptBuilder
|
|
@@ -143,8 +144,11 @@ class AgentFactory:
|
|
|
143
144
|
return self._skill_registry
|
|
144
145
|
|
|
145
146
|
def _executor_max_turns(self) -> int:
|
|
146
|
-
"""Return the configured
|
|
147
|
-
|
|
147
|
+
"""Return the configured executor max turns, falling back to 20."""
|
|
148
|
+
configured = getattr(
|
|
149
|
+
self._config.agents.executor, "max_turns", None
|
|
150
|
+
)
|
|
151
|
+
return configured if configured else 20
|
|
148
152
|
|
|
149
153
|
def build(
|
|
150
154
|
self,
|
|
@@ -153,7 +157,6 @@ class AgentFactory:
|
|
|
153
157
|
git_summary: str | None = None,
|
|
154
158
|
plugin_summary: str | None = None,
|
|
155
159
|
execution_plan: str | None = None,
|
|
156
|
-
extra_tools: list[Capability] | None = None,
|
|
157
160
|
) -> Agent:
|
|
158
161
|
"""Build (or rebuild) an Agent from the current configuration.
|
|
159
162
|
|
|
@@ -164,9 +167,6 @@ class AgentFactory:
|
|
|
164
167
|
repo_map: Optional repo-map block to inject into the system prompt.
|
|
165
168
|
git_summary: Optional git-checkpoint status summary.
|
|
166
169
|
plugin_summary: Optional loaded plugin manifest summary.
|
|
167
|
-
execution_plan: Optional structured plan produced by the planner agent.
|
|
168
|
-
extra_tools: Optional additional tools (e.g. sub-agent AgentTools) to
|
|
169
|
-
register with the Agent.
|
|
170
170
|
|
|
171
171
|
Returns:
|
|
172
172
|
A nonoka Agent instance.
|
|
@@ -233,12 +233,6 @@ class AgentFactory:
|
|
|
233
233
|
builder = builder.tool(capability)
|
|
234
234
|
logger.debug("agent_factory_mcp_tools", count=len(mcp_tools))
|
|
235
235
|
|
|
236
|
-
# Extra tools (e.g. planner/reviewer sub-agents) injected by Orchestrator.
|
|
237
|
-
if extra_tools:
|
|
238
|
-
for tool in extra_tools:
|
|
239
|
-
builder = builder.tool(tool)
|
|
240
|
-
logger.debug("agent_factory_extra_tools", count=len(extra_tools))
|
|
241
|
-
|
|
242
236
|
self._agent = builder.build()
|
|
243
237
|
return self._agent
|
|
244
238
|
|
|
@@ -274,7 +268,6 @@ class AgentFactory:
|
|
|
274
268
|
git_summary: str | None = None,
|
|
275
269
|
plugin_summary: str | None = None,
|
|
276
270
|
execution_plan: str | None = None,
|
|
277
|
-
extra_tools: list[Capability] | None = None,
|
|
278
271
|
) -> Agent:
|
|
279
272
|
"""Build an Agent for host-managed (e.g. OpenCode) mode.
|
|
280
273
|
|
|
@@ -296,9 +289,6 @@ class AgentFactory:
|
|
|
296
289
|
in nonoka.yaml.
|
|
297
290
|
external_mcp_servers: Optional host-managed MCP server definitions.
|
|
298
291
|
external_skills: Optional host-managed skill definitions.
|
|
299
|
-
execution_plan: Optional structured plan produced by the planner agent.
|
|
300
|
-
extra_tools: Optional additional tools (e.g. sub-agent AgentTools) to
|
|
301
|
-
register with the Agent.
|
|
302
292
|
"""
|
|
303
293
|
if not self._config.model:
|
|
304
294
|
raise AgentBuildError("No model configured. Set 'model' in config.yaml.")
|
|
@@ -454,12 +444,6 @@ class AgentFactory:
|
|
|
454
444
|
for server, cap in mcp_tools:
|
|
455
445
|
builder = builder.tool(PrefixedCapability(cap, f"mcp__{server}__"))
|
|
456
446
|
|
|
457
|
-
# Extra tools (e.g. planner/reviewer sub-agents) injected by Orchestrator.
|
|
458
|
-
if extra_tools:
|
|
459
|
-
for tool in extra_tools:
|
|
460
|
-
builder = builder.tool(tool)
|
|
461
|
-
logger.debug("agent_factory_extra_tools", count=len(extra_tools))
|
|
462
|
-
|
|
463
447
|
return builder.build()
|
|
464
448
|
|
|
465
449
|
def _skill_registry_for_build(
|
|
@@ -26,7 +26,6 @@ from nonoka_cli.core.context_trimmer import TurnBasedContextTrimmer
|
|
|
26
26
|
from nonoka_cli.core.git_service import GitService, build_git_service
|
|
27
27
|
from nonoka_cli.core.mcp_service import MCPService
|
|
28
28
|
from nonoka_cli.core.planning_service import PlanningService, build_planning_service
|
|
29
|
-
from nonoka_cli.core.review_service import ReviewService, build_review_service
|
|
30
29
|
from nonoka_cli.core.plugin_manifest import (
|
|
31
30
|
PluginManifestLoader,
|
|
32
31
|
format_manifest_summary,
|
|
@@ -99,7 +98,6 @@ class Orchestrator:
|
|
|
99
98
|
self._git_service: GitService | None = None
|
|
100
99
|
self._repo_map_service: RepoMapService | None = None
|
|
101
100
|
self._planning_service: PlanningService | None = None
|
|
102
|
-
self._review_service: ReviewService | None = None
|
|
103
101
|
self._plugin_manifests: list[Any] = []
|
|
104
102
|
self._initialized = False
|
|
105
103
|
|
|
@@ -162,10 +160,6 @@ class Orchestrator:
|
|
|
162
160
|
working_dir=Path.cwd(),
|
|
163
161
|
config=self._config,
|
|
164
162
|
)
|
|
165
|
-
self._review_service = build_review_service(
|
|
166
|
-
working_dir=Path.cwd(),
|
|
167
|
-
config=self._config,
|
|
168
|
-
)
|
|
169
163
|
|
|
170
164
|
# Load project plugin manifests before building the Agent so that
|
|
171
165
|
# allowed_tools / plugin summary can be taken into account.
|
|
@@ -191,7 +185,6 @@ class Orchestrator:
|
|
|
191
185
|
repo_map=repo_map_block,
|
|
192
186
|
git_summary=git_summary,
|
|
193
187
|
plugin_summary=plugin_summary,
|
|
194
|
-
extra_tools=self._subagent_tools(),
|
|
195
188
|
)
|
|
196
189
|
self._tool_service = ToolService(self._agent_factory, self._tool_loader)
|
|
197
190
|
self._mcp_service = MCPService(self._mcp_manager, self._agent_factory)
|
|
@@ -354,19 +347,6 @@ class Orchestrator:
|
|
|
354
347
|
merged = merge_manifests(self._plugin_manifests)
|
|
355
348
|
return merged.allowed_tools
|
|
356
349
|
|
|
357
|
-
def _subagent_tools(self) -> list[Any]:
|
|
358
|
-
"""Return enabled sub-agent AgentTools for injection into the executor.
|
|
359
|
-
|
|
360
|
-
Only planner/reviewer tools whose model is configured are returned, so the
|
|
361
|
-
default executor tool list stays small.
|
|
362
|
-
"""
|
|
363
|
-
tools: list[Any] = []
|
|
364
|
-
if self._planning_service is not None and self._planning_service.enabled:
|
|
365
|
-
tools.append(self._planning_service.agent_tool)
|
|
366
|
-
if self._review_service is not None and self._review_service.enabled:
|
|
367
|
-
tools.append(self._review_service.agent_tool)
|
|
368
|
-
return tools
|
|
369
|
-
|
|
370
350
|
def _on_config_changed(self, config: CLIConfig) -> None:
|
|
371
351
|
"""ConfigManager hot-reload listener: update local reference only."""
|
|
372
352
|
self._config = config
|
|
@@ -450,10 +430,7 @@ class Orchestrator:
|
|
|
450
430
|
logger.info("execute_plan_result", plan_preview=plan[:200] if plan else None)
|
|
451
431
|
if plan and not plan.startswith(("Error", "Planning is disabled")):
|
|
452
432
|
logger.info("execution_plan_generated", plan_length=len(plan))
|
|
453
|
-
agent = self._agent_factory.build(
|
|
454
|
-
execution_plan=plan,
|
|
455
|
-
extra_tools=self._subagent_tools(),
|
|
456
|
-
)
|
|
433
|
+
agent = self._agent_factory.build(execution_plan=plan)
|
|
457
434
|
except Exception as exc:
|
|
458
435
|
logger.warning("execution_plan_generation_failed", error=str(exc))
|
|
459
436
|
|
|
@@ -560,7 +537,6 @@ class Orchestrator:
|
|
|
560
537
|
git_summary=git_summary,
|
|
561
538
|
plugin_summary=plugin_summary,
|
|
562
539
|
execution_plan=execution_plan,
|
|
563
|
-
extra_tools=self._subagent_tools(),
|
|
564
540
|
)
|
|
565
541
|
|
|
566
542
|
logger.info(
|
|
@@ -618,7 +594,6 @@ class Orchestrator:
|
|
|
618
594
|
repo_map=repo_map_block,
|
|
619
595
|
git_summary=git_summary,
|
|
620
596
|
plugin_summary=plugin_summary,
|
|
621
|
-
extra_tools=self._subagent_tools(),
|
|
622
597
|
)
|
|
623
598
|
|
|
624
599
|
logger.info(
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Planning service for nonoka-cli.
|
|
2
|
+
|
|
3
|
+
Wires planner/executor configuration (``agents.planner``) to the orchestrator.
|
|
4
|
+
The actual planner implementation has been removed from nonoka-agent in 1.3.4;
|
|
5
|
+
nonoka-cli will provide its own LLM-based planner here in a future release.
|
|
6
|
+
For now the service reports itself as disabled when invoked so the orchestrator
|
|
7
|
+
falls back to single-agent execution.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from nonoka_cli.config.models import AgentsConfig, CLIConfig
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PlanningService:
|
|
18
|
+
"""Adapts the nonoka-agent planner tool to CLI config."""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
working_dir: Path,
|
|
23
|
+
config: AgentsConfig | None = None,
|
|
24
|
+
default_model: str | None = None,
|
|
25
|
+
):
|
|
26
|
+
self._working_dir = working_dir
|
|
27
|
+
self._config = config or AgentsConfig()
|
|
28
|
+
self._default_model = default_model
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def enabled(self) -> bool:
|
|
32
|
+
return bool(self.planner_model)
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def planner_model(self) -> str:
|
|
36
|
+
"""Return the effective planner model identifier."""
|
|
37
|
+
configured = self._config.planner.model
|
|
38
|
+
if configured:
|
|
39
|
+
return configured
|
|
40
|
+
if self._default_model:
|
|
41
|
+
return self._default_model
|
|
42
|
+
return ""
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def executor_model(self) -> str:
|
|
46
|
+
"""Return the effective executor model identifier."""
|
|
47
|
+
configured = self._config.executor.model
|
|
48
|
+
if configured:
|
|
49
|
+
return configured
|
|
50
|
+
if self._default_model:
|
|
51
|
+
return self._default_model
|
|
52
|
+
return ""
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def planner_max_turns(self) -> int:
|
|
56
|
+
return self._config.planner.max_turns
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def executor_max_turns(self) -> int:
|
|
60
|
+
return self._config.executor.max_turns
|
|
61
|
+
|
|
62
|
+
async def plan(self, task: str, max_steps: int = 10) -> str:
|
|
63
|
+
"""Generate a structured plan for *task*.
|
|
64
|
+
|
|
65
|
+
Returns a human-readable plan or an error message. The planner tool has
|
|
66
|
+
been removed from nonoka-agent in 1.3.4; a CLI-native planner will replace
|
|
67
|
+
this stub in a future release.
|
|
68
|
+
"""
|
|
69
|
+
if not self.enabled:
|
|
70
|
+
return "Planning is disabled: no planner model configured."
|
|
71
|
+
return (
|
|
72
|
+
"Planning is disabled: the built-in planner tool was removed from "
|
|
73
|
+
"nonoka-agent 1.3.4. Configure a planner model to opt into the future "
|
|
74
|
+
"CLI-native planner implementation."
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def build_planning_service(
|
|
79
|
+
working_dir: Path,
|
|
80
|
+
config: CLIConfig | None = None,
|
|
81
|
+
) -> PlanningService:
|
|
82
|
+
"""Factory for creating a PlanningService from CLI configuration."""
|
|
83
|
+
agents_config = config.agents if config is not None else AgentsConfig()
|
|
84
|
+
default_model = config.model if config is not None else None
|
|
85
|
+
return PlanningService(
|
|
86
|
+
working_dir=working_dir,
|
|
87
|
+
config=agents_config,
|
|
88
|
+
default_model=default_model,
|
|
89
|
+
)
|
|
@@ -181,20 +181,11 @@ class SystemPromptBuilder:
|
|
|
181
181
|
def _build_planner_block(self) -> str:
|
|
182
182
|
"""Return guidance for the planner/executor workflow.
|
|
183
183
|
|
|
184
|
-
The ``plan_task``
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
UI via ``todowrite``.
|
|
184
|
+
The built-in ``plan_task`` tool was removed from nonoka-agent in 1.3.4; the
|
|
185
|
+
planner block is currently suppressed until nonoka-cli provides its own
|
|
186
|
+
LLM-based planner implementation.
|
|
188
187
|
"""
|
|
189
|
-
|
|
190
|
-
return ""
|
|
191
|
-
return (
|
|
192
|
-
"## Planner / Executor Workflow\n"
|
|
193
|
-
"For complex multi-file or multi-step tasks, first call the ``plan_task`` "
|
|
194
|
-
"tool to produce a structured plan. Then execute the steps using the "
|
|
195
|
-
"file, search, and run-command tools. After each step, call ``todowrite`` "
|
|
196
|
-
"to update the TODO list so the user can follow progress in the OpenCode TUI."
|
|
197
|
-
)
|
|
188
|
+
return ""
|
|
198
189
|
|
|
199
190
|
def _build_execution_plan_block(self) -> str:
|
|
200
191
|
"""Return the concrete execution plan produced by the planner agent."""
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""Agent-level tools re-exported from nonoka-agent.
|
|
2
2
|
|
|
3
3
|
These tools are implemented in nonoka-agent (the core agent framework) and are
|
|
4
|
-
made available to every nonoka-cli Agent.
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
made available to every nonoka-cli Agent. Keeping the implementation in
|
|
5
|
+
nonoka-agent lets other consumers reuse them, while this module declares the
|
|
6
|
+
subset the CLI exposes by default.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
@@ -151,28 +151,3 @@ def test_opencode_init_disables_native_skill_tool(tmp_path: Path):
|
|
|
151
151
|
|
|
152
152
|
data = json.loads((tmp_path / "opencode.json").read_text())
|
|
153
153
|
assert data.get("tools", {}).get("skill") is False
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def test_opencode_init_permissions_override(tmp_path: Path):
|
|
157
|
-
config_path = tmp_path / "nonoka.yaml"
|
|
158
|
-
ConfigLoader.save(
|
|
159
|
-
CLIConfig(
|
|
160
|
-
model="deepseek-chat",
|
|
161
|
-
permissions={"read": "allow", "bash": "ask"},
|
|
162
|
-
),
|
|
163
|
-
config_path,
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
args = argparse.Namespace(
|
|
167
|
-
config=str(config_path),
|
|
168
|
-
cwd=str(tmp_path),
|
|
169
|
-
global_=False,
|
|
170
|
-
)
|
|
171
|
-
assert cmd_init(args) == 0
|
|
172
|
-
|
|
173
|
-
data = json.loads((tmp_path / "opencode.json").read_text())
|
|
174
|
-
assert data["permission"]["read"] == "allow"
|
|
175
|
-
assert data["permission"]["bash"] == "ask"
|
|
176
|
-
assert data["permission"]["write"] == "ask"
|
|
177
|
-
assert data["agent"]["build"]["permission"]["read"] == "allow"
|
|
178
|
-
assert data["agent"]["build"]["permission"]["bash"] == "ask"
|