Linux 下 Claude Code、Codex、OpenCode 更优雅的注册方式#39
Linux 下 Claude Code、Codex、OpenCode 更优雅的注册方式#39shiro123444 wants to merge 13 commits intoHKUDS:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
该 PR 旨在把 CLI-Anything 在 Claude Code / OpenCode / Codex 三端的接入、安装与一致性校验收敛到“registry 单一事实源 + 生成/同步脚本 + 统一安装入口”的链路中,并引入本地 Python control plane(anythingcli)作为统一控制面。
Changes:
- 新增
registry/作为共享命令规范、HARNESS 与 adapter 模板的单一事实源,并提供生成/同步/校验脚本 - 新增统一安装/管理入口
scripts/anythingcli.sh+scripts/install-anythingcli.sh,支持 install/uninstall/status/doctor - 新增
anythingcli/Python 包作为本地 control plane,并添加 CI 工作流做漂移校验与语法检查
Reviewed changes
Copilot reviewed 33 out of 37 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/verify-agent-pack.py | 新增仓库级校验:模板/产物一致性、HARNESS/commands 同步、frontmatter/manifest 完整性等 |
| scripts/sync-command-registry.py | 将 registry/generator/commands/*.md 与 HARNESS.md 同步到 Claude 插件目录 |
| scripts/install-anythingcli.sh | install 的便捷 wrapper,委托给 scripts/anythingcli.sh |
| scripts/generate-adapters.py | 从 registry/package.yaml 与 adapter 模板生成 Claude/OpenCode/Codex 的产物,并支持 --check |
| scripts/anythingcli_core.py | Python 入口包装(anythingcli.control_plane.main) |
| scripts/anythingcli.sh | 统一控制脚本:install/uninstall/status/doctor + build/refine/test/validate/list 委托给 python -m anythingcli |
| registry/package.yaml | registry 包元数据(name/version/description/targets 等) |
| registry/generator/commands/cli-anything.md | canonical build 命令规范 |
| registry/generator/commands/refine.md | canonical refine 命令规范 |
| registry/generator/commands/test.md | canonical test 命令规范 |
| registry/generator/commands/validate.md | canonical validate 命令规范 |
| registry/generator/commands/list.md | canonical list 命令规范 |
| registry/generator/HARNESS.md | canonical HARNESS 单一事实源(方法论与规范) |
| registry/adapters/claude/plugin.json.template | Claude 插件 manifest 模板 |
| registry/adapters/opencode/SKILL.md.template | OpenCode skill 模板 |
| registry/adapters/codex/SKILL.md.template | Codex skill 模板 |
| registry/README.md | 说明 registry 的 source-of-truth 角色与同步/生成脚本 |
| opencode/skills/cli-anything/SKILL.md | OpenCode 一等适配产物 |
| opencode/README.md | OpenCode skill 安装与使用说明 |
| codex/skills/cli-anything/SKILL.md | Codex 一等适配产物 |
| codex/README.md | Codex skill 安装与使用说明(含 scope) |
| cli-anything-plugin/scripts/setup-cli-anything.sh | 去除 /root/cli-anything 硬编码,改为相对插件目录解析 HARNESS |
| cli-anything-plugin/commands/cli-anything.md | Claude 命令文档(兼容副本)格式/内容对齐 registry |
| cli-anything-plugin/commands/refine.md | 同上 |
| cli-anything-plugin/commands/test.md | 同上 |
| cli-anything-plugin/commands/validate.md | 同上 |
| cli-anything-plugin/commands/list.md | 同上 |
| cli-anything-plugin/HARNESS.md | Claude 侧 HARNESS 兼容副本(强调 registry 为 canonical) |
| cli-anything-plugin/.claude-plugin/plugin.json | Claude 插件 manifest(由模板生成/对齐) |
| anythingcli/control_plane.py | 本地 control plane:build/refine/test/validate 输出上下文;list 枚举 installed/generated 工具 |
| anythingcli/main.py | python -m anythingcli 入口 |
| anythingcli/init.py | 导出 main |
| README.md | 更新安装推荐路径为统一安装入口,并补充 OpenCode/Codex 说明 |
| README_CN.md | 同上(中文) |
| DEPLOYMENT_UNIFICATION_PLAN.md | 统一部署方案与分层架构的长文说明 |
| .gitignore | 放开新增目录/文件(registry/anythingcli/scripts/.github 等) |
| .github/workflows/verify-agent-pack.yml | CI:模板漂移检查、pack 校验、Python compileall、bash -n |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
anythingcli/control_plane.py
Outdated
| if action in {"refine", "test", "validate"}: | ||
| raise ValueError(f"{action} requires a local source path, not a GitHub URL") |
There was a problem hiding this comment.
The control-plane rejects GitHub URLs for refine/test/validate with a hard error, but the canonical command specs (registry/generator/commands/test.md and validate.md) state those commands accept a GitHub URL and clone first. Either adjust the specs (if local-only is intended) or make the control-plane accept GitHub URLs and return a context/next_step similar to build instead of erroring.
| if action in {"refine", "test", "validate"}: | |
| raise ValueError(f"{action} requires a local source path, not a GitHub URL") |
anythingcli/control_plane.py
Outdated
|
|
||
| def list_generated_tools(search_path: Path, depth: int | None) -> dict[str, dict[str, Any]]: | ||
| generated: dict[str, dict[str, Any]] = {} | ||
| relative_patterns = [pattern.replace(str(search_path) + "/", "") for pattern in build_glob_patterns(search_path, depth)] |
There was a problem hiding this comment.
list_generated_tools builds relative glob patterns by string-replacing str(search_path) + "/". This is brittle (e.g., --path / makes the prefix "//", so the replace won’t happen and you may pass an absolute pattern into Path.glob, which is invalid). Consider generating relative patterns directly (e.g., keep patterns relative in build_glob_patterns, or use Path.relative_to/os.path.relpath).
| relative_patterns = [pattern.replace(str(search_path) + "/", "") for pattern in build_glob_patterns(search_path, depth)] | |
| patterns = build_glob_patterns(search_path, depth) | |
| relative_patterns: list[str] = [] | |
| for pattern in patterns: | |
| pattern_path = Path(pattern) | |
| try: | |
| relative_pattern = pattern_path.relative_to(search_path) | |
| except ValueError: | |
| # Skip patterns that are not under the search path | |
| continue | |
| relative_patterns.append(str(relative_pattern)) |
|
|
||
| If a GitHub URL is provided, the agent clones the repo locally first, then works on the local copy. | ||
|
|
||
| The software name is derived from the directory name. The agent locates the CLI harness at `/root/cli-anything/<software-name>/agent-harness/`. |
There was a problem hiding this comment.
This spec hardcodes /root/cli-anything/<software-name>/agent-harness/ as the harness location. Given the PR’s goal of more portable, unified installation, this should be expressed relative to the provided <software-path-or-repo> (or clearly labeled as an example), otherwise it will mislead non-root/non-/root/cli-anything setups.
| The software name is derived from the directory name. The agent locates the CLI harness at `/root/cli-anything/<software-name>/agent-harness/`. | |
| The software name is derived from the directory name. The agent expects the CLI harness to live under `<software-path-or-repo>/agent-harness/` (for example, if you pass `/home/user/gimp`, it looks for `/home/user/gimp/agent-harness/`). |
|
|
||
| If a GitHub URL is provided, the agent clones the repo locally first, then works on the local copy. | ||
|
|
||
| The software name is derived from the directory name. The agent locates the CLI harness at `/root/cli-anything/<software-name>/agent-harness/`. |
There was a problem hiding this comment.
This spec hardcodes /root/cli-anything/<software-name>/... as the harness location, which conflicts with the “no hardcoded paths” guidance later in the same document and with the repo’s move toward relative/portable installs. Consider rewriting this to be derived from the user-provided <software-path-or-repo> (or clearly marking /root/cli-anything as an example only).
| The software name is derived from the directory name. The agent locates the CLI harness at `/root/cli-anything/<software-name>/agent-harness/`. | |
| The software name is derived from the directory name. The agent locates the CLI harness relative to `<software-path-or-repo>`, typically at `<software-path-or-repo>/agent-harness/`. |
| CLI Harness Validation Report | ||
| Software: gimp | ||
| Path: /root/cli-anything/gimp/agent-harness/cli_anything/gimp | ||
|
|
There was a problem hiding this comment.
The sample report path is still /root/cli-anything/..., which is environment-specific and contradicts the doc’s later “No hardcoded paths” validation rule. Suggest updating this example to a generic path (e.g., based on the provided source path) to avoid implying /root/cli-anything is required.
scripts/anythingcli.sh
Outdated
| run_local_cli() { | ||
| if has_command python3; then | ||
| exec python3 -m anythingcli "$@" | ||
| elif has_command python; then | ||
| exec python -m anythingcli "$@" | ||
| else | ||
| die "python is required to run the anythingcli control plane" | ||
| fi |
There was a problem hiding this comment.
run_local_cli uses python -m anythingcli without ensuring the repo root is on sys.path. If this script is invoked from outside the repository root (e.g., via an absolute path), module resolution will likely fail. Consider cd "$REPO_ROOT" before exec, or set PYTHONPATH="$REPO_ROOT" for the exec call.
scripts/anythingcli.sh
Outdated
| --force) | ||
| shift | ||
| ;; | ||
| -h|--help) |
There was a problem hiding this comment.
--force is advertised in the usage text and parsed, but it has no effect. install_target always rm -rf's the destination path regardless, so callers may assume --force is required when it isn't (and the flag currently provides no safety/behavior change). Either implement force-gating (e.g., refuse to remove existing installs unless --force is set) or remove the option from both usage and parsing.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 33 out of 36 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
scripts/sync-command-registry.py
Outdated
| synced = [] | ||
| for source in sorted(REGISTRY_DIR.glob("*.md")): | ||
| destination = CLAUDE_DIR / source.name | ||
| shutil.copyfile(source, destination) | ||
| synced.append(destination.name) |
There was a problem hiding this comment.
The sync script only copies registry/generator/commands/*.md into cli-anything-plugin/commands/ but never removes files that no longer exist in the registry. This can leave stale command docs behind in the plugin directory without any obvious signal. Consider cleaning the destination directory (or at least deleting markdown files not present in the registry) before copying.
anythingcli/control_plane.py
Outdated
| for dist in distributions(): | ||
| name = dist.metadata.get("Name", "") | ||
| if not name.startswith("cli-anything-"): | ||
| continue | ||
| software = name.replace("cli-anything-", "") | ||
| installed[software] = { | ||
| "name": software, | ||
| "status": "installed", | ||
| "version": dist.version, | ||
| "executable": shutil.which(f"cli-anything-{software}"), | ||
| "source": None, |
There was a problem hiding this comment.
list can report the same tool twice because installed packages are keyed by the distribution suffix (e.g. cli-anything-obs-studio -> obs-studio) while generated harnesses are keyed by the Python package directory (e.g. cli_anything/obs_studio -> obs_studio). This prevents merge_tool_results() from merging installed+generated entries for hyphenated software names. Normalize the software identifier consistently in both list_installed_tools() and list_generated_tools() (e.g., map - <-> _ via a shared helper) and keep a separate display name if needed.
scripts/generate-adapters.py
Outdated
| def build_context() -> dict[str, str]: | ||
| metadata = parse_simple_yaml(PACKAGE_METADATA) | ||
| return { | ||
| "package_name": metadata.get("name", "cli-anything"), | ||
| "package_version": metadata.get("version", "0.0.0"), | ||
| "package_description": metadata.get("description", ""), | ||
| } |
There was a problem hiding this comment.
build_context() calls parse_simple_yaml(PACKAGE_METADATA) without checking that registry/package.yaml exists, so running this script from an incomplete checkout fails with an unhandled FileNotFoundError. Consider adding an explicit is_file() guard with a clear error message (and exit code 1) before reading the file, similar to the checks already done for template paths.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 33 out of 36 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
scripts/anythingcli.sh
Outdated
| parse_options() { | ||
| while [[ $# -gt 0 ]]; do | ||
| case "$1" in | ||
| --targets) | ||
| TARGETS="${2:-}" | ||
| shift 2 | ||
| ;; | ||
| --mode) | ||
| MODE="${2:-}" | ||
| shift 2 | ||
| ;; | ||
| --scope) | ||
| SCOPE="${2:-}" | ||
| shift 2 | ||
| ;; |
There was a problem hiding this comment.
parse_options shifts by 2 for --targets/--mode/--scope without validating that an argument is present. With set -e, calling e.g. scripts/anythingcli.sh install --targets will trigger shift: shift count out of range instead of a clear error message. Add an explicit check for a missing/empty value before shift 2 and fail with die (ideally showing usage).
anythingcli/control_plane.py
Outdated
| def build_action_context(action: str, source: str, focus: str | None) -> ActionContext: | ||
| spec_path = COMMAND_SPECS[action] | ||
| software_name = derive_software_name(source) | ||
|
|
||
| if is_github_source(source): | ||
| if action in {"refine", "test", "validate"}: | ||
| raise ValueError(f"{action} requires a local source path, not a GitHub URL") | ||
| return ActionContext( | ||
| action=action, | ||
| source=source, | ||
| source_kind="github_url", | ||
| software_name=software_name, | ||
| spec_path=str(spec_path), | ||
| harness_path=None, | ||
| cli_package_root=None, | ||
| source_path=None, | ||
| focus=focus, | ||
| can_execute_locally=False, | ||
| next_step="clone the repository locally, then rerun the command with a local source path", | ||
| ) | ||
|
|
||
| source_path = resolve_local_source(source) | ||
| harness_path = source_path / "agent-harness" | ||
| cli_package_root = harness_path / "cli_anything" / software_name.replace("-", "_") | ||
|
|
There was a problem hiding this comment.
For local sources, software_name is derived from the raw user input before path normalization. Inputs like . / .. / ~/proj will produce incorrect software_name (e.g., .) and therefore an incorrect cli_package_root. Resolve/normalize the local path first and derive the software name from the resolved path’s directory name; also consider only stripping a .git suffix for GitHub URLs, not local paths.
|
感谢贡献,确实是一个很好的vision和很大的PR。目前看到的一些问题是:
|
感谢反馈,理解了喵。 确认一次现在的问题
我会按 先只做统一注册相关改动 的方向把这个 PR 的代码重新打磨,尽量保持现有 plugin/skills 内容不动,并把 registry / 重复脚本 / 重复副本相关内容移出这版 PR。 后续如果有需要,再单独提交更小的 PR 讨论这些演进方向www |
Add scripts/register.sh that installs Claude Code, OpenCode, and Codex adapters from a single command instead of separate manual steps per platform. Usage: scripts/register.sh install --targets all scripts/register.sh status Also adds native /register commands so users can trigger registration from within their agent: - Claude Code: /register [targets] - OpenCode: /cli-anything-register [targets] No existing files are modified except .gitignore (allow scripts/ dir).
Swap register.sh (152 lines, monolithic) for a modular Python system: scripts/register.py CLI entry point scripts/adapters/base.py Abstract Adapter base class scripts/adapters/claude.py Claude Code adapter scripts/adapters/opencode.py OpenCode adapter scripts/adapters/codex.py Codex adapter Adding a new platform (Cursor, Copilot, etc.) means dropping one .py file in adapters/ — no other files need to change. Usage unchanged: python3 scripts/register.py install --targets all python3 scripts/register.py status python3 scripts/register.py list
Adds 'bootstrap' subcommand that auto-detects an available agent, installs CLI-Anything to it, then prints next-step guidance for using /register from within that agent to manage the rest.
- unify first-install command with --target/--dry-run/--debug - add install-all for one-click install of all supported agents - add detailed registration guide for users and adapter developers - update Claude/OpenCode register command guidance
- use register as canonical command name across platforms - set OpenCode entrypoint to /register and stop installing old alias - clarify Claude namespaced form /cli-anything:register - update registration docs and migration guidance
- support python3 register.py from repo root - keep scripts/register.py as implementation backend - make user-facing hints prefer python3 register.py - update docs and command guidance to root-level entrypoint - whitelist register.py in gitignore
- update existing ~/.claude/plugins/cli-anything via copytree dirs_exist_ok - ensure newly added commands (e.g. register) are propagated - add troubleshooting note about Claude command list refresh
…install - sync cli-anything updates to installPath entries in installed_plugins.json - keep direct plugin dir in sync as well - align command title to cli-anything:register format for parser consistency
- do not default to all when user omits targets - ask user to choose claude/opencode/codex/all first - keep explicit all support for one-click installation
- extend skill scope to bootstrap/install/status/list workflows - require explicit target confirmation when omitted - keep explicit install-all support - update Codex agent metadata prompt/description
- extend codex cli-anything skill with register mode and target confirmation policy - update Codex agent metadata for registration workflows - change codex adapter to update existing install instead of skipping
- redirect cross-agent adapter registration to register command - add explicit clarification question for register vs account sign-up - reduce false interpretation of 注册 as website signup
- update existing command files instead of skipping - ensure behavior/docs changes propagate to ~/.config/opencode/commands - keep deprecated alias cleanup logic
d690454 to
4a722e0
Compare
已更新,欢迎审核~ |
(∠・ω< ) ̄☆ Ciallo~
Important
本 PR 当前主要整理的是:Linux 下 CLI-Anything 在 Claude Code / Codex / OpenCode 三端的统一注册接入方案 🐧
已经已经根据当前 maintainer 建议与主线代码状态做了收敛优化:聚焦“统一注册链路 + 可持续更新 + 可扩展适配”。
思路
按建议收敛到一条主线:
架构图(当前方案)
这次主要改动(按能力归纳)
统一入口与命令面
python3 register.py bootstrap --target <auto|claude|opencode|codex>python3 register.py install --targets <claude,opencode,codex|all>python3 register.py install-allpython3 register.py status --targets allpython3 register.py list --debug首次安装与后续管理分层
bootstrap命令语义统一
register语义组织/cli-anything:register(namespaced)/register二次安装同步修复(关键)
默认安全策略
Choose install target: claude / opencode / codex / allall“注册”意图澄清
如何参考并适配了各平台差异
Claude Code
installed_plugins.json指向的活跃 cache 路径,保证新增命令可见OpenCode
/registerCodex
本地验证(Linux)
验证重点:
updated(非纯skip)后续开发者如何优雅接入新平台www
建议沿用“薄适配器 + 稳编排”规范:
detect() / source() / destination() / install() / status()all需显式确认installed / updated / skip / errorstatus、debug、dry-runTip
依旧适配的linux~
后续加平台例如cursor等只需补 adapter,不必重做入口与流程设计。
截图展示~
展示三个平台的“注册动作“





