xone-cli 0.1.2__tar.gz → 0.1.4__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 (54) hide show
  1. {xone_cli-0.1.2 → xone_cli-0.1.4}/CHANGELOG.md +11 -0
  2. {xone_cli-0.1.2/src/xone_cli.egg-info → xone_cli-0.1.4}/PKG-INFO +11 -2
  3. {xone_cli-0.1.2 → xone_cli-0.1.4}/README.md +10 -1
  4. {xone_cli-0.1.2 → xone_cli-0.1.4}/README.zh-CN.md +10 -1
  5. {xone_cli-0.1.2 → xone_cli-0.1.4}/docs/feedback/open-source-feedback-ledger.md +2 -0
  6. {xone_cli-0.1.2 → xone_cli-0.1.4}/docs/install.md +32 -0
  7. {xone_cli-0.1.2 → xone_cli-0.1.4}/pyproject.toml +1 -1
  8. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli/__init__.py +1 -1
  9. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli/cli.py +17 -0
  10. xone_cli-0.1.4/src/xone_cli/open_source_eval.py +244 -0
  11. {xone_cli-0.1.2 → xone_cli-0.1.4/src/xone_cli.egg-info}/PKG-INFO +11 -2
  12. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli.egg-info/SOURCES.txt +2 -0
  13. {xone_cli-0.1.2 → xone_cli-0.1.4}/tests/test_cli.py +1 -1
  14. {xone_cli-0.1.2 → xone_cli-0.1.4}/tests/test_docs.py +6 -3
  15. xone_cli-0.1.4/tests/test_open_source_eval.py +173 -0
  16. {xone_cli-0.1.2 → xone_cli-0.1.4}/LICENSE +0 -0
  17. {xone_cli-0.1.2 → xone_cli-0.1.4}/MANIFEST.in +0 -0
  18. {xone_cli-0.1.2 → xone_cli-0.1.4}/docs/product-foundation.md +0 -0
  19. {xone_cli-0.1.2 → xone_cli-0.1.4}/docs/release.md +0 -0
  20. {xone_cli-0.1.2 → xone_cli-0.1.4}/fixtures/open-source-samples/aider.json +0 -0
  21. {xone_cli-0.1.2 → xone_cli-0.1.4}/fixtures/open-source-samples/anthropics-claude-code.json +0 -0
  22. {xone_cli-0.1.2 → xone_cli-0.1.4}/fixtures/open-source-samples/cline.json +0 -0
  23. {xone_cli-0.1.2 → xone_cli-0.1.4}/fixtures/open-source-samples/continue.json +0 -0
  24. {xone_cli-0.1.2 → xone_cli-0.1.4}/fixtures/open-source-samples/desktop-commander-mcp.json +0 -0
  25. {xone_cli-0.1.2 → xone_cli-0.1.4}/fixtures/open-source-samples/docker-mcp-gateway.json +0 -0
  26. {xone_cli-0.1.2 → xone_cli-0.1.4}/fixtures/open-source-samples/github-github-mcp-server.json +0 -0
  27. {xone_cli-0.1.2 → xone_cli-0.1.4}/fixtures/open-source-samples/microsoft-playwright-mcp.json +0 -0
  28. {xone_cli-0.1.2 → xone_cli-0.1.4}/fixtures/open-source-samples/modelcontextprotocol-servers.json +0 -0
  29. {xone_cli-0.1.2 → xone_cli-0.1.4}/fixtures/open-source-samples/openai-codex.json +0 -0
  30. {xone_cli-0.1.2 → xone_cli-0.1.4}/fixtures/open-source-samples/openhands.json +0 -0
  31. {xone_cli-0.1.2 → xone_cli-0.1.4}/fixtures/open-source-samples/upstash-context7.json +0 -0
  32. {xone_cli-0.1.2 → xone_cli-0.1.4}/ops/constraints/main-entry.md +0 -0
  33. {xone_cli-0.1.2 → xone_cli-0.1.4}/ops/constraints/production.md +0 -0
  34. {xone_cli-0.1.2 → xone_cli-0.1.4}/ops/opt-overlay.md +0 -0
  35. {xone_cli-0.1.2 → xone_cli-0.1.4}/ops/skills/evolution.md +0 -0
  36. {xone_cli-0.1.2 → xone_cli-0.1.4}/setup.cfg +0 -0
  37. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli/__main__.py +0 -0
  38. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli/evidence.py +0 -0
  39. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli/lab.py +0 -0
  40. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli/model.py +0 -0
  41. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli/release.py +0 -0
  42. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli/repo_context.py +0 -0
  43. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli/risk.py +0 -0
  44. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli/runbook.py +0 -0
  45. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli/tooling.py +0 -0
  46. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli.egg-info/dependency_links.txt +0 -0
  47. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli.egg-info/entry_points.txt +0 -0
  48. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli.egg-info/requires.txt +0 -0
  49. {xone_cli-0.1.2 → xone_cli-0.1.4}/src/xone_cli.egg-info/top_level.txt +0 -0
  50. {xone_cli-0.1.2 → xone_cli-0.1.4}/tests/test_evidence.py +0 -0
  51. {xone_cli-0.1.2 → xone_cli-0.1.4}/tests/test_release.py +0 -0
  52. {xone_cli-0.1.2 → xone_cli-0.1.4}/tests/test_repo_context.py +0 -0
  53. {xone_cli-0.1.2 → xone_cli-0.1.4}/tests/test_risk_lab.py +0 -0
  54. {xone_cli-0.1.2 → xone_cli-0.1.4}/tests/test_tooling.py +0 -0
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.4
4
+
5
+ - Add a `curl` fallback for GitHub metadata refresh when Python's local certificate store cannot verify the GitHub API certificate.
6
+ - Keep open-source adoption evaluation from failing on macOS/Python certificate-store drift.
7
+
8
+ ## 0.1.3
9
+
10
+ - Add `xone eval open-source` for repeatable, sanitized open-source adoption evaluation.
11
+ - Support metadata-only evaluation and optional `--clone-root` shallow public clone evaluation.
12
+ - Keep the evaluator read-only by default and document that it does not run third-party scripts, containers, tests, or project commands.
13
+
3
14
  ## 0.1.2
