@kevlns/v-cli 0.2.0-beta.4 → 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,13 @@
7
7
 
8
8
  ## 核心约定(v-cli 本体)
9
9
 
10
- - 环境要求 Node.js >= 20;v-cli 版本 @kevlns/v-cli@0.2.0-beta.4
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),无匹配则跳过
16
17
  - **首次调用规范**:首次调用任何 official 插件命令前,必须先运行 `v-cli agent docs <命令名>`,
17
18
  掌握该插件包内 `AGENTS.md`;使用规范、快速流程与禁止事项以插件 AGENTS.md 为准。
18
19
  - 官方插件命令(`v-cli xlmerge …`、`v-cli unity …`)在子进程中运行(stdio 继承):v-cli 只做路由,
@@ -22,7 +23,7 @@
22
23
 
23
24
  ## @kevlns/u-cli-mod — 命令 `v-cli unity …`
24
25
 
25
- **版本**:0.1.0-beta.4
26
+ **版本**:0.1.0
26
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).
27
28
  **平台**:win32(仅 Windows 主机可用;非 Windows 上 v-cli 会拒绝路由)
28
29
 
@@ -33,7 +34,7 @@
33
34
 
34
35
  ## @kevlns/xlmerge — 命令 `v-cli xlmerge …`
35
36
 
36
- **版本**:1.2.1-beta.5
37
+ **版本**:1.2.1
37
38
  **描述**:Visual resolver for Git merge conflicts in .xlsx/.xlsm planning tables: three-way sheet/row/cell diff, local UI, write-back and commit.
38
39
  **平台**:darwin, linux, win32
39
40
 
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.4";
630
+ var VERSION = "0.2.0";
631
631
 
632
632
  // src/core/agent-docs.ts
633
633
  var BUNDLED_DOCS_FILE = "AGENTS.md";
