@kevlns/v-cli 0.2.0-beta.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/AGENTS.md CHANGED
@@ -7,12 +7,15 @@
7
7
 
8
8
  ## 核心约定(v-cli 本体)
9
9
 
10
- - 环境要求 Node.js >= 20;v-cli 版本 @kevlns/v-cli@0.2.0-beta.3
10
+ - 环境要求 Node.js >= 20;v-cli 版本 @kevlns/v-cli@0.2.0
11
11
  - 命令分三类:builtin(内置)、local(~/.v-cli/commands/ 下的本地插件)、official(官方插件白名单);
12
12
  **最新、live 的命令集合以实际发现为准**:先运行 `v-cli agent index --json` 获取全部命令与 agent 元数据
13
13
  - 单个命令的完整元数据用 `v-cli agent describe <命令名> --json` 查看
14
14
  - AI Agent 引导文档:`v-cli agent docs` 输出本文件原文(`--json` 含 sha256/content);
15
- `v-cli agent init .` 把它写入工作区(已存在默认拒绝,`--force` 覆盖,`--dry-run` 预览;符号链接目标 fail-closed)
15
+ `v-cli agent init .` 把它写入工作区(已存在默认拒绝,`--force` 覆盖,`--dry-run` 预览;符号链接目标 fail-closed);
16
+ 同时把随包发布的 v-cli skill(skills/v-cli)装配到 <目录> 下匹配的 agent 技能目录(如 .claude/skills、.agent/skill、AgentHome/skills 等,清单见 src/core/agent-dirs.ts),无匹配则跳过
17
+ - **首次调用规范**:首次调用任何 official 插件命令前,必须先运行 `v-cli agent docs <命令名>`,
18
+ 掌握该插件包内 `AGENTS.md`;使用规范、快速流程与禁止事项以插件 AGENTS.md 为准。
16
19
  - 官方插件命令(`v-cli xlmerge …`、`v-cli unity …`)在子进程中运行(stdio 继承):v-cli 只做路由,
17
20
  不解析、不改写插件的 stdout/stderr;插件 `--help`/`--json` 等参数由插件自己消费
18
21
  - 插件对 worktree 的写入/提交行为以插件清单 v-cli.plugin.json 的 `agent.safety` 为准:
@@ -20,55 +23,25 @@
20
23
 
21
24
  ## @kevlns/u-cli-mod — 命令 `v-cli unity …`
22
25
 
23
- **版本**:0.1.0-beta.3
26
+ **版本**:0.1.0
24
27
  **描述**:Pin a Unity project to its exact editor version route, download the verified Unity CLI and install the adapted com.unity.pipeline package for Unity 2022 (Windows-first, non-official Unity tooling).
25
28
  **平台**:win32(仅 Windows 主机可用;非 Windows 上 v-cli 会拒绝路由)
26
29
 
27
30
  **何时使用**:Use for Windows-first Unity Editor 2022 workflows that must stay pinned to an exact editor version: diagnose a project against its route, list pinned routes, download the verified Unity CLI, transactionally install the adapted com.unity.pipeline package, or run Unity Pipeline CLI commands with enforced project targeting. Never use on non-Windows hosts.
28
31
 
29
- **全局选项**:
30
- - `-V, --version` — output the version number
31
- - `-h, --help` — display help for command
32
-
33
- **子命令**:
34
- - `doctor` — 检查工程版本、路由、CLI 与 Pipeline 状态(用法:`v-cli unity doctor <project> [options]`)
35
- - 安全标签:read-only; never writes the project or the cache;fail-closed exact m_EditorVersion + m_EditorVersionWithRevision match (no wildcard, no fallback);queries running Unity.exe processes via PowerShell; a failed query is reported in the unityProcesses output, not fatal
36
- - `routes` — 列出所有已配置的 Editor 精确路由(用法:`v-cli unity routes [options]`)
37
- - 安全标签:read-only; no network access;uses only embedded pinned route metadata shipped in the package
38
- - `cli install` — 下载并校验固定版本的 Unity CLI(SHA-256 + Authenticode)(用法:`v-cli unity cli install [options]`)
39
- - 安全标签:writes only under the user cache: %LOCALAPPDATA%\editor-pipeline-cli\cache\cli;downloads from the pinned Unity official CDN HTTPS URL only;verifies fixed SHA-256 + expected size + Authenticode signer subject and certificate thumbprint before the binary is placed;unverified temp download is deleted on any failure; the final file is created by atomic rename
40
- - `pipeline install` — 按工程 Editor 版本事务式安装适配后的 com.unity.pipeline(用法:`v-cli unity pipeline install <project> [options]`)
41
- - 安全标签:transactional install: stage -> verify -> backup -> replace -> re-verify -> receipt; automatic rollback on any failure;fail-closed when a matching Unity Editor is running, unless --allow-running-editor;--dry-run never writes the project and never writes a receipt;source and destination trees must match the pinned expected-tree (385 files, SHA-256) before and after placement;writes only under <project>/Packages/com.unity.pipeline and <project>/Library/editor-pipeline-cli
42
- - `setup` — cli install + pipeline install(用法:`v-cli unity setup <project> [options]`)
43
- - 安全标签:combines the cli install and pipeline install safety contracts;fail-closed running-Editor guard unless --allow-running-editor;--dry-run never writes the project;--skip-cli avoids all CLI downloads
44
- - `exec` — 调用路由 CLI 执行 Unity Pipeline 命令;--project-path 由工具统一绑定(用法:`v-cli unity exec <project> [options] -- <unity-cli-args...>`)
45
- - 安全标签:re-verifies the pinned CLI SHA-256 before every invocation (fail-closed on tamper);rejects every --project-path variant (case-insensitive: --project-path, --projectPath, -projectPath, --project_path, mixed case);always appends the resolved --project-path as the final argument; the wrapper owns project targeting;no route fallback: the project must match a pinned route exactly;the documented -- separator is consumed by the wrapper and never forwarded to the Unity CLI
46
- - `cache clean` — 清理下载缓存与生成的适配包(用法:`v-cli unity cache clean [options]`)
47
- - 安全标签:removes only directories under the user cache root %LOCALAPPDATA%\editor-pipeline-cli;default scope: generated packages + pipeline downloads only; without --all the CLI cache and logs are preserved
32
+ **首次调用前必读**:`v-cli agent docs unity`(插件包内 AGENTS.md 规范正本)
33
+ **实时参数/命令**:`v-cli agent describe unity --json`
48
34
 
