@ultime/codecraft 1.2.1 → 1.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.0 - 2026-10-27
4
+
5
+ - 仓库改为 `skills/codecraft` 标准集合布局,可被 `skills` CLI 直接发现。
6
+ - 移除 `codecraft install`、agent 注册表与自定义 agent;安装改由 `npx skills add ultime-space/skills --skill codecraft` 或 `skills-npm` 负责。
7
+ - 发布 workflow、README、适配器与评测路径同步到集合布局。
8
+
9
+ ## 1.3.0 - 2026-10-27
10
+
11
+ - 新增 agent 注册表:内置 generic / claude / cursor / trae / trae-cn,`--list-agents` 查看项目 / 全局路径与检测结果。
12
+ - 支持项目级 `codecraft.agents.json` 与用户级 `~/.codecraft/agents.json` 自定义 agent(project / global / markers),项目级覆盖用户级。
13
+ - `auto` 按注册表检测所有匹配的 agent,不优先任何一家;重复目标的 agent 自动合并,避免重复复制。
14
+ - 仍保留 `--dest` 作为临时目录逃生口。
15
+
3
16
  ## 1.2.1 - 2026-10-27
4
17
 
5
18
  - 修复 pack 自动判定:识别 Nuxt 2 的 `pages/`、Nuxt 3 的 `app/`、uni-app 的 `manifest.json + pages.json`、Taro / uni-app 依赖,以及 workspace 内的嵌套应用。
package/README.md CHANGED
@@ -43,44 +43,33 @@
43
43
 
44
44
  ## 安装
45
45
 
46
- ### 方式 A:npm + skills-npm(推荐,多项目 / 团队)
46
+ ### 方式 A:skills CLI(推荐)
47
47
 
48
48
  ```bash
49
- npm i -D @ultime/codecraft skills-npm
50
- npx skills-npm setup
49
+ npx skills add ultime-space/skills --skill codecraft
51
50
  ```
52
51
 
53
- 之后每次 `npm install` 都会把 `node_modules/@ultime/codecraft` 里的 skill 同步到 agent 目录:`.agents/skills/codecraft`,以及检测到的 Claude / Trae / Cursor 目录。需要 Node.js >= 22.20(`skills-npm` 的要求)。
54
-
55
- ### 方式 B:Codecraft 自带安装器(单次,无额外依赖)
56
-
57
- ```bash
58
- npx @ultime/codecraft install
59
- ```
52
+ 不传 `-a` 时,CLI 会检测本机 agent 并让你多选;`-g` 装到用户目录,`-a` 指定 agent。agent 支持由 `skills` CLI 维护,本 skill 不枚举。
60
53
 
61
- 自动检测当前项目的 agent,把 `SKILL.md`、`docs/`、`scenarios/`、`prompts/` 复制到 `.agents/skills/codecraft` 和对应 agent 目录(`.claude/skills`、`.cursor/skills`、`.trae/skills`、`.trae-cn/skills`)。不传 `--agent` 时只按检测结果安装,不优先任何一家。
54
+ ### 方式 B:npm + skills-npm(多项目 / 团队)
62
55
 
63
56
  ```bash
64
- npx @ultime/codecraft install --agent generic # 指定 agent,可逗号分隔
65
- npx @ultime/codecraft install --global --agent claude # 装到用户目录
66
- npx @ultime/codecraft install --global --agent trae-cn # Trae CN 的全局目录
67
- npx @ultime/codecraft install --global --dest .trae-cn/skills # 自定义目录
68
- npx @ultime/codecraft install --dry-run # 只看计划
69
- npx @ultime/codecraft install --force # 覆盖已有目录
57
+ npm i -D @ultime/codecraft skills-npm
58
+ npx skills-npm setup
70
59
  ```
71
60
 
72
- 可选 agent:`generic` / `claude` / `cursor` / `trae` / `trae-cn`;未知目录用 `--dest`。
61
+ 之后每次 `npm install` 都会把 `node_modules/@ultime/codecraft` 里的 skill 同步到 agent 目录:`.agents/skills/codecraft`,以及检测到的 agent 目录。需要 Node.js >= 22.20(`skills-npm` 的要求)。
73
62
 
74
63
  ### 方式 C:手动指针
75
64
 
76
65
  不复制文件,只在项目规则里留一行:
77
66
 
