@ultime/codecraft 1.3.0 → 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 +6 -0
- package/README.md +15 -54
- package/VERSION +1 -1
- package/adapters/README.md +1 -1
- package/docs/07-ai-prompts.md +7 -5
- package/evals/golden-tasks.md +3 -5
- package/package.json +3 -3
- package/src/cli.mjs +1 -135
- package/src/install.mjs +0 -172
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
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
|
+
|
|
3
9
|
## 1.3.0 - 2026-10-27
|
|
4
10
|
|
|
5
11
|
- 新增 agent 注册表:内置 generic / claude / cursor / trae / trae-cn,`--list-agents` 查看项目 / 全局路径与检测结果。
|
package/README.md
CHANGED
|
@@ -43,62 +43,33 @@
|
|
|
43
43
|
|
|
44
44
|
## 安装
|
|
45
45
|
|
|
46
|
-
### 方式 A:
|
|
46
|
+
### 方式 A:skills CLI(推荐)
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
|
|
50
|
-
npx skills-npm setup
|
|
49
|
+
npx skills add ultime-space/skills --skill codecraft
|
|
51
50
|
```
|
|
52
51
|
|
|
53
|
-
|
|
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
|
-
|
|
54
|
+
### 方式 B:npm + skills-npm(多项目 / 团队)
|
|
62
55
|
|
|
63
56
|
```bash
|
|
64
|
-
|
|
65
|
-
npx
|
|
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 # 覆盖已有目录
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
可选内置 agent:`generic` / `claude` / `cursor` / `trae` / `trae-cn`;用 `--list-agents` 查看实际路径与检测结果。
|
|
73
|
-
|
|
74
|
-
自定义 agent 放在项目级 `codecraft.agents.json` 或用户级 `~/.codecraft/agents.json`(项目级覆盖用户级):
|
|
75
|
-
|
|
76
|
-
```json
|
|
77
|
-
{
|
|
78
|
-
"agents": {
|
|
79
|
-
"my-agent": {
|
|
80
|
-
"project": ".my/skills",
|
|
81
|
-
"global": ".my/skills",
|
|
82
|
-
"markers": [".my"]
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
57
|
+
npm i -D @ultime/codecraft skills-npm
|
|
58
|
+
npx skills-npm setup
|
|
86
59
|
```
|
|
87
60
|
|
|
88
|
-
|
|
89
|
-
- `markers`:`auto` 检测用的目录或文件;不填则只能显式 `--agent my-agent`。
|
|
90
|
-
- 未知或临时目录仍可用 `--dest`,不必先写配置。
|
|
61
|
+
之后每次 `npm install` 都会把 `node_modules/@ultime/codecraft` 里的 skill 同步到 agent 目录:`.agents/skills/codecraft`,以及检测到的 agent 目录。需要 Node.js >= 22.20(`skills-npm` 的要求)。
|
|
91
62
|
|
|
92
63
|
### 方式 C:手动指针
|
|
93
64
|
|
|
94
65
|
不复制文件,只在项目规则里留一行:
|
|
95
66
|
|
|
96
|
-
-
|
|
67
|
+
- 通用:`AGENTS.md`
|
|
97
68
|
- Claude:`CLAUDE.md` 或 `~/.claude/CLAUDE.md`
|
|
98
|
-
-
|
|
69
|
+
- Trae:`.trae/rules/codecraft.md`
|
|
99
70
|
|
|
100
71
|
```md
|
|
101
|
-
代码任务先读 <Codecraft
|
|
72
|
+
代码任务先读 <Codecraft 仓库路径>/skills/codecraft/adapters/trae/SKILL.md
|
|
102
73
|
```
|
|
103
74
|
|
|
104
75
|
平台入口:
|
|
@@ -106,8 +77,8 @@ npx @ultime/codecraft install --force # 覆盖已有目录
|
|
|
106
77
|
| 平台 | 入口文件 |
|
|
107
78
|
|---|---|
|
|
108
79
|
| 通用(读 `AGENTS.md`) | [`adapters/generic/AGENTS.md`](./adapters/generic/AGENTS.md) |
|
|
109
|
-
| Trae | [`adapters/trae/SKILL.md`](./adapters/trae/SKILL.md) |
|
|
110
80
|
| Claude | [`adapters/claude/SKILL.md`](./adapters/claude/SKILL.md) |
|
|
81
|
+
| Trae | [`adapters/trae/SKILL.md`](./adapters/trae/SKILL.md) |
|
|
111
82
|
|
|
112
83
|
## 第一次使用
|
|
113
84
|
|
|
@@ -154,7 +125,6 @@ codecraft <command> [options]
|
|
|
154
125
|
| `audit` | 内置规则 + `codecraft.config.json` 里的 gates,出只读报告 |
|
|
155
126
|
| `plan` | 把 findings 转成只读升级计划;默认不跑 gates |
|
|
156
127
|
| `init` | 生成 app / library 骨架;默认 dry-run |
|
|
157
|
-
| `install` | 把 skill 安装到 agent skills 目录;默认写入,`--dry-run` 只看计划 |
|
|
158
128
|
| `version` | 输出版本号 |
|
|
159
129
|
|
|
160
130
|
### 常用
|
|
@@ -169,9 +139,6 @@ codecraft audit --config codecraft.config.json --fail-fast
|
|
|
169
139
|
codecraft plan --root . --with-gates
|
|
170
140
|
codecraft init --root . --name demo # dry-run
|
|
171
141
|
codecraft init --root . --name demo --apply # 写盘
|
|
172
|
-
codecraft install # 自动检测并安装
|
|
173
|
-
codecraft install --agent trae,claude --force
|
|
174
|
-
codecraft install --dry-run
|
|
175
142
|
```
|
|
176
143
|
|
|
177
144
|
### 参数
|
|
@@ -189,18 +156,13 @@ codecraft install --dry-run
|
|
|
189
156
|
| `--with-gates` | — | 关 | `plan` 执行 gates |
|
|
190
157
|
| `--name <name>` | 字符串 | — | `init` 的 app / package 名称;`--apply` 时必填 |
|
|
191
158
|
| `--apply` | — | 关 | `init` 写盘;已存在路径跳过,不覆盖 |
|
|
192
|
-
| `--agent <list>` | `auto` / `claude` / `trae` / `cursor` / `generic` | `auto` | `install` 目标,可逗号分隔 |
|
|
193
|
-
| `--global` | — | 关 | `install` 写入用户目录 |
|
|
194
|
-
| `--dest <path>` | 目录 | — | `install` 的显式目标 |
|
|
195
|
-
| `--force` | — | 关 | `install` 覆盖已有 codecraft 目录 |
|
|
196
|
-
| `--dry-run` | — | 关 | `install` 只输出计划,不写盘 |
|
|
197
159
|
|
|
198
160
|
### 退出码
|
|
199
161
|
|
|
200
162
|
| 码 | 含义 |
|
|
201
163
|
|---|---|
|
|
202
164
|
| `0` | 通过;warning 不阻断 |
|
|
203
|
-
| `1` | 有 error findings
|
|
165
|
+
| `1` | 有 error findings,或有 gate 失败 |
|
|
204
166
|
| `2` | 用法错误、root 不存在、配置非法 |
|
|
205
167
|
|
|
206
168
|
### 输出
|
|
@@ -265,7 +227,7 @@ docs/ 宪法、族卡、AI 契约、风格总图、模式、架构、AI
|
|
|
265
227
|
scenarios/ 新项目 / 旧项目审计 / 只出报告 / 重构评审
|
|
266
228
|
adapters/ generic / Trae / Claude 手动指针
|
|
267
229
|
prompts/ 输出格式
|
|
268
|
-
src/ + bin/ CLI
|
|
230
|
+
src/ + bin/ CLI(check / audit / plan / init)
|
|
269
231
|
tests/ node:test 用例
|
|
270
232
|
templates/ init --apply 的 app / library 骨架
|
|
271
233
|
evals/ golden tasks(人工回归)
|
|
@@ -281,14 +243,13 @@ ROADMAP.md 后续版本计划与交接说明
|
|
|
281
243
|
- 不按「感觉重要」升 major;`2.0` 不设时间表。
|
|
282
244
|
- 规则 ID `A1–A21` 稳定;增删规则属于 minor 以上的变更。
|
|
283
245
|
- 报告始终携带版本号;跨版本对比以 JSON 为准。
|
|
284
|
-
- 后续计划:[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)。
|
|
285
247
|
|
|
286
248
|
## FAQ
|
|
287
249
|
|
|
288
250
|
- **静态检查误报怎么办?** 在 `codecraft.config.json` 的 `ignore` 里登记 `rule` + `file` + `reason`,必要时加 `expires`;不要直接删规则。
|
|
289
251
|
- **`Files: 0` 是什么意思?** 没有扫到源文件,命令会输出警告并以 2 退出;用 `--pack app|library` 指定正确类型,或检查 `--root`。
|
|
290
|
-
-
|
|
291
|
-
- **怎么支持新的 agent?** 在 `codecraft.agents.json` 里加 `project` / `global` / `markers`,再跑 `--list-agents` 确认;不需要等 Codecraft 发版。
|
|
252
|
+
- **怎么装到指定 agent?** `npx skills add ultime-space/skills --skill codecraft`;交互选 agent,或用 `-a <agent>` 非交互。安装路径由 `skills` CLI 维护,本 skill 不枚举。
|
|
292
253
|
- **`check` / `audit` / `plan` 有什么区别?** `check` 只跑内置规则;`audit` 额外跑 config 里的 gates;`plan` 只读,把 findings 转成计划,默认不跑 gates。
|
|
293
254
|
- **为什么不自动跑 typecheck / test?** 门禁由项目提供,Codecraft 只编排,避免第二套实现。
|
|
294
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.
|
|
1
|
+
1.4.0
|
package/adapters/README.md
CHANGED
|
@@ -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
|
|
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)。
|
package/docs/07-ai-prompts.md
CHANGED
|
@@ -10,9 +10,11 @@
|
|
|
10
10
|
最快的方式:
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
npx
|
|
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
|
任务:<一句话>。
|
package/evals/golden-tasks.md
CHANGED
|
@@ -109,12 +109,10 @@
|
|
|
109
109
|
- 期望:命中 A21;只报文件与行号,不回显密钥;提示轮换。
|
|
110
110
|
- 失败信号:漏报;把密钥原文打印到报告;把普通变量名误报为密钥。
|
|
111
111
|
|
|
112
|
-
## 16.
|
|
112
|
+
## 16. 安装(已移出本 skill)
|
|
113
113
|
|
|
114
|
-
-
|
|
115
|
-
-
|
|
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.
|
|
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, listAgents, loadAgents, 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,19 +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 <name>[,<name>] Install target (default: auto; see --list-agents)
|
|
28
|
-
--list-agents List registered agents, paths and detection
|
|
29
|
-
--global Install into the home directory
|
|
30
|
-
--dest <path> Explicit destination (relative to root / home), e.g. .trae-cn/skills
|
|
31
|
-
--force Replace an existing codecraft skill directory
|
|
32
|
-
--dry-run Show the install plan without writing
|
|
33
25
|
--no-gates Skip gates in audit / plan
|
|
34
26
|
--with-gates Run configured gates in plan
|
|
35
27
|
--fail-fast Stop gates after the first failure
|
|
@@ -60,7 +52,7 @@ export function main(argv) {
|
|
|
60
52
|
}
|
|
61
53
|
|
|
62
54
|
const command = args.command || 'check'
|
|
63
|
-
if (!['check', 'audit', 'plan', 'init'
|
|
55
|
+
if (!['check', 'audit', 'plan', 'init'].includes(command)) {
|
|
64
56
|
console.error(`codecraft: unknown command "${command}"\n next: run codecraft --help`)
|
|
65
57
|
return 2
|
|
66
58
|
}
|
|
@@ -71,59 +63,6 @@ export function main(argv) {
|
|
|
71
63
|
return 2
|
|
72
64
|
}
|
|
73
65
|
|
|
74
|
-
if (args.listAgents) {
|
|
75
|
-
try {
|
|
76
|
-
const agents = listAgents(root, { global: args.global })
|
|
77
|
-
if (args.format === 'json') {
|
|
78
|
-
console.log(JSON.stringify({ version: VERSION, agents }, null, 2))
|
|
79
|
-
} else {
|
|
80
|
-
console.log(formatAgents(agents))
|
|
81
|
-
}
|
|
82
|
-
return 0
|
|
83
|
-
} catch (error) {
|
|
84
|
-
console.error(`codecraft: ${error.message}`)
|
|
85
|
-
return 2
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (command === 'install') {
|
|
90
|
-
let agents
|
|
91
|
-
let registry
|
|
92
|
-
try {
|
|
93
|
-
registry = loadAgents(root, { global: args.global })
|
|
94
|
-
agents = parseAgents(args.agent, [...registry.keys()])
|
|
95
|
-
} catch (error) {
|
|
96
|
-
console.error(`codecraft: ${error.message}`)
|
|
97
|
-
return 2
|
|
98
|
-
}
|
|
99
|
-
try {
|
|
100
|
-
const plan = buildInstallPlan(root, { agents, global: args.global, dest: args.dest, registry })
|
|
101
|
-
const applied = args.dryRun ? null : applyInstallPlan(plan, { force: args.force })
|
|
102
|
-
const meta = {
|
|
103
|
-
version: VERSION,
|
|
104
|
-
mode: 'install',
|
|
105
|
-
root,
|
|
106
|
-
scope: args.global ? 'global' : 'project',
|
|
107
|
-
agent: args.agent,
|
|
108
|
-
dest: args.dest || null,
|
|
109
|
-
dryRun: args.dryRun,
|
|
110
|
-
targets: plan.length,
|
|
111
|
-
created: applied?.created.length ?? 0,
|
|
112
|
-
replaced: applied?.replaced.length ?? 0,
|
|
113
|
-
skipped: applied?.skipped.length ?? 0,
|
|
114
|
-
}
|
|
115
|
-
if (args.format === 'json') {
|
|
116
|
-
console.log(JSON.stringify({ ...meta, plan, applied }, null, 2))
|
|
117
|
-
} else {
|
|
118
|
-
console.log(formatInstall(meta, plan, applied))
|
|
119
|
-
}
|
|
120
|
-
return plan.length ? 0 : 1
|
|
121
|
-
} catch (error) {
|
|
122
|
-
console.error(`codecraft: ${error.message}`)
|
|
123
|
-
return 1
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
66
|
let config
|
|
128
67
|
try {
|
|
129
68
|
config = loadConfig(root, args.config)
|
|
@@ -261,12 +200,6 @@ function parseArgs(argv) {
|
|
|
261
200
|
failFast: false,
|
|
262
201
|
name: '',
|
|
263
202
|
apply: false,
|
|
264
|
-
agent: 'auto',
|
|
265
|
-
global: false,
|
|
266
|
-
dest: '',
|
|
267
|
-
force: false,
|
|
268
|
-
dryRun: false,
|
|
269
|
-
listAgents: false,
|
|
270
203
|
format: 'md',
|
|
271
204
|
severity: 'warn',
|
|
272
205
|
ci: false,
|
|
@@ -283,13 +216,7 @@ function parseArgs(argv) {
|
|
|
283
216
|
else if (arg === '--with-gates') args.withGates = true
|
|
284
217
|
else if (arg === '--fail-fast') args.failFast = true
|
|
285
218
|
else if (arg === '--apply') args.apply = true
|
|
286
|
-
else if (arg === '--global') args.global = true
|
|
287
|
-
else if (arg === '--force') args.force = true
|
|
288
|
-
else if (arg === '--dry-run') args.dryRun = true
|
|
289
|
-
else if (arg === '--list-agents') args.listAgents = true
|
|
290
219
|
else if (arg === '--name') args.name = argv[++i] ?? ''
|
|
291
|
-
else if (arg === '--agent') args.agent = argv[++i] ?? 'auto'
|
|
292
|
-
else if (arg === '--dest') args.dest = argv[++i] ?? ''
|
|
293
220
|
else if (arg === '--root') args.root = argv[++i] ?? '.'
|
|
294
221
|
else if (arg === '--pack') args.pack = argv[++i] ?? 'auto'
|
|
295
222
|
else if (arg === '--config') args.config = argv[++i] ?? ''
|
|
@@ -521,67 +448,6 @@ function formatInit(meta, plan, applied) {
|
|
|
521
448
|
return lines.filter((line, index) => !(line === '' && lines[index - 1] === '')).join('\n')
|
|
522
449
|
}
|
|
523
450
|
|
|
524
|
-
function formatAgents(agents) {
|
|
525
|
-
const lines = [
|
|
526
|
-
'# Codecraft Agents',
|
|
527
|
-
'',
|
|
528
|
-
`- Version: ${VERSION}`,
|
|
529
|
-
'',
|
|
530
|
-
'| Name | Source | Project | Global | Detected |',
|
|
531
|
-
'|---|---|---|---|---|',
|
|
532
|
-
]
|
|
533
|
-
for (const agent of agents) {
|
|
534
|
-
const detected =
|
|
535
|
-
[agent.detected.project ? 'project' : '', agent.detected.global ? 'global' : ''].filter(Boolean).join(', ') ||
|
|
536
|
-
'—'
|
|
537
|
-
lines.push(`| ${agent.name} | ${agent.source} | ${agent.project} | ${agent.global} | ${detected} |`)
|
|
538
|
-
}
|
|
539
|
-
return lines.join('\n')
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
function formatInstall(meta, plan, applied) {
|
|
543
|
-
const lines = [
|
|
544
|
-
'# Codecraft Install',
|
|
545
|
-
'',
|
|
546
|
-
`- Version: ${meta.version}`,
|
|
547
|
-
`- Mode: install`,
|
|
548
|
-
`- Scope: ${meta.scope}`,
|
|
549
|
-
`- Agent: ${meta.agent}`,
|
|
550
|
-
`- Root: ${meta.root}`,
|
|
551
|
-
`- Dest: ${meta.dest ?? '(auto)'}`,
|
|
552
|
-
`- Dry run: ${meta.dryRun}`,
|
|
553
|
-
`- Targets: ${meta.targets}`,
|
|
554
|
-
]
|
|
555
|
-
|
|
556
|
-
if (applied) {
|
|
557
|
-
lines.push(
|
|
558
|
-
`- Created: ${meta.created}`,
|
|
559
|
-
`- Replaced: ${meta.replaced}`,
|
|
560
|
-
`- Skipped: ${meta.skipped}`,
|
|
561
|
-
)
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
if (!plan.length) {
|
|
565
|
-
lines.push('', '未找到可安装的目标;请用 `--agent` 指定,或先创建 agent 配置目录。')
|
|
566
|
-
} else {
|
|
567
|
-
lines.push('', '## Plan', '', '| Agent | Target | Action |', '|---|---|---|')
|
|
568
|
-
for (const entry of plan) {
|
|
569
|
-
lines.push(`| ${entry.agent} | ${entry.target} | ${entry.action} |`)
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
lines.push(
|
|
574
|
-
'',
|
|
575
|
-
'## Next',
|
|
576
|
-
'',
|
|
577
|
-
meta.dryRun
|
|
578
|
-
? '- Re-run without `--dry-run` to write the skill.'
|
|
579
|
-
: '- Restart the agent or reload skills;然后说:`用 Codecraft 审计本项目,只读,不改文件。`',
|
|
580
|
-
)
|
|
581
|
-
|
|
582
|
-
return lines.join('\n')
|
|
583
|
-
}
|
|
584
|
-
|
|
585
451
|
function header(meta) {
|
|
586
452
|
return [
|
|
587
453
|
`# Codecraft ${title(meta.mode)}`,
|
package/src/install.mjs
DELETED
|
@@ -1,172 +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 USER_AGENTS_FILE = path.join(os.homedir(), '.codecraft', 'agents.json')
|
|
11
|
-
const PROJECT_AGENTS_FILE = 'codecraft.agents.json'
|
|
12
|
-
|
|
13
|
-
const BUILTIN_AGENTS = {
|
|
14
|
-
generic: { project: '.agents/skills', global: '.agents/skills', markers: ['.agents'] },
|
|
15
|
-
claude: { project: '.claude/skills', global: '.claude/skills', markers: ['.claude', 'CLAUDE.md'] },
|
|
16
|
-
cursor: { project: '.cursor/skills', global: '.cursor/skills', markers: ['.cursor'] },
|
|
17
|
-
trae: { project: '.trae/skills', global: '.trae/skills', markers: ['.trae'] },
|
|
18
|
-
'trae-cn': { project: '.trae-cn/skills', global: '.trae-cn/skills', markers: ['.trae-cn'] },
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const AGENTS = Object.keys(BUILTIN_AGENTS)
|
|
22
|
-
|
|
23
|
-
export function loadAgents(root, options = {}) {
|
|
24
|
-
const registry = new Map(
|
|
25
|
-
Object.entries(BUILTIN_AGENTS).map(([name, definition]) => [name, { ...definition, source: 'builtin' }]),
|
|
26
|
-
)
|
|
27
|
-
mergeAgentFile(registry, options.userFile ?? USER_AGENTS_FILE, 'user')
|
|
28
|
-
mergeAgentFile(registry, options.projectFile ?? path.join(root, PROJECT_AGENTS_FILE), 'project')
|
|
29
|
-
return registry
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function listAgents(root, options = {}) {
|
|
33
|
-
const registry = loadAgents(root, options)
|
|
34
|
-
return [...registry.entries()].map(([name, definition]) => ({
|
|
35
|
-
name,
|
|
36
|
-
project: definition.project,
|
|
37
|
-
global: definition.global,
|
|
38
|
-
source: definition.source,
|
|
39
|
-
detected: {
|
|
40
|
-
project: isDetected(root, definition.markers),
|
|
41
|
-
global: isDetected(os.homedir(), definition.markers),
|
|
42
|
-
},
|
|
43
|
-
}))
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function parseAgents(value, names = AGENTS) {
|
|
47
|
-
if (!value || value === 'auto') return []
|
|
48
|
-
const agents = [...new Set(value.split(',').map((item) => item.trim()).filter(Boolean))]
|
|
49
|
-
const invalid = agents.filter((agent) => !names.includes(agent))
|
|
50
|
-
if (invalid.length) {
|
|
51
|
-
throw new Error(`unknown agent: ${invalid.join(', ')}(可选:${names.join(', ')};用 --list-agents 查看)`)
|
|
52
|
-
}
|
|
53
|
-
return agents
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function buildInstallPlan(root, options = {}) {
|
|
57
|
-
const registry = options.registry ?? loadAgents(root, options)
|
|
58
|
-
const base = options.global ? os.homedir() : path.resolve(root)
|
|
59
|
-
const names = [...registry.keys()]
|
|
60
|
-
const explicit = options.agents ?? []
|
|
61
|
-
const invalid = explicit.filter((agent) => !registry.has(agent))
|
|
62
|
-
if (invalid.length) {
|
|
63
|
-
throw new Error(`unknown agent: ${invalid.join(', ')}(可选:${names.join(', ')};用 --list-agents 查看)`)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const agents = explicit.length ? explicit : detectAgents(base, registry, options.global)
|
|
67
|
-
const destinations = options.dest
|
|
68
|
-
? [{ agent: 'custom', dir: path.resolve(base, options.dest) }]
|
|
69
|
-
: agents.map((agent) => {
|
|
70
|
-
const definition = registry.get(agent)
|
|
71
|
-
return { agent, dir: path.join(base, options.global ? definition.global : definition.project) }
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
assertSource()
|
|
75
|
-
|
|
76
|
-
const byTarget = new Map()
|
|
77
|
-
for (const { agent, dir } of destinations) {
|
|
78
|
-
const target = path.join(dir, 'codecraft')
|
|
79
|
-
if (!byTarget.has(target)) byTarget.set(target, { target, agents: [] })
|
|
80
|
-
byTarget.get(target).agents.push(agent)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return [...byTarget.values()].map(({ target, agents: targetAgents }) => {
|
|
84
|
-
const exists = fs.existsSync(target)
|
|
85
|
-
return {
|
|
86
|
-
agent: targetAgents.join(','),
|
|
87
|
-
agents: targetAgents,
|
|
88
|
-
target,
|
|
89
|
-
exists,
|
|
90
|
-
action: exists ? 'exists' : 'create',
|
|
91
|
-
}
|
|
92
|
-
})
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function applyInstallPlan(plan, options = {}) {
|
|
96
|
-
const created = []
|
|
97
|
-
const skipped = []
|
|
98
|
-
const replaced = []
|
|
99
|
-
|
|
100
|
-
for (const entry of plan) {
|
|
101
|
-
if (entry.exists && !options.force) {
|
|
102
|
-
skipped.push(entry.target)
|
|
103
|
-
continue
|
|
104
|
-
}
|
|
105
|
-
if (entry.exists) {
|
|
106
|
-
fs.rmSync(entry.target, { recursive: true, force: true })
|
|
107
|
-
replaced.push(entry.target)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
fs.mkdirSync(entry.target, { recursive: true })
|
|
111
|
-
for (const file of SKILL_FILES) {
|
|
112
|
-
fs.copyFileSync(path.join(PACKAGE_ROOT, file), path.join(entry.target, file))
|
|
113
|
-
}
|
|
114
|
-
for (const dir of SKILL_DIRS) {
|
|
115
|
-
fs.cpSync(path.join(PACKAGE_ROOT, dir), path.join(entry.target, dir), { recursive: true })
|
|
116
|
-
}
|
|
117
|
-
created.push(entry.target)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return { created, skipped, replaced }
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function mergeAgentFile(registry, file, source) {
|
|
124
|
-
if (!fs.existsSync(file)) return
|
|
125
|
-
|
|
126
|
-
let parsed
|
|
127
|
-
try {
|
|
128
|
-
parsed = JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
129
|
-
} catch (error) {
|
|
130
|
-
throw new Error(`invalid agents file ${file}: ${error.message} (fix the JSON syntax or remove the file)`)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const agents = parsed?.agents
|
|
134
|
-
if (!agents || typeof agents !== 'object' || Array.isArray(agents)) {
|
|
135
|
-
throw new Error(`invalid agents file ${file}: expected { "agents": { "<name>": { project, global, markers } } }`)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
for (const [name, definition] of Object.entries(agents)) {
|
|
139
|
-
if (!/^[a-z0-9][a-z0-9-]*$/.test(name)) {
|
|
140
|
-
throw new Error(`invalid agents file ${file}: agent name "${name}" must be lowercase kebab-case`)
|
|
141
|
-
}
|
|
142
|
-
const project = typeof definition?.project === 'string' ? definition.project.trim() : ''
|
|
143
|
-
if (!project) {
|
|
144
|
-
throw new Error(`invalid agents file ${file}: agent "${name}" needs a non-empty "project" path`)
|
|
145
|
-
}
|
|
146
|
-
const global = typeof definition?.global === 'string' && definition.global.trim() ? definition.global.trim() : project
|
|
147
|
-
const markers = Array.isArray(definition?.markers)
|
|
148
|
-
? definition.markers.filter((marker) => typeof marker === 'string' && marker.trim())
|
|
149
|
-
: []
|
|
150
|
-
registry.set(name, { project, global, markers, source })
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function detectAgents(base, registry, global) {
|
|
155
|
-
const found = [...registry.entries()]
|
|
156
|
-
.filter(([, definition]) => isDetected(base, definition.markers))
|
|
157
|
-
.map(([name]) => name)
|
|
158
|
-
if (global) return found.length ? found : ['generic']
|
|
159
|
-
return [...new Set([...found, 'generic'])]
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function isDetected(base, markers) {
|
|
163
|
-
return markers.some((marker) => fs.existsSync(path.join(base, marker)))
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function assertSource() {
|
|
167
|
-
for (const file of SKILL_FILES) {
|
|
168
|
-
if (!fs.existsSync(path.join(PACKAGE_ROOT, file))) {
|
|
169
|
-
throw new Error(`skill source is incomplete: missing ${file}`)
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|