49
35
  ## @kevlns/xlmerge — 命令 `v-cli xlmerge …`
50
36
 
51
- **版本**:1.2.1-beta.3
37
+ **版本**:1.2.1
52
38
  **描述**:Visual resolver for Git merge conflicts in .xlsx/.xlsm planning tables: three-way sheet/row/cell diff, local UI, write-back and commit.
53
39
  **平台**:darwin, linux, win32
54
40
 
55
41
  **何时使用**:When a user asks to resolve Git merge conflicts in .xlsx/.xlsm planning or configuration tables (策划表/配置表冲突): run detect first; when count > 0 run launch and give the returned url to the user. Do not inspect workbook cells or summarize diffs yourself; the resolver owns diff, choices, write-back and commit.
56
42
 
57
- **全局选项**:
58
- - `--repo <path>` — Git repository (default: current directory; searches upward for the Git root).
59
- - `--runtime-dir <dir>` — Optional directory for extracted Git stage workbooks and manifest (default: system temp). Used by prepare, resolve and launch.
60
-
61
- **子命令**:
62
- - `detect` — List unresolved .xlsx/.xlsm files in the repository.(用法:`v-cli xlmerge --repo <repo> detect`)
63
- - 安全标签:read-only;no-worktree-modification
64
- - `prepare` — Extract base/ours/theirs stage versions from the Git index and build a sheet-aware manifest.(用法:`v-cli xlmerge --repo <repo> prepare [--path <file>]`)
65
- - 安全标签:writes-runtime-dir;no-worktree-modification;no-commit
66
- - `resolve` — Prepare conflicts and serve the local visual resolver in the foreground (blocking).(用法:`v-cli xlmerge --repo <repo> resolve [--path <file>] [--no-browser]`)
67
- - 安全标签:blocking;binds-loopback;opens-browser-by-default;no-push;writes-runtime-dir;writes-worktree-via-ui;commits-by-default-via-ui
68
- - `launch` — Prepare conflicts, start the visual resolver in the background and return its URL.(用法:`v-cli xlmerge --repo <repo> launch [--path <file>] [--no-browser]`)
69
- - 安全标签:starts-background-server;binds-loopback;opens-browser-by-default;no-push;writes-runtime-dir;writes-worktree-via-ui;commits-by-default-via-ui
70
- - `apply` — Write back decisions from a decisions JSON without launching the UI.(用法:`v-cli xlmerge --repo <repo> apply --manifest <manifest.json> --decisions <decisions.json> [--no-commit] [--push] [--message <text>]`)
71
- - 安全标签:writes-worktree;commits-by-default;pushes-only-with-flag
43
+ **首次调用前必读**:`v-cli agent docs xlmerge`(插件包内 AGENTS.md 规范正本)
44
+ **实时参数/命令**:`v-cli agent describe xlmerge --json`
72
45
 
73
46
  ---
74
47
 
package/dist/cli.mjs CHANGED
@@ -75,8 +75,8 @@ function defaultHomeDir() {
75
75
  }
76
76
 
77
77
  // src/core/loader.ts
78
- import fs5 from "fs";
79
- import path7 from "path";
78
+ import fs6 from "fs";
79
+ import path8 from "path";
80
80
  import { pathToFileURL } from "url";
81
81
 
82
82
  // src/core/official.ts
@@ -618,7 +618,7 @@ function validateLocalCommand(candidate, options = {}) {
618
618
  }
619
619
 
620
620
  // src/commands/agent.ts
621
- import path5 from "path";
621
+ import path6 from "path";
622
622
 
623
623
  // src/core/agent-docs.ts
624
624
  import fs3 from "fs";
@@ -627,7 +627,7 @@ import { createHash, randomBytes } from "crypto";
627
627
  import { fileURLToPath as fileURLToPath2 } from "url";
628
628
 
629
629
  // src/version.ts
630
- var VERSION = "0.2.0-beta.3";
630
+ var VERSION = "0.2.0";
631
631
 
632
632
  // src/core/agent-docs.ts
633
633
  var BUNDLED_DOCS_FILE = "AGENTS.md";
@@ -699,6 +699,51 @@ function readBundledAgentsMd(opts = {}) {
699
699
  bytes: Buffer.byteLength(content, "utf-8")
700
700
  };
701
701
  }
702
+ function readOfficialAgentsMd(command, opts = {}) {
703
+ const spec = OFFICIAL_PLUGINS.find((item) => item.command === command);
704
+ if (!spec) {
705
+ throw new Error(
706
+ `\u672A\u627E\u5230\u5B98\u65B9\u63D2\u4EF6\u547D\u4EE4: ${command}\u3002\u8BF7\u5148\u8FD0\u884C v-cli agent index --json \u67E5\u770B\u53EF\u7528\u5B98\u65B9\u63D2\u4EF6\u3002`
707
+ );
708
+ }
709
+ const resolvePackage = opts.resolvePackage ?? defaultResolvePackage;
710
+ const pkgJsonPath = resolvePackage(spec.package);
711
+ if (!pkgJsonPath) {
712
+ throw new Error(
713
+ `\u672A\u5B89\u88C5\u5B98\u65B9\u63D2\u4EF6\u5305 ${spec.package}\uFF08\u547D\u4EE4 ${command}\uFF09\uFF0C\u6216\u5B83\u4E0D\u5728\u53EF\u89E3\u6790\u8DEF\u5F84\u3002\u8BF7\u5148\u5B89\u88C5\u540E\u91CD\u8BD5\u3002`
714
+ );
715
+ }
716
+ let pkgJson;
717
+ try {
718
+ pkgJson = JSON.parse(fs3.readFileSync(pkgJsonPath, "utf-8"));
719
+ } catch (err) {
720
+ throw new Error(
721
+ `\u65E0\u6CD5\u8BFB\u53D6\u5B98\u65B9\u63D2\u4EF6 ${spec.package} \u7684 package.json\uFF08${pkgJsonPath}\uFF09: ${err instanceof Error ? err.message : String(err)}`
722
+ );
723
+ }
724
+ if (pkgJson.name !== spec.package || typeof pkgJson.version !== "string") {
725
+ throw new Error(`\u5B98\u65B9\u63D2\u4EF6 package.json \u8EAB\u4EFD\u65E0\u6548: \u671F\u671B ${spec.package} \u4E14 version \u4E3A\u5B57\u7B26\u4E32\u3002`);
726
+ }
727
+ const file = path4.join(path4.dirname(pkgJsonPath), BUNDLED_DOCS_FILE);
728
+ let content;
729
+ try {
730
+ if (!fs3.lstatSync(file).isFile()) throw new Error("\u76EE\u6807\u4E0D\u662F\u666E\u901A\u6587\u4EF6");
731
+ content = fs3.readFileSync(file, "utf-8");
732
+ } catch (err) {
733
+ throw new Error(
734
+ `\u5B98\u65B9\u63D2\u4EF6 ${spec.package}@${pkgJson.version} \u672A\u63D0\u4F9B\u53EF\u8BFB\u7684 ${BUNDLED_DOCS_FILE}\uFF08${file}\uFF09: ${err instanceof Error ? err.message : String(err)}\u3002\u8BF7\u5347\u7EA7\u6216\u91CD\u65B0\u5B89\u88C5\u8BE5\u63D2\u4EF6\u3002`
735
+ );
736
+ }
737
+ return {
738
+ package: spec.package,
739
+ command,
740
+ version: pkgJson.version,
741
+ file,
742
+ content,
743
+ sha256: sha256Hex(content),
744
+ bytes: Buffer.byteLength(content, "utf-8")
745
+ };
746
+ }
702
747
  function sha256Hex(content) {
703
748
  return createHash("sha256").update(content, "utf-8").digest("hex");
704
749
  }
