@tt-a1i/mco 0.1.3 → 0.2.0

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.
package/README.md CHANGED
@@ -1,189 +1,173 @@
1
- # MCO Docs Index
1
+ # MCO
2
+
3
+ **MCO — One Prompt. Five AI Agents. One Result.**
2
4
 
3
5
  English | [简体中文](./README.zh-CN.md)
4
6
 
5
- ## Read First
6
- 1. [multi-cli-orchestrator-proposal.md](./multi-cli-orchestrator-proposal.md)
7
- 2. [capability-research.md](./capability-research.md)
8
- 3. [notes.md](./notes.md)
7
+ ## What is MCO
8
+
9
+ MCO (Multi-CLI Orchestrator) is a neutral orchestration layer that dispatches a single prompt to multiple AI coding agents in parallel and aggregates their results. No vendor lock-in. No workflow rewrite. Just fan-out, wait-all, and collect.
9
10
 
10
- ## Gate Artifacts
11
- 1. [capability-probe-spec.md](./capability-probe-spec.md)
12
- 2. [adapter-contract-tests.md](./adapter-contract-tests.md)
13
- 3. [dry-run-plan.md](./dry-run-plan.md)
14
- 4. [implementation-gate-checklist.md](./implementation-gate-checklist.md)
11
+ You keep using Claude Code, Codex CLI, Gemini CLI, OpenCode, and Qwen Code as they are. MCO wires them into a unified execution pipeline with structured output, progress-driven timeouts, and reproducible artifacts.
15
12
 
16
- ## Implementation Freeze
17
- 1. [docs/implementation/step0-interface-freeze.md](./docs/implementation/step0-interface-freeze.md)
18
- 2. [docs/contracts/cli-json-v0.1.x.md](./docs/contracts/cli-json-v0.1.x.md)
19
- 3. [docs/contracts/provider-permissions-v0.1.x.md](./docs/contracts/provider-permissions-v0.1.x.md)
13
+ ## Key Highlights
20
14
 
21
- ## Planning and Tracking
22
- 1. [task_plan.md](./task_plan.md)
15
+ - **Parallel fan-out** — dispatch to all providers simultaneously, wait-all semantics
16
+ - **Progress-driven timeouts** — agents run freely until completion; cancel only when output goes idle
17
+ - **Dual mode** — `mco review` for structured code review findings, `mco run` for general task execution
18
+ - **Provider-neutral** — uniform adapter contract across 5 CLI tools, no favoring any vendor
19
+ - **Machine-readable output** — JSON result payloads and per-provider artifact trees for downstream automation
23
20
 
24
- ## Release Notes
25
- 1. [docs/releases/v0.1.2.md](./docs/releases/v0.1.2.md)
26
- 2. [docs/releases/v0.1.2.zh-CN.md](./docs/releases/v0.1.2.zh-CN.md)
27
- 3. [docs/releases/v0.1.1.md](./docs/releases/v0.1.1.md)
28
- 4. [docs/releases/v0.1.1.zh-CN.md](./docs/releases/v0.1.1.zh-CN.md)
29
- 5. [docs/releases/v0.1.0.md](./docs/releases/v0.1.0.md)
30
- 6. [docs/releases/v0.1.0.zh-CN.md](./docs/releases/v0.1.0.zh-CN.md)
21
+ ## Supported Providers
31
22
 
32
- ## Unified CLI (Step 2)
33
- `mco review` is the unified entrypoint for running a review task.
23
+ | Provider | CLI | Status |
24
+ |----------|-----|--------|
25
+ | Claude Code | `claude` | Supported |
26
+ | Codex CLI | `codex` | Supported |
27
+ | Gemini CLI | `gemini` | Supported |
28
+ | OpenCode | `opencode` | Supported |
29
+ | Qwen Code | `qwen` | Supported |
34
30
 
35
- `mco run` is the generalized execution entrypoint for agent-style task orchestration (no forced findings schema).
31
+ No project migration. No command relearning. No single-tool lock-in.
36
32
 
37
- ## Installation
33
+ ## Quick Start
38
34
 
39
- NPM wrapper (available now, Python 3 required on PATH):
35
+ Install via npm (Python 3 required on PATH):
40
36
 
41
37
  ```bash
42
38
  npm i -g @tt-a1i/mco
43
- mco --help
44
39
  ```
45
40
 
46
- Install from source (editable):
41
+ Or install from source:
47
42
 
48
43
  ```bash
49
44
  git clone https://github.com/tt-a1i/mco.git
50
45
  cd mco
51
46
  python3 -m pip install -e .
52
- mco --help
53
47
  ```
54
48
 
55
- Python package via PyPI:
56
- - Not published yet.
57
- - Publish workflow is ready and will be enabled after PyPI Trusted Publisher setup.
49
+ Run your first multi-agent review:
58
50
 