78
- - Trae:`.trae/rules/codecraft.md`
67
+ - 通用:`AGENTS.md`
79
68
  - Claude:`CLAUDE.md` 或 `~/.claude/CLAUDE.md`
80
- - 其他:`AGENTS.md`
69
+ - Trae:`.trae/rules/codecraft.md`
81
70
 
82
71
  ```md
83
- 代码任务先读 <Codecraft 路径>/codecraft/adapters/trae/SKILL.md
72
+ 代码任务先读 <Codecraft 仓库路径>/skills/codecraft/adapters/trae/SKILL.md
84
73
  ```
85
74
 
86
75
  平台入口:
@@ -88,8 +77,8 @@ npx @ultime/codecraft install --force # 覆盖已有目录
88
77
  | 平台 | 入口文件 |
89
78
  |---|---|
90
79
  | 通用(读 `AGENTS.md`) | [`adapters/generic/AGENTS.md`](./adapters/generic/AGENTS.md) |
91
- | Trae | [`adapters/trae/SKILL.md`](./adapters/trae/SKILL.md) |
92
80
  | Claude | [`adapters/claude/SKILL.md`](./adapters/claude/SKILL.md) |
81
+ | Trae | [`adapters/trae/SKILL.md`](./adapters/trae/SKILL.md) |
93
82
 
94
83
  ## 第一次使用
95
84
 
@@ -136,7 +125,6 @@ codecraft <command> [options]
136
125
  | `audit` | 内置规则 + `codecraft.config.json` 里的 gates,出只读报告 |
137
126
  | `plan` | 把 findings 转成只读升级计划;默认不跑 gates |
138
127
  | `init` | 生成 app / library 骨架;默认 dry-run |
139
- | `install` | 把 skill 安装到 agent skills 目录;默认写入,`--dry-run` 只看计划 |
140
128
  | `version` | 输出版本号 |
141
129
 
142
130
  ### 常用
@@ -151,9 +139,6 @@ codecraft audit --config codecraft.config.json --fail-fast
151
139
  codecraft plan --root . --with-gates
152
140
  codecraft init --root . --name demo # dry-run
153
141
  codecraft init --root . --name demo --apply # 写盘
