diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 158f822..b9ce6ba 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "codex-quota-optimizer", - "version": "0.2.0", + "version": "0.2.1", "description": "A zero-friction Codex usage governor with task-aware routing, soft budgets, focused context, local audits, and layered verification.", "author": { "name": "Daniel Chen", diff --git a/.github/workflows/python-cli-tests.yml b/.github/workflows/python-cli-tests.yml index 5e7e17b..f1a9347 100644 --- a/.github/workflows/python-cli-tests.yml +++ b/.github/workflows/python-cli-tests.yml @@ -27,3 +27,4 @@ jobs: python3 skills/codex-quota-optimizer/scripts/cqo.py status python3 skills/codex-quota-optimizer/scripts/cqo.py audit --note ci-smoke python3 skills/codex-quota-optimizer/scripts/cqo.py history --limit 1 + python3 skills/codex-quota-optimizer/scripts/cqo.py doctor diff --git a/CHANGELOG.md b/CHANGELOG.md index 909c179..48873ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to this project will be documented here. +## [0.2.1] - 2026-09-21 + +### Added +- `cqo doctor` for zero-network local installation diagnostics. +- Doctor checks Python compatibility, Skill presence, journal writability, Git availability, CLI PATH visibility, and active-session state. + +### Changed +- `cqo audit` output now separates observed local facts from CQO policy guarantees for clearer, more honest reporting. +- README now reflects successful HOL Codex Plugin Catalog listing. + ## [0.2.0] - 2026-09-20 ### Added diff --git a/README.md b/README.md index 27dec0d..0cea349 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ v0.2 turns the original Skill policy into a lightweight usage governor while kee | **Soft Session Budget** | Suggests discovery, reasoning, verification and subagent scope without blocking execution | | **Local Usage Journal** | Stores task-level metadata locally under `~/.cqo`; no telemetry and no private account scraping | | **Usage Audit** | Records local change surface and CQO policy guardrails without inventing token-savings percentages | -| **`cqo` CLI** | Optional `start / status / audit / history` inspection layer; Codex does not depend on it | +| **`cqo` CLI** | Optional `start / status / audit / history / doctor` inspection layer; Codex does not depend on it | CQO itself adds **no automatic model call, no network request, no blocking budget gate, and no automatic subagent**. If correctness requires more context or verification than the suggested budget, Codex should simply continue. @@ -293,6 +293,7 @@ cqo start "Fix the checkout bug" --mode economy cqo status cqo audit cqo history +cqo doctor ``` It uses the Python standard library only, performs no network requests, and writes task-level state to `~/.cqo` (or `CQO_HOME`). @@ -303,6 +304,8 @@ If the `cqo` shortcut is not installed, run: python ~/.agents/skills/codex-quota-optimizer/scripts/cqo.py status ``` +Run `cqo doctor` any time to check Python compatibility, Skill installation, local journal writability, Git availability, and whether the `cqo` shortcut is on PATH — with zero network checks. + The repository snapshot and change-scope scripts are also local-only and dependency-light. --- @@ -365,7 +368,7 @@ codex-quota-optimizer/ - [x] End-of-task **Usage Audit** with honest task-level local observations - [ ] Framework-aware focused-test discovery - [x] Plugin packaging for dual Skill / Plugin distribution -- [ ] HOL Codex Plugin Catalog listing +- [x] HOL Codex Plugin Catalog listing - [x] One-line Skills CLI installation - [ ] Benchmark suite comparing default and optimized workflows diff --git a/README.zh-CN.md b/README.zh-CN.md index db879ef..cfe6266 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -58,7 +58,7 @@ v0.2 在原有 Skill 策略上增加了一个轻量的 Usage Governor,但不 | **Soft Session Budget** | 给探索、推理、验证、Subagent 提供建议范围,但绝不阻塞任务 | | **Local Usage Journal** | 只在本地 `~/.cqo` 保存任务级信息;无 Telemetry、不抓私人账户数据 | | **Usage Audit** | 记录本地改动面和 CQO 的策略约束,不虚构 Token 节省比例 | -| **`cqo` CLI** | 可选的 `start / status / audit / history` 查看层;Codex 不依赖它运行 | +| **`cqo` CLI** | 可选的 `start / status / audit / history / doctor` 查看层;Codex 不依赖它运行 | CQO 自身不会额外发起模型调用、不会访问网络、不会设置阻塞式 Budget Gate,也不会自动拉起 Subagent。只要正确性需要更多上下文或验证,Codex 应直接继续完成任务。 @@ -295,6 +295,7 @@ cqo start "修复结算页 Bug" --mode economy cqo status cqo audit cqo history +cqo doctor ``` 它只使用 Python 标准库,不访问网络,任务级状态保存在 `~/.cqo`(或 `CQO_HOME`)。 @@ -305,6 +306,8 @@ cqo history python ~/.agents/skills/codex-quota-optimizer/scripts/cqo.py status ``` +你可以随时运行 `cqo doctor` 检查 Python 版本、Skill 安装、本地 Journal 写入权限、Git 是否可用,以及 `cqo` 快捷命令是否已进入 PATH;整个检查过程 0 网络请求。 + Repository Snapshot 与 Change Scope 两个脚本同样只在本地运行,不上传项目数据。 --- @@ -367,7 +370,7 @@ codex-quota-optimizer/ - [x] **Usage Audit**:任务结束输出诚实的本地任务级观察 - [ ] 自动识别不同框架最合适的定向测试 - [x] Plugin 打包:同时支持 Skill 直装与 Plugin 分发 -- [ ] HOL Codex Plugin Catalog 收录 +- [x] HOL Codex Plugin Catalog 收录 - [x] Skills CLI 一行命令安装 - [ ] Benchmark:对比默认工作流和优化工作流 diff --git a/plugin.json b/plugin.json index 4720e81..e78c088 100644 --- a/plugin.json +++ b/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": "codex-quota-optimizer", - "version": "0.2.0", + "version": "0.2.1", "description": "A zero-friction Codex usage governor with task-aware routing, soft budgets, focused context, local audits, and layered verification.", "author": { "name": "Daniel Chen", diff --git a/skills/codex-quota-optimizer/scripts/cqo.py b/skills/codex-quota-optimizer/scripts/cqo.py index 4a10479..1a4a194 100644 --- a/skills/codex-quota-optimizer/scripts/cqo.py +++ b/skills/codex-quota-optimizer/scripts/cqo.py @@ -10,6 +10,7 @@ import argparse import json import os +import shutil import subprocess import sys import uuid @@ -17,7 +18,7 @@ from pathlib import Path from typing import Any -VERSION = "0.2.0" +VERSION = "0.2.1" HIGH_RISK_TERMS = { "security", "auth", "authentication", "authorization", "permission", @@ -386,8 +387,18 @@ def print_audit(audit: dict[str, Any] | None) -> None: repo = audit["repo_at_audit"] print(f"CQO audit {audit['id']} · completed") print(f"Task: {audit['size']} · Risk: {audit['risk']} · Mode: {audit['mode']}") - print(f"Current repository change surface: {repo['changed_file_count']} file(s)") - print("CQO overhead: 0 network requests · 0 blocking gates · 0 automatic model calls") + print("Observed:") + print(f"- Local repository change surface: {repo['changed_file_count']} file(s)") + if repo.get("branch"): + print(f"- Branch: {repo['branch']}") + print("CQO guarantees:") + print("- No forced repository-wide scan") + print("- No forced full test suite") + print("- No automatic subagent") + print("- No automatic model escalation") + print("- 0 CQO network requests · 0 blocking gates · 0 automatic model calls") + if audit.get("note"): + print(f"Note: {audit['note']}") print(f"Recorded locally in {history_path()}") @@ -406,6 +417,91 @@ def print_history(items: list[dict[str, Any]]) -> None: ) + +def doctor_report() -> dict[str, Any]: + """Inspect the local CQO setup without network access.""" + skill_root = Path(__file__).resolve().parents[1] + skill_file = skill_root / "SKILL.md" + + home_ok = False + home_error: str | None = None + try: + ensure_home() + probe = cqo_home() / ".doctor-write-test" + probe.write_text("ok\n", encoding="utf-8") + probe.unlink() + home_ok = True + except OSError as exc: + home_error = str(exc) + + python_ok = sys.version_info >= (3, 10) + cli_path = shutil.which("cqo") + git_path = shutil.which("git") + + required_ok = python_ok and skill_file.is_file() and home_ok + return { + "version": VERSION, + "ok": required_ok, + "python": { + "version": ".".join(map(str, sys.version_info[:3])), + "ok": python_ok, + "required": ">=3.10", + }, + "skill": { + "root": str(skill_root), + "skill_md": str(skill_file), + "ok": skill_file.is_file(), + }, + "journal": { + "home": str(cqo_home()), + "writable": home_ok, + "error": home_error, + }, + "git": { + "path": git_path, + "available": bool(git_path), + "required": False, + }, + "cli": { + "path": cli_path, + "on_path": bool(cli_path), + "required": False, + }, + "active_session": bool(read_current()), + "network_checks": 0, + } + + +def print_doctor(report: dict[str, Any]) -> None: + mark = lambda ok: "OK" if ok else "WARN" + print(f"CQO doctor · v{report['version']}") + print( + f"[{mark(report['python']['ok'])}] Python " + f"{report['python']['version']} (required {report['python']['required']})" + ) + print( + f"[{mark(report['skill']['ok'])}] Skill " + f"{report['skill']['skill_md']}" + ) + print( + f"[{mark(report['journal']['writable'])}] Local journal " + f"{report['journal']['home']}" + ) + print( + f"[{mark(report['git']['available'])}] Git " + f"{report['git']['path'] or 'not found'} (optional for CLI)" + ) + print( + f"[{mark(report['cli']['on_path'])}] cqo shortcut " + f"{report['cli']['path'] or 'not on PATH'} (optional)" + ) + print( + "Zero-friction check: 0 network requests · " + "0 model calls · 0 blocking gates" + ) + print("Required setup: ready" if report["ok"] else "Required setup: needs attention") + + def build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( prog="cqo", @@ -448,6 +544,12 @@ def build_parser() -> argparse.ArgumentParser: history.add_argument("--limit", type=int, default=10) history.add_argument("--json", action="store_true") + doctor = sub.add_parser( + "doctor", + help="Check local CQO installation and optional CLI setup.", + ) + doctor.add_argument("--json", action="store_true") + return parser @@ -483,6 +585,14 @@ def main(argv: list[str] | None = None) -> int: print_history(items) return 0 + if args.command == "doctor": + report = doctor_report() + if args.json: + print_json(report) + else: + print_doctor(report) + return 0 if report["ok"] else 1 + return 2 diff --git a/tests/test_cqo.py b/tests/test_cqo.py index 1b69611..8106366 100644 --- a/tests/test_cqo.py +++ b/tests/test_cqo.py @@ -63,6 +63,16 @@ def test_local_session_lifecycle(self): self.assertEqual(len(history), 1) self.assertEqual(history[0]["note"], "unit-test") + def test_doctor_reports_required_setup(self): + with tempfile.TemporaryDirectory() as tmp: + with patch.dict(os.environ, {"CQO_HOME": tmp}, clear=False): + report = cqo.doctor_report() + self.assertTrue(report["python"]["ok"]) + self.assertTrue(report["skill"]["ok"]) + self.assertTrue(report["journal"]["writable"]) + self.assertEqual(report["network_checks"], 0) + self.assertTrue(report["ok"]) + if __name__ == "__main__": unittest.main()