59
- Quick start:
60
51
  ```bash
61
- ./mco review \
52
+ mco review \
62
53
  --repo . \
63
54
  --prompt "Review this repository for high-risk bugs and security issues." \
64
- --providers claude,codex
55
+ --providers claude,codex,qwen
65
56
  ```
66
57
 
67
- Machine-readable output:
68
- ```bash
69
- ./mco review --repo . --prompt "Review for bugs." --providers claude,codex --json
70
- ```
58
+ ## Usage
59
+
60
+ ### Review Mode
61
+
62
+ Structured code review with findings schema. Each provider returns normalized findings with severity, category, evidence, and recommendations.
71
63
 
72
- Stdout-only result mode (for caller rendering, no `summary.md/decision.md/findings.json/run.json` write):
73
64
  ```bash
74
- ./mco review --repo . --prompt "Review for bugs." --providers claude,codex --result-mode stdout --json
65
+ mco review \
66
+ --repo . \
67
+ --prompt "Review for security vulnerabilities and performance issues." \
68
+ --providers claude,codex,gemini,opencode,qwen \
69
+ --json
75
70
  ```
76
71
 
77
- General run mode:
72
+ ### Run Mode
73
+
74
+ General-purpose multi-agent execution. No forced output schema — providers complete the task freely.
75
+
78
76
  ```bash
79
- ./mco run --repo . --prompt "Summarize the current repo architecture." --providers claude,codex --json
77
+ mco run \
78
+ --repo . \
79
+ --prompt "Summarize the architecture of this project." \
80
+ --providers claude,codex \
81
+ --json
80
82
  ```
81
83
 
82
- Config file (JSON):
83
- ```json
84
- {
85
- "providers": ["claude", "codex"],
86
- "artifact_base": "reports/review",
87
- "state_file": ".mco/state.json",
88
- "policy": {
89
- "timeout_seconds": 180,
90
- "stall_timeout_seconds": 900,
91
- "poll_interval_seconds": 1.0,
92
- "review_hard_timeout_seconds": 1800,
93
- "enforce_findings_contract": false,
94
- "max_retries": 1,
95
- "high_escalation_threshold": 1,
96
- "require_non_empty_findings": true,
97
- "max_provider_parallelism": 0,
98
- "allow_paths": [".", "runtime", "scripts"],
99
- "enforcement_mode": "strict",
100
- "provider_permissions": {
101
- "claude": {
102
- "permission_mode": "plan"
103
- },
104
- "codex": {
105
- "sandbox": "workspace-write"
106
- }
107
- },
108
- "provider_timeouts": {
109
- "claude": 300,
110
- "codex": 240,
111
- "qwen": 240
112
- }
113
- }
114
- }
115
- ```
84
+ ### Result Modes
85
+
86
+ | Mode | Behavior |
87
+ |------|----------|
88
+ | `--result-mode artifact` | Write artifact files, print summary (default) |
89
+ | `--result-mode stdout` | Print full result to stdout, skip artifact files |
90
+ | `--result-mode both` | Write artifacts and print full result |
91
+
92
+ ### Path Constraints
93
+
94
+ Restrict which files agents can access:
116
95
 
117
- Run with config:
118
96
  ```bash
119
- ./mco review --config ./mco.example.json --repo . --prompt "Review for bugs and security issues."
97
+ mco run \
98
+ --repo . \
99
+ --prompt "Analyze the adapter layer." \
100
+ --providers claude,codex \
101
+ --allow-paths runtime,scripts \
102
+ --target-paths runtime/adapters \
103
+ --enforcement-mode strict
120
104
  ```
121
105
 
122
- Override fan-out and per-provider timeout from CLI:
106
+ ## Defaults and Overrides
107
+
108
+ MCO is zero-config by default. You can run it directly with built-in defaults and override behavior with CLI flags only.
109
+
110
+ ### Key Runtime Flags
111
+
112
+ | Flag | Default | Description |
113
+ |------|---------|-------------|
114
+ | `--providers` | `claude,codex` | Comma-separated provider list |
115
+ | `--stall-timeout` | `900` | Cancel when no output progress for this duration |
116
+ | `--review-hard-timeout` | `1800` | Hard deadline for review mode (`0` = disabled) |
117
+ | `--max-provider-parallelism` | `0` | `0` = full parallelism across selected providers |
118
+ | `--enforcement-mode` | `strict` | `strict` fails closed on unmet permissions |
119
+ | `--provider-timeouts` | unset | Per-provider stall-timeout overrides (`provider=seconds`) |
120
+ | `--provider-permissions-json` | unset | Provider permission mapping JSON |
121
+
122
+ Example:
123
+
123
124
  ```bash
124
- ./mco review \
125
+ mco review \
125
126
  --repo . \
126
- --prompt "Review for bugs and security issues." \
127
- --providers claude,codex,gemini,opencode,qwen \
128
- --strict-contract \
129
- --max-provider-parallelism 2 \
127
+ --prompt "Review for bugs." \
128
+ --providers claude,codex,qwen \
130
129
  --stall-timeout 900 \
131
130
  --review-hard-timeout 1800 \
131
+ --max-provider-parallelism 0 \
132
132
  --provider-timeouts qwen=900,codex=900
133
133
  ```