154
- codecraft install # 自动检测并安装
155
- codecraft install --agent trae,claude --force
156
- codecraft install --dry-run
157
142
  ```
158
143
 
159
144
  ### 参数
@@ -171,18 +156,13 @@ codecraft install --dry-run
171
156
  | `--with-gates` | — | 关 | `plan` 执行 gates |
172
157
  | `--name <name>` | 字符串 | — | `init` 的 app / package 名称;`--apply` 时必填 |
173
158
  | `--apply` | — | 关 | `init` 写盘;已存在路径跳过,不覆盖 |
174
- | `--agent <list>` | `auto` / `claude` / `trae` / `cursor` / `generic` | `auto` | `install` 目标,可逗号分隔 |
175
- | `--global` | — | 关 | `install` 写入用户目录 |
176
- | `--dest <path>` | 目录 | — | `install` 的显式目标 |
177
- | `--force` | — | 关 | `install` 覆盖已有 codecraft 目录 |
178
- | `--dry-run` | — | 关 | `install` 只输出计划,不写盘 |
179
159
 
180
160
  ### 退出码
181
161
 
182
162
  | 码 | 含义 |
183
163
  |---|---|
184
164
  | `0` | 通过;warning 不阻断 |
185
- | `1` | 有 error findings、install 失败,或有 gate 失败 |
165
+ | `1` | 有 error findings,或有 gate 失败 |
186
166
  | `2` | 用法错误、root 不存在、配置非法 |
187
167
 
188
168
  ### 输出
@@ -247,7 +227,7 @@ docs/ 宪法、族卡、AI 契约、风格总图、模式、架构、AI
247
227
  scenarios/ 新项目 / 旧项目审计 / 只出报告 / 重构评审
248
228
  adapters/ generic / Trae / Claude 手动指针
249
229
  prompts/ 输出格式
250
- src/ + bin/ CLI(含 install)
230
+ src/ + bin/ CLI(check / audit / plan / init)
251
231
  tests/ node:test 用例
252
232
  templates/ init --apply 的 app / library 骨架
253
233
  evals/ golden tasks(人工回归)
@@ -263,13 +243,13 @@ ROADMAP.md 后续版本计划与交接说明
263
243
  - 不按「感觉重要」升 major;`2.0` 不设时间表。
264
244
  - 规则 ID `A1–A21` 稳定;增删规则属于 minor 以上的变更。
265
245
  - 报告始终携带版本号;跨版本对比以 JSON 为准。
266
- - 后续计划:[ROADMAP.md](https://github.com/ultime-space/skills/blob/main/codecraft/ROADMAP.md)。
246
+ - 后续计划:[ROADMAP.md](https://github.com/ultime-space/skills/blob/main/skills/codecraft/ROADMAP.md)。
267
247
 
268
248
  ## FAQ
269
249
 
270
250
  - **静态检查误报怎么办?** 在 `codecraft.config.json` 的 `ignore` 里登记 `rule` + `file` + `reason`,必要时加 `expires`;不要直接删规则。
271
251
  - **`Files: 0` 是什么意思?** 没有扫到源文件,命令会输出警告并以 2 退出;用 `--pack app|library` 指定正确类型,或检查 `--root`。
272
- - **Trae CN 装到哪?** 用 `--agent trae-cn`(`~/.trae-cn/skills`);未知目录用 `--dest`。
252
+ - **怎么装到指定 agent?** `npx skills add ultime-space/skills --skill codecraft`;交互选 agent,或用 `-a <agent>` 非交互。安装路径由 `skills` CLI 维护,本 skill 不枚举。
273
253
  - **`check` / `audit` / `plan` 有什么区别?** `check` 只跑内置规则;`audit` 额外跑 config 里的 gates;`plan` 只读,把 findings 转成计划,默认不跑 gates。
274
254
  - **为什么不自动跑 typecheck / test?** 门禁由项目提供,Codecraft 只编排,避免第二套实现。
275
255
  - **monorepo 为什么被判成 app / library?** 有 `pages/`、`app/`、`manifest.json + pages.json`、Taro / uni-app 依赖或嵌套应用时按 `app`;有 `packages/` 或 `src/` 时按 `library`;仍不确定就显式传 `--pack`。
package/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.1
1
+ 1.4.0
@@ -9,6 +9,6 @@ Core 是 `docs/` + `scenarios/` + `scripts/`;adapters 只做入口,不复制
9
9
 
10
10
  规则:adapter 只写“读什么、按什么流程、输出什么”,规则正文留在 `docs/`。
11
11
 
12
- 包根的 [`SKILL.md`](../SKILL.md) 是标准 agent skill 入口(`skills-npm` / `codecraft install` 使用);本目录的平台文件用于手动指针接入。两者二选一,不要同时维护两套正文。
12
+ 包根的 [`SKILL.md`](../SKILL.md) 是标准 agent skill 入口(`skills add` / `skills-npm` 使用);本目录的平台文件用于手动指针接入。两者二选一,不要同时维护两套正文。
13
13
 
14
14
  使用者提示词(怎么对 AI 说):[`../docs/07-ai-prompts.md`](../docs/07-ai-prompts.md)。
@@ -10,9 +10,11 @@
10
10
  最快的方式:
11
11
 
12
12
  ```bash
13
- npx @ultime/codecraft install
13
+ npx skills add ultime-space/skills --skill codecraft
14
14
  ```
15
15
 
16
+ 交互选择 agent;非交互用 `-a <agent>`,全局加 `-g`。
17
+
16
18
  或走 npm 同步:
17
19
 
18
20
  ```bash
@@ -24,12 +26,12 @@ npx skills-npm setup
24
26
 
25
27
  也可以手动在 agent 入口里留一条指针,不要复制规则:
26
28
 
27
- - Trae:项目 `.trae/rules/codecraft.md`
28
- - Claude:`CLAUDE.md`
29
29
  - 通用:`AGENTS.md`
30
+ - Claude:`CLAUDE.md`
31
+ - Trae:项目 `.trae/rules/codecraft.md`
30
32
 
31
33
  ```md
32
- 代码任务先读 <Codecraft 仓库路径>/codecraft/adapters/<平台>/...
34
+ 代码任务先读 <Codecraft 仓库路径>/skills/codecraft/adapters/<平台>/...
33
35
  ```
34
36
 
35
37
  平台对应:Trae → `adapters/trae/SKILL.md`,Claude → `adapters/claude/SKILL.md`,其他 → `adapters/generic/AGENTS.md`。
@@ -39,7 +41,7 @@ npx skills-npm setup
39
41
  任何任务都可以先用这段,再补场景:
40
42
 
