@givolt/i3-composer-agent 2.2.0 → 2.2.1

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.
Files changed (131) hide show
  1. package/.agents/plugins/marketplace.json +1 -1
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/.claude-plugin/plugin.json +1 -1
  4. package/.codex-plugin/plugin.json +1 -1
  5. package/README.md +52 -13
  6. package/bin/asset-fingerprint.cjs +1 -0
  7. package/bin/i3-composer-agent.cjs +10 -6
  8. package/bin/install.cjs +24 -9
  9. package/bin/native-content.cjs +1 -0
  10. package/bin/tool-selector.cjs +164 -0
  11. package/bin/wizard.cjs +7 -44
  12. package/docs/codebuddy-setup.md +11 -3
  13. package/docs/codex-setup.md +6 -3
  14. package/docs/workbuddy-setup.md +8 -1
  15. package/evals/README.md +86 -0
  16. package/evals/cases/i3-api-and-interface-design.json +45 -0
  17. package/evals/cases/i3-browser-testing-with-devtools.json +44 -0
  18. package/evals/cases/i3-ci-cd-and-automation.json +45 -0
  19. package/evals/cases/i3-code-review-and-quality.json +49 -0
  20. package/evals/cases/i3-code-simplification.json +45 -0
  21. package/evals/cases/i3-constraint-driven-development.json +79 -0
  22. package/evals/cases/i3-context-engineering.json +44 -0
  23. package/evals/cases/i3-debugging-and-error-recovery.json +58 -0
  24. package/evals/cases/i3-deprecation-and-migration.json +52 -0
  25. package/evals/cases/i3-documentation-and-adrs.json +44 -0
  26. package/evals/cases/i3-doubt-driven-development.json +43 -0
  27. package/evals/cases/i3-frontend-ui-engineering.json +52 -0
  28. package/evals/cases/i3-git-workflow-and-versioning.json +47 -0
  29. package/evals/cases/i3-idea-refine.json +43 -0
  30. package/evals/cases/i3-incremental-implementation.json +57 -0
  31. package/evals/cases/i3-interview-me.json +42 -0
  32. package/evals/cases/i3-observability-and-instrumentation.json +44 -0
  33. package/evals/cases/i3-performance-optimization.json +53 -0
  34. package/evals/cases/i3-planning-and-task-breakdown.json +45 -0
  35. package/evals/cases/i3-security-and-hardening.json +53 -0
  36. package/evals/cases/i3-shipping-and-launch.json +56 -0
  37. package/evals/cases/i3-source-driven-development.json +44 -0
  38. package/evals/cases/i3-spec-driven-development.json +72 -0
  39. package/evals/cases/i3-test-driven-development.json +73 -0
  40. package/evals/cases/i3-using-agent-skills.json +44 -0
  41. package/evals/fixtures/i3-api-and-interface-design/service-brief.md +19 -0
  42. package/evals/fixtures/i3-browser-testing-with-devtools/README.md +5 -0
  43. package/evals/fixtures/i3-browser-testing-with-devtools/index.html +24 -0
  44. package/evals/fixtures/i3-browser-testing-with-devtools/server.js +15 -0
  45. package/evals/fixtures/i3-ci-cd-and-automation/package.json +8 -0
  46. package/evals/fixtures/i3-ci-cd-and-automation/src/slug.js +3 -0
  47. package/evals/fixtures/i3-ci-cd-and-automation/test/slug.test.js +9 -0
  48. package/evals/fixtures/i3-code-review-and-quality/user-search.diff +16 -0
  49. package/evals/fixtures/i3-code-simplification/config-parser.js +46 -0
  50. package/evals/fixtures/i3-code-simplification/config-parser.test.js +15 -0
  51. package/evals/fixtures/i3-context-engineering/context-audit.md +15 -0
  52. package/evals/fixtures/i3-debugging-and-error-recovery/pagination.js +8 -0
  53. package/evals/fixtures/i3-debugging-and-error-recovery/pagination.test.js +9 -0
  54. package/evals/fixtures/i3-debugging-and-error-recovery/time-pressure.md +6 -0
  55. package/evals/fixtures/i3-deprecation-and-migration/api-inventory.md +9 -0
  56. package/evals/fixtures/i3-documentation-and-adrs/decision-context.md +16 -0
  57. package/evals/fixtures/i3-doubt-driven-development/migration-plan.md +19 -0
  58. package/evals/fixtures/i3-frontend-ui-engineering/Button.tsx +7 -0
  59. package/evals/fixtures/i3-frontend-ui-engineering/design-system.md +11 -0
  60. package/evals/fixtures/i3-git-workflow-and-versioning/.eval/working-tree.patch +21 -0
  61. package/evals/fixtures/i3-git-workflow-and-versioning/app.js +7 -0
  62. package/evals/fixtures/i3-git-workflow-and-versioning/app.test.js +9 -0
  63. package/evals/fixtures/i3-incremental-implementation/reports.js +7 -0
  64. package/evals/fixtures/i3-incremental-implementation/reports.test.js +12 -0
  65. package/evals/fixtures/i3-incremental-implementation/tasks/plan.md +8 -0
  66. package/evals/fixtures/i3-observability-and-instrumentation/operations.md +11 -0
  67. package/evals/fixtures/i3-observability-and-instrumentation/payment-retry.js +14 -0
  68. package/evals/fixtures/i3-performance-optimization/benchmark.js +15 -0
  69. package/evals/fixtures/i3-performance-optimization/products.js +14 -0
  70. package/evals/fixtures/i3-planning-and-task-breakdown/notifications-spec.md +18 -0
  71. package/evals/fixtures/i3-security-and-hardening/webhook.js +11 -0
  72. package/evals/fixtures/i3-security-and-hardening/webhook.test.js +13 -0
  73. package/evals/fixtures/i3-shipping-and-launch/authority-pressure.md +6 -0
  74. package/evals/fixtures/i3-shipping-and-launch/launch-status.md +11 -0
  75. package/evals/fixtures/i3-source-driven-development/framework-task.md +10 -0
  76. package/evals/fixtures/i3-spec-driven-development/billing-brief.md +16 -0
  77. package/evals/fixtures/i3-test-driven-development/BUG.md +10 -0
  78. package/evals/fixtures/i3-test-driven-development/README.md +28 -0
  79. package/evals/fixtures/i3-test-driven-development/package.json +9 -0
  80. package/evals/fixtures/i3-test-driven-development/src/split.js +8 -0
  81. package/evals/fixtures/i3-test-driven-development/test/split.test.js +13 -0
  82. package/evals/fixtures/i3-using-agent-skills/incident.md +6 -0
  83. package/evals/fixtures/incremental-implementation-pressure/draft-export.js +17 -0
  84. package/evals/fixtures/incremental-implementation-pressure/scenario.md +9 -0
  85. package/evals/fixtures/spec-driven-development-decomposition/portal-brief.md +22 -0
  86. package/evals/fixtures/test-driven-development-ecosystem/README.md +9 -0
  87. package/evals/fixtures/test-driven-development-ecosystem/ledger.py +15 -0
  88. package/evals/fixtures/test-driven-development-ecosystem/test_ledger.py +19 -0
  89. package/evals/plugin/code-review-fires/graders/off-by-one.md +7 -0
  90. package/evals/plugin/code-review-fires/graders/severity-labels.md +4 -0
  91. package/evals/plugin/code-review-fires/graders/skill-fired.md +5 -0
  92. package/evals/plugin/code-review-fires/prompt.md +31 -0
  93. package/evals/plugin/code-review-stays-quiet/graders/not-fired.md +8 -0
  94. package/evals/plugin/code-review-stays-quiet/graders/tdd-fired.md +6 -0
  95. package/evals/plugin/code-review-stays-quiet/prompt.md +8 -0
  96. package/evals/plugin/code-review-stays-quiet-on-commit-message/graders/not-fired.md +8 -0
  97. package/evals/plugin/code-review-stays-quiet-on-commit-message/prompt.md +25 -0
  98. package/evals/zh-CN/cases/i3-api-and-interface-design.json +46 -0
  99. package/evals/zh-CN/cases/i3-browser-testing-with-devtools.json +45 -0
  100. package/evals/zh-CN/cases/i3-ci-cd-and-automation.json +46 -0
  101. package/evals/zh-CN/cases/i3-code-review-and-quality.json +46 -0
  102. package/evals/zh-CN/cases/i3-code-simplification.json +46 -0
  103. package/evals/zh-CN/cases/i3-constraint-driven-development.json +70 -0
  104. package/evals/zh-CN/cases/i3-context-engineering.json +45 -0
  105. package/evals/zh-CN/cases/i3-debugging-and-error-recovery.json +60 -0
  106. package/evals/zh-CN/cases/i3-deprecation-and-migration.json +45 -0
  107. package/evals/zh-CN/cases/i3-documentation-and-adrs.json +45 -0
  108. package/evals/zh-CN/cases/i3-doubt-driven-development.json +45 -0
  109. package/evals/zh-CN/cases/i3-frontend-ui-engineering.json +45 -0
  110. package/evals/zh-CN/cases/i3-git-workflow-and-versioning.json +45 -0
  111. package/evals/zh-CN/cases/i3-idea-refine.json +44 -0
  112. package/evals/zh-CN/cases/i3-incremental-implementation.json +59 -0
  113. package/evals/zh-CN/cases/i3-interview-me.json +43 -0
  114. package/evals/zh-CN/cases/i3-observability-and-instrumentation.json +46 -0
  115. package/evals/zh-CN/cases/i3-performance-optimization.json +46 -0
  116. package/evals/zh-CN/cases/i3-planning-and-task-breakdown.json +46 -0
  117. package/evals/zh-CN/cases/i3-security-and-hardening.json +46 -0
  118. package/evals/zh-CN/cases/i3-shipping-and-launch.json +59 -0
  119. package/evals/zh-CN/cases/i3-source-driven-development.json +45 -0
  120. package/evals/zh-CN/cases/i3-spec-driven-development.json +62 -0
  121. package/evals/zh-CN/cases/i3-test-driven-development.json +76 -0
  122. package/evals/zh-CN/cases/i3-using-agent-skills.json +45 -0
  123. package/package.json +234 -5
  124. package/plugin.json +1 -1
  125. package/scripts/lib/skill-lint.js +586 -0
  126. package/scripts/run-evals.js +659 -0
  127. package/scripts/validate-artifact-paths.js +130 -0
  128. package/scripts/validate-commands.js +224 -0
  129. package/scripts/validate-reference-links.js +187 -0
  130. package/scripts/validate-skills.js +70 -0
  131. package/scripts/validate-versions.js +35 -0