@@ -849,7 +849,140 @@ function atomicWrite(target, content) {
849
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}`);
850
850
  }
851
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
+
852
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
+ }
853
986
  function buildAgentIndex(builtin, local, official) {
854
987
  const rows = [];
855
988
  for (const cmd of builtin) {
@@ -964,7 +1097,8 @@ var DOCS_HELP_TEXT = [
964
1097
  " v-cli agent docs xlmerge --json"
965
1098
  ].join("\n");
966
1099
  var INIT_HELP_TEXT = [
967
- "\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",
968
1102
  "",
969
1103
  "\u53C2\u6570\uFF1A",
970
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",
@@ -1036,7 +1170,7 @@ var agent = {
1036
1170
  {
1037
1171
  path: ["init"],
1038
1172
  usage: "v-cli agent init [directory] [--force] [--dry-run] [--json]",
1039
- 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",
1040
1174
  arguments: [
1041
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" }
1042
1176
  ],
@@ -1047,7 +1181,7 @@ var agent = {
1047
1181
  ],
1048
1182
  output: {
1049
1183
  format: "stdout",
1050
- 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"
1051
1185
  },
1052
1186
  exitCodes: {
1053
1187
  "0": "\u6210\u529F\u6216\u5E72\u8DD1",
@@ -1058,7 +1192,8 @@ var agent = {
1058
1192
  "refuses-existing",
1059
1193
  "fail-closed-symlink",
1060
1194
  "dry-run-supported",
1061
- "no-commit"
1195
+ "no-commit",
1196
+ "assembles-skill"
1062
1197
  ]
1063
1198
  }
1064
1199
  ]
@@ -1153,16 +1288,48 @@ var agent = {
1153
1288
  process.exitCode = 1;
1154
1289
  return;
1155
1290
  }
1291
+ const resolvedDir = path6.resolve(directory ?? process.cwd());
1156
1292
  const result = performAgentInit({
1157
- directory: path5.resolve(directory ?? process.cwd()),
1293
+ directory: resolvedDir,
1158
1294
  docs,
1159
1295
  force: opts.force,
1160
1296
  dryRun: opts.dryRun,
1161
1297
  // 显式传入目录才做目录符号链接/联接 fail-closed;默认 cwd 不受限
1162
1298
  explicitDirectory: directory !== void 0
1163
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
+ }
1164
1331
  if (json) {
1165
- ctx.log.result(result);
1332
+ ctx.log.result({ ...result, skill: skillOutcome });
1166
1333
  if (!result.ok && result.reason) ctx.log.error(result.reason);
1167
1334
  if (!result.ok) process.exitCode = 1;
1168
1335
  return;
@@ -1172,15 +1339,8 @@ var agent = {
1172
1339
  process.exitCode = 1;
1173
1340
  return;
1174
1341
  }
1175
- if (result.dryRun) {
1176
- ctx.log.result(
1177
- `[dry-run] \u5C06${result.action === "overwrite" ? "\u8986\u76D6" : "\u5199\u5165"} ${result.target}\uFF08${result.bytes} \u5B57\u8282\uFF09`
1178
- );
1179
- } else {
1180
- ctx.log.result(
1181
- `\u5DF2${result.action === "overwritten" ? "\u8986\u76D6" : "\u5199\u5165"} ${result.target}\uFF08${result.bytes} \u5B57\u8282\uFF0CSHA-256 ${result.sha256.slice(0, 12)}\u2026\uFF09`
1182
- );
1183
- }
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"));
1184
1344
  }
1185
1345
  );
1186
1346
  program.addHelpText("after", AGENT_HELP_TEXT);
@@ -1188,7 +1348,7 @@ var agent = {
1188
1348
  };
1189
1349
 
1190
1350
  // src/commands/doctor.ts
1191
- import fs4 from "fs";
1351
+ import fs5 from "fs";
1192
1352
  var doctor = {
1193
1353
  name: "doctor",
1194
1354
  description: "\u4F53\u68C0\uFF1Anode/v-cli \u7248\u672C\u3001homeDir\u3001config\u3001\u63D2\u4EF6\u72B6\u6001",
@@ -1197,7 +1357,7 @@ var doctor = {
1197
1357
  program.option("--json", "\u8F93\u51FA\u673A\u5668\u53EF\u8BFB JSON").action(async (opts) => {
1198
1358
  const json = ctx.json || opts.json;
1199
1359
  const homeDir = ctx.homeDir;
1200
- const exists = fs4.existsSync(homeDir);
1360
+ const exists = fs5.existsSync(homeDir);
1201
1361
  let configWritable = false;
1202
1362
  try {
1203
1363
  ctx.config.set((cfg) => cfg);
@@ -1251,7 +1411,7 @@ var doctor = {
1251
1411
  };
1252
1412
 
1253
1413
  // src/commands/plugin.ts
1254
- import path6 from "path";
1414
+ import path7 from "path";
1255
1415
  var plugin = {
1256
1416
  name: "plugin",
1257
1417
  description: "\u63D2\u4EF6\u7BA1\u7406\uFF1Alist \u5217\u51FA\u547D\u4EE4\uFF0Cpath \u663E\u793A\u672C\u5730\u63D2\u4EF6\u76EE\u5F55",
@@ -1290,7 +1450,7 @@ var plugin = {
1290
1450
  }
1291
1451
  });
1292
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) => {
1293
- const dir = path6.join(ctx.homeDir, "commands");
1453
+ const dir = path7.join(ctx.homeDir, "commands");
1294
1454
  if (ctx.json || opts.json) {
1295
1455
  ctx.log.result({ dir });
1296
1456
  } else {
@@ -1358,12 +1518,12 @@ async function loadBuiltinCommands() {
1358
1518
  return builtinCommands.map((command) => ({ command, source: "builtin" }));
1359
1519
  }
1360
1520
  async function loadLocalPlugins(ctx) {
1361
- const dir = path7.join(ctx.homeDir, "commands");
1521
+ const dir = path8.join(ctx.homeDir, "commands");
1362
1522
  const loaded = [];
1363
- if (!fs5.existsSync(dir)) return loaded;
1364
- 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();
1365
1525
  for (const file of files) {
1366
- const abs = path7.join(dir, file);
1526
+ const abs = path8.join(dir, file);
1367
1527
  try {
1368
1528
  const mod = await import(
1369
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.4",
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.5",
50
- "@kevlns/u-cli-mod": "0.1.0-beta.4"
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
+ ```