41
43
  ```text
42
- 使用 Codecraft(<Codecraft 仓库路径>/codecraft)。
44
+ 使用 Codecraft(<Codecraft 仓库路径>/skills/codecraft)。
43
45
  模式:Audit / Plan / Apply。
44
46
  范围:<目录 / 包 / 文件 / diff>。
45
47
  任务:<一句话>。
@@ -109,12 +109,10 @@
109
109
  - 期望:命中 A21;只报文件与行号,不回显密钥;提示轮换。
110
110
  - 失败信号:漏报;把密钥原文打印到报告;把普通变量名误报为密钥。
111
111
 
112
- ## 16. 安装到 agent
112
+ ## 16. 安装(已移出本 skill)
113
113
 
114
- - 输入:空项目,无 agent 配置目录。
115
- - 命令:`codecraft install --root . --agent generic`;再执行一次;最后 `--force`。
116
- - 期望:生成 `.agents/skills/codecraft/SKILL.md`、`docs/`、`scenarios/`、`prompts/`;第二次 skip;`--force` 覆盖。
117
- - 失败信号:复制到错误目录;覆盖用户已有内容;`SKILL.md` 引用断链。
114
+ - 安装由 `skills` CLI 负责:`npx skills add ultime-space/skills --skill codecraft`。
115
+ - 不在 Codecraft 规则评估范围内;本文件不再评估安装器行为。
118
116
 
119
117
  ## 评分
120
118
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultime/codecraft",
3
- "version": "1.2.1",
3
+ "version": "1.4.0",
4
4
  "description": "面向 JS/TS 的代码规范与架构设计(前端默认,Node 后端可复用结构规则),作为可安装的 skill 包。",
5
5
  "keywords": [
6
6
  "ai",
@@ -18,9 +18,9 @@
18
18
  "repository": {
19
19
  "type": "git",
20
20
  "url": "git+https://github.com/ultime-space/skills.git",
21
- "directory": "codecraft"
21
+ "directory": "skills/codecraft"
22
22
  },
23
- "homepage": "https://github.com/ultime-space/skills/tree/main/codecraft",
23
+ "homepage": "https://github.com/ultime-space/skills/tree/main/skills/codecraft",
24
24
  "bugs": {
25
25
  "url": "https://github.com/ultime-space/skills/issues"
26
26
  },
package/src/cli.mjs CHANGED
@@ -5,7 +5,6 @@ import { loadConfig, runGates } from './gates.mjs'
5
5
  import { buildPlan, summarizePlan } from './plan.mjs'
6
6
  import { applyInitPlan, buildInitPlan } from './init.mjs'
7
7
  import { applyIgnores } from './ignore.mjs'
8
- import { applyInstallPlan, buildInstallPlan, parseAgents } from './install.mjs'
9
8
 
10
9
  const PACKAGE = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
11
10
  const VERSION = PACKAGE.version
@@ -17,18 +16,12 @@ Commands:
17
16
  audit check + configured gates; read-only report
18
17
  plan Read-only upgrade plan from findings
19
18
  init Scaffold an app or package (dry-run unless --apply)
20
- install Install the skill into agent skills directories
21
19
  version Print version
22
20
 
23
21
  Options:
24
22
  --root <path> Project root (default: cwd)
25
23
  --pack app|library|design-system|auto Rule pack (default: auto)
26
24
  --config <path> Config file (default: codecraft.config.json)
27
- --agent auto|generic|claude|cursor|trae|trae-cn Install target, comma-separated (default: auto)
28
- --global Install into the home directory
29
- --dest <path> Explicit destination (relative to root / home), e.g. .trae-cn/skills
30
- --force Replace an existing codecraft skill directory
31
- --dry-run Show the install plan without writing
32
25
  --no-gates Skip gates in audit / plan
33
26
  --with-gates Run configured gates in plan
34
27
  --fail-fast Stop gates after the first failure
@@ -59,7 +52,7 @@ export function main(argv) {
59
52
  }
60
53
 
61
54
  const command = args.command || 'check'
62
- if (!['check', 'audit', 'plan', 'init', 'install'].includes(command)) {
55
+ if (!['check', 'audit', 'plan', 'init'].includes(command)) {
63
56
  console.error(`codecraft: unknown command "${command}"\n next: run codecraft --help`)
64
57
  return 2
65
58
  }
@@ -70,42 +63,6 @@ export function main(argv) {
70
63
  return 2
71
64
  }
72
65
 
73
- if (command === 'install') {
74
- let agents
75
- try {
76
- agents = parseAgents(args.agent)
77
- } catch (error) {
78
- console.error(`codecraft: ${error.message}`)
79
- return 2
80
- }
81
- try {
82
- const plan = buildInstallPlan(root, { agents, global: args.global, dest: args.dest })
83
- const applied = args.dryRun ? null : applyInstallPlan(plan, { force: args.force })
84
- const meta = {
85
- version: VERSION,
86
- mode: 'install',
87
- root,
88
- scope: args.global ? 'global' : 'project',
89
- agent: args.agent,
90
- dest: args.dest || null,
91
- dryRun: args.dryRun,
92
- targets: plan.length,
93
- created: applied?.created.length ?? 0,
94
- replaced: applied?.replaced.length ?? 0,
95
- skipped: applied?.skipped.length ?? 0,
96
- }
97
- if (args.format === 'json') {
98
- console.log(JSON.stringify({ ...meta, plan, applied }, null, 2))
99
- } else {
100
- console.log(formatInstall(meta, plan, applied))
101
- }
102
- return plan.length ? 0 : 1
103
- } catch (error) {
104
- console.error(`codecraft: ${error.message}`)
105
- return 1
106
- }
107
- }
108
-
109
66
  let config
110
67
  try {
111
68
  config = loadConfig(root, args.config)
@@ -243,11 +200,6 @@ function parseArgs(argv) {
243
200
  failFast: false,
244
201
  name: '',
245
202
  apply: false,
246
- agent: 'auto',
247
- global: false,
248
- dest: '',
249
- force: false,
250
- dryRun: false,
251
203
  format: 'md',
252
204
  severity: 'warn',
253
205
  ci: false,
@@ -264,12 +216,7 @@ function parseArgs(argv) {
264
216
  else if (arg === '--with-gates') args.withGates = true
265
217
  else if (arg === '--fail-fast') args.failFast = true
266
218
  else if (arg === '--apply') args.apply = true
267
- else if (arg === '--global') args.global = true
268
- else if (arg === '--force') args.force = true
269
- else if (arg === '--dry-run') args.dryRun = true
270
219
  else if (arg === '--name') args.name = argv[++i] ?? ''
271
- else if (arg === '--agent') args.agent = argv[++i] ?? 'auto'
272
- else if (arg === '--dest') args.dest = argv[++i] ?? ''
273
220
  else if (arg === '--root') args.root = argv[++i] ?? '.'
274
221
  else if (arg === '--pack') args.pack = argv[++i] ?? 'auto'
275
222
  else if (arg === '--config') args.config = argv[++i] ?? ''
@@ -501,49 +448,6 @@ function formatInit(meta, plan, applied) {
501
448
  return lines.filter((line, index) => !(line === '' && lines[index - 1] === '')).join('\n')
502
449
  }
503
450
 
504
- function formatInstall(meta, plan, applied) {
505
- const lines = [
506
- '# Codecraft Install',
507
- '',
508
- `- Version: ${meta.version}`,
509
- `- Mode: install`,
510
- `- Scope: ${meta.scope}`,
511
- `- Agent: ${meta.agent}`,
512
- `- Root: ${meta.root}`,
513
- `- Dest: ${meta.dest ?? '(auto)'}`,
514
- `- Dry run: ${meta.dryRun}`,
515
- `- Targets: ${meta.targets}`,
516
- ]
517
-
518
- if (applied) {
519
- lines.push(
520
- `- Created: ${meta.created}`,
521
- `- Replaced: ${meta.replaced}`,
522
- `- Skipped: ${meta.skipped}`,
523
- )
524
- }
525
-
526
- if (!plan.length) {
527
- lines.push('', '未找到可安装的目标;请用 `--agent` 指定,或先创建 agent 配置目录。')
528
- } else {
529
- lines.push('', '## Plan', '', '| Agent | Target | Action |', '|---|---|---|')
530
- for (const entry of plan) {
531
- lines.push(`| ${entry.agent} | ${entry.target} | ${entry.action} |`)
532
- }
533
- }
534
-
535
- lines.push(
536
- '',
537
- '## Next',
538
- '',
539
- meta.dryRun
540
- ? '- Re-run without `--dry-run` to write the skill.'
541
- : '- Restart the agent or reload skills;然后说:`用 Codecraft 审计本项目,只读,不改文件。`',
542
- )
543
-
544
- return lines.join('\n')
545
- }
546
-
547
451
  function header(meta) {
548
452
  return [
549
453
  `# Codecraft ${title(meta.mode)}`,