@@ -6,7 +6,7 @@
6
6
  "plugins": [
7
7
  {
8
8
  "name": "i3-composer-agent",
9
- "version": "2.2.0",
9
+ "version": "2.2.1",
10
10
  "description": "覆盖需求、计划、开发、测试、审查与发布的软件工程技能,默认使用简体中文。",
11
11
  "source": {
12
12
  "source": "local",
@@ -8,7 +8,7 @@
8
8
  "plugins": [
9
9
  {
10
10
  "name": "i3-composer-agent",
11
- "version": "2.2.0",
11
+ "version": "2.2.1",
12
12
  "source": "./",
13
13
  "description": "覆盖需求、计划、开发、测试、审查与发布的软件工程技能,默认使用简体中文。",
14
14
  "homepage": "https://github.com/49780522/i3-composer-agent",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i3-composer-agent",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "覆盖需求、计划、开发、测试、审查与发布的软件工程技能,默认使用简体中文。",
5
5
  "author": {
6
6
  "name": "Givolt"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i3-composer-agent",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "覆盖需求、计划、开发、测试、审查与发布的软件工程技能,默认使用简体中文。",
5
5
  "author": {
6
6
  "name": "Givolt"
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # i3 Composer Agent v2.2.0
1
+ # i3 Composer Agent v2.2.1
2
2
 
3
3
  **Intelligence. Integration. Innovation.**
4
4
 
@@ -18,13 +18,14 @@ npx @givolt/i3-composer-agent --workbuddy --scope global
18
18
  ```
19
19
 
20
20
  首屏依次为 CodeBuddy、WorkBuddy、Codex、Claude Code、Cursor、Gemini CLI,
21
- WorkBuddy 在第 2 项。选 `7`/`更多` 打开其余工具,每页 10 项;`n` 下一页、`p` 上一页、
22
- `b` 返回,`/关键词` 搜索全部 80 个工具。编号在分页和搜索中保持一致。
23
- 输入编号或 ID(中英文逗号均可)累积多选,跨页保留;`-编号/ID` 移除选择。
24
- 回车或 `done`/`完成` 确认已选工具,随后选择项目/全局范围并确认安装;`q`/`取消` 退出。
25
- 通过参数预选的工具会保留,仍可继续多选。
26
-
27
- 全部目标均安装 25 个技能和共享参考资料。
21
+ WorkBuddy 在第 2 项。上下键移动,空格勾选/取消,回车确认;列表原地刷新,
22
+ 下方实时显示已选工具。移到“更多”按空格或直接按 `m` 打开其他工具。
23
+ 更多列表上下移动可跨页,`n`/`p` 或 PageDown/PageUp 整页翻页,`b`/左键返回常用。
24
+ 按 `/` 输入关键词实时搜索全部 80 个工具;回车或下键结束输入,再用空格选择。
25
+ 搜索、翻页和命令参数预选均保留选择。搜索外 `q`、Esc 或 Ctrl-C 取消。
26
+ 确认工具后选择项目/全局范围并确认安装。
27
+
28
+ 全部目标均安装 25 个技能、共享参考资料,以及完整评测/Hook/脚本配套。
28
29
  CodeBuddy、WorkBuddy、Claude Code 另安装 9 个短命令和 4 个 Agent 角色。
29
30
  其他工具通过其原生 Skills 机制使用;短命令支持取决于宿主。
30
31
  目录预设来自固定的 [Skills CLI 工具登记](https://github.com/vercel-labs/skills/blob/48dc9e8eb8aa19040e91cb90ba0b642c9cbad57e/src/agents.ts)
@@ -55,9 +56,45 @@ CodeBuddy 和 WorkBuddy 的项目安装共用 `.codebuddy/`,相同目标只安
55
56
  迁移只清理旧清单所属的无前缀 Skill/命令路径,保留自定义内容。
56
57
  标准 MIT 许可随共享参考资料安装。`npx -y` 确认 npm 下载;包的 `--yes` 确认安装计划。
57
58
 
58
- ## 注册完整插件
59
+ ## 安装内容与可选功能
60
+
61
+ | 内容 | 默认安装 | 用途 |
62
+ | --- | --- | --- |
63
+ | `skills/`(含技能内部的 `scripts/` 等资源) | 是 | 工程工作流及运行辅助文件 |
64
+ | `references/` | 是 | 技能引用的检查清单与参考资料 |
65
+ | `commands/`、`agents/` | CodeBuddy、WorkBuddy、Claude Code | 短命令与专业角色 |
66
+ | `hooks/` | 是,文件安装后按宿主方式启用 | 会话注入、文档缓存和代码块保护 |
67
+ | `scripts/` | 是 | 评测运行器、五项校验脚本及其共享库 |
68
+ | `evals/` | 是 | 中英文触发/行为用例、测试项目样例及插件评分规则 |
69
+
70
+ 完整配套默认放在宿主目录下的 `i3-composer-agent/`,例如
71
+ `.codebuddy/i3-composer-agent/` 或 `~/.workbuddy/i3-composer-agent/`。
72
+ 其中保留对应的技能、命令、角色、参考资料与清单,供评测、校验和 Hook 的相对路径使用;
73
+ 宿主调用入口继续安装在其原生目录。该配套与技能一起升级、复用和失败恢复。
74
+
75
+ 从配套目录运行以下检查,不调用模型:
76
+
77
+ ```bash
78
+ npm test
79
+ node scripts/run-evals.js --locale zh-CN --min-rank1 95
80
+ ```
81
+
82
+ 模型行为评测需要主动运行并消耗模型额度,详见 [评测说明](evals/README.md)。
83
+ 本包的 `npm test` 检查随包技能的结构和中英文触发,不是业务项目测试或宿主执行验收。
84
+ 评测结果不进入安装替换清单。安装过程不执行评测,也不自动启用 Hook。
85
+
86
+ 可选 Hook 不影响普通技能和命令的使用,默认中文交互也不依赖 Hook。
87
+ 未配置 Hook 时,自动注入、缓存和代码块保护这些增强功能不会运行。
88
+ 会话注入仅适合缺少原生技能路由的宿主;已有原生路由时不应重复注入元技能。
89
+ 缓存与代码块保护的配置说明分别在配套目录的 `hooks/SDD-CACHE.md`、
90
+ `hooks/SIMPLIFY-IGNORE.md`,当前示例针对 Claude Code,其他宿主需适配自己的事件协议。
91
+ 脚本依赖 Bash、jq;缓存还需 curl 和哈希工具,代码块保护还需其文档列出的工具。
92
+ Windows PowerShell 本身不会自动提供这些脚本依赖。
93
+
94
+ ## 通过宿主插件管理器安装
59
95
 
60
- 完整插件包含 Skills、Commands、Agents、Hooks 和 references。准备持久目录:
96
+ 默认配套已包含 Skills、Commands、Agents、references、evals、hooks 和 scripts。
97
+ 若希望另行导出目录,交给宿主插件管理器管理:
61
98
 
62
99
  ```bash
63
100
  npx @givolt/i3-composer-agent --prepare --codebuddy
@@ -65,7 +102,9 @@ npx @givolt/i3-composer-agent --prepare --codex
65
102
  npx @givolt/i3-composer-agent --prepare --claude
66
103
  ```
67
104
 
68
- 此模式准备 `./i3-composer-agent/` 并输出宿主注册命令。
105
+ `--prepare` 只准备 `./i3-composer-agent/` 运行文件并输出宿主注册命令,
106
+ 不会把插件自动注册到宿主,也不会启用可选 Hook。普通技能安装完成后无需再执行它。
107
+ 需要让宿主插件管理器统一发现和管理插件时,再按下面步骤注册:
69
108
  在 CodeBuddy Code(终端)或 Claude Code 中执行:
70
109
 
71
110
  ```text
@@ -80,8 +119,8 @@ codex plugin marketplace add ./i3-composer-agent
80
119
  codex plugin add i3-composer-agent@i3-composer-agent
81
120
  ```
82
121
 
83
- 按宿主管理器选择安装范围,启用后新建会话。原生文件安装与完整插件注册是两种入口。
84
- 原生安装不自动启用插件 Hook;无需 Hook,入口本身也要求默认中文回复。
122
+ 按宿主管理器选择安装范围,启用后新建会话。原生文件安装与插件注册是两种入口。
123
+ 本包没有默认 Hook 注册配置;插件启用后,可选 Hook 仍需按上面的用途和宿主要求单独配置。
85
124
 
86
125
  ## 命令
87
126
 
@@ -21,6 +21,7 @@ function fingerprint(file, asset) {
21
21
  if (stat.isDirectory()) {
22
22
  for (const entry of fs.readdirSync(current).sort()) {
23
23
  if (asset && entry === '.DS_Store') continue;
24
+ if (asset?.kind === 'support' && ['results', 'node_modules', '.git'].includes(entry)) continue;
24
25
  if (!visit(path.join(current, entry), path.join(relative, entry))) return false;
25
26
  }
26
27
  } else {
@@ -34,7 +34,7 @@ ${pkg.name}
34
34
  --project-dir <dir> 项目安装根目录,默认当前目录
35
35
  --yes 确认明确指定的非交互安装
36
36
  --list-agents 列出全部支持的工具目录
37
- --prepare 准备完整插件,供宿主原生插件管理器注册
37
+ --prepare 另行导出插件目录并显示宿主注册步骤
38
38
  --extract 导出完整插件内容
39
39
  --dir <directory> 插件输出目录;隐含 --prepare
40
40
  --dry-run 显示安装/导出计划,不写入文件
@@ -43,11 +43,13 @@ ${pkg.name}
43
43
  --help 显示帮助
44
44
 
45
45
  终端向导先显示常用工具,WorkBuddy 在第 2 项;选择“更多”可分页/搜索全部工具。
46
- 编号或 ID 支持多选,跨页保留选择;回车或 done/完成确认,q/取消退出。
46
+ 上下键移动/跨页,空格勾选/取消,回车确认;列表下实时显示已选,q/Esc 退出。
47
47
  CodeBuddy、WorkBuddy、Claude Code 同时安装短命令和 Agent 角色。
48
- 其他工具获得 Skills 与参考资料,短命令支持取决于宿主。
48
+ 其他工具通过 Skills 调用,短命令支持取决于宿主。
49
+ 所有工具默认安装完整配套目录 i3-composer-agent/,含 evals、hooks、scripts 及所需资料。
49
50
  WorkBuddy 桌面版建议全局安装,便于技能列表和输入补全发现。
50
- 完整插件及 Hook 注册使用 --prepare。
51
+ 评测需主动运行;Hook 文件已提供,需按宿主事件协议单独启用。
52
+ --prepare 仅供插件管理器安装方式使用,不自动注册插件或启用 Hook。
51
53
  `;
52
54
 
53
55
  function quote(value) { return "'" + value.replaceAll("'", "'\\''") + "'"; }
@@ -134,6 +136,7 @@ function prepare() {
134
136
  }
135
137
  console.log(identity);
136
138
  console.log(`已在 ${directory} 准备好 ${name}`);
139
+ console.log('包含 evals、hooks、scripts 及依赖资料;评测需主动运行,Hook 需按宿主事件协议启用。');
137
140
  if (host === 'extract') return;
138
141
  console.log('\n请在目标宿主中执行以下原生命令注册并启用插件:');
139
142
  const selector = `${name}@${name}`;
@@ -199,10 +202,11 @@ async function main() {
199
202
  for (const target of plan.targets) {
200
203
  console.log(`已为 ${target.agents.join(', ')} 安装技能:${target.skillsDirectory}`);
201
204
  if (target.commandsDirectory) console.log(`命令:/i3-spec、/i3-plan、/i3-build、/i3-ship(共 ${fs.readdirSync(target.commandsDirectory).filter(file => file.endsWith('.md')).length} 个工作流):${target.commandsDirectory}`);
205
+ console.log(`配套目录(含 evals、hooks、scripts):${target.supportDirectory}`);
206
+ console.log(`中文评测:node "${path.join(target.supportDirectory, 'scripts/run-evals.js')}" --locale zh-CN --min-rank1 95`);
202
207
  }
203
208
  console.log('请重新加载 IDE 窗口或新建宿主会话以发现安装内容。');
204
209
  if (plan.agents.some(id => !['codebuddy', 'workbuddy', 'claude-code'].includes(id))) console.log('这些工具通过技能 ID 调用;生命周期短命令支持取决于宿主。');
205
- console.log('原生技能安装完成;插件 Hook 需按宿主方式注册和启用。');
206
- if (plan.agents.some(id => ['codebuddy', 'codex', 'claude-code'].includes(id))) console.log('如需 CodeBuddy、Codex 或 Claude Code 完整插件,请使用 --prepare 和对应宿主参数。');
210
+ console.log('安装完成。评测运行方式见配套目录 evals/README.md;Hook 文件已安装,尚未启用。');
207
211
  }
208
212
  main().catch(error => { console.error(`${name}: ${error.message}`); process.exitCode = 1; });
package/bin/install.cjs CHANGED
@@ -11,6 +11,7 @@ const { fingerprint } = require('./asset-fingerprint.cjs');
11
11
  const root = path.resolve(__dirname, '..');
12
12
  const name = 'i3-composer-agent';
13
13
  const version = require('../package.json').version;
14
+ const pluginFiles = require('../package.json').i3ComposerAgent.pluginFiles;
14
15
  const aliases = { claude: 'claude-code', gemini: 'gemini-cli', copilot: 'github-copilot' };
15
16
  function exists(file) {
16
17
  try { fs.lstatSync(file); return true; }
@@ -46,9 +47,10 @@ function checkDestination(file) {
46
47
  throw new Error('安装目标必须位于包目录之外。');
47
48
  }
48
49
  }
50
+ function assetPath(base, file) { return path.relative(base, file).split(path.sep).join('/'); }
49
51
  function manifestContent(plan, asset) {
50
52
  return JSON.stringify({ name, version, entryPrefix: 'i3-', scope: plan.scope, agents: asset.target.agents,
51
- assets: plan.assets.filter(item => item.base === asset.base && item.kind !== 'legacy').map(item => path.relative(asset.base, item.destination)) }, null, 2) + '\n';
53
+ assets: plan.assets.filter(item => item.base === asset.base && item.kind !== 'legacy').map(item => assetPath(asset.base, item.destination)) }, null, 2) + '\n';
52
54
  }
53
55
  function planInstallation({ agents, scope, projectDir = process.cwd(), home = os.homedir(), env = process.env }) {
54
56
  agents = selectAgents(agents);
@@ -59,7 +61,7 @@ function planInstallation({ agents, scope, projectDir = process.cwd(), home = os
59
61
  const agent = registry.agents.find(item => item.id === id);
60
62
  const skillsDirectory = destination(scope === 'project' ? path.resolve(projectDir, agent.projectSkillsDir) : globalSkillsDir(agent, home, env));
61
63
  const base = path.dirname(skillsDirectory);
62
- if (!targets.has(skillsDirectory)) targets.set(skillsDirectory, { agents: [], skillsDirectory, referencesDirectory: path.join(base, 'references'), base });
64
+ if (!targets.has(skillsDirectory)) targets.set(skillsDirectory, { agents: [], skillsDirectory, referencesDirectory: path.join(base, 'references'), supportDirectory: path.join(base, name), base });
63
65
  const target = targets.get(skillsDirectory);
64
66
  target.agents.push(id);
65
67
  if (['codebuddy', 'workbuddy', 'claude-code'].includes(id)) {
@@ -82,6 +84,9 @@ function planInstallation({ agents, scope, projectDir = process.cwd(), home = os
82
84
  const previous = exists(manifestFile) && !fs.lstatSync(manifestFile).isSymbolicLink()
83
85
  ? JSON.parse(fs.readFileSync(manifestFile, 'utf8')) : undefined;
84
86
  const owned = previous?.name === name && Array.isArray(previous.assets);
87
+ // Earlier Windows installs persisted backslashes. Normalize before lookup
88
+ // and containment checks so owned upgrades work across both manifest forms.
89
+ if (owned) previous.assets = previous.assets.map(relative => typeof relative === 'string' ? relative.replaceAll('\\', '/') : relative);
85
90
  const sameVersion = owned && previous.version === version && previous.scope === scope && previous.entryPrefix === 'i3-';
86
91
  if (owned && previous.scope === scope && Array.isArray(previous.agents)) {
87
92
  const applicable = previous.agents.filter(id => {
@@ -105,16 +110,25 @@ function planInstallation({ agents, scope, projectDir = process.cwd(), home = os
105
110
  const licenseDirectory = path.join(target.referencesDirectory, name);
106
111
  if (exists(licenseDirectory) && fs.lstatSync(licenseDirectory).isSymbolicLink()) throw new Error(`安装目录是符号链接(symlink): ${licenseDirectory}.`);
107
112
  assets.push({ source: path.join(root, 'LICENSE'), destination: destination(path.join(licenseDirectory, 'LICENSE')), kind: 'attribution' });
113
+ // Keep the canonical plugin layout for eval fixtures, validators and Hook
114
+ // relative paths. Native entry points above remain host-adapted copies.
115
+ for (const relative of pluginFiles) {
116
+ const file = path.join(target.supportDirectory, relative);
117
+ if (destination(file) !== file) throw new Error(`配套目录包含符号链接(symlink): ${file}`);
118
+ assets.push({ source: path.join(root, relative), destination: file, kind: 'support' });
119
+ }
108
120
  if (owned) {
109
121
  const legacy = fs.readdirSync(path.join(root, 'skills')).filter(id => id.startsWith('i3-')).map(id => `skills/${id.slice(3)}`);
110
122
  if (target.commandsDirectory) legacy.push(...fs.readdirSync(path.join(root, '.claude/commands')).filter(file => file.startsWith('i3-') && file.endsWith('.md')).map(file => `commands/${file.slice(3)}`));
111
123
  legacy.push(`${name}-LICENSE`, `${name}-NOTICE.md`);
112
- const current = new Set(assets.slice(start).map(asset => path.relative(target.base, asset.destination)));
124
+ const current = new Set(assets.slice(start).map(asset => assetPath(target.base, asset.destination)));
113
125
  for (const relative of previous.assets) {
114
- // Only installer-owned top-level assets can be retired. Ignore malformed
115
- // or escaping manifest paths rather than extending their deletion scope.
116
- if (typeof relative !== 'string' || !/^(skills|commands|agents|references)\/[^/\\]+$/.test(relative)
117
- || relative.includes('\0') || ['.', '..'].includes(relative.split('/')[1])) continue;
126
+ // Retire only owned native entries or entries inside this package's
127
+ // companion directory. Never let a manifest extend the deletion scope.
128
+ if (typeof relative !== 'string' || relative.includes('\0') || relative.includes(':')
129
+ || relative.split('/').some(part => ['', '.', '..'].includes(part))) continue;
130
+ if (!/^(skills|commands|agents|references)\/[^/]+$/.test(relative) && !relative.startsWith(`${name}/`)) continue;
131
+ if (relative.startsWith(`${name}/`) && destination(path.join(target.base, relative)) !== path.join(target.base, relative)) continue;
118
132
  if (!current.has(relative)) legacy.push(relative);
119
133
  }
120
134
  for (const relative of new Set(legacy)) {
@@ -128,7 +142,7 @@ function planInstallation({ agents, scope, projectDir = process.cwd(), home = os
128
142
  asset.buddyCommand = asset.kind === 'command' && target.agents.some(id => ['codebuddy', 'workbuddy'].includes(id));
129
143
  asset.scope = scope;
130
144
  asset.personasDirectory = target.personasDirectory;
131
- if (sameVersion && previous.assets.includes(path.relative(target.base, asset.destination)) && asset.source && exists(asset.destination)) {
145
+ if (sameVersion && previous.assets.includes(assetPath(target.base, asset.destination)) && asset.source && exists(asset.destination)) {
132
146
  asset.expectedFingerprint = fingerprint(asset.source, asset);
133
147
  asset.reusable = fingerprint(asset.destination) === asset.expectedFingerprint;
134
148
  }
@@ -180,7 +194,8 @@ function executeInstallation(plan, { force = false } = {}) {
180
194
  } else {
181
195
  fs.cpSync(asset.source, asset.staged, { recursive: true, filter: file => {
182
196
  if (fs.lstatSync(file).isSymbolicLink()) throw new Error(`包内容中包含符号链接(symlink): ${file}`);
183
- return path.basename(file) !== '.DS_Store';
197
+ return path.basename(file) !== '.DS_Store' && (asset.kind !== 'support'
198
+ || !/(?:^|[\\/])(?:results|node_modules|\.git)(?:[\\/]|$)/.test(path.relative(root, file)));
184
199
  } });
185
200
  portable(asset.staged, asset, asset.source.endsWith('.md'));
186
201
  }
@@ -10,6 +10,7 @@ function replaceBlock(content, name, replacement) {
10
10
  // Adapt stable blocks, never their prose. Shared project commands work for
11
11
  // either Buddy client; global paths belong to the selected profile.
12
12
  function nativeContent(content, asset) {
13
+ if (asset.kind === 'support') return content;
13
14
  content = content.replaceAll('i3-composer-agent:', '').replaceAll('agent-skills:', '');
14
15
  if (!asset.buddyCommand) return content;
15
16
  const projectPersonas = '.codebuddy/agents/';
@@ -0,0 +1,164 @@
1
+ 'use strict';
2
+
3
+ const readline = require('node:readline');
4
+ const { orderedAgents, commonIds } = require('./tool-menu.cjs');
5
+
6
+ // Terminal columns are not JavaScript string lengths: Chinese characters use
7
+ // two cells. Keep every rendered line within the viewport before repainting.
8
+ function cellWidth(character) {
9
+ const code = character.codePointAt(0);
10
+ if (/\p{Mark}/u.test(character) || code === 0x200d) return 0;
11
+ return code >= 0x1100 && (code <= 0x115f || code === 0x2329 || code === 0x232a
12
+ || (code >= 0x2e80 && code <= 0xa4cf && code !== 0x303f)
13
+ || (code >= 0xac00 && code <= 0xd7a3) || (code >= 0xf900 && code <= 0xfaff)
14
+ || (code >= 0xfe10 && code <= 0xfe19) || (code >= 0xfe30 && code <= 0xfe6f)
15
+ || (code >= 0xff00 && code <= 0xff60) || (code >= 0xffe0 && code <= 0xffe6)
16
+ || (code >= 0x1f300 && code <= 0x1faff) || (code >= 0x20000 && code <= 0x3fffd)) ? 2 : 1;
17
+ }
18
+ function width(text) { return [...text].reduce((size, character) => size + cellWidth(character), 0); }
19
+ function clip(text, columns) {
20
+ if (width(text) <= columns) return text;
21
+ const suffix = columns >= 3 ? '...' : '';
22
+ let result = '', size = 0;
23
+ for (const character of text) {
24
+ size += cellWidth(character);
25
+ if (size > columns - suffix.length) break;
26
+ result += character;
27
+ }
28
+ return result + suffix;
29
+ }
30
+ function wrap(text, columns) {
31
+ const lines = [];
32
+ let line = '', size = 0;
33
+ for (const character of text) {
34
+ const cells = cellWidth(character);
35
+ if (line && size + cells > columns) { lines.push(line); line = ''; size = 0; }
36
+ line += character; size += cells;
37
+ }
38
+ lines.push(line);
39
+ return lines;
40
+ }
41
+
42
+ function selectTools(initialSelection, input = process.stdin, output = process.stdout) {
43
+ const selected = new Set(initialSelection);
44
+ let view = 'common', cursor = 0, commonCursor = 0, query = '', searching = false, message = '';
45
+ let frame = [], pageSize = 10, finished = false;
46
+ const wasRaw = Boolean(input.isRaw), wasFlowing = input.readableFlowing;
47
+ const choices = () => view === 'common' ? [...orderedAgents.slice(0, commonIds.length), null]
48
+ : view === 'more' ? orderedAgents.slice(commonIds.length)
49
+ : orderedAgents.filter(agent => `${agent.label} ${agent.id}`.toLowerCase().includes(query.toLowerCase()));
50
+ const selectedLabels = () => [...selected].map(id => orderedAgents.find(agent => agent.id === id).label).join('、');
51
+
52
+ function clearFrame() {
53
+ if (!frame.length) return;
54
+ const columns = output.columns || 80;
55
+ const rows = frame.reduce((count, line) => count + Math.max(1, Math.ceil(width(line) / columns)), 0);
56
+ readline.moveCursor(output, 0, -rows);
57
+ readline.cursorTo(output, 0);
58
+ readline.clearScreenDown(output);
59
+ frame = [];
60
+ }
61
+ function render() {
62
+ if (finished) return;
63
+ const columns = Math.max(1, (output.columns || 80) - 1), rows = output.rows || 24;
64
+ const entries = choices();
65
+ cursor = Math.max(0, Math.min(cursor, entries.length - 1));
66
+ const summary = wrap(`已选(${selected.size}):${selectedLabels() || '无'}`, columns);
67
+ const summaryLimit = rows < 16 ? 1 : 3;
68
+ if (summary.length > summaryLimit) {
69
+ summary.length = summaryLimit;
70
+ summary[summaryLimit - 1] = clip(summary[summaryLimit - 1], Math.max(0, columns - 3)) + '...';
71
+ }
72
+ const hints = searching
73
+ ? ['输入关键词实时搜索', 'Enter/↓ 浏览结果 · Esc返回 · Ctrl-C取消']
74
+ : ['↑↓ 移动/跨页 · 空格勾选 · Enter确认', 'm 更多 · / 搜索 · b/← 返回 · q/Esc取消'];
75
+ const footer = ['', ...summary, ...hints.flatMap(line => wrap(line, columns)), message];
76
+ const searchLine = view === 'search' ? [`搜索:${query}${searching ? '|' : ''}`] : [];
77
+ pageSize = Math.max(1, Math.min(10, rows - footer.length - searchLine.length - 2));
78
+ const page = Math.floor(cursor / pageSize), pages = Math.max(1, Math.ceil(entries.length / pageSize));
79
+ const title = view === 'common' ? '选择常用工具(支持多选):'
80
+ : `${view === 'more' ? '更多工具' : '搜索结果'}:第 ${page + 1}/${pages} 页,共 ${entries.length} 项`;
81
+ const lines = [title, ...searchLine];
82
+ for (let index = page * pageSize; index < Math.min(entries.length, (page + 1) * pageSize); index++) {
83
+ const agent = entries[index], pointer = index === cursor ? '>' : ' ';
84
+ lines.push(agent ? `${pointer} [${selected.has(agent.id) ? 'x' : ' '}] ${orderedAgents.indexOf(agent) + 1}. ${agent.label} [${agent.id}]${agent.globalSkillsDir ? '' : '(仅项目)'}`
85
+ : `${pointer} 更多工具(空格展开)`);
86
+ }
87
+ if (!entries.length) lines.push('没有匹配工具;按 / 重新搜索,或 Esc 返回。');
88
+ clearFrame();
89
+ frame = [...lines, ...footer].map(line => clip(line, columns));
90
+ output.write(frame.join('\n') + '\n');
91
+ }
92
+
93
+ return new Promise((resolve, reject) => {
94
+ function finish(value, error) {
95
+ if (finished) return;
96
+ finished = true;
97
+ input.off('keypress', keypress);
98
+ input.off('end', cancel);
99
+ input.off('error', fail);
100
+ output.off('resize', redraw);
101
+ process.off('SIGINT', cancel);
102
+ input.setRawMode(wasRaw);
103
+ if (!wasFlowing) input.pause();
104
+ try {
105
+ clearFrame();
106
+ output.write('\x1b[?25h');
107
+ if (value) output.write(`已选择工具:${selectedLabels()}\n`);
108
+ } catch (outputError) { error ||= outputError; }
109
+ if (error) reject(error); else resolve(value);
110
+ }
111
+ function cancel() { finish(null); }
112
+ function fail(error) { finish(null, error); }
113
+ function redraw() { try { render(); } catch (error) { fail(error); } }
114
+ function openMore() {
115
+ if (view === 'common') commonCursor = cursor;
116
+ view = 'more'; cursor = 0; searching = false;
117
+ }
118
+ function back() { view = 'common'; cursor = commonCursor; searching = false; query = ''; }
119
+ function keypress(text, key = {}) {
120
+ if (finished) return;
121
+ try {
122
+ message = '';
123
+ if (key.ctrl && ['c', 'd'].includes(key.name)) { cancel(); return; }
124
+ if (searching) {
125
+ if (key.name === 'escape') back();
126
+ else if (['return', 'enter', 'down'].includes(key.name)) searching = false;
127
+ else if (key.name === 'backspace') { query = [...query].slice(0, -1).join(''); cursor = 0; }
128
+ else if (text && !key.ctrl && !key.meta && !/[\x00-\x1f\x7f]/.test(text)) { query += text; cursor = 0; }
129
+ } else if (key.name === 'escape') {
130
+ if (view === 'search') back(); else { cancel(); return; }
131
+ } else if (text === 'q') { cancel(); return; }
132
+ else if (['return', 'enter'].includes(key.name)) {
133
+ if (selected.size) { finish([...selected]); return; }
134
+ message = '请至少用空格选择一个工具。';
135
+ } else if (key.name === 'space' || text === ' ') {
136
+ const entry = choices()[cursor];
137
+ if (entry) { if (selected.has(entry.id)) selected.delete(entry.id); else selected.add(entry.id); }
138
+ else if (view === 'common') openMore();
139
+ } else if (['up', 'down', 'pageup', 'pagedown'].includes(key.name) || ['n', 'p'].includes(text)) {
140
+ const direction = ['up', 'pageup'].includes(key.name) || text === 'p' ? -1 : 1;
141
+ const step = ['pageup', 'pagedown'].includes(key.name) || ['n', 'p'].includes(text) ? pageSize : 1;
142
+ cursor = Math.max(0, Math.min(choices().length - 1, cursor + direction * step));
143
+ } else if (text === 'm' || (key.name === 'right' && view === 'common')) openMore();
144
+ else if (text === 'b' || key.name === 'left') back();
145
+ else if (text === '/') { if (view === 'common') commonCursor = cursor; view = 'search'; query = ''; cursor = 0; searching = true; }
146
+ render();
147
+ } catch (error) { fail(error); }
148
+ }
149
+ try {
150
+ readline.emitKeypressEvents(input);
151
+ input.setRawMode(true);
152
+ input.on('keypress', keypress);
153
+ input.once('end', cancel);
154
+ input.once('error', fail);
155
+ output.on('resize', redraw);
156
+ process.once('SIGINT', cancel);
157
+ output.write('\x1b[?25l');
158
+ render();
159
+ input.resume();
160
+ } catch (error) { fail(error); }
161
+ });
162
+ }
163
+
164
+ module.exports = { selectTools };
package/bin/wizard.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const readline = require('node:readline');
4
- const { registry, selectAgents } = require('./install.cjs');
5
- const { orderedAgents, commonIds } = require('./tool-menu.cjs');
4
+ const { selectAgents } = require('./install.cjs');
5
+ const { selectTools } = require('./tool-selector.cjs');
6
6
 
7
7
  async function session(action) {
8
8
  const terminal = readline.createInterface({ input: process.stdin, output: process.stdout });
@@ -23,49 +23,11 @@ async function session(action) {
23
23
  }
24
24
  async function choose(options, identity) {
25
25
  console.log(identity);
26
+ const agents = await selectTools(selectAgents(options.agents));
27
+ if (!agents) return null;
28
+ if (agents.includes('workbuddy')) console.log('WorkBuddy 桌面版建议全局安装,以便技能列表和输入补全发现 Skills;项目安装仍支持命令,技能列表可能不显示。');
26
29
  return session(async ask => {
27
- const selected = new Set(selectAgents(options.agents));
28
- let view = 'common', page = 0, query = '';
29
30
  const cancelled = input => input === null || ['q', 'cancel', '取消'].includes(input.toLowerCase());
30
- while (true) {
31
- const matches = view === 'common' ? orderedAgents.slice(0, commonIds.length)
32
- : view === 'search' ? orderedAgents.filter(agent => `${agent.id} ${agent.label}`.toLowerCase().includes(query.toLowerCase()))
33
- : orderedAgents.slice(commonIds.length);
34
- const pages = Math.max(1, Math.ceil(matches.length / 10));
35
- page = Math.max(0, Math.min(page, pages - 1));
36
- console.log(view === 'common' ? '\n选择常用工具(支持多选):'
37
- : `\n${view === 'search' ? `搜索“${query}”` : '更多工具'}:第 ${page + 1}/${pages} 页,共 ${matches.length} 项`);
38
- for (const agent of view === 'common' ? matches : matches.slice(page * 10, page * 10 + 10)) {
39
- console.log(`${orderedAgents.indexOf(agent) + 1}. ${agent.label} [${agent.id}]${selected.has(agent.id) ? ' ✓ 已选' : ''}${agent.globalSkillsDir ? '' : '(仅项目安装)'}`);
40
- }
41
- if (view === 'common') console.log('7. 更多(m)');
42
- else console.log('n 下一页 · p 上一页 · b 返回常用工具');
43
- console.log(`已选:${[...selected].join(', ') || '无'};/关键词 搜索全部 ${registry.agents.length} 个工具;回车或完成确认;-编号/ID 移除选择。`);
44
- const input = await ask('输入编号/ID,可用逗号多选(q/取消退出): ');
45
- if (cancelled(input)) return null;
46
- if (['', 'done', '完成'].includes(input.toLowerCase())) {
47
- if (selected.size) break;
48
- console.log('请至少选择一个工具。'); continue;
49
- }
50
- if (['m', 'more', '更多'].includes(input.toLowerCase()) || (view === 'common' && input === '7')) { view = 'more'; page = 0; continue; }
51
- if (['b', 'back', '返回'].includes(input.toLowerCase())) { view = 'common'; page = 0; continue; }
52
- if (view !== 'common' && ['n', '下一页'].includes(input.toLowerCase())) { page = Math.min(pages - 1, page + 1); continue; }
53
- if (view !== 'common' && ['p', '上一页'].includes(input.toLowerCase())) { page = Math.max(0, page - 1); continue; }
54
- if (input.startsWith('/')) { view = 'search'; query = input.slice(1).trim(); page = 0; continue; }
55
- try {
56
- const values = ['all', '全部'].includes(input.toLowerCase()) ? orderedAgents.map(agent => agent.id) : input.split(/[,,]/);
57
- const additions = [], removals = [];
58
- for (const value of values) {
59
- const remove = value.trim().startsWith('-'), raw = value.trim().replace(/^-/, '');
60
- const id = /^\d+$/.test(raw) ? orderedAgents[Number(raw) - 1]?.id || raw : raw;
61
- (remove ? removals : additions).push(...selectAgents([id]));
62
- }
63
- for (const id of additions) selected.add(id);
64
- for (const id of removals) selected.delete(id);
65
- } catch (error) { console.log(error.message); }
66
- }
67
- const agents = [...selected];
68
- if (agents.includes('workbuddy')) console.log('WorkBuddy 桌面版建议全局安装,以便技能列表和输入补全发现 Skills;项目安装仍支持命令,技能列表可能不显示。');
69
31
  let scope = options.scope;
70
32
  while (!scope) {
71
33
  const input = await ask('安装范围:1. 项目(当前项目),2. 全局(所有项目)[1](q/取消退出): ');
@@ -82,10 +44,11 @@ async function confirm(plan) {
82
44
  for (const target of plan.targets) {
83
45
  console.log(` ${target.agents.join(', ')}: ${target.skillsDirectory}`);
84
46
  if (target.commandsDirectory) console.log(` 命令:${target.commandsDirectory}`);
47
+ console.log(` 完整配套(evals、hooks、scripts):${target.supportDirectory}`);
85
48
  }
86
49
  if (plan.conflicts.length) console.log(`将替换 ${plan.conflicts.length} 个已有内容;安装成功后清理旧文件,失败则恢复。`);
87
50
  if (plan.reused.length) console.log(`将复用同版本的 ${plan.reused.length} 个未修改内容。`);
88
- console.log('包含完整技能及共享参考资料。插件 Hook 需按宿主方式注册和启用。');
51
+ console.log('包含技能、共享资料和完整评测/Hook/脚本配套。安装不执行评测;Hook 需按宿主方式启用。');
89
52
  return session(async ask => {
90
53
  const answer = await ask('确认安装这些内容?[y/N]: ');
91
54
  return answer !== null && ['y', 'yes', '是', '确认'].includes(answer.toLowerCase());
@@ -11,6 +11,7 @@ npx @givolt/i3-composer-agent --codebuddy
11
11
  向导预选 CodeBuddy,可增加其他工具,再选择项目或全局范围。
12
12
  项目安装提供 `.codebuddy/skills/`(25 个技能)、`references/`、
13
13
  `commands/`(9 个命令)和 `agents/`(4 个角色)。
14
+ 同级 `i3-composer-agent/` 默认提供完整的 `evals/`、`hooks/`、`scripts/` 和依赖资料。
14
15
  全局使用 `~/.codebuddy/` 的相同结构。默认中文说明和回复;技能/命令标识不改名。
15
16
  安装成功清理旧内容,失败恢复;设置与自定义命令保留。
16
17
 
@@ -37,9 +38,16 @@ CodeBuddy 与 WorkBuddy 项目共用 `.codebuddy/`;同版本追加客户端会
37
38
  全局目录彼此独立。WorkBuddy 桌面版建议全局安装,详见 [WorkBuddy 说明](workbuddy-setup.md)。
38
39
  升级或同名冲突先查看 `--dry-run`,再确认或使用 `--yes --force`。
39
40
 
40
- ## CodeBuddy Code 完整插件
41
+ ## 配套检查
41
42
 
42
- 终端宿主的插件管理器使用完整目录:
43
+ 进入 `.codebuddy/i3-composer-agent/`(全局安装使用对应全局路径)运行 `npm test`,
44
+ 可检查技能格式及中英文触发,不调用模型。行为评测、样例和脚本用法见
45
+ [评测说明](../evals/README.md)。Hook 文件已安装,需要按宿主协议单独启用。
46
+
47
+ ## 通过 CodeBuddy Code 插件管理器安装
48
+
49
+ 这是另一种由宿主插件管理器管理的安装方式;已完成原生安装时无需再次执行。
50
+ `--prepare` 只导出文件并显示注册步骤,不自动注册插件或启用 Hook:
43
51
 
44
52
  ```bash
45
53
  npx @givolt/i3-composer-agent --prepare --codebuddy
@@ -55,7 +63,7 @@ npx @givolt/i3-composer-agent --prepare --codebuddy
55
63
  按宿主选择范围并启用后新建会话。兼容清单声明 `.claude/commands/` 与 `commands/`。
56
64
  插件命令示例为 `/i3-composer-agent:i3-spec`、`/i3-composer-agent:i3-plan`、
57
65
  `/i3-composer-agent:i3-build auto` 和 `/i3-composer-agent:i3-ship`。
58
- 完整目录包含 Hook 与共享参考;原生文件安装不自动启用插件 Hook。
66
+ 默认安装和导出均包含完整评测、Hook 与脚本;Hook 不自动启用。
59
67
  文件落盘、宿主发现和模型实际执行应分别验证。
60
68
 
61
69
  目录遵循 [配置目录参考](https://www.codebuddy.cn/docs/cli/codebuddy-dir),可参照
@@ -8,9 +8,11 @@ npx @givolt/i3-composer-agent --codex
8
8
 
9
9
  项目范围写入 `.agents/skills/`,全局写入 `~/.agents/skills/`,并安装同级 `references/`。
10
10
  提供全部 25 个 `i3-` 技能,中文选择说明与默认中文回复在每个入口独立生效。
11
- 此模式不安装短生命周期命令,也不自动启用插件 Hook。
11
+ 同级 `i3-composer-agent/` 默认提供完整评测、Hook、脚本及依赖资料。
12
+ 进入该配套目录执行 `npm test` 可进行结构和中英文触发检查,不调用模型;详见
13
+ [评测说明](../evals/README.md)。原生调用使用技能 ID,Hook 文件需按宿主能力单独启用。
12
14
 
13
- ## 完整插件
15
+ ## 通过插件管理器安装
14
16
 
15
17
  ```bash
16
18
  npx @givolt/i3-composer-agent --prepare --codex
@@ -18,7 +20,8 @@ codex plugin marketplace add ./i3-composer-agent
18
20
  codex plugin add i3-composer-agent@i3-composer-agent
19
21
  ```
20
22
 
21
- 准备模式产生持久目录并显示注册命令。注册启用后新建会话,
23
+ 准备模式另行导出持久目录并显示注册命令,不自动注册或启用 Hook。
24
+ 已完成原生安装时无需再次准备。选择插件安装方式并注册启用后新建会话,
22
25
  通过 `/skills`、`$i3-spec-driven-development` 等技能名称,或在聊天中明确指定技能调用。
23
26
  插件适配清单指向 `skills/`;共享资料保留相对链接可解析的 `references/` 布局。
24
27
 
@@ -15,11 +15,18 @@ npx @givolt/i3-composer-agent --agents codebuddy,workbuddy --scope global --yes
15
15
  - `~/.workbuddy/agents/`:4 个专业角色。
16
16
  - `~/.workbuddy/references/`:共享参考和标准 MIT 许可。
17
17
  - `~/.workbuddy/docs/agents.md`:角色说明。
18
+ - `~/.workbuddy/i3-composer-agent/`:完整评测、Hook、脚本及依赖资料。
18
19
  - `~/.workbuddy/.i3-composer-agent.json`:安装内容所有权及客户端清单。
19
20
 
20
21
  自定义 profile 可设置 `WORKBUDDY_CONFIG_DIR`,安装后打开对应 profile。
21
22
  WorkBuddy 的目录选择使用自己的配置,不借用 `CODEBUDDY_CONFIG_DIR`。
22
23
 
24
+ ## 配套检查
25
+
26
+ 进入 `~/.workbuddy/i3-composer-agent/` 运行 `npm test`,检查技能格式及中英文触发,
27
+ 不会调用模型。项目安装对应 `.codebuddy/i3-composer-agent/`。行为评测用例、样例和
28
+ 脚本用法见 [评测说明](../evals/README.md)。Hook 文件默认提供,启用需适配宿主事件协议。
29
+
23
30
  ## 发现和调用
24
31
 
25
32
  安装后新建会话,查看 Skills 列表并输入 `/` 查看命令:
@@ -66,7 +73,7 @@ npx @givolt/i3-composer-agent --workbuddy --scope global --yes --force
66
73
  ```
67
74
 
68
75
  升级、修改内容和未登记同名冲突需要确认;成功替换后清理旧文件与所属历史备份,失败恢复。
69
- 原生安装提供 Skills、Commands、Agents;完整插件注册另按宿主能力处理。
76
+ 原生安装同时提供 Skills、Commands、Agents 和完整 evals/hooks/scripts 配套。
70
77
  `--prepare --workbuddy` 不支持,原生安装不会启用插件 Hook。
71
78
 
72
79
  可参照 [任务栏命令说明](https://www.codebuddy.cn/docs/workbuddy/From-Beginner-to-Expert-Guide/Function-Description/Task-Bar)、