From 5d2800ee6327aa698754b9bf19e7a29a47f371cf Mon Sep 17 00:00:00 2001 From: zuiho <2324465096@qq.com> Date: Fri, 31 Jul 2026 12:01:22 +0800 Subject: [PATCH] add imupdate release knowledge skill --- .claude-plugin/marketplace.json | 2 +- .cursor-plugin/marketplace.json | 2 +- .github/workflows/vllm-omni-release-audit.yml | 1 + README.md | 13 ++- doc/MCP.md | 6 +- doc/VLLM_OMNI_RELEASE_MAINTENANCE.md | 31 ++++++- .../.claude-plugin/plugin.json | 2 +- .../.codex-plugin/plugin.json | 12 +-- .../.cursor-plugin/plugin.json | 2 +- .../skills/imupdate/SKILL.md | 89 +++++++++++++++++++ .../skills/imupdate/agents/openai.yaml | 4 + scripts/install_mcp.py | 39 ++++---- test/test_imupdate_skill.py | 37 ++++++++ test/test_install_mcp_script.py | 5 ++ 14 files changed, 218 insertions(+), 27 deletions(-) create mode 100644 plugins/infermatrix-copilot/skills/imupdate/SKILL.md create mode 100644 plugins/infermatrix-copilot/skills/imupdate/agents/openai.yaml create mode 100644 test/test_imupdate_skill.py diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index e26d09b..0a86bc7 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ { "name": "infermatrix-copilot", "source": "./plugins/infermatrix-copilot", - "description": "Maintainer-grounded vLLM-Omni review knowledge over MCP." + "description": "InferMatrixCopilot — governed PR review and release-driven knowledge maintenance over MCP and skills." } ] } diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index adcd9aa..ca9ad92 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -10,7 +10,7 @@ { "name": "infermatrix-copilot", "source": "plugins/infermatrix-copilot", - "description": "Maintainer-grounded vLLM-Omni review knowledge over MCP." + "description": "Maintainer-grounded vLLM-Omni review and release knowledge updates." } ] } diff --git a/.github/workflows/vllm-omni-release-audit.yml b/.github/workflows/vllm-omni-release-audit.yml index 7437e90..2c9891e 100644 --- a/.github/workflows/vllm-omni-release-audit.yml +++ b/.github/workflows/vllm-omni-release-audit.yml @@ -7,6 +7,7 @@ on: - adapters/vllm_omni/manifest.yaml - adapters/vllm_omni/release_baseline.yaml - doc/VLLM_OMNI_RELEASE_MAINTENANCE.md + - plugins/infermatrix-copilot/skills/imupdate/** - knowledge/repos/vllm-omni/** - tools/audit_vllm_omni_release.py - tools/vllm_omni_release_audit.py diff --git a/README.md b/README.md index 5f09d4b..3427400 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ vLLM-Omni 的兼容性、正确性和性能问题。 ## 快速开始 私有仓库过渡期,克隆后运行一个入口。脚本会自动识别本机的 Codex、Claude Code -和 Cursor,并同时安装 MCP 与 `imreview` Skill。 +和 Cursor,并同时安装 MCP、`imreview` 和 `imupdate` Skill。 ```text git clone git@github.com:JiusiServe/InferMatrixCopilot.git @@ -35,9 +35,13 @@ install.cmd ```text /imreview https://github.com/vllm-project/vllm-omni/pull/5172 +/imupdate D:\path\to\vllm-omni +/imupdate vllmomni ``` 不带地址时,`/imreview` 会审查当前 PR 或本地工作区。 +`/imupdate` 接受本地 Git 路径,也接受仓库名、别名或 URL。本地路径直接走机器审计; +名字或别名先由 Agent 找到权威仓库和目标版本,再尽量获取临时 checkout 运行机器审计。 公共市场上线后的安装方法和通用 MCP 配置见 [`doc/MCP.md`](doc/MCP.md)。 @@ -49,6 +53,13 @@ install.cmd → InferMatrixCopilot 返回知识库入口 → Agent 按改动选择相关模型、组件和通用规则 → Agent 输出带文件和行号的审查结论 + +你发起 /imupdate <仓库> + → 本地路径:直接读取 baseline 和目标仓库 HEAD + → 仓库名/别名:Agent 先解析权威仓库、版本和可用 checkout + → 只读审计器对比 Git 对象、注册表、pipeline、deploy 和路径路由 + → Agent 根据报告更新结构事实,不自动生成 owner 规则 + → enforce、知识检查和相关测试确认更新完整 ``` 代码理解和推理由 Agent 当前模型完成。InferMatrixCopilot 只负责提供知识地图和维护者 diff --git a/doc/MCP.md b/doc/MCP.md index 80184c3..426353d 100644 --- a/doc/MCP.md +++ b/doc/MCP.md @@ -3,7 +3,7 @@ InferMatrixCopilot 的核心发布物保持一份: - `plugins/infermatrix-copilot/.mcp.json`:标准 stdio MCP 描述; -- `plugins/infermatrix-copilot/skills/imreview/SKILL.md`:开放 Agent Skills; +- `plugins/infermatrix-copilot/skills/`:开放 Agent Skills; - Claude、Codex、Cursor 的市场文件只负责把同一个插件展示出来,不包含各自的安装逻辑。 ## 私有仓库过渡安装 @@ -32,6 +32,7 @@ install.cmd ```text /imreview +/imupdate ``` 市场只安装同一份 MCP 与 Skill;不会把逐 Agent 逻辑带回核心包。 @@ -44,6 +45,7 @@ install.cmd ```text plugins/infermatrix-copilot/skills/imreview/SKILL.md +plugins/infermatrix-copilot/skills/imupdate/SKILL.md ``` 核心 MCP 配置: @@ -71,6 +73,8 @@ plugins/infermatrix-copilot/skills/imreview/SKILL.md ```text /imreview https://github.com/vllm-project/vllm-omni/pull/5172 +/imupdate D:\path\to\vllm-omni +/imupdate vllmomni ``` 默认 Direct 模式只提供知识库,由 Agent 当前模型完成审查;不会自动评论或推送代码。 diff --git a/doc/VLLM_OMNI_RELEASE_MAINTENANCE.md b/doc/VLLM_OMNI_RELEASE_MAINTENANCE.md index a4b4070..aa868c2 100644 --- a/doc/VLLM_OMNI_RELEASE_MAINTENANCE.md +++ b/doc/VLLM_OMNI_RELEASE_MAINTENANCE.md @@ -4,7 +4,29 @@ InferMatrixCopilot 的模型清单、代码 owner 路由和源码引用会随着 vLLM-Omni 发版而过期。这个审计只读取两个 Git 提交和本仓库声明,不会 checkout 目标版本,也不会自动修改知识规则。 -## 本地运行 +## 用户入口 + +安装 InferMatrixCopilot 后,在 Codex、Claude Code 或 Cursor 中运行: + +```text +/imupdate D:\path\to\vllm-omni +/imupdate vllm-omni +/imupdate vllmomni +``` + +支持三种用法: + +1. 本地 Git 路径:直接用 baseline 的 `audited_sha` 对比仓库 `HEAD`,机器审计后 + 由 Agent 更新知识。 +2. 仓库名、别名或 URL:Agent 先解析权威仓库与目标版本,寻找已配置 checkout + 或建立临时 clone,再尽量进入同一机器审计流程;无法运行机器审计时必须明确标记 + 为模型分析,不能宣称 `CLEAN`。选择版本时不得因为 GitHub “latest release” + 忽略预发布版本而退回到比当前 baseline 更旧的版本。 +3. 下方完整 Python 命令:纯机器规则对比,不调用 LLM,也不修改文件。 + +`/imupdate` 的可选第二个参数可以指定目标 tag 或 SHA。 + +## 底层命令 先在 vLLM-Omni checkout 中 fetch 需要比较的 tag 或 SHA,然后运行: @@ -30,6 +52,13 @@ python tools/audit_vllm_omni_release.py ` 默认 `--mode enforce`:存在未解释漂移时退出 1,输入或 Git 失败时退出 2。 `--mode report-only` 仍报告 `DRIFT`,但退出 0,供定时巡检使用。 +底层实现不 checkout 版本:它用 `git rev-parse` 固定两个提交,用 +`git diff --name-status -M` 找新增、修改、删除和重命名,再用 +`git show :` 直接读取 Git 对象。Python AST 负责提取模型和 +pipeline registry,deploy YAML 直接按提交列举;结果做排序和哈希后与 baseline、 +owner 路由、manifest、知识 `sources:` 和 pin 对账,最后输出稳定 JSON。 +这个命令只报告证据,不编辑任何文件。 + ## 更新一个 release 1. 用当前 `release_baseline.yaml` 的 audited SHA 作为 `--from`,新 tag 作为 diff --git a/plugins/infermatrix-copilot/.claude-plugin/plugin.json b/plugins/infermatrix-copilot/.claude-plugin/plugin.json index 26bbf20..472687d 100644 --- a/plugins/infermatrix-copilot/.claude-plugin/plugin.json +++ b/plugins/infermatrix-copilot/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "infermatrix-copilot", "version": "0.1.0", - "description": "Maintainer-grounded vLLM-Omni review knowledge over MCP.", + "description": "Maintainer-grounded vLLM-Omni review and release knowledge updates.", "author": { "name": "JiusiServe", "url": "https://github.com/JiusiServe" diff --git a/plugins/infermatrix-copilot/.codex-plugin/plugin.json b/plugins/infermatrix-copilot/.codex-plugin/plugin.json index e5fd589..c008fef 100644 --- a/plugins/infermatrix-copilot/.codex-plugin/plugin.json +++ b/plugins/infermatrix-copilot/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "infermatrix-copilot", "version": "0.1.0", - "description": "Maintainer-grounded vLLM-Omni review knowledge over MCP.", + "description": "Maintainer-grounded vLLM-Omni review and release knowledge updates.", "author": { "name": "JiusiServe", "url": "https://github.com/JiusiServe" @@ -18,17 +18,19 @@ "mcpServers": "./.mcp.json", "interface": { "displayName": "InferMatrixCopilot", - "shortDescription": "Review vLLM-Omni changes with maintainer knowledge", - "longDescription": "Use maintainer-grounded vLLM-Omni knowledge to review pull requests and local changes.", + "shortDescription": "Review and update vLLM-Omni knowledge", + "longDescription": "Use maintainer-grounded vLLM-Omni knowledge to review changes and update release-driven facts.", "developerName": "JiusiServe", "category": "Developer Tools", "capabilities": [ "MCP", - "Code review" + "Code review", + "Knowledge maintenance" ], "websiteURL": "https://github.com/JiusiServe/InferMatrixCopilot", "defaultPrompt": [ - "Review this PR with InferMatrixCopilot." + "Review this PR with InferMatrixCopilot.", + "Update InferMatrixCopilot knowledge from this repository." ] } } diff --git a/plugins/infermatrix-copilot/.cursor-plugin/plugin.json b/plugins/infermatrix-copilot/.cursor-plugin/plugin.json index 167b48b..c475b0f 100644 --- a/plugins/infermatrix-copilot/.cursor-plugin/plugin.json +++ b/plugins/infermatrix-copilot/.cursor-plugin/plugin.json @@ -2,7 +2,7 @@ "name": "infermatrix-copilot", "displayName": "InferMatrixCopilot", "version": "0.1.0", - "description": "Maintainer-grounded vLLM-Omni review knowledge over MCP.", + "description": "Maintainer-grounded vLLM-Omni review and release knowledge updates.", "author": { "name": "JiusiServe" }, diff --git a/plugins/infermatrix-copilot/skills/imupdate/SKILL.md b/plugins/infermatrix-copilot/skills/imupdate/SKILL.md new file mode 100644 index 0000000..aaf8ba3 --- /dev/null +++ b/plugins/infermatrix-copilot/skills/imupdate/SKILL.md @@ -0,0 +1,89 @@ +--- +name: imupdate +description: Resolve an upstream repository from a local path, repository name, alias, or URL, then update InferMatrixCopilot's release-driven knowledge. Use when the user invokes /imupdate or $imupdate, asks to sync knowledge after an upstream release, compare vLLM-Omni tags or SHAs, or refresh the release baseline, model catalog, source maps, and manifests. +--- + +# InferMatrix update + +```text +/imupdate [target-tag-or-sha] +``` + +The installed InferMatrixCopilot root is: + +```text +{{INFERMATRIX_COPILOT_ROOT}} +``` + +If that placeholder was not replaced, locate or temporarily clone the +InferMatrixCopilot checkout containing `tools/audit_vllm_omni_release.py`. + +## Select the input mode + +### Local Git path + +When the argument exists and is a Git checkout, use the deterministic audit. +Read `adapters/vllm_omni/release_baseline.yaml`; use +`upstream.audited_sha` as the old revision and the optional target or checkout +`HEAD` as the new revision. Refuse an accidental downgrade when the target is +older than the audited revision. Do not fetch or checkout unless explicitly +asked. + +### Repository name, alias, or URL + +When the argument is not a local path, use the host model to resolve it: + +1. Treat `vllm-omni`, `vllmomni`, `vllm omni`, and + `vllm-project/vllm-omni` as `https://github.com/vllm-project/vllm-omni`. +2. Check the configured repo map and current workspace for a matching checkout. + If found, continue as the local-path mode. +3. Otherwise verify the canonical repository from authoritative GitHub + metadata. Choose the supplied target, otherwise the newest release or tag + that is not older than the audited revision, otherwise the default-branch + HEAD; state that choice. Never silently downgrade because GitHub's "latest + release" can exclude newer prereleases. +4. Prefer a temporary clone and run the deterministic audit. If repository + access or cloning is unavailable, inspect the authoritative source with the + host model, update only source-backed facts, and clearly report that machine + audit/enforce validation was unavailable. Never call that result `CLEAN`. +5. Ask for clarification only when multiple plausible canonical repositories + remain after resolution. + +## Audit and update + +1. Resolve old and new revisions to full SHAs. +2. Create a temporary JSON path and run the read-only audit in report mode when + a local or temporary Git checkout is available: + + ```text + python /tools/audit_vllm_omni_release.py \ + --from \ + --to \ + --repo \ + --mode report-only \ + --json-output + ``` + +3. Explain the registry, pipeline, deploy, path-routing, source, and pin drift. + Read the relevant upstream diff before changing InferMatrixCopilot. +4. Update only facts proven by the report and source: the release baseline, + model catalog, component source maps, pins, or adapter manifest. Never + generate or rewrite owner rules automatically; report semantic-rule + candidates for human review. +5. When machine audit is available, run the same command with `--mode enforce`, + then run: + + ```text + python knowledge/tools/check_knowledge_tree.py + python knowledge/tools/check_wiki_lint.py + python -m pytest test/test_release_audit.py test/test_adapters.py \ + test/test_routing.py test/test_knowledge_source.py \ + test/test_skills_scope.py test/test_capabilities.py -q + ``` + +6. Delete temporary reports and clones. Return the canonical repository, input + mode, old and new SHAs, structural deltas, files changed, validation + results, and any unresolved or model-only findings. + +The audit is evidence collection, not the updater. Codex performs the bounded +knowledge edits and must not commit, push, or open a PR unless asked. diff --git a/plugins/infermatrix-copilot/skills/imupdate/agents/openai.yaml b/plugins/infermatrix-copilot/skills/imupdate/agents/openai.yaml new file mode 100644 index 0000000..d95c912 --- /dev/null +++ b/plugins/infermatrix-copilot/skills/imupdate/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "InferMatrix Update" + short_description: "Audit and update release-driven repository knowledge" + default_prompt: "Use $imupdate to resolve a repository path or name and update InferMatrix knowledge." diff --git a/scripts/install_mcp.py b/scripts/install_mcp.py index 5c7ff77..f813a32 100644 --- a/scripts/install_mcp.py +++ b/scripts/install_mcp.py @@ -15,9 +15,7 @@ PROJECT_ROOT = Path(__file__).resolve().parent.parent -SKILL_SOURCE = ( - PROJECT_ROOT / "plugins" / "infermatrix-copilot" / "skills" / "imreview" -) +SKILLS_SOURCE = PROJECT_ROOT / "plugins" / "infermatrix-copilot" / "skills" SERVER_NAME = "infermatrix-copilot" PACKAGE = ( "infermatrix-copilot[mcp] @ " @@ -46,9 +44,23 @@ def _run_quiet(command: Sequence[str]) -> None: ) -def _install_skill(destination: Path) -> None: - destination.parent.mkdir(parents=True, exist_ok=True) - shutil.copytree(SKILL_SOURCE, destination, dirs_exist_ok=True) +def _install_skills(destination_root: Path) -> None: + destination_root.mkdir(parents=True, exist_ok=True) + for source in sorted(path for path in SKILLS_SOURCE.iterdir() if path.is_dir()): + destination = destination_root / source.name + shutil.copytree(source, destination, dirs_exist_ok=True) + for path in destination.rglob("*"): + if not path.is_file(): + continue + content = path.read_text(encoding="utf-8") + if "{{INFERMATRIX_COPILOT_ROOT}}" in content: + path.write_text( + content.replace( + "{{INFERMATRIX_COPILOT_ROOT}}", + str(PROJECT_ROOT), + ), + encoding="utf-8", + ) def _install_codex(config_root: Path) -> None: @@ -60,7 +72,7 @@ def _install_codex(config_root: Path) -> None: [codex, "mcp", "add", SERVER_NAME, "--", *SERVER_COMMAND], "Codex MCP registration failed.", ) - _install_skill(config_root / ".codex" / "skills" / "imreview") + _install_skills(config_root / ".codex" / "skills") def _install_claude(config_root: Path) -> None: @@ -83,7 +95,7 @@ def _install_claude(config_root: Path) -> None: ], "Claude Code MCP registration failed.", ) - _install_skill(config_root / ".claude" / "skills" / "imreview") + _install_skills(config_root / ".claude" / "skills") def _install_cursor(config_root: Path) -> None: @@ -120,7 +132,7 @@ def _install_cursor(config_root: Path) -> None: json.dumps(config, ensure_ascii=False, indent=2) + "\n", encoding="utf-8", ) - _install_skill(cursor_root / "skills" / "imreview") + _install_skills(cursor_root / "skills") def _cursor_installed(config_root: Path) -> bool: @@ -128,10 +140,7 @@ def _cursor_installed(config_root: Path) -> bool: return True candidates = [ config_root / ".cursor", - Path(os.environ.get("LOCALAPPDATA", "")) - / "Programs" - / "cursor" - / "Cursor.exe", + Path(os.environ.get("LOCALAPPDATA", "")) / "Programs" / "cursor" / "Cursor.exe", Path("/Applications/Cursor.app"), ] return any(path.exists() for path in candidates) @@ -201,7 +210,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int: output = PROJECT_ROOT / "infermatrix-copilot.mcp.json" _write_generic_config(output) print(f"No known Agent detected. MCP config written to:\n {output}") - print(f"Portable Skill:\n {SKILL_SOURCE}") + print(f"Portable Skills:\n {SKILLS_SOURCE}") return 0 failures = [] @@ -223,7 +232,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int: print(f" {failure}", file=sys.stderr) return 1 - print("Restart your Agent, then run: /imreview ") + print("Restart your Agent, then run: /imreview or /imupdate ") return 0 diff --git a/test/test_imupdate_skill.py b/test/test_imupdate_skill.py new file mode 100644 index 0000000..c8daec8 --- /dev/null +++ b/test/test_imupdate_skill.py @@ -0,0 +1,37 @@ +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +SKILL = ROOT / "plugins" / "infermatrix-copilot" / "skills" / "imupdate" / "SKILL.md" + + +def test_imupdate_skill_supports_three_input_modes() -> None: + text = SKILL.read_text(encoding="utf-8") + + assert "/imupdate [target-tag-or-sha]" in text + assert "### Local Git path" in text + assert "### Repository name, alias, or URL" in text + for alias in ("`vllm-omni`", "`vllmomni`", "`vllm omni`"): + assert alias in text + assert "temporary clone" in text + assert "Never call that result `CLEAN`" in text + assert "Never silently downgrade" in text + + assert "upstream.audited_sha" in text + assert "tools/audit_vllm_omni_release.py" in text + for argument in ("--from", "--to", "--repo", "--mode report-only"): + assert argument in text + assert "--mode enforce" in text + assert "generate or rewrite owner rules automatically" in text + + +def test_installer_and_docs_expose_imupdate() -> None: + installer = (ROOT / "scripts" / "install_mcp.py").read_text(encoding="utf-8") + readme = (ROOT / "README.md").read_text(encoding="utf-8") + + assert "SKILLS_SOURCE" in installer + assert '_install_skills(config_root / ".codex" / "skills")' in installer + assert '_install_skills(config_root / ".claude" / "skills")' in installer + assert '_install_skills(cursor_root / "skills")' in installer + assert "/imupdate D:\\path\\to\\vllm-omni" in readme + assert "/imupdate vllmomni" in readme diff --git a/test/test_install_mcp_script.py b/test/test_install_mcp_script.py index 7825ca1..cb88f18 100644 --- a/test/test_install_mcp_script.py +++ b/test/test_install_mcp_script.py @@ -52,3 +52,8 @@ def test_cursor_install_preserves_existing_config(tmp_path): assert config["mcpServers"]["infermatrix-copilot"]["command"] == "uvx" assert list(cursor_root.glob("mcp.json.*.bak")) assert (cursor_root / "skills" / "imreview" / "SKILL.md").is_file() + update_skill = cursor_root / "skills" / "imupdate" / "SKILL.md" + assert update_skill.is_file() + text = update_skill.read_text(encoding="utf-8") + assert "{{INFERMATRIX_COPILOT_ROOT}}" not in text + assert str(ROOT) in text