package/src/install.mjs DELETED
@@ -1,102 +0,0 @@
1
- import fs from 'node:fs'
2
- import os from 'node:os'
3
- import path from 'node:path'
4
- import { fileURLToPath } from 'node:url'
5
-
6
- const PACKAGE_ROOT = fileURLToPath(new URL('..', import.meta.url))
7
- const SKILL_FILES = ['SKILL.md']
8
- const SKILL_DIRS = ['docs', 'scenarios', 'prompts']
9
-
10
- const AGENT_DIRS = {
11
- generic: '.agents/skills',
12
- claude: '.claude/skills',
13
- cursor: '.cursor/skills',
14
- trae: '.trae/skills',
15
- 'trae-cn': '.trae-cn/skills',
16
- }
17
-
18
- const AGENT_MARKERS = {
19
- generic: ['.agents'],
20
- claude: ['.claude', 'CLAUDE.md'],
21
- cursor: ['.cursor'],
22
- trae: ['.trae'],
23
- 'trae-cn': ['.trae-cn'],
24
- }
25
-
26
- export const AGENTS = Object.keys(AGENT_DIRS)
27
-
28
- export function parseAgents(value) {
29
- if (!value || value === 'auto') return []
30
- const agents = [...new Set(value.split(',').map((item) => item.trim()).filter(Boolean))]
31
- const invalid = agents.filter((agent) => !AGENTS.includes(agent))
32
- if (invalid.length) {
33
- throw new Error(`unknown agent: ${invalid.join(', ')}(可选:auto, ${AGENTS.join(', ')})`)
34
- }
35
- return agents
36
- }
37
-
38
- export function buildInstallPlan(root, options = {}) {
39
- const base = options.global ? os.homedir() : path.resolve(root)
40
- const explicit = options.agents ?? []
41
- const invalid = explicit.filter((agent) => !AGENTS.includes(agent))
42
- if (invalid.length) {
43
- throw new Error(`unknown agent: ${invalid.join(', ')}(可选:${AGENTS.join(', ')})`)
44
- }
45
-
46
- const agents = explicit.length ? explicit : detectAgents(base, options.global)
47
- const destinations = options.dest
48
- ? [{ agent: 'custom', dir: path.resolve(base, options.dest) }]
49
- : agents.map((agent) => ({ agent, dir: path.join(base, AGENT_DIRS[agent]) }))
50
-
51
- assertSource()
52
-
53
- return destinations.map(({ agent, dir }) => {
54
- const target = path.join(dir, 'codecraft')
55
- const exists = fs.existsSync(target)
56
- return { agent, target, exists, action: exists ? 'exists' : 'create' }
57
- })
58
- }
59
-
60
- export function applyInstallPlan(plan, options = {}) {
61
- const created = []
62
- const skipped = []
63
- const replaced = []
64
-
65
- for (const entry of plan) {
66
- if (entry.exists && !options.force) {
67
- skipped.push(entry.target)
68
- continue
69
- }
70
- if (entry.exists) {
71
- fs.rmSync(entry.target, { recursive: true, force: true })
72
- replaced.push(entry.target)
73
- }
74
-
75
- fs.mkdirSync(entry.target, { recursive: true })
76
- for (const file of SKILL_FILES) {
77
- fs.copyFileSync(path.join(PACKAGE_ROOT, file), path.join(entry.target, file))
78
- }
79
- for (const dir of SKILL_DIRS) {
80
- fs.cpSync(path.join(PACKAGE_ROOT, dir), path.join(entry.target, dir), { recursive: true })
81
- }
82
- created.push(entry.target)
83
- }
84
-
85
- return { created, skipped, replaced }
86
- }
87
-
88
- function detectAgents(base, global) {
89
- const found = AGENTS.filter((agent) =>
90
- AGENT_MARKERS[agent].some((marker) => fs.existsSync(path.join(base, marker))),
91
- )
92
- if (global) return found.length ? found : ['generic']
93
- return [...new Set([...found, 'generic'])]
94
- }
95
-
96
- function assertSource() {
97
- for (const file of SKILL_FILES) {
98
- if (!fs.existsSync(path.join(PACKAGE_ROOT, file))) {
99
- throw new Error(`skill source is incomplete: missing ${file}`)
100
- }
101
- }
102
- }