134
134
 
135
- Run mode with hard path constraints:
136
- ```bash
137
- ./mco run \
138
- --repo . \
139
- --prompt "Compare adapter behaviors and return a short markdown summary." \
140
- --providers claude,codex \
141
- --allow-paths runtime,scripts \
142
- --target-paths runtime/adapters,runtime/review_engine.py \
143
- --enforcement-mode strict \
144
- --provider-permissions-json '{"codex":{"sandbox":"workspace-write"},"claude":{"permission_mode":"plan"}}' \
145
- --json
135
+ Run `mco review --help` for the full flag list.
136
+
137
+ ## How It Works
138
+
139
+ ```
140
+ prompt ─> MCO ─┬─> Claude Code ─┐
141
+ ├─> Codex CLI ├─> aggregate ─> artifacts + JSON
142
+ ├─> Gemini CLI │
143
+ ├─> OpenCode │
144
+ └─> Qwen Code ──┘
146
145
  ```
147
146
 
148
- Artifacts are written to:
149
- - `<artifact_base>/<task_id>/summary.md`
150
- - `<artifact_base>/<task_id>/decision.md`
151
- - `<artifact_base>/<task_id>/findings.json`
152
- - `<artifact_base>/<task_id>/run.json`
153
- - `<artifact_base>/<task_id>/providers/*.json`
154
- - `<artifact_base>/<task_id>/raw/*.log`
155
-
156
- `run.json` includes audit fields for reproducibility:
157
- - `effective_cwd`
158
- - `allow_paths_hash`
159
- - `permissions_hash`
160
-
161
- Notes:
162
- - YAML config requires `pyyaml` installed; otherwise use JSON config.
163
- - Review prompt is wrapped with a JSON finding contract, but strict parse enforcement is optional.
164
- - Enable strict gate behavior with `--strict-contract` (or `policy.enforce_findings_contract=true` in config).
165
- - `run` mode does not force findings schema; it focuses on execution aggregation and provider success.
166
- - `result_mode=artifact` (default): write user-facing artifacts and print compact result.
167
- - `result_mode=stdout`: print provider-level result payload to stdout, skip user-facing artifact files.
168
- - `result_mode=both`: write artifacts and print provider-level payload.
169
- - Execution model is `wait-all`: one provider timeout/failure does not stop others.
170
- - Timeout behavior is progress-driven:
171
- - `stall_timeout_seconds`: cancel only when output progress is idle beyond threshold.
172
- - `review_hard_timeout_seconds`: hard deadline applied only in `review` mode.
173
- - `max_provider_parallelism=0` (or omitted) means full parallelism across selected providers.
174
- - `provider_timeouts` are provider-specific stall-timeout overrides.
175
- - `allow_paths` and `target_paths` are validated against `repo_root`; path escape is rejected.
176
- - `enforcement_mode=strict` (default) fails closed when provider permission requirements cannot be honored.
177
-
178
- ## Step5 Benchmark Script
179
- Use this script to generate serial vs full-parallel evidence and write reports under `reports/adapter-contract/<date>/`:
147
+ Each provider runs as an independent subprocess through a uniform adapter contract:
148
+
149
+ 1. **Detect** — check binary presence and auth status
150
+ 2. **Run** — spawn CLI process with prompt, capture stdout/stderr
151
+ 3. **Poll** — monitor process + output byte growth for progress detection
152
+ 4. **Cancel** — SIGTERM/SIGKILL on stall timeout or hard deadline
153
+ 5. **Normalize** — extract structured findings from raw output
154
+
155
+ Execution model is **wait-all**: one provider's timeout or failure never stops others.
156
+
157
+ ## Artifacts
158
+
159
+ Each run produces a structured artifact tree:
180
160
 
181
- ```bash
182
- ./scripts/run_step5_parallel_benchmark.sh
183
161
  ```
162
+ reports/review/<task_id>/
163
+ summary.md # Human-readable summary
164
+ decision.md # PASS / FAIL / ESCALATE / PARTIAL
165
+ findings.json # Aggregated normalized findings (review mode)
166
+ run.json # Machine-readable execution metadata
167
+ providers/ # Per-provider result JSON
168
+ raw/ # Raw stdout/stderr logs
169
+ ```
170
+
171
+ ## License
184
172
 
185
- The generated summary JSON includes separated parse-vs-findings metrics:
186
- - `providers_total`
187
- - `parse_success_rate`
188
- - `effective_findings_count`
189
- - `zero_finding_provider_count`
173
+ UNLICENSED
package/README.zh-CN.md CHANGED
@@ -1,196 +1,173 @@
1
- # MCO 文档索引
1
+ # MCO
2
+
3
+ **MCO — 一条提示词,五个 AI Agent,一份结果。**
2
4
 
3
5
  [English](./README.md) | 简体中文
4
6
 
5
- ## 先读这些
6
- 1. [multi-cli-orchestrator-proposal.md](./multi-cli-orchestrator-proposal.md)
7
- 2. [capability-research.md](./capability-research.md)
8
- 3. [notes.md](./notes.md)
7
+ ## MCO 是什么
8
+
9
+ MCO(Multi-CLI Orchestrator)是一个中立的编排层,将单条提示词并行分发给多个 AI 编程 Agent,汇总执行结果。不绑定任何厂商,不改变你的工作流。Fan-out、Wait-all、Collect。
10
+
11
+ 你继续照常使用 Claude Code、Codex CLI、Gemini CLI、OpenCode、Qwen Code。MCO 负责把它们串联成统一的执行管线,提供结构化输出、进度驱动超时、可复现的产物。
9
12
 
10
- ## 门禁产物
11
- 1. [capability-probe-spec.md](./capability-probe-spec.md)
12
- 2. [adapter-contract-tests.md](./adapter-contract-tests.md)
13
- 3. [dry-run-plan.md](./dry-run-plan.md)
14
- 4. [implementation-gate-checklist.md](./implementation-gate-checklist.md)
13
+ ## 核心特性
15
14
 
16
- ## 接口冻结
17
- 1. [docs/implementation/step0-interface-freeze.md](./docs/implementation/step0-interface-freeze.md)
18
- 2. [docs/contracts/cli-json-v0.1.x.md](./docs/contracts/cli-json-v0.1.x.md)
19
- 3. [docs/contracts/provider-permissions-v0.1.x.md](./docs/contracts/provider-permissions-v0.1.x.md)
15
+ - **并行扇出** — 同时分发到所有 provider,wait-all 语义
16
+ - **进度驱动超时** — agent 自由跑完,仅在长时间无输出时取消
17
+ - **双模式** — `mco review` 结构化代码审查,`mco run` 通用任务执行
18
+ - **厂商中立** — 5 个 CLI 工具统一适配器契约,不偏向任何厂商
19
+ - **机器可读输出** — JSON 结果 + 每个 provider 独立产物树,便于下游自动化
20
20
 
21
- ## 计划与跟踪
22
- 1. [task_plan.md](./task_plan.md)
21
+ ## 支持的 Provider
23
22
 
24
- ## 发布说明
25
- 1. [docs/releases/v0.1.2.md](./docs/releases/v0.1.2.md)
26
- 2. [docs/releases/v0.1.2.zh-CN.md](./docs/releases/v0.1.2.zh-CN.md)
27
- 3. [docs/releases/v0.1.1.md](./docs/releases/v0.1.1.md)
28
- 4. [docs/releases/v0.1.1.zh-CN.md](./docs/releases/v0.1.1.zh-CN.md)
29
- 5. [docs/releases/v0.1.0.md](./docs/releases/v0.1.0.md)
30
- 6. [docs/releases/v0.1.0.zh-CN.md](./docs/releases/v0.1.0.zh-CN.md)
23
+ | Provider | CLI | 状态 |
24
+ |----------|-----|------|
25
+ | Claude Code | `claude` | 已支持 |
26
+ | Codex CLI | `codex` | 已支持 |
27
+ | Gemini CLI | `gemini` | 已支持 |
28
+ | OpenCode | `opencode` | 已支持 |
29
+ | Qwen Code | `qwen` | 已支持 |
31
30
 
32
- ## 统一 CLI(Step 2)
33
- `mco review`:统一的审查入口。
34
- `mco run`:通用任务执行入口(不强制 findings schema)。
31
+ 无需迁移项目,无需重学命令,无需绑定单一工具。
35
32
 
36
- ## 安装
33
+ ## 快速开始
37
34
 
38
- npm 包装器(当前可用,系统需有 Python 3):
35
+ 通过 npm 安装(需要系统有 Python 3):
39
36
 
40
37
  ```bash
41
38
  npm i -g @tt-a1i/mco
42
- mco --help
43
39
  ```
44
40
 
45
- 源码可编辑安装:
41
+ 或从源码安装:
46
42
 
47
43
  ```bash
48
44
  git clone https://github.com/tt-a1i/mco.git
49
45
  cd mco
50
46
  python3 -m pip install -e .
51
- mco --help
52
47
  ```
53
48
 
54
- Python 包(PyPI):
55
- - 目前尚未发布。
56
- - 发布流程已就绪,待完成 PyPI Trusted Publisher 配置后开启。
57
-
58
- 快速开始:
49
+ 运行第一次多 Agent 审查:
59
50
 
60
51
  ```bash
61
- ./mco review \
52
+ mco review \
62
53
  --repo . \
63
54
  --prompt "Review this repository for high-risk bugs and security issues." \
64
- --providers claude,codex
55
+ --providers claude,codex,qwen
65
56
  ```
66
57
 
67
- 机器可读输出:
58
+ ## 使用方式
68
59
 
69
- ```bash
70
- ./mco review --repo . --prompt "Review for bugs." --providers claude,codex --json
71
- ```
60
+ ### Review 模式
72
61
 
73
- stdout 输出结果(不写 `summary.md/decision.md/findings.json/run.json`):
62
+ 结构化代码审查,输出标准化的 findings(含严重级别、分类、证据、建议)。
74
63
 
75
64
  ```bash
76
- ./mco review --repo . --prompt "Review for bugs." --providers claude,codex --result-mode stdout --json
65
+ mco review \
66
+ --repo . \
67
+ --prompt "Review for security vulnerabilities and performance issues." \
68
+ --providers claude,codex,gemini,opencode,qwen \
69
+ --json
77
70
  ```
78
71
 
79
- 通用 run 模式:
72
+ ### Run 模式
73
+
74
+ 通用多 Agent 任务执行,不强制输出格式,provider 自由完成任务。
80
75
 
81
76
  ```bash
82
- ./mco run --repo . --prompt "Summarize the current repo architecture." --providers claude,codex --json
77
+ mco run \
78
+ --repo . \
79
+ --prompt "Summarize the architecture of this project." \
80
+ --providers claude,codex \
81
+ --json
83
82
  ```
84
83
 
85
- 配置文件(JSON):
86
-
87
- ```json
88
- {
89
- "providers": ["claude", "codex"],
90
- "artifact_base": "reports/review",
91
- "state_file": ".mco/state.json",
92
- "policy": {
93
- "timeout_seconds": 180,
94
- "stall_timeout_seconds": 900,
95
- "poll_interval_seconds": 1.0,
96
- "review_hard_timeout_seconds": 1800,
97
- "enforce_findings_contract": false,
98
- "max_retries": 1,
99
- "high_escalation_threshold": 1,
100
- "require_non_empty_findings": true,
101
- "max_provider_parallelism": 0,
102
- "allow_paths": [".", "runtime", "scripts"],
103
- "enforcement_mode": "strict",
104
- "provider_permissions": {
105
- "claude": {
106
- "permission_mode": "plan"
107
- },
108
- "codex": {
109
- "sandbox": "workspace-write"
110
- }
111
- },
112
- "provider_timeouts": {
113
- "claude": 300,
114
- "codex": 240,
115
- "qwen": 240
116
- }
117
- }
118
- }
119
- ```
84
+ ### 结果模式
85
+
86
+ | 模式 | 行为 |
87
+ |------|------|
88
+ | `--result-mode artifact` | 写产物文件,输出摘要(默认) |
89
+ | `--result-mode stdout` | 完整结果输出到 stdout,不写产物文件 |
90
+ | `--result-mode both` | 既写产物又输出完整结果 |
120
91
 
121
- 按配置运行:
92
+ ### 路径约束
93
+
94
+ 限制 agent 可访问的文件范围:
122
95
 
123
96
  ```bash
124
- ./mco review --config ./mco.example.json --repo . --prompt "Review for bugs and security issues."
97
+ mco run \
98
+ --repo . \
99
+ --prompt "Analyze the adapter layer." \
100
+ --providers claude,codex \
101
+ --allow-paths runtime,scripts \
102
+ --target-paths runtime/adapters \
103
+ --enforcement-mode strict
125
104
  ```
126
105
 
127
- CLI 覆盖并发与超时:
106
+ ## 默认值与参数覆盖
107
+
108
+ MCO 默认零配置可用。直接运行即可,按需通过命令行参数覆盖行为。
109
+
110
+ ### 关键运行参数
111
+
112
+ | 参数 | 默认值 | 说明 |
113
+ |------|--------|------|
114
+ | `--providers` | `claude,codex` | 逗号分隔 provider 列表 |
115
+ | `--stall-timeout` | `900` | 无输出进展超过此时间才取消 |
116
+ | `--review-hard-timeout` | `1800` | review 模式硬截止(`0` = 禁用) |
117
+ | `--max-provider-parallelism` | `0` | `0` = 选中 provider 全并行 |
118
+ | `--enforcement-mode` | `strict` | 权限不满足时 fail-closed |
119
+ | `--provider-timeouts` | 未设置 | provider 级 stall timeout 覆盖(`provider=seconds`) |
120
+ | `--provider-permissions-json` | 未设置 | provider 权限映射 JSON |
121
+
122
+ 示例:
128
123
 
129
124
  ```bash
130
- ./mco review \
125
+ mco review \
131
126
  --repo . \
132
- --prompt "Review for bugs and security issues." \
133
- --providers claude,codex,gemini,opencode,qwen \
134
- --strict-contract \
135
- --max-provider-parallelism 2 \
127
+ --prompt "Review for bugs." \
128
+ --providers claude,codex,qwen \
136
129
  --stall-timeout 900 \
137
130
  --review-hard-timeout 1800 \
131
+ --max-provider-parallelism 0 \
138
132
  --provider-timeouts qwen=900,codex=900
139
133
  ```
140
134
 
141
- 带路径硬约束的 run 模式:
135
+ 运行 `mco review --help` 查看完整参数列表。
142
136
 
143
- ```bash
144
- ./mco run \
145
- --repo . \
146
- --prompt "Compare adapter behaviors and return a short markdown summary." \
147
- --providers claude,codex \
148
- --allow-paths runtime,scripts \
149
- --target-paths runtime/adapters,runtime/review_engine.py \
150
- --enforcement-mode strict \
151
- --provider-permissions-json '{"codex":{"sandbox":"workspace-write"},"claude":{"permission_mode":"plan"}}' \
152
- --json
137
+ ## 工作原理
138
+
139
+ ```
140
+ prompt ─> MCO ─┬─> Claude Code ─┐
141
+ ├─> Codex CLI ├─> 聚合 ─> 产物 + JSON
142
+ ├─> Gemini CLI │
143
+ ├─> OpenCode │
144
+ └─> Qwen Code ──┘
153
145
  ```
154
146
 
155
- 产物目录:
156
- - `<artifact_base>/<task_id>/summary.md`
157
- - `<artifact_base>/<task_id>/decision.md`
158
- - `<artifact_base>/<task_id>/findings.json`
159
- - `<artifact_base>/<task_id>/run.json`
160
- - `<artifact_base>/<task_id>/providers/*.json`
161
- - `<artifact_base>/<task_id>/raw/*.log`
162
-
163
- `run.json` 审计字段:
164
- - `effective_cwd`
165
- - `allow_paths_hash`
166
- - `permissions_hash`
167
-
168
- 说明:
169
- - YAML 配置需要 `pyyaml`;否则使用 JSON 配置。
170
- - review 提示词会附加 JSON finding 合同;是否强制由策略控制。
171
- - 可用 `--strict-contract`(或配置 `policy.enforce_findings_contract=true`)开启严格合同。
172
- - `run` 模式不强制 findings schema,聚焦执行与聚合。
173
- - `result_mode=artifact`(默认):写产物并输出简报。
174
- - `result_mode=stdout`:输出 provider 结果,不写用户侧产物。
175
- - `result_mode=both`:既写产物又输出 provider 结果。
176
- - 执行模型为 `wait-all`:单 provider 失败/超时不会中断其它 provider。
177
- - 超时是进度驱动:
178
- - `stall_timeout_seconds`:仅在长时间无输出进展时取消。
179
- - `review_hard_timeout_seconds`:仅 `review` 模式硬截止。
180
- - `max_provider_parallelism=0`(或省略)表示全并行。
181
- - `provider_timeouts` 为 provider 级 stall-timeout 覆盖项。
182
- - `allow_paths` 与 `target_paths` 会对 `repo_root` 做越界校验。
183
- - `enforcement_mode=strict`(默认)下,权限要求不满足会 fail-closed。
184
-
185
- ## Step5 性能脚本
186
- 用于产出串行 vs 全并行对比报告(写入 `reports/adapter-contract/<date>/`):
147
+ 每个 provider 通过统一的适配器契约作为独立子进程运行:
148
+
149
+ 1. **Detect** — 检测二进制文件和认证状态
150
+ 2. **Run** — 启动 CLI 进程,传入提示词,捕获 stdout/stderr
151
+ 3. **Poll** — 监控进程状态 + 输出字节增长,判断活跃度
152
+ 4. **Cancel** — stall timeout 或硬截止时 SIGTERM/SIGKILL
153
+ 5. **Normalize** — 从原始输出中提取结构化 findings
154
+
155
+ 执行模型是 **wait-all**:单个 provider 超时或失败不会中断其他 provider。
156
+
157
+ ## 产物结构
158
+
159
+ 每次执行生成结构化产物树:
187
160
 
188
- ```bash
189
- ./scripts/run_step5_parallel_benchmark.sh
190
161
  ```
162
+ reports/review/<task_id>/
163
+ summary.md # 人类可读摘要
164
+ decision.md # PASS / FAIL / ESCALATE / PARTIAL
165
+ findings.json # 聚合后的标准化 findings(review 模式)
166
+ run.json # 机器可读执行元数据
167
+ providers/ # 各 provider 结果 JSON
168
+ raw/ # 原始 stdout/stderr 日志
169
+ ```
170
+
171
+ ## 许可证
191
172
 
192
- 生成的 summary JSON 包含:
193
- - `providers_total`
194
- - `parse_success_rate`
195
- - `effective_findings_count`
196
- - `zero_finding_provider_count`
173
+ UNLICENSED
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tt-a1i/mco",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Node wrapper for the mco CLI (Python runtime required).",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {
package/runtime/cli.py CHANGED
@@ -6,7 +6,7 @@ import sys
6
6
  from pathlib import Path
7
7
  from typing import Dict, List
8
8
 
9
- from .config import ReviewConfig, ReviewPolicy, load_review_config
9
+ from .config import ReviewConfig, ReviewPolicy
10
10
  from .review_engine import ReviewRequest, run_review
11
11
 
12
12
 
@@ -141,7 +141,6 @@ def _add_common_execution_args(parser: argparse.ArgumentParser) -> None:
141
141
  parser.add_argument("--repo", default=".", help="Repository root path")
142
142
  parser.add_argument("--prompt", required=True, help="Task prompt")
143
143
  parser.add_argument("--providers", default="", help="Comma-separated providers, e.g. claude,codex")
144
- parser.add_argument("--config", default="", help="Config file path (.json or .yaml/.yml)")
145
144
  parser.add_argument("--artifact-base", default="", help="Artifact base directory override")
146
145
  parser.add_argument("--state-file", default="", help="Runtime state file override")
147
146
  parser.add_argument("--task-id", default="", help="Optional stable task id")
@@ -215,7 +214,7 @@ def build_parser() -> argparse.ArgumentParser:
215
214
 
216
215
 
217
216
  def _resolve_config(args: argparse.Namespace) -> ReviewConfig:
218
- cfg = load_review_config(args.config or None)
217
+ cfg = ReviewConfig()
219
218
  providers = _parse_providers(args.providers) if args.providers else cfg.providers
220
219
  artifact_base = args.artifact_base or cfg.artifact_base
221
220
  state_file = args.state_file or cfg.state_file
@@ -239,7 +238,7 @@ def _resolve_config(args: argparse.Namespace) -> ReviewConfig:
239
238
  review_hard_timeout_seconds = cfg.policy.review_hard_timeout_seconds
240
239
  if args.review_hard_timeout is not None and args.review_hard_timeout >= 0:
241
240
  review_hard_timeout_seconds = args.review_hard_timeout
242
- enforce_findings_contract = cfg.policy.enforce_findings_contract or bool(args.strict_contract)
241
+ enforce_findings_contract = bool(args.strict_contract)
243
242
 
244
243
  policy = ReviewPolicy(
245
244
  timeout_seconds=cfg.policy.timeout_seconds,
@@ -268,7 +267,7 @@ def main(argv: List[str] | None = None) -> int:
268
267
 
269
268
  try:
270
269
  cfg = _resolve_config(args)
271
- except (FileNotFoundError, RuntimeError, ValueError) as exc:
270
+ except ValueError as exc:
272
271
  print(f"Configuration error: {exc}", file=sys.stderr)
273
272
  return 2
274
273
  repo_root = str(Path(args.repo).resolve())
package/runtime/config.py CHANGED
@@ -1,9 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- import json
4
3
  from dataclasses import dataclass, field
5
- from pathlib import Path
6
- from typing import Any, Dict, List, Optional
4
+ from typing import Dict, List
7
5
 
8
6
  DEFAULT_PROVIDER_TIMEOUTS: Dict[str, int] = {
9
7
  }
@@ -32,158 +30,3 @@ class ReviewConfig:
32
30
  artifact_base: str = "reports/review"
33
31
  state_file: str = ".mco/state.json"
34
32
  policy: ReviewPolicy = field(default_factory=ReviewPolicy)
35
-
36
-
37
- def _as_bool(value: Any, default: bool) -> bool:
38
- if isinstance(value, bool):
39
- return value
40
- if isinstance(value, str):
41
- lowered = value.strip().lower()
42
- if lowered in ("true", "1", "yes", "y", "on"):
43
- return True
44
- if lowered in ("false", "0", "no", "n", "off"):
45
- return False
46
- return default
47
-
48
-
49
- def _to_policy(payload: Dict[str, Any]) -> ReviewPolicy:
50
- raw_provider_timeouts = payload.get("provider_timeouts", {})
51
- provider_timeouts: Dict[str, int] = dict(DEFAULT_PROVIDER_TIMEOUTS)
52
- if isinstance(raw_provider_timeouts, dict):
53
- for key, value in raw_provider_timeouts.items():
54
- provider = str(key).strip()
55
- if not provider:
56
- continue
57
- try:
58
- timeout = int(value)
59
- except Exception:
60
- continue
61
- if timeout <= 0:
62
- continue
63
- provider_timeouts[provider] = timeout
64
-
65
- try:
66
- max_parallel = int(payload.get("max_provider_parallelism", 0))
67
- except Exception:
68
- max_parallel = 0
69
- if max_parallel < 0:
70
- max_parallel = 0
71
-
72
- raw_allow_paths = payload.get("allow_paths", ["."])
73
- allow_paths: List[str]
74
- if isinstance(raw_allow_paths, str):
75
- allow_paths = [item.strip() for item in raw_allow_paths.split(",") if item.strip()]
76
- elif isinstance(raw_allow_paths, list):
77
- allow_paths = [str(item).strip() for item in raw_allow_paths if str(item).strip()]
78
- else:
79
- allow_paths = ["."]
80
- if not allow_paths:
81
- allow_paths = ["."]
82
-
83
- raw_provider_permissions = payload.get("provider_permissions", {})
84
- provider_permissions: Dict[str, Dict[str, str]] = {}
85
- if isinstance(raw_provider_permissions, dict):
86
- for provider, permissions in raw_provider_permissions.items():
87
- provider_name = str(provider).strip()
88
- if not provider_name or not isinstance(permissions, dict):
89
- continue
90
- normalized: Dict[str, str] = {}
91
- for key, value in permissions.items():
92
- key_name = str(key).strip()
93
- if not key_name:
94
- continue
95
- normalized[key_name] = str(value)
96
- if normalized:
97
- provider_permissions[provider_name] = normalized
98
-
99
- enforcement_mode = str(payload.get("enforcement_mode", "strict")).strip().lower()
100
- if enforcement_mode not in ("strict", "best_effort"):
101
- enforcement_mode = "strict"
102
-
103
- try:
104
- stall_timeout_seconds = int(payload.get("stall_timeout_seconds", 900))
105
- except Exception:
106
- stall_timeout_seconds = 900
107
- if stall_timeout_seconds <= 0:
108
- stall_timeout_seconds = 900
109
-
110
- try:
111
- poll_interval_seconds = float(payload.get("poll_interval_seconds", 1.0))
112
- except Exception:
113
- poll_interval_seconds = 1.0
114
- if poll_interval_seconds <= 0:
115
- poll_interval_seconds = 1.0
116
-
117
- try:
118
- review_hard_timeout_seconds = int(payload.get("review_hard_timeout_seconds", 1800))
119
- except Exception:
120
- review_hard_timeout_seconds = 1800
121
- if review_hard_timeout_seconds < 0:
122
- review_hard_timeout_seconds = 1800
123
-
124
- return ReviewPolicy(
125
- timeout_seconds=int(payload.get("timeout_seconds", 180)),
126
- stall_timeout_seconds=stall_timeout_seconds,
127
- poll_interval_seconds=poll_interval_seconds,
128
- review_hard_timeout_seconds=review_hard_timeout_seconds,
129
- enforce_findings_contract=_as_bool(payload.get("enforce_findings_contract", False), False),
130
- max_retries=int(payload.get("max_retries", 1)),
131
- high_escalation_threshold=int(payload.get("high_escalation_threshold", 1)),
132
- require_non_empty_findings=_as_bool(payload.get("require_non_empty_findings", True), True),
133
- max_provider_parallelism=max_parallel,
134
- provider_timeouts=provider_timeouts,
135
- allow_paths=allow_paths,
136
- provider_permissions=provider_permissions,
137
- enforcement_mode=enforcement_mode,
138
- )
139
-
140
-
141
- def _normalize_payload(payload: Dict[str, Any]) -> ReviewConfig:
142
- policy_payload = payload.get("policy", {})
143
- if not isinstance(policy_payload, dict):
144
- policy_payload = {}
145
- providers = payload.get("providers", ["claude", "codex"])
146
- if isinstance(providers, str):
147
- providers = [item.strip() for item in providers.split(",") if item.strip()]
148
- if not isinstance(providers, list):
149
- providers = ["claude", "codex"]
150
- providers = [str(item).strip() for item in providers if str(item).strip()]
151
- if not providers:
152
- providers = ["claude", "codex"]
153
-
154
- return ReviewConfig(
155
- providers=providers,
156
- artifact_base=str(payload.get("artifact_base", "reports/review")),
157
- state_file=str(payload.get("state_file", ".mco/state.json")),
158
- policy=_to_policy(policy_payload),
159
- )
160
-
161
-
162
- def load_review_config(config_path: Optional[str]) -> ReviewConfig:
163
- if not config_path:
164
- return ReviewConfig()
165
- path = Path(config_path)
166
- if not path.exists():
167
- raise FileNotFoundError(f"config file not found: {config_path}")
168
-
169
- suffix = path.suffix.lower()
170
- raw_text = path.read_text(encoding="utf-8")
171
- if suffix == ".json":
172
- payload = json.loads(raw_text)
173
- if not isinstance(payload, dict):
174
- raise ValueError("config root must be an object")
175
- return _normalize_payload(payload)
176
-
177
- if suffix in (".yaml", ".yml"):
178
- try:
179
- import yaml # type: ignore
180
- except Exception as exc:
181
- raise RuntimeError(
182
- "YAML config requires pyyaml. Install with: pip install pyyaml, or use a .json config."
183
- ) from exc
184
- payload = yaml.safe_load(raw_text)
185
- if not isinstance(payload, dict):
186
- raise ValueError("config root must be a map")
187
- return _normalize_payload(payload)
188
-
189
- raise ValueError(f"unsupported config format: {config_path}")