@@ -804,7 +849,140 @@ function atomicWrite(target, content) {
804
849
  throw new Error(`\u539F\u5B50\u5199\u5165\u5931\u8D25\uFF08\u4E34\u65F6\u6587\u4EF6\u547D\u540D\u78B0\u649E\u91CD\u8BD5 ${MAX_TMP_RETRIES} \u6B21\u672A\u679C\uFF09: ${detail}`);
805
850
  }
806
851
 
852
+ // src/core/agent-skill.ts
853
+ import fs4 from "fs";
854
+ import path5 from "path";
855
+
856
+ // src/core/agent-dirs.ts
857
+ var AGENT_SKILL_DIRS = [
858
+ ".claude/skills",
859
+ ".claude/skill",
860
+ ".cursor/skills",
861
+ ".cursor/rules",
862
+ ".github/prompts",
863
+ ".gemini/skills",
864
+ ".codex/skills",
865
+ ".agents/skills",
866
+ ".agent/skills",
867
+ ".agent/skill",
868
+ "AgentHome/skills",
869
+ ".windsurf/skills",
870
+ ".trae/skills",
871
+ ".kilocode/skills",
872
+ ".roo/skills",
873
+ ".opencode/skills",
874
+ ".augment/skills",
875
+ ".kiro/skills",
876
+ ".amazonq/skills",
877
+ ".continue/skills"
878
+ ];
879
+
880
+ // src/core/agent-skill.ts
881
+ var SKILL_NAME = "v-cli";
882
+ var SKILL_FILE = "SKILL.md";
883
+ function resolveSkillSourceRoot(opts = {}) {
884
+ const agentsMd = resolveBundledAgentsMd(opts);
885
+ if (!agentsMd) return void 0;
886
+ const root = path5.join(path5.dirname(agentsMd), "skills", SKILL_NAME);
887
+ try {
888
+ if (fs4.statSync(root).isDirectory()) return root;
889
+ } catch {
890
+ }
891
+ return void 0;
892
+ }
893
+ function readSkillSource(opts = {}) {
894
+ const root = resolveSkillSourceRoot(opts);
895
+ if (!root) {
896
+ throw new Error(
897
+ `\u65E0\u6CD5\u5B9A\u4F4D ${BUNDLED_DOCS_PACKAGE} \u5185\u7F6E skill\uFF08skills/${SKILL_NAME}\uFF09\uFF1A\u8BF7\u786E\u8BA4\u5B89\u88C5\u7684\u5305\u5185\u5305\u542B skills/ \u76EE\u5F55\uFF08\u91CD\u65B0\u5B89\u88C5 @kevlns/v-cli \u540E\u91CD\u8BD5\uFF09\u3002`
898
+ );
899
+ }
900
+ const file = path5.join(root, SKILL_FILE);
901
+ let content;
902
+ try {
903
+ if (!fs4.lstatSync(file).isFile()) throw new Error("\u5165\u53E3\u4E0D\u662F\u666E\u901A\u6587\u4EF6");
904
+ content = fs4.readFileSync(file, "utf-8");
905
+ } catch (err) {
906
+ throw new Error(
907
+ `\u65E0\u6CD5\u8BFB\u53D6 ${BUNDLED_DOCS_PACKAGE} \u5185\u7F6E skill\uFF08${file}\uFF09: ${err instanceof Error ? err.message : String(err)}`
908
+ );
909
+ }
910
+ return {
911
+ root,
912
+ file,
913
+ content,
914
+ sha256: sha256Hex(content),
915
+ bytes: Buffer.byteLength(content, "utf-8")
916
+ };
917
+ }
918
+ function collectAgentSkillDirs(directory) {
919
+ const hits = [];
920
+ for (const rel of AGENT_SKILL_DIRS) {
921
+ const abs = path5.join(directory, rel);
922
+ try {
923
+ if (fs4.statSync(abs).isDirectory()) hits.push(abs);
924
+ } catch {
925
+ }
926
+ }
927
+ return hits;
928
+ }
929
+ function copySkillDir(srcRoot, destDir) {
930
+ fs4.rmSync(destDir, { recursive: true, force: true });
931
+ fs4.cpSync(srcRoot, destDir, { recursive: true });
932
+ }
933
+ function performAgentSkillAssembly(opts) {
934
+ const { directory, source, dryRun = false } = opts;
935
+ const base = {
936
+ ok: true,
937
+ dryRun,
938
+ directory,
939
+ assembled: [],
940
+ skill: { name: SKILL_NAME, sha256: source.sha256, bytes: source.bytes }
941
+ };
942
+ let dirStat;
943
+ try {
944
+ dirStat = fs4.statSync(directory);
945
+ } catch {
946
+ return { ...base, ok: false, reason: `\u76EE\u6807\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u8BBF\u95EE: ${directory}` };
947
+ }
948
+ if (!dirStat.isDirectory()) {
949
+ return { ...base, ok: false, reason: `\u76EE\u6807\u4E0D\u662F\u76EE\u5F55: ${directory}` };
950
+ }
951
+ const hits = collectAgentSkillDirs(directory);
952
+ for (const dir of hits) {
953
+ const target = path5.join(dir, SKILL_NAME);
954
+ let overwrite = false;
955
+ try {
956
+ fs4.lstatSync(target);
957
+ overwrite = true;
958
+ } catch {
959
+ overwrite = false;
960
+ }
961
+ const action = dryRun ? "assemble" : "assembled";
962
+ if (!dryRun) {
963
+ copySkillDir(source.root, target);
964
+ }
965
+ base.assembled.push({ dir, target, action, overwrite });
966
+ }
967
+ return base;
968
+ }
969
+
807
970
  // src/commands/agent.ts
971
+ function skillOutcomeText(outcome, dryRun) {
972
+ const verb = dryRun ? "\u5C06\u88C5\u914D\u5230" : "\u5DF2\u88C5\u914D\u5230";
973
+ switch (outcome.status) {
974
+ case "assembled": {
975
+ const dirs = outcome.assembled.map((t) => t.dir).join(", ");
976
+ return `[skill v-cli] ${verb} ${outcome.assembled.length} \u4E2A agent \u76EE\u5F55\uFF1A${dirs}`;
977
+ }
978
+ case "skipped-none":
979
+ return "[skill v-cli] \u672A\u68C0\u6D4B\u5230\u5339\u914D\u7684 agent \u6280\u80FD\u76EE\u5F55\uFF0C\u8DF3\u8FC7\u88C5\u914D";
980
+ case "skipped-source-missing":
981
+ return `[skill v-cli] \u8DF3\u8FC7\u88C5\u914D\uFF08\u6E90\u7F3A\u5931\uFF09\uFF1A${outcome.reason}`;
982
+ case "skipped-init-failed":
983
+ return "[skill v-cli] \u8DF3\u8FC7\u88C5\u914D\uFF08AGENTS.md \u672A\u5C31\u7EEA\uFF09";
984
+ }
985
+ }
808
986
  function buildAgentIndex(builtin, local, official) {
809
987
  const rows = [];
810
988
  for (const cmd of builtin) {
@@ -880,7 +1058,7 @@ var AGENT_HELP_TEXT = [
880
1058
  "agent \u5B50\u547D\u4EE4\u4E00\u89C8\uFF1A",
881
1059
  " index \u5217\u51FA\u5168\u90E8\u547D\u4EE4\uFF08builtin/local/official\uFF09\u4E0E agent \u5143\u6570\u636E\uFF1B--json \u8F93\u51FA\u7A33\u5B9A JSON \u6570\u7EC4",
882
1060
  " describe \u67E5\u770B\u5355\u4E2A\u547D\u4EE4\u7684\u5B8C\u6574\u8BB0\u5F55\uFF08\u7528\u6CD5/\u53C2\u6570/\u9009\u9879/\u8F93\u51FA/\u9000\u51FA\u7801/\u5B89\u5168\u6807\u7B7E\uFF09\uFF1B--json \u8F93\u51FA\u5355\u6761\u8BB0\u5F55",
883
- " docs \u8F93\u51FA\u5F53\u524D @kevlns/v-cli \u5305\u5185\u7F6E AGENTS.md \u539F\u6587\uFF1B--json \u8F93\u51FA { package, version, sha256, content }",
1061
+ " docs \u65E0\u53C2\u8F93\u51FA v-cli AGENTS.md\uFF1B\u4F20 <command> \u8F93\u51FA\u5DF2\u5B89\u88C5\u5B98\u65B9\u63D2\u4EF6\u5305\u5185 AGENTS.md",
884
1062
  " init \u628A\u5185\u7F6E AGENTS.md \u5199\u5165\u5DE5\u4F5C\u533A\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09\uFF1B\u5DF2\u5B58\u5728\u9ED8\u8BA4\u62D2\u7EDD\uFF0C--force \u8986\u76D6\uFF0C--dry-run \u9884\u89C8",
885
1063
  "",
886
1064
  "JSON\uFF1A\u6240\u6709\u5B50\u547D\u4EE4\u652F\u6301 --json\uFF08\u6216\u524D\u7F6E\u5168\u5C40 --json\uFF0C\u5982 v-cli --json agent index\uFF09",
@@ -894,28 +1072,33 @@ var AGENT_HELP_TEXT = [
894
1072
  "\u793A\u4F8B\uFF1A",
895
1073
  " v-cli agent index --json # \u5168\u91CF\u547D\u4EE4\u7D22\u5F15",
896
1074
  " v-cli agent describe ts --json # \u5355\u547D\u4EE4\u8BE6\u60C5",
897
- " v-cli agent docs # \u5185\u7F6E\u5F15\u5BFC\u6587\u6863\u539F\u6587",
1075
+ " v-cli agent docs # v-cli \u7626\u7D22\u5F15\u539F\u6587",
1076
+ " v-cli agent docs unity # u-cli-mod \u5305\u5185\u4F7F\u7528\u89C4\u8303",
1077
+ " v-cli agent docs xlmerge --json # xlmerge \u5305\u5185\u89C4\u8303\uFF08JSON\uFF09",
898
1078
  " v-cli agent init . --dry-run # \u9884\u89C8\u521D\u59CB\u5316\u76EE\u6807"
899
1079
  ].join("\n");
900
1080
  var DOCS_HELP_TEXT = [
901
- "\u8F93\u51FA\u5F53\u524D\u5B89\u88C5\u7684 @kevlns/v-cli \u5305\u5185\u7F6E AGENTS.md \u539F\u6587\uFF08AI Agent \u5F15\u5BFC\u6587\u6863\uFF09\uFF0C\u9010\u5B57\u8282\u539F\u6837\u8F93\u51FA\u3001\u4E0D\u8FFD\u52A0\u6362\u884C\u3002",
1081
+ "\u8F93\u51FA AGENTS.md \u539F\u6587\uFF08\u9010\u5B57\u8282\u539F\u6837\u8F93\u51FA\u3001\u4E0D\u8FFD\u52A0\u6362\u884C\uFF09\u3002",
1082
+ " \u7701\u7565 [command]\uFF1A\u5F53\u524D @kevlns/v-cli \u5305\u5185\u7F6E\u7626\u7D22\u5F15\u3002",
1083
+ " \u4F20\u5165 [command]\uFF1A\u5DF2\u5B89\u88C5\u5B98\u65B9\u63D2\u4EF6\u5305\u6839 AGENTS.md\uFF08\u5982 unity / xlmerge\uFF09\u3002",
1084
+ "",
1085
+ "\u53C2\u6570\uFF1A",
1086
+ " [command] official \u63D2\u4EF6\u547D\u4EE4\u540D\uFF1B\u5148\u7528 v-cli agent index --json \u67E5\u770B",
902
1087
  "",
903
1088
  "JSON \u884C\u4E3A\uFF08--json \u6216\u524D\u7F6E\u5168\u5C40 --json\uFF09\uFF1A",
904
1089
  " \u6210\u529F\uFF1Astdout \u8F93\u51FA\u7A33\u5B9A\u5BF9\u8C61 { package, version, sha256, content }",
905
- " package \u5305\u540D @kevlns/v-cli",
906
- " version \u5F53\u524D\u5305\u7248\u672C",
907
- " sha256 content \u7684 SHA-256\uFF08hex\uFF09\uFF0C\u53EF\u6821\u9A8C\u5F15\u5BFC\u6587\u6863\u672A\u88AB\u7BE1\u6539",
908
- " content AGENTS.md \u539F\u6587",
909
1090
  " \u5931\u8D25\uFF1Astdout \u8F93\u51FA\u7A00\u758F\u5BF9\u8C61 { ok: false, error }\uFF0Cstderr \u518D\u8F93\u51FA\u540C\u4E00 error\uFF0C\u9000\u51FA\u7801 1",
910
1091
  "",
911
- "\u9000\u51FA\u7801\uFF1A0 \u6210\u529F\uFF1B1 \u5185\u7F6E AGENTS.md \u7F3A\u5931/\u4E0D\u53EF\u8BFB",
1092
+ "\u9000\u51FA\u7801\uFF1A0 \u6210\u529F\uFF1B1 \u547D\u4EE4\u672A\u77E5 / \u63D2\u4EF6\u672A\u5B89\u88C5 / AGENTS.md \u7F3A\u5931\u6216\u4E0D\u53EF\u8BFB",
912
1093
  "",
913
1094
  "\u793A\u4F8B\uFF1A",
914
1095
  " v-cli agent docs",
915
- " v-cli agent docs --json"
1096
+ " v-cli agent docs unity",
1097
+ " v-cli agent docs xlmerge --json"
916
1098
  ].join("\n");
917
1099
  var INIT_HELP_TEXT = [
918
- "\u628A\u5F53\u524D @kevlns/v-cli \u5305\u5185\u7F6E AGENTS.md \u539F\u6837\u5199\u5165 <\u76EE\u5F55>/AGENTS.md\uFF0CAI Agent \u4F1A\u81EA\u52A8\u8BFB\u53D6\u5DE5\u4F5C\u533A\u6587\u6863\u3002",
1100
+ "\u628A\u5F53\u524D @kevlns/v-cli \u5305\u5185\u7F6E AGENTS.md \u539F\u6837\u5199\u5165 <\u76EE\u5F55>/AGENTS.md\uFF0CAI Agent \u4F1A\u81EA\u52A8\u8BFB\u53D6\u5DE5\u4F5C\u533A\u6587\u6863\uFF1B",
1101
+ "\u540C\u65F6\u628A\u968F\u5305\u53D1\u5E03\u7684 v-cli skill\uFF08skills/v-cli\uFF09\u88C5\u914D\u5230 <\u76EE\u5F55> \u4E0B\u5339\u914D\u7684 agent \u6280\u80FD\u76EE\u5F55\uFF08\u5982 .claude/skills\u3001.agent/skill\u3001AgentHome/skills \u7B49\uFF0C\u6E05\u5355\u89C1 src/core/agent-dirs.ts\uFF09\uFF0C\u65E0\u5339\u914D\u76EE\u5F55\u5219\u8DF3\u8FC7\u3002",
919
1102
  "",
920
1103
  "\u53C2\u6570\uFF1A",
921
1104
  " [directory] \u76EE\u6807\u76EE\u5F55\uFF0C\u9ED8\u8BA4\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF1B\u5FC5\u987B\u5DF2\u5B58\u5728\u4E14\u4E3A\u76EE\u5F55",
@@ -973,21 +1156,21 @@ var agent = {
973
1156
  },
974
1157
  {
975
1158
  path: ["docs"],
976
- usage: "v-cli agent docs [--json]",
977
- description: "\u8F93\u51FA\u5F53\u524D @kevlns/v-cli \u5305\u5185\u7F6E AGENTS.md \u539F\u6587",
978
- arguments: [],
1159
+ usage: "v-cli agent docs [command] [--json]",
1160
+ description: "\u65E0\u53C2\u8F93\u51FA v-cli AGENTS.md\uFF1B\u4F20 command \u8F93\u51FA\u5B98\u65B9\u63D2\u4EF6\u5305\u5185 AGENTS.md",
1161
+ arguments: [{ name: "command", required: false, description: "\u5B98\u65B9\u63D2\u4EF6\u547D\u4EE4\u540D\uFF08\u5982 unity\u3001xlmerge\uFF09" }],
979
1162
  options: [{ flags: "--json", description: "\u8F93\u51FA { package, version, sha256, content }" }],
980
1163
  output: {
981
1164
  format: "stdout",
982
1165
  description: "AGENTS.md \u539F\u6587\u9010\u5B57\u8282\u8F93\u51FA\u5230 stdout\uFF1B--json \u65F6\u8F93\u51FA\u542B sha256/content \u7684\u7A33\u5B9A JSON"
983
1166
  },
984
- exitCodes: { "0": "\u6210\u529F", "1": "\u5185\u7F6E AGENTS.md \u7F3A\u5931/\u4E0D\u53EF\u8BFB" },
1167
+ exitCodes: { "0": "\u6210\u529F", "1": "\u547D\u4EE4\u672A\u77E5 / \u63D2\u4EF6\u672A\u5B89\u88C5 / AGENTS.md \u7F3A\u5931\u6216\u4E0D\u53EF\u8BFB" },
985
1168
  safety: ["read-only", "no-network"]
986
1169
  },
987
1170
  {
988
1171
  path: ["init"],
989
1172
  usage: "v-cli agent init [directory] [--force] [--dry-run] [--json]",
990
- description: "\u628A\u5185\u7F6E AGENTS.md \u5199\u5165 <\u76EE\u5F55>/AGENTS.md\uFF1B\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF1B\u5DF2\u5B58\u5728\u9ED8\u8BA4\u62D2\u7EDD",
1173
+ description: "\u521D\u59CB\u5316 <\u76EE\u5F55>\uFF1A\u5199\u5165 AGENTS.md \u5E76\u88C5\u914D v-cli skill \u5230\u5339\u914D\u7684 agent \u6280\u80FD\u76EE\u5F55\uFF1B\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF1B\u5DF2\u5B58\u5728\u9ED8\u8BA4\u62D2\u7EDD",
991
1174
  arguments: [
992
1175
  { name: "directory", required: false, description: "\u76EE\u6807\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\uFF1B\u987B\u5DF2\u5B58\u5728\u4E14\u4E3A\u76EE\u5F55\uFF09" }
993
1176
  ],
@@ -998,7 +1181,7 @@ var agent = {
998
1181
  ],
999
1182
  output: {
1000
1183
  format: "stdout",
1001
- description: "\u7ED3\u679C\u6587\u672C\u8F93\u51FA\u5230 stdout\uFF1B--json \u65F6\u8F93\u51FA\u7A33\u5B9A JSON\uFF08ok/dryRun/action/directory/target/package/version/sha256/bytes\uFF09"
1184
+ description: "\u7ED3\u679C\u6587\u672C\u8F93\u51FA\u5230 stdout\uFF1B--json \u65F6\u8F93\u51FA\u7A33\u5B9A JSON\uFF08ok/dryRun/action/directory/target/package/version/sha256/bytes \u53CA skill \u88C5\u914D\u7ED3\u679C\uFF09"
1002
1185
  },
1003
1186
  exitCodes: {
1004
1187
  "0": "\u6210\u529F\u6216\u5E72\u8DD1",
@@ -1009,7 +1192,8 @@ var agent = {
1009
1192
  "refuses-existing",
1010
1193
  "fail-closed-symlink",
1011
1194
  "dry-run-supported",
1012
- "no-commit"
1195
+ "no-commit",
1196
+ "assembles-skill"
1013
1197
  ]
1014
1198
  }
1015
1199
  ]
@@ -1059,11 +1243,20 @@ var agent = {
1059
1243
  }
1060
1244
  ctx.log.result(lines.join("\n"));
1061
1245
  });
1062
- program.command("docs").description("\u8F93\u51FA\u5F53\u524D @kevlns/v-cli \u5305\u5185\u7F6E AGENTS.md \u539F\u6587\uFF08AI Agent \u5F15\u5BFC\u6587\u6863\uFF09").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON\uFF08\u542B sha256/content\uFF09").addHelpText("after", DOCS_HELP_TEXT).action((opts) => {
1246
+ program.command("docs").description("\u65E0\u53C2\u8F93\u51FA v-cli AGENTS.md\uFF1B\u4F20 command \u8F93\u51FA\u5B98\u65B9\u63D2\u4EF6\u5305\u5185 AGENTS.md").argument("[command]", "\u5B98\u65B9\u63D2\u4EF6\u547D\u4EE4\u540D\uFF08\u5982 unity\u3001xlmerge\uFF09").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON\uFF08\u542B sha256/content\uFF09").addHelpText("after", DOCS_HELP_TEXT).action((command, opts) => {
1063
1247
  const json = ctx.json || opts.json;
1064
1248
  let docs;
1249
+ let packageName = BUNDLED_DOCS_PACKAGE;
1250
+ let version = VERSION;
1065
1251
  try {
1066
- docs = readBundledAgentsMd();
1252
+ if (command) {
1253
+ const pluginDocs = readOfficialAgentsMd(command);
1254
+ docs = pluginDocs;
1255
+ packageName = pluginDocs.package;
1256
+ version = pluginDocs.version;
1257
+ } else {
1258
+ docs = readBundledAgentsMd();
1259
+ }
1067
1260
  } catch (err) {
1068
1261
  const message = err instanceof Error ? err.message : String(err);
1069
1262
  if (json) ctx.log.result({ ok: false, error: message });
@@ -1073,8 +1266,8 @@ var agent = {
1073
1266
  }
1074
1267
  if (json) {
1075
1268
  ctx.log.result({
1076
- package: BUNDLED_DOCS_PACKAGE,
1077
- version: VERSION,
1269
+ package: packageName,
1270
+ version,
1078
1271
  sha256: docs.sha256,
1079
1272
  content: docs.content
1080
1273
  });
@@ -1095,16 +1288,48 @@ var agent = {
1095
1288
  process.exitCode = 1;
1096
1289
  return;
1097
1290
  }
1291
+ const resolvedDir = path6.resolve(directory ?? process.cwd());
1098
1292
  const result = performAgentInit({
1099
- directory: path5.resolve(directory ?? process.cwd()),
1293
+ directory: resolvedDir,
1100
1294
  docs,
1101
1295
  force: opts.force,
1102
1296
  dryRun: opts.dryRun,
1103
1297
  // 显式传入目录才做目录符号链接/联接 fail-closed;默认 cwd 不受限
1104
1298
  explicitDirectory: directory !== void 0
1105
1299
  });
1300
+ let skillOutcome;
1301
+ if (result.ok) {
1302
+ try {
1303
+ const source = readSkillSource();
1304
+ const skill = performAgentSkillAssembly({
1305
+ directory: resolvedDir,
1306
+ source,
1307
+ dryRun: opts.dryRun
1308
+ });
1309
+ skillOutcome = skill.assembled.length > 0 ? {
1310
+ status: "assembled",
1311
+ assembled: skill.assembled,
1312
+ name: skill.skill.name,
1313
+ sha256: skill.skill.sha256,
1314
+ bytes: skill.skill.bytes
1315
+ } : {
1316
+ status: "skipped-none",
1317
+ assembled: [],
1318
+ name: skill.skill.name,
1319
+ sha256: skill.skill.sha256,
1320
+ bytes: skill.skill.bytes
1321
+ };
1322
+ } catch (err) {
1323
+ skillOutcome = {
1324
+ status: "skipped-source-missing",
1325
+ reason: err instanceof Error ? err.message : String(err)
1326
+ };
1327
+ }
1328
+ } else {
1329
+ skillOutcome = { status: "skipped-init-failed" };
1330
+ }
1106
1331
  if (json) {
1107
- ctx.log.result(result);
1332
+ ctx.log.result({ ...result, skill: skillOutcome });
1108
1333
  if (!result.ok && result.reason) ctx.log.error(result.reason);
1109
1334
  if (!result.ok) process.exitCode = 1;
1110
1335
  return;
@@ -1114,15 +1339,8 @@ var agent = {
1114
1339
  process.exitCode = 1;
1115
1340
  return;
1116
1341
  }
1117
- if (result.dryRun) {
1118
- ctx.log.result(
1119
- `[dry-run] \u5C06${result.action === "overwrite" ? "\u8986\u76D6" : "\u5199\u5165"} ${result.target}\uFF08${result.bytes} \u5B57\u8282\uFF09`
1120
- );
1121
- } else {
1122
- ctx.log.result(
1123
- `\u5DF2${result.action === "overwritten" ? "\u8986\u76D6" : "\u5199\u5165"} ${result.target}\uFF08${result.bytes} \u5B57\u8282\uFF0CSHA-256 ${result.sha256.slice(0, 12)}\u2026\uFF09`
1124
- );
1125
- }
1342
+ const head = result.dryRun ? `[dry-run] \u5C06${result.action === "overwrite" ? "\u8986\u76D6" : "\u5199\u5165"} ${result.target}\uFF08${result.bytes} \u5B57\u8282\uFF09` : `\u5DF2${result.action === "overwritten" ? "\u8986\u76D6" : "\u5199\u5165"} ${result.target}\uFF08${result.bytes} \u5B57\u8282\uFF0CSHA-256 ${result.sha256.slice(0, 12)}\u2026\uFF09`;
1343
+ ctx.log.result([head, skillOutcomeText(skillOutcome, result.dryRun)].join("\n"));
1126
1344
  }
1127
1345
  );
1128
1346
  program.addHelpText("after", AGENT_HELP_TEXT);
@@ -1130,7 +1348,7 @@ var agent = {
1130
1348
  };
1131
1349
 
1132
1350
  // src/commands/doctor.ts
1133
- import fs4 from "fs";
1351
+ import fs5 from "fs";
1134
1352
  var doctor = {
1135
1353
  name: "doctor",
1136
1354
  description: "\u4F53\u68C0\uFF1Anode/v-cli \u7248\u672C\u3001homeDir\u3001config\u3001\u63D2\u4EF6\u72B6\u6001",
@@ -1139,7 +1357,7 @@ var doctor = {
1139
1357
  program.option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action(async (opts) => {
1140
1358
  const json = ctx.json || opts.json;
1141
1359
  const homeDir = ctx.homeDir;
1142
- const exists = fs4.existsSync(homeDir);
1360
+ const exists = fs5.existsSync(homeDir);
1143
1361
  let configWritable = false;
1144
1362
  try {
1145
1363
  ctx.config.set((cfg) => cfg);
@@ -1193,7 +1411,7 @@ var doctor = {
1193
1411
  };
1194
1412
 
1195
1413
  // src/commands/plugin.ts
1196
- import path6 from "path";
1414
+ import path7 from "path";
1197
1415
  var plugin = {
1198
1416
  name: "plugin",
1199
1417
  description: "\u63D2\u4EF6\u7BA1\u7406\uFF1Alist \u5217\u51FA\u547D\u4EE4\uFF0Cpath \u663E\u793A\u672C\u5730\u63D2\u4EF6\u76EE\u5F55",
@@ -1232,7 +1450,7 @@ var plugin = {
1232
1450
  }
1233
1451
  });
1234
1452
  program.command("path").description("\u6253\u5370\u672C\u5730\u63D2\u4EF6\u76EE\u5F55\uFF08\u653E\u5165 .mjs \u6587\u4EF6\u5373\u751F\u6548\uFF09").option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action((opts) => {
1235
- const dir = path6.join(ctx.homeDir, "commands");
1453
+ const dir = path7.join(ctx.homeDir, "commands");
1236
1454
  if (ctx.json || opts.json) {
1237
1455
  ctx.log.result({ dir });
1238
1456
  } else {
@@ -1300,12 +1518,12 @@ async function loadBuiltinCommands() {
1300
1518
  return builtinCommands.map((command) => ({ command, source: "builtin" }));
1301
1519
  }
1302
1520
  async function loadLocalPlugins(ctx) {
1303
- const dir = path7.join(ctx.homeDir, "commands");
1521
+ const dir = path8.join(ctx.homeDir, "commands");
1304
1522
  const loaded = [];
1305
- if (!fs5.existsSync(dir)) return loaded;
1306
- const files = fs5.readdirSync(dir).filter((f) => f.endsWith(".mjs")).sort();
1523
+ if (!fs6.existsSync(dir)) return loaded;
1524
+ const files = fs6.readdirSync(dir).filter((f) => f.endsWith(".mjs")).sort();
1307
1525
  for (const file of files) {
1308
- const abs = path7.join(dir, file);
1526
+ const abs = path8.join(dir, file);
1309
1527
  try {
1310
1528
  const mod = await import(
1311
1529
  /* @vite-ignore */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kevlns/v-cli",
3
- "version": "0.2.0-beta.3",
3
+ "version": "0.2.0",
4
4
  "description": "kevlns 的个人工具箱 CLI(插件化架构,内置 + 本地插件)+ doctor/plugin/ts 命令",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,6 +9,7 @@
9
9
  "files": [
10
10
  "dist/",
11
11
  "schemas/",
12
+ "skills/",
12
13
  "AGENTS.md"
13
14
  ],
14
15
  "scripts": {
@@ -46,8 +47,8 @@
46
47
  ],
47
48
  "dependencies": {
48
49
  "commander": "^12.1.0",
49
- "@kevlns/xlmerge": "1.2.1-beta.3",
50
- "@kevlns/u-cli-mod": "0.1.0-beta.3"
50
+ "@kevlns/xlmerge": "1.2.1",
51
+ "@kevlns/u-cli-mod": "0.1.0"
51
52
  },
52
53
  "devDependencies": {
53
54
  "@types/node": "^20.14.0",
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: v-cli
3
+ description: >
4
+ 使用 kevlns 的个人工具箱 CLI(v-cli)处理配置表冲突与 Unity 工程精确版本工具链。当任务提到 v-cli、xlmerge、
5
+ unity 命令、配置表 .xlsx/.xlsm Git 冲突处理、Unity CLI 安装、Unity 工程诊断/体检(doctor)、
6
+ com.unity.pipeline 适配包安装时使用本 skill。
7
+ ---
8
+
9
+ # v-cli 使用规范
10
+
11
+ ## 工具概述
12
+
13
+ - `@kevlns/v-cli` 是 npm 全局安装的个人工具箱 CLI(当前 0.2.0-beta.x),插件化架构。
14
+ - 命令分三类:
15
+ - **builtin**(内置):`doctor`(环境体检)、`plugin list/path`(插件管理)、`ts`(时间戳互转)、`agent index/describe/docs/init`(agent 引导)。
16
+ - **local**:`~/.v-cli/commands/` 下的本地插件(本项目未使用)。
17
+ - **official**(官方插件,经 v-cli 路由):`xlmerge`、`unity`。
18
+ - 环境要求:Node.js >= 20(仅 Windows 主机可使用 `unity` 插件,其他平台 v-cli 拒绝路由)。
19
+
20
+ ## 能力发现协议(核心规则,必须遵守)
21
+
22
+ **一切能力、参数、用法以 v-cli 自己的 agent 命令输出为准,严禁无头搜索 npm 安装目录、源码或 README 猜测用法。**
23
+
24
+ - `v-cli agent docs` — 输出 v-cli 内置 AGENTS.md(宿主规范全文;`--json` 含 sha256/content)。
25
+ - `v-cli agent index --json` — 枚举全部命令(builtin/local/official)与 agent 元数据,获取最新命令集合。
26
+ - `v-cli agent describe <命令名> --json` — 单个命令的完整记录:用法、参数、选项、输出格式、退出码、安全标签。
27
+ - `v-cli agent docs <命令名>` — 输出官方插件包内 AGENTS.md(该插件的使用规范正本)。
28
+ - `v-cli agent init .` — 可选:把内置 AGENTS.md 写入工作区(已存在默认拒绝,`--force` 覆盖,`--dry-run` 预览)。
29
+ - live 命令集合以实际发现为准:先 `agent index --json`,再对目标命令 `agent describe <命令> --json`。
30
+
31
+ ## 首调规范
32
+
33
+ **首次调用任何 official 插件命令(`v-cli xlmerge …`、`v-cli unity …`)前,必须先运行 `v-cli agent docs <命令名>` 读取该插件包内的 AGENTS.md 规范正本。** 使用规范、快速流程与禁止事项以插件自身 AGENTS.md 为准。
34
+
35
+ 官方插件命令在子进程中运行(stdio 继承):v-cli 只做路由,不解析、不改写插件输出;插件 `--help`/`--json` 等参数由插件自己消费。插件对 worktree 的写入/提交行为以插件清单的安全标签为准;**未经显式 flag 不得 push**。
36
+
37
+ ## 官方插件一:xlmerge(跨平台)
38
+
39
+ Git 中 `.xlsx` / `.xlsm` 策划表/配置表冲突的可视化解决工具。
40
+
41
+ - 流程:
42
+ 1. `v-cli xlmerge --repo <仓库路径> detect` — 检测冲突。
43
+ 2. 冲突数 > 0 时运行 `launch`,**把返回的 URL 交给用户在本地 UI 处理**。
44
+ - **agent 不得自行检查工作簿单元格、不得自己总结 diff**:resolver 拥有 diff、选择、写回与提交。
45
+ - 用户要求「解决配置表冲突」时:先 detect;count > 0 时 launch 并给出 URL。
46
+
47
+ ## 官方插件二:unity(仅 win32)
48
+
49
+ Unity 2022 工程**精确版本路由**(版本号 + revision 双重匹配)+ 下载经验证的 Unity CLI(SHA-256 + Authenticode)+ 事务式安装适配版 `com.unity.pipeline` 包。
50
+
51
+ ### 核心命令
52
+
53
+ | 命令 | 说明 |
54
+ |---|---|
55
+ | `v-cli unity doctor <project>` | 只读体检:路由匹配 / CLI 状态 / Pipeline 状态 / 运行中的 Unity 进程 / 支持版本列表 |
56
+ | `v-cli unity setup <project>` | CLI + 适配包一键就绪(**推荐首次入口**,等价 cli install + pipeline install;`--dry-run` 预览、`--skip-cli` 跳过下载) |
57
+ | `v-cli unity pipeline install <project>` | 事务式安装适配包(staging → 校验 → 备份 → 替换 → 再校验 → receipt,失败自动回滚;`--dry-run` 只预览不写入) |
58
+ | `v-cli unity cli install` | 下载并校验固定版本 Unity CLI(`--editor <版本>` 限定、`--force` 重下) |
59
+ | `v-cli unity exec <project> -- <unity-cli-args>` | 调用路由 CLI 执行 Unity Pipeline 命令 |
60
+ | `v-cli unity routes` | 列出所有已配置的 Editor 精确路由(`-e <版本>` 过滤) |
61
+ | `v-cli unity cache clean` | 清理下载缓存与生成的适配包(`--all` 连 CLI 缓存一起清) |
62
+
63
+ ### 关键规则(违反即报错或导致损坏,必须遵守)
64
+
65
+ 1. **exec 前必须 doctor 通过且适配包已安装**(setup 或 pipeline install 已完成);未就绪时先跑 setup,不要直接 exec。
66
+ 2. **禁止在 exec 参数中传入任何 `--project-path` 变体**(`-projectPath`、`--project_path`、大小写混合、`=` 形式等):目标工程由工具统一绑定,exec 会拒绝所有变体并把解析后的 `--project-path` 作为最后一个参数附加;`--` 分隔符被包装器消费,不转发给 Unity CLI。
67
+ 3. **运行中的 Unity Editor 是 fail-closed**:安装被阻止时引导用户先关闭目标工程的 Editor;**除非用户显式要求,不得使用 `--allow-running-editor` 绕过**。
68
+ 4. **安装是事务式的**:不要手动清理工程内 `Packages/com.unity.pipeline` 或 `Library/editor-pipeline-cli`;失败会自动回滚,人为清理会破坏回滚与 receipt 校验。
69
+ 5. **版本路由是精确匹配**(`m_EditorVersion` + revision 同时一致),无"就近版本"回退;工程版本不在路由表内时如实报告支持列表(`doctor` 输出的 `supportedVersions`),不得猜测、不得改写 `ProjectVersion.txt`。
70
+ 6. **exec 每次调用前都会重新校验 CLI 哈希**(防篡改,属正常行为);校验失败按提示 `v-cli unity cli install --force` 修复即可。
71
+
72
+ ## 典型流程
73
+
74
+ ```bash
75
+ v-cli agent docs unity # 首调前必读插件规范正本
76
+ v-cli unity doctor <project> # 1. 体检(只读)
77
+ v-cli unity setup <project> --dry-run # 2.(可选)先预览
78
+ v-cli unity setup <project> # 3. 就绪(CLI + 适配包)
79
+ v-cli unity exec <project> -- command editor_status # 4. 执行 Pipeline 命令
80
+ ```