moa-cli 0.3.2__tar.gz → 0.3.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: moa-cli
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Ask one question to multiple local AI coding CLIs in parallel and collect their answers.
5
5
  Keywords: llm,agents,cli,claude,codex,agy,opencode,peer-review
6
6
  Author: Paul-Louis Pröve
@@ -50,7 +50,7 @@ A single model gives you one perspective. Asking three frontier models the same
50
50
 
51
51
  ```text
52
52
  $ moa ask "Is Postgres or SQLite better for a desktop app?"
53
- Asking claude, codex, agy (timeout 600s, read-only)
53
+ Asking claude, codex, agy (timeout 900s, read-only)
54
54
 
55
55
  ──────────────── claude (opus) · OK · 3.2s ────────────────
56
56
 
@@ -103,11 +103,16 @@ is enforced by spawning each CLI with its own read-only flags:
103
103
 
104
104
  | Provider | Read-only (default) | Reads files | Web research |
105
105
  | ---------- | -------------------------- | ----------- | ------------------------- |
106
- | `claude` | `--permission-mode plan` | yes | yes |
106
+ | `claude` | `--permission-mode default` | yes | yes |
107
107
  | `codex` | `-s read-only` | yes | **no** (sandbox blocks network) |
108
108
  | `opencode` | `--agent plan` | yes | yes |
109
109
  | `agy` | `--sandbox` (partial: shell only - can still edit files) | yes | yes |
110
110
 
111
+ `claude`'s `--permission-mode default` is read-only in moa's non-interactive use: it reads
112
+ files and researches online with the full toolset, but any write or edit needs an interactive
113
+ approval that never comes under `-p`, so all mutations are denied. (`plan` mode is **not**
114
+ usable headless - it emits a plan and waits for approval instead of answering.)
115
+
111
116
  `codex`'s read-only mode is a kernel sandbox that also blocks network, so codex does no
112
117
  web research in the default mode (it still reads local files). `agy` has **no true
113
118
  read-only mode**: its `--sandbox` flag restricts agy's terminal/shell but does **not** stop
@@ -39,7 +39,7 @@ A single model gives you one perspective. Asking three frontier models the same
39
39
 
40
40
  ```text
41
41
  $ moa ask "Is Postgres or SQLite better for a desktop app?"
42
- Asking claude, codex, agy (timeout 600s, read-only)
42
+ Asking claude, codex, agy (timeout 900s, read-only)
43
43
 
44
44
  ──────────────── claude (opus) · OK · 3.2s ────────────────
45
45
 
@@ -92,11 +92,16 @@ is enforced by spawning each CLI with its own read-only flags:
92
92
 
93
93
  | Provider | Read-only (default) | Reads files | Web research |
94
94
  | ---------- | -------------------------- | ----------- | ------------------------- |
95
- | `claude` | `--permission-mode plan` | yes | yes |
95
+ | `claude` | `--permission-mode default` | yes | yes |
96
96
  | `codex` | `-s read-only` | yes | **no** (sandbox blocks network) |
97
97
  | `opencode` | `--agent plan` | yes | yes |
98
98
  | `agy` | `--sandbox` (partial: shell only - can still edit files) | yes | yes |
99
99
 
100
+ `claude`'s `--permission-mode default` is read-only in moa's non-interactive use: it reads
101
+ files and researches online with the full toolset, but any write or edit needs an interactive
102
+ approval that never comes under `-p`, so all mutations are denied. (`plan` mode is **not**
103
+ usable headless - it emits a plan and waits for approval instead of answering.)
104
+
100
105
  `codex`'s read-only mode is a kernel sandbox that also blocks network, so codex does no
101
106
  web research in the default mode (it still reads local files). `agy` has **no true
102
107
  read-only mode**: its `--sandbox` flag restricts agy's terminal/shell but does **not** stop
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "moa-cli"
3
- version = "0.3.2"
3
+ version = "0.3.3"
4
4
  description = "Ask one question to multiple local AI coding CLIs in parallel and collect their answers."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,3 +1,3 @@
1
1
  """MOA CLI package."""
2
2
 
3
- __version__ = "0.3.2"
3
+ __version__ = "0.3.3"
@@ -143,7 +143,11 @@ def _opencode(
143
143
  PROVIDERS: dict[str, Provider] = {
144
144
  "claude": Provider(
145
145
  "claude", "claude", "opus", _claude,
146
- readonly=("--permission-mode", "plan"),
146
+ # In headless `-p`, `default` keeps the full toolset (Read, read-only Bash
147
+ # like git/grep, WebFetch) but has no way to approve a write/edit, so
148
+ # mutations are denied: effectively read-only with all tools. (`plan` mode
149
+ # instead emits a plan-approval stub and never answers under `-p`.)
150
+ readonly=("--permission-mode", "default"),
147
151
  yolo=("--permission-mode", "bypassPermissions"),
148
152
  unset_env=("CLAUDECODE",),
149
153
  ),
@@ -170,7 +174,10 @@ PROVIDERS: dict[str, Provider] = {
170
174
  "opencode": Provider(
171
175
  "opencode", "opencode", "", _opencode,
172
176
  readonly=("--agent", "plan"),
173
- yolo=(), # default = build agent (full access)
177
+ # Bare `build` (the default agent) still gates doom_loop/external_directory
178
+ # as `ask`, which auto-rejects headless - so true full access needs the
179
+ # explicit skip-permissions flag. (Verified on opencode 1.17.8.)
180
+ yolo=("--dangerously-skip-permissions",),
174
181
  # Verified against installed opencode: `--variant <value>` is the
175
182
  # "model variant (provider-specific reasoning effort, e.g., high)" flag
176
183
  # (`opencode run --help`). The value is pasted verbatim.
@@ -754,7 +761,7 @@ _MODERATOR_MODES: tuple[str, ...] = ("auto",)
754
761
  # serialize_config renders them back as `[providers.<name>]` blocks.
755
762
  _CONFIG_DEFAULTS: dict = {
756
763
  "num": 3,
757
- "timeout": 600.0,
764
+ "timeout": 900.0,
758
765
  "synthesizer": "auto",
759
766
  "moderator": "auto",
760
767
  "exclude": [],
@@ -1144,7 +1151,7 @@ def resolve_run(
1144
1151
  raise typer.BadParameter(f"{config_path()}: {exc}") from exc
1145
1152
 
1146
1153
  num = resolve_option(num, "num", config, default_num)
1147
- timeout = resolve_option(timeout, "timeout", config, 600.0)
1154
+ timeout = resolve_option(timeout, "timeout", config, 900.0)
1148
1155
  # Repeatable flags are an empty list when omitted, not None, so treat empty
1149
1156
  # as "fall back to config" for exclude.
1150
1157
  exclude_names = tuple(exclude) if exclude else tuple(config.get("exclude", ()))