4
15
 
5
16
  - Pin `xone doctor --install-plan` to the current recommended X-One tool versions.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xone-cli
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Unified CLI entry point for X-One Agent Evidence Loop workflows.
5
5
  Author: X-One-AI
6
6
  License-Expression: MIT
@@ -61,6 +61,14 @@ xone runbook --head HEAD --dry-run
61
61
 
62
62
  `xone runbook` auto-detects the local repository default branch. Remote GitHub URLs are not accepted yet; clone the repository first, then pass the local path with `--repo`.
63
63
 
64
+ For repeatable open-source adoption checks, use the read-only evaluator:
65
+
66
+ ```bash
67
+ xone eval open-source --repos repos.txt --output evaluation.md --format markdown
68
+ ```
69
+
70
+ Add `--clone-root .xone-eval-clones` when you want X-One to shallow-clone public repositories and run only X-One dry-run commands against those local clones. It does not run third-party install scripts, tests, containers, or project commands.
71
+
64
72
  `xone-cli` orchestrates these X-One tools:
65
73
 
66
74
  - `agent-pr-evidence`
@@ -81,7 +89,8 @@ Current recommended tool versions:
81
89
  - It does not post GitHub comments.
82
90
  - It does not modify repositories automatically.
83
91
  - It does not make allow/deny runtime enforcement decisions.
84
- - It does not make hidden network calls.
92
+ - It does not run third-party project scripts.
93
+ - `xone eval open-source` makes explicit GitHub API and `git clone --depth 1` calls only when requested by the command.
85
94
 
86
95
  ## Docs
87
96
 
@@ -34,6 +34,14 @@ xone runbook --head HEAD --dry-run
34
34
 
35
35
  `xone runbook` auto-detects the local repository default branch. Remote GitHub URLs are not accepted yet; clone the repository first, then pass the local path with `--repo`.
36
36
 
37
+ For repeatable open-source adoption checks, use the read-only evaluator:
38
+
39
+ ```bash
40
+ xone eval open-source --repos repos.txt --output evaluation.md --format markdown
41
+ ```
42
+
43
+ Add `--clone-root .xone-eval-clones` when you want X-One to shallow-clone public repositories and run only X-One dry-run commands against those local clones. It does not run third-party install scripts, tests, containers, or project commands.
44
+
37
45
  `xone-cli` orchestrates these X-One tools:
38
46
 
39
47
  - `agent-pr-evidence`
@@ -54,7 +62,8 @@ Current recommended tool versions:
54
62
  - It does not post GitHub comments.
55
63
  - It does not modify repositories automatically.
56
64
  - It does not make allow/deny runtime enforcement decisions.
57
- - It does not make hidden network calls.
65
+ - It does not run third-party project scripts.
66
+ - `xone eval open-source` makes explicit GitHub API and `git clone --depth 1` calls only when requested by the command.
58
67
 
59
68
  ## Docs
60
69
 
@@ -34,6 +34,14 @@ xone runbook --head HEAD --dry-run
34
34
 
35
35
  `xone runbook` 会自动探测本地仓库默认分支。当前还不直接接收远程 GitHub URL;请先 clone 仓库,再用 `--repo` 传本地路径。
36
36
 
37
+ 如果要重复执行开源样本替代真实用户反馈评测,可以使用只读评测入口:
38
+
39
+ ```bash
40
+ xone eval open-source --repos repos.txt --output evaluation.md --format markdown
41
+ ```
42
+
43
+ 当你希望 X-One shallow clone 公开仓库并只运行 X-One dry-run 命令时,增加 `--clone-root .xone-eval-clones`。它不会运行第三方安装脚本、测试、容器或项目命令。
44
+
37
45
  `xone-cli` 编排这些 X-One 工具:
38
46
 
39
47
  - `agent-pr-evidence`
@@ -54,7 +62,8 @@ xone runbook --head HEAD --dry-run
54
62
  - 不自动发布 GitHub 评论。
55
63
  - 不自动修改仓库。
56
64
  - 不做 runtime allow/deny enforcement。
57
- - 不做隐藏网络调用。
65
+ - 不运行第三方项目脚本。
66
+ - `xone eval open-source` 只会在用户显式执行该命令时调用 GitHub API 和 `git clone --depth 1`。
58
67
 
59
68
  ## 文档
60
69
 
@@ -12,6 +12,8 @@ Rules:
12
12
 
13
13
  The first ledger entries live in `fixtures/open-source-samples/`.
14
14
 
15
+ Use `xone eval open-source --repos repos.txt --output evaluation.json --format json` to refresh sanitized evaluation records. Add `--clone-root <dir>` only when shallow public clones are acceptable for the review.
16
+
15
17
  | Source | Stars | Forks | Type | Feedback |
16
18
  | --- | ---: | ---: | --- | --- |
17
19
  | modelcontextprotocol/servers | 87186 | 10998 | mcp-server-examples | catalog-update |
@@ -8,6 +8,7 @@ xone --version
8
8
  xone doctor
9
9
  xone doctor --install-plan
10
10
  xone runbook --head HEAD --dry-run
11
+ xone eval open-source --repos repos.txt --output evaluation.md --format markdown
11
12
  ```
12
13
 
13
14
  ## User Install
@@ -17,6 +18,7 @@ python -m pip install xone-cli
17
18
  xone doctor
18
19
  xone doctor --install-plan
19
20
  xone runbook --head HEAD --dry-run
21
+ xone eval open-source --repos repos.txt --output evaluation.md --format markdown
20
22
  ```
21
23
 
22
24
  `xone doctor` reports missing X-One tools and shows install guidance.
@@ -35,3 +37,33 @@ Current scenario groups:
35
37
  git clone --depth 1 https://github.com/owner/repo
36
38
  xone runbook --repo repo --head HEAD --dry-run
37
39
  ```
40
+
41
+ ## Open-Source Adoption Evaluation
42
+
43
+ Use `xone eval open-source` when you want a repeatable, sanitized surrogate feedback pass over public repositories.
44
+
45
+ Create a repo list:
46
+
47
+ ```text
48
+ openai/codex
49
+ https://github.com/docker/mcp-gateway
50
+ ```
51
+
52
+ Metadata-only evaluation:
53
+
54
+ ```bash
55
+ xone eval open-source --repos repos.txt --output evaluation.json --format json
56
+ ```
57
+
58
+ Public shallow-clone evaluation:
59
+
60
+ ```bash
61
+ xone eval open-source --repos repos.txt --clone-root .xone-eval-clones --output evaluation.md --format markdown
62
+ ```
63
+
64
+ Safety boundary:
65
+
66
+ - The command records public repository metadata and X-One command summaries.
67
+ - It does not label third-party repositories as safe or unsafe.
68
+ - It does not run third-party install scripts, test scripts, containers, or project commands.
69
+ - `--clone-root` performs `git clone --depth 1` only, then runs X-One dry-run commands against the local clones.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "xone-cli"
7
- version = "0.1.2"
7
+ version = "0.1.4"
8
8
  description = "Unified CLI entry point for X-One Agent Evidence Loop workflows."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.1.2"
3
+ __version__ = "0.1.4"
@@ -9,6 +9,7 @@ from collections.abc import Sequence
9
9
  from xone_cli import __version__
10
10
  from xone_cli.evidence import build_failure_packet, collect_evidence, collect_for_runbook, gate_evidence
11
11
  from xone_cli.lab import render_evidence_loop_lab
12
+ from xone_cli.open_source_eval import evaluate_open_source
12
13
  from xone_cli.risk import render_risk_context
13
14
  from xone_cli.runbook import write_runbook
14
15
  from xone_cli.release import print_release_report, verify_release
@@ -71,6 +72,14 @@ def build_parser() -> argparse.ArgumentParser:
71
72
  verify.add_argument("--smoke", action="store_true")
72
73
  verify.add_argument("--json", action="store_true")
73
74
 
75
+ eval_parser = subparsers.add_parser("eval", help="run safe X-One evaluation workflows")
76
+ eval_subparsers = eval_parser.add_subparsers(dest="eval_command")
77
+ open_source = eval_subparsers.add_parser("open-source", help="evaluate public repositories as surrogate feedback")
78
+ open_source.add_argument("--repos", type=Path, required=True, help="text file containing owner/repo or GitHub URLs")
79
+ open_source.add_argument("--clone-root", type=Path, help="optional directory for shallow public clones")
80
+ open_source.add_argument("--output", type=Path, required=True, help="evaluation output path")
81
+ open_source.add_argument("--format", choices=("json", "markdown"), default="json")
82
+
74
83
  runbook = subparsers.add_parser("runbook", help="assemble a local X-One evidence runbook")
75
84
  _add_evidence_collection_args(runbook, require_base=False)
76
85
  runbook.add_argument("--output", type=Path)
@@ -145,6 +154,14 @@ def main(argv: Sequence[str] | None = None) -> int:
145
154
  print_release_report(report, as_json=args.json)
146
155
  return 0 if report["ok"] else 1
147
156
 
157
+ if args.command == "eval" and args.eval_command == "open-source":
158
+ return evaluate_open_source(
159
+ repos_file=args.repos,
160
+ clone_root=args.clone_root,
161
+ output=args.output,
162
+ output_format=args.format,
163
+ )
164
+
148
165
  if args.command == "runbook":
149
166
  code, report, message = collect_for_runbook(
150
167
  repo=args.repo,
@@ -0,0 +1,244 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import subprocess
5
+ import urllib.error
6
+ import urllib.request
7
+ from dataclasses import dataclass
8
+ from datetime import date
9
+ from pathlib import Path
10
+ from urllib.parse import urlparse
11
+
12
+ from xone_cli.evidence import collect_for_runbook
13
+ from xone_cli.tooling import doctor_status
14
+
15
+
16
+ BATCH_SCHEMA_VERSION = "xone.open_source_evaluation_batch.v1"
17
+ RECORD_SCHEMA_VERSION = "xone.open_source_evaluation.v1"
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class EvalRecord:
22
+ source: str
23
+ source_url: str
24
+ snapshot_date: str
25
+ stars: int
26
+ forks: int
27
+ open_issues: int
28
+ default_branch: str
29
+ language: str | None
30
+ commands_run: list[dict]
31
+ sanitization: dict
32
+ decision: str
33
+
34
+ def to_dict(self) -> dict:
35
+ return {
36
+ "schema_version": RECORD_SCHEMA_VERSION,
37
+ "source": self.source,
38
+ "source_url": self.source_url,
39
+ "snapshot_date": self.snapshot_date,
40
+ "stars": self.stars,
41
+ "forks": self.forks,
42
+ "open_issues": self.open_issues,
43
+ "default_branch": self.default_branch,
44
+ "language": self.language,
45
+ "categories": [],
46
+ "xone_tools_used": ["xone-cli"],
47
+ "commands_run": self.commands_run,
48
+ "role_findings": [],
49
+ "sanitization": self.sanitization,
50
+ "decision": self.decision,
51
+ }
52
+
53
+
54
+ def evaluate_open_source(
55
+ *,
56
+ repos_file: Path,
57
+ output: Path,
58
+ output_format: str,
59
+ clone_root: Path | None = None,
60
+ snapshot_date: date | None = None,
61
+ ) -> int:
62
+ snapshot = snapshot_date or date.today()
63
+ sources = read_repo_refs(repos_file)
64
+ records = []
65
+ for source in sources:
66
+ metadata = fetch_github_metadata(source, snapshot)
67
+ commands = [
68
+ {
69
+ "name": "doctor-json",
70
+ "command": "xone doctor --json",
71
+ "mode": "read-only",
72
+ "result": "pass" if doctor_status().ok else "needs-install",
73
+ }
74
+ ]
75
+ if clone_root is None:
76
+ commands.append(
77
+ {
78
+ "name": "runbook-dry-run",
79
+ "command": "xone runbook --repo <local-clone> --head HEAD --dry-run",
80
+ "mode": "read-only",
81
+ "result": "skipped",
82
+ "reason": "clone-root not provided",
83
+ }
84
+ )
85
+ else:
86
+ try:
87
+ local_repo = clone_repo(metadata, clone_root)
88
+ except Exception as exc:
89
+ commands.append(
90
+ {
91
+ "name": "shallow-clone",
92
+ "command": f"git clone --depth 1 {metadata['source_url']} <clone-root>",
93
+ "mode": "read-only",
94
+ "result": "fail",
95
+ "output_summary": str(exc),
96
+ }
97
+ )
98
+ else:
99
+ code, _report, message = collect_for_runbook(
100
+ repo=str(local_repo),
101
+ base=None,
102
+ head="HEAD",
103
+ test_logs=[],
104
+ profile="strict",
105
+ dry_run=True,
106
+ )
107
+ commands.append(
108
+ {
109
+ "name": "runbook-dry-run",
110
+ "command": "xone runbook --repo <local-clone> --head HEAD --dry-run",
111
+ "mode": "read-only",
112
+ "result": "pass" if code == 0 else "fail",
113
+ "output_summary": message.strip(),
114
+ }
115
+ )
116
+ records.append(_record_from_metadata(metadata, commands).to_dict())
117
+
118
+ batch = {
119
+ "schema_version": BATCH_SCHEMA_VERSION,
120
+ "snapshot_date": snapshot.isoformat(),
121
+ "records": records,
122
+ }
123
+ if output_format == "json":
124
+ output.write_text(json.dumps(batch, indent=2, sort_keys=True) + "\n", encoding="utf-8")
125
+ else:
126
+ output.write_text(render_markdown(batch), encoding="utf-8")
127
+ print(f"Wrote {len(records)} open-source evaluation records to {output}")
128
+ return 0
129
+
130
+
131
+ def read_repo_refs(path: Path) -> list[str]:
132
+ refs = []
133
+ for line in path.read_text(encoding="utf-8").splitlines():
134
+ value = line.strip()
135
+ if not value or value.startswith("#"):
136
+ continue
137
+ refs.append(normalize_repo_ref(value))
138
+ return refs
139
+
140
+
141
+ def normalize_repo_ref(value: str) -> str:
142
+ parsed = urlparse(value)
143
+ if parsed.netloc == "github.com":
144
+ parts = [part for part in parsed.path.split("/") if part]
145
+ if len(parts) >= 2:
146
+ return f"{parts[0]}/{parts[1].removesuffix('.git')}"
147
+ return value.removesuffix(".git")
148
+
149
+
150
+ def fetch_github_metadata(repo: str, snapshot_date: date) -> dict:
151
+ payload = _fetch_github_payload(repo)
152
+ return {
153
+ "source": payload["full_name"],
154
+ "source_url": payload["html_url"],
155
+ "snapshot_date": snapshot_date.isoformat(),
156
+ "stars": payload["stargazers_count"],
157
+ "forks": payload["forks_count"],
158
+ "open_issues": payload["open_issues_count"],
159
+ "default_branch": payload["default_branch"],
160
+ "language": payload.get("language"),
161
+ }
162
+
163
+
164
+ def _fetch_github_payload(repo: str) -> dict:
165
+ request = urllib.request.Request(
166
+ f"https://api.github.com/repos/{repo}",
167
+ headers={"Accept": "application/vnd.github+json", "User-Agent": "xone-cli-open-source-eval"},
168
+ )
169
+ try:
170
+ with urllib.request.urlopen(request, timeout=20) as response:
171
+ return json.load(response)
172
+ except (urllib.error.URLError, TimeoutError, OSError) as exc:
173
+ try:
174
+ completed = subprocess.run(
175
+ ["curl", "-fsSL", f"https://api.github.com/repos/{repo}"],
176
+ check=True,
177
+ text=True,
178
+ stdout=subprocess.PIPE,
179
+ stderr=subprocess.PIPE,
180
+ )
181
+ except (FileNotFoundError, subprocess.CalledProcessError) as fallback_exc:
182
+ raise RuntimeError(f"Unable to fetch GitHub metadata for {repo}: {exc}; fallback failed: {fallback_exc}") from fallback_exc
183
+ return json.loads(completed.stdout)
184
+
185
+
186
+ def clone_repo(record: dict, root: Path) -> Path:
187
+ root.mkdir(parents=True, exist_ok=True)
188
+ target = root / record["source"].replace("/", "-")
189
+ if (target / ".git").exists():
190
+ return target
191
+ subprocess.run(
192
+ ["git", "clone", "--depth", "1", record["source_url"], str(target)],
193
+ check=True,
194
+ text=True,
195
+ stdout=subprocess.PIPE,
196
+ stderr=subprocess.PIPE,
197
+ )
198
+ return target
199
+
200
+
201
+ def render_markdown(batch: dict) -> str:
202
+ lines = [
203
+ "# X-One Open-Source Adoption Evaluation",
204
+ "",
205
+ f"Snapshot date: {batch['snapshot_date']}",
206
+ "",
207
+ "| Source | Stars | Forks | Issues | Default Branch | Decision |",
208
+ "|---|---:|---:|---:|---|---|",
209
+ ]
210
+ for record in batch["records"]:
211
+ lines.append(
212
+ f"| {record['source']} | {record['stars']} | {record['forks']} | "
213
+ f"{record['open_issues']} | {record['default_branch']} | {record['decision']} |"
214
+ )
215
+ lines.extend(["", "## Command Evidence", ""])
216
+ for record in batch["records"]:
217
+ lines.append(f"### {record['source']}")
218
+ for command in record["commands_run"]:
219
+ lines.append(f"- {command.get('name', command['command'])}: {command['result']}")
220
+ lines.append(f" - command: `{command['command']}`")
221
+ if command.get("output_summary"):
222
+ lines.append(f" - summary: `{command['output_summary']}`")
223
+ lines.append("")
224
+ return "\n".join(lines).rstrip() + "\n"
225
+
226
+
227
+ def _record_from_metadata(metadata: dict, commands: list[dict]) -> EvalRecord:
228
+ return EvalRecord(
229
+ source=metadata["source"],
230
+ source_url=metadata["source_url"],
231
+ snapshot_date=metadata["snapshot_date"],
232
+ stars=metadata["stars"],
233
+ forks=metadata["forks"],
234
+ open_issues=metadata["open_issues"],
235
+ default_branch=metadata["default_branch"],
236
+ language=metadata.get("language"),
237
+ commands_run=commands,
238
+ sanitization={
239
+ "raw_logs_committed": False,
240
+ "secrets_removed": True,
241
+ "third_party_safety_label_used": False,
242
+ },
243
+ decision="defer-with-reason",
244
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xone-cli
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Unified CLI entry point for X-One Agent Evidence Loop workflows.
5
5
  Author: X-One-AI
6
6
  License-Expression: MIT
@@ -61,6 +61,14 @@ xone runbook --head HEAD --dry-run
61
61
 
62
62
  `xone runbook` auto-detects the local repository default branch. Remote GitHub URLs are not accepted yet; clone the repository first, then pass the local path with `--repo`.
63
63
 
64
+ For repeatable open-source adoption checks, use the read-only evaluator:
65
+
66
+ ```bash
67
+ xone eval open-source --repos repos.txt --output evaluation.md --format markdown
68
+ ```
69
+
70
+ Add `--clone-root .xone-eval-clones` when you want X-One to shallow-clone public repositories and run only X-One dry-run commands against those local clones. It does not run third-party install scripts, tests, containers, or project commands.
71
+
64
72
  `xone-cli` orchestrates these X-One tools:
65
73
 
66
74
  - `agent-pr-evidence`
@@ -81,7 +89,8 @@ Current recommended tool versions:
81
89
  - It does not post GitHub comments.
82
90
  - It does not modify repositories automatically.
83
91
  - It does not make allow/deny runtime enforcement decisions.
84
- - It does not make hidden network calls.
92
+ - It does not run third-party project scripts.
93
+ - `xone eval open-source` makes explicit GitHub API and `git clone --depth 1` calls only when requested by the command.
85
94
 
86
95
  ## Docs
87
96
 
@@ -30,6 +30,7 @@ src/xone_cli/cli.py
30
30
  src/xone_cli/evidence.py
31
31
  src/xone_cli/lab.py
32
32
  src/xone_cli/model.py
33
+ src/xone_cli/open_source_eval.py
33
34
  src/xone_cli/release.py
34
35
  src/xone_cli/repo_context.py
35
36
  src/xone_cli/risk.py
@@ -44,6 +45,7 @@ src/xone_cli.egg-info/top_level.txt
44
45
  tests/test_cli.py
45
46
  tests/test_docs.py
46
47
  tests/test_evidence.py
48
+ tests/test_open_source_eval.py
47
49
  tests/test_release.py
48
50
  tests/test_repo_context.py
49
51
  tests/test_risk_lab.py
@@ -12,7 +12,7 @@ def _fake_tool(bin_dir: Path, name: str, body: str = "echo tool") -> None:
12
12
 
13
13
  def test_version_outputs_package_version(capsys):
14
14
  assert main(["--version"]) == 0
15
- assert capsys.readouterr().out.strip() == "xone 0.1.2"
15
+ assert capsys.readouterr().out.strip() == "xone 0.1.4"
16
16
 
17
17
 
18
18
  def test_doctor_json_reports_required_tools(tmp_path, monkeypatch, capsys):
@@ -19,17 +19,20 @@ def test_docs_and_package_metadata_stay_aligned():
19
19
  assert "xone doctor" in english
20
20
  assert "xone doctor --install-plan" in english
21
21
  assert "xone runbook --head HEAD --dry-run" in english
22
+ assert "xone eval open-source" in english
22
23
  assert "Remote GitHub URLs are not accepted yet" in english
23
24
  assert "xone doctor" in chinese
24
25
  assert "xone doctor --install-plan" in chinese
25
26
  assert "xone runbook --head HEAD --dry-run" in chinese
27
+ assert "xone eval open-source" in chinese
26
28
  assert "远程 GitHub URL" in chinese
27
29
  assert "git clone --depth 1 https://github.com/owner/repo" in Path("docs/install.md").read_text(encoding="utf-8")
30
+ assert "xone eval open-source --repos repos.txt" in Path("docs/install.md").read_text(encoding="utf-8")
28
31
  assert "xone release verify --build --install --smoke" in Path("docs/release.md").read_text(encoding="utf-8")
29
32
  assert 'name = "xone-cli"' in pyproject
30
- assert 'version = "0.1.2"' in pyproject
31
- assert __version__ == "0.1.2"
32
- assert "## 0.1.2" in changelog
33
+ assert 'version = "0.1.4"' in pyproject
34
+ assert __version__ == "0.1.4"
35
+ assert "## 0.1.4" in changelog
33
36
  assert 'xone = "xone_cli.cli:entrypoint"' in pyproject
34
37
  assert "python -m pytest -q" in ci
35
38
  assert "xone runbook --base HEAD~1 --head HEAD --dry-run" in ci
@@ -0,0 +1,173 @@
1
+ import json
2
+ import urllib.error
3
+ from datetime import date
4
+ from types import SimpleNamespace
5
+
6
+ from xone_cli.cli import main
7
+ from xone_cli.open_source_eval import fetch_github_metadata
8
+
9
+
10
+ def test_eval_open_source_json_outputs_sanitized_records(tmp_path, monkeypatch):
11
+ repos = tmp_path / "repos.txt"
12
+ repos.write_text("openai/codex\nhttps://github.com/docker/mcp-gateway\n", encoding="utf-8")
13
+ output = tmp_path / "records.json"
14
+
15
+ def fake_metadata(repo, snapshot_date):
16
+ return {
17
+ "source": repo,
18
+ "source_url": f"https://github.com/{repo}",
19
+ "snapshot_date": snapshot_date.isoformat(),
20
+ "stars": 10,
21
+ "forks": 2,
22
+ "open_issues": 1,
23
+ "default_branch": "main",
24
+ "language": "Python",
25
+ }
26
+
27
+ monkeypatch.setattr("xone_cli.open_source_eval.fetch_github_metadata", fake_metadata)
28
+
29
+ assert main(["eval", "open-source", "--repos", str(repos), "--output", str(output), "--format", "json"]) == 0
30
+
31
+ payload = json.loads(output.read_text(encoding="utf-8"))
32
+ assert payload["schema_version"] == "xone.open_source_evaluation_batch.v1"
33
+ assert [record["source"] for record in payload["records"]] == ["openai/codex", "docker/mcp-gateway"]
34
+ assert payload["records"][0]["sanitization"] == {
35
+ "raw_logs_committed": False,
36
+ "secrets_removed": True,
37
+ "third_party_safety_label_used": False,
38
+ }
39
+ assert payload["records"][0]["commands_run"][0]["command"] == "xone doctor --json"
40
+ assert payload["records"][0]["commands_run"][1]["result"] == "skipped"
41
+
42
+
43
+ def test_eval_open_source_with_clone_root_runs_local_runbook_dry_run(tmp_path, monkeypatch):
44
+ repos = tmp_path / "repos.txt"
45
+ repos.write_text("upstash/context7\n", encoding="utf-8")
46
+ output = tmp_path / "records.md"
47
+ clone_root = tmp_path / "clones"
48
+
49
+ def fake_metadata(repo, snapshot_date):
50
+ return {
51
+ "source": repo,
52
+ "source_url": f"https://github.com/{repo}",
53
+ "snapshot_date": snapshot_date.isoformat(),
54
+ "stars": 20,
55
+ "forks": 3,
56
+ "open_issues": 4,
57
+ "default_branch": "master",
58
+ "language": "TypeScript",
59
+ }
60
+
61
+ def fake_clone(record, root):
62
+ repo = root / "upstash-context7"
63
+ (repo / ".git").mkdir(parents=True)
64
+ (repo / ".git" / "HEAD").write_text("ref: refs/heads/master\n", encoding="utf-8")
65
+ return repo
66
+
67
+ monkeypatch.setattr("xone_cli.open_source_eval.fetch_github_metadata", fake_metadata)
68
+ monkeypatch.setattr("xone_cli.open_source_eval.clone_repo", fake_clone)
69
+
70
+ assert main(
71
+ [
72
+ "eval",
73
+ "open-source",
74
+ "--repos",
75
+ str(repos),
76
+ "--clone-root",
77
+ str(clone_root),
78
+ "--output",
79
+ str(output),
80
+ "--format",
81
+ "markdown",
82
+ ]
83
+ ) == 0
84
+
85
+ markdown = output.read_text(encoding="utf-8")
86
+ assert "# X-One Open-Source Adoption Evaluation" in markdown
87
+ assert "upstash/context7" in markdown
88
+ assert "runbook-dry-run" in markdown
89
+ assert "--base master" in markdown
90
+
91
+
92
+ def test_eval_open_source_records_clone_failure_without_crashing(tmp_path, monkeypatch):
93
+ repos = tmp_path / "repos.txt"
94
+ repos.write_text("cline/cline\nopenai/codex\n", encoding="utf-8")
95
+ output = tmp_path / "records.json"
96
+ clone_root = tmp_path / "clones"
97
+
98
+ def fake_metadata(repo, snapshot_date):
99
+ return {
100
+ "source": repo,
101
+ "source_url": f"https://github.com/{repo}",
102
+ "snapshot_date": snapshot_date.isoformat(),
103
+ "stars": 10,
104
+ "forks": 2,
105
+ "open_issues": 1,
106
+ "default_branch": "main",
107
+ "language": "TypeScript",
108
+ }
109
+
110
+ def fake_clone(record, root):
111
+ if record["source"] == "cline/cline":
112
+ raise RuntimeError("clone failed")
113
+ repo = root / "openai-codex"
114
+ (repo / ".git").mkdir(parents=True)
115
+ (repo / ".git" / "HEAD").write_text("ref: refs/heads/main\n", encoding="utf-8")
116
+ return repo
117
+
118
+ monkeypatch.setattr("xone_cli.open_source_eval.fetch_github_metadata", fake_metadata)
119
+ monkeypatch.setattr("xone_cli.open_source_eval.clone_repo", fake_clone)
120
+
121
+ assert main(
122
+ [
123
+ "eval",
124
+ "open-source",
125
+ "--repos",
126
+ str(repos),
127
+ "--clone-root",
128
+ str(clone_root),
129
+ "--output",
130
+ str(output),
131
+ "--format",
132
+ "json",
133
+ ]
134
+ ) == 0
135
+
136
+ payload = json.loads(output.read_text(encoding="utf-8"))
137
+ assert [record["source"] for record in payload["records"]] == ["cline/cline", "openai/codex"]
138
+ failed_command = payload["records"][0]["commands_run"][1]
139
+ assert failed_command["name"] == "shallow-clone"
140
+ assert failed_command["result"] == "fail"
141
+ assert failed_command["mode"] == "read-only"
142
+ assert payload["records"][1]["commands_run"][1]["name"] == "runbook-dry-run"
143
+
144
+
145
+ def test_fetch_github_metadata_falls_back_when_urllib_cert_fails(monkeypatch):
146
+ def fail_urlopen(_request, timeout):
147
+ raise urllib.error.URLError("certificate verify failed")
148
+
149
+ def fake_run(command, check, text, stdout, stderr):
150
+ assert command == ["curl", "-fsSL", "https://api.github.com/repos/openai/codex"]
151
+ return SimpleNamespace(
152
+ stdout=json.dumps(
153
+ {
154
+ "full_name": "openai/codex",
155
+ "html_url": "https://github.com/openai/codex",
156
+ "stargazers_count": 1,
157
+ "forks_count": 2,
158
+ "open_issues_count": 3,
159
+ "default_branch": "main",
160
+ "language": "Rust",
161
+ }
162
+ ),
163
+ stderr="",
164
+ )
165
+
166
+ monkeypatch.setattr("xone_cli.open_source_eval.urllib.request.urlopen", fail_urlopen)
167
+ monkeypatch.setattr("xone_cli.open_source_eval.subprocess.run", fake_run)
168
+
169
+ metadata = fetch_github_metadata("openai/codex", date(2026, 6, 15))
170
+
171
+ assert metadata["source"] == "openai/codex"
172
+ assert metadata["snapshot_date"] == "2026-06-15"
173
+ assert metadata["stars"] == 1
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes