@ultime/codecraft 1.2.0 → 1.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.
- package/CHANGELOG.md +7 -0
- package/README.md +12 -6
- package/VERSION +1 -1
- package/package.json +1 -1
- package/src/cli.mjs +82 -4
- package/src/install.mjs +6 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.1 - 2026-10-27
|
|
4
|
+
|
|
5
|
+
- 修复 pack 自动判定:识别 Nuxt 2 的 `pages/`、Nuxt 3 的 `app/`、uni-app 的 `manifest.json + pages.json`、Taro / uni-app 依赖,以及 workspace 内的嵌套应用。
|
|
6
|
+
- 新增 `trae-cn` 安装目标(`~/.trae-cn/skills`);`--agent` 顺序改为 generic / claude / cursor / trae / trae-cn,不再默认优先 Trae。
|
|
7
|
+
- 扫描 0 个源文件时输出警告并以 2 退出,避免把空扫描当成通过。
|
|
8
|
+
- README 补充 agent 列表、`--dest`、`Files: 0` 与 Trae CN 的说明。
|
|
9
|
+
|
|
3
10
|
## 1.2.0 - 2026-10-27
|
|
4
11
|
|
|
5
12
|
- 新增 `ignore` 例外配置(A12):`rule` / `file` / `reason` 必填,`expires` 可选;支持 `*` / `**` / `?` 路径匹配;安全规则 `A21` 不可豁免。
|
package/README.md
CHANGED
|
@@ -58,15 +58,19 @@ npx skills-npm setup
|
|
|
58
58
|
npx @ultime/codecraft install
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
自动检测当前项目的 agent,把 `SKILL.md`、`docs/`、`scenarios/`、`prompts/` 复制到 `.agents/skills/codecraft` 和对应 agent 目录(`.claude/skills`、`.trae/skills`、`.
|
|
61
|
+
自动检测当前项目的 agent,把 `SKILL.md`、`docs/`、`scenarios/`、`prompts/` 复制到 `.agents/skills/codecraft` 和对应 agent 目录(`.claude/skills`、`.cursor/skills`、`.trae/skills`、`.trae-cn/skills`)。不传 `--agent` 时只按检测结果安装,不优先任何一家。
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
npx @ultime/codecraft install --agent
|
|
65
|
-
npx @ultime/codecraft install --global --agent claude
|
|
66
|
-
npx @ultime/codecraft install --
|
|
67
|
-
npx @ultime/codecraft install --
|
|
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 # 覆盖已有目录
|
|
68
70
|
```
|
|
69
71
|
|
|
72
|
+
可选 agent:`generic` / `claude` / `cursor` / `trae` / `trae-cn`;未知目录用 `--dest`。
|
|
73
|
+
|
|
70
74
|
### 方式 C:手动指针
|
|
71
75
|
|
|
72
76
|
不复制文件,只在项目规则里留一行:
|
|
@@ -264,9 +268,11 @@ ROADMAP.md 后续版本计划与交接说明
|
|
|
264
268
|
## FAQ
|
|
265
269
|
|
|
266
270
|
- **静态检查误报怎么办?** 在 `codecraft.config.json` 的 `ignore` 里登记 `rule` + `file` + `reason`,必要时加 `expires`;不要直接删规则。
|
|
271
|
+
- **`Files: 0` 是什么意思?** 没有扫到源文件,命令会输出警告并以 2 退出;用 `--pack app|library` 指定正确类型,或检查 `--root`。
|
|
272
|
+
- **Trae CN 装到哪?** 用 `--agent trae-cn`(`~/.trae-cn/skills`);未知目录用 `--dest`。
|
|
267
273
|
- **`check` / `audit` / `plan` 有什么区别?** `check` 只跑内置规则;`audit` 额外跑 config 里的 gates;`plan` 只读,把 findings 转成计划,默认不跑 gates。
|
|
268
274
|
- **为什么不自动跑 typecheck / test?** 门禁由项目提供,Codecraft 只编排,避免第二套实现。
|
|
269
|
-
- **monorepo 为什么被判成 library?** 有 `
|
|
275
|
+
- **monorepo 为什么被判成 app / library?** 有 `pages/`、`app/`、`manifest.json + pages.json`、Taro / uni-app 依赖或嵌套应用时按 `app`;有 `packages/` 或 `src/` 时按 `library`;仍不确定就显式传 `--pack`。
|
|
270
276
|
- **支持哪些文件?** 只扫 JS/TS 相关扩展名与 `package.json`;其它语言不覆盖。
|
|
271
277
|
|
|
272
278
|
## 边界
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.2.
|
|
1
|
+
1.2.1
|
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -24,9 +24,9 @@ Options:
|
|
|
24
24
|
--root <path> Project root (default: cwd)
|
|
25
25
|
--pack app|library|design-system|auto Rule pack (default: auto)
|
|
26
26
|
--config <path> Config file (default: codecraft.config.json)
|
|
27
|
-
--agent auto|claude|trae|
|
|
27
|
+
--agent auto|generic|claude|cursor|trae|trae-cn Install target, comma-separated (default: auto)
|
|
28
28
|
--global Install into the home directory
|
|
29
|
-
--dest <path> Explicit destination (relative to root / home)
|
|
29
|
+
--dest <path> Explicit destination (relative to root / home), e.g. .trae-cn/skills
|
|
30
30
|
--force Replace an existing codecraft skill directory
|
|
31
31
|
--dry-run Show the install plan without writing
|
|
32
32
|
--no-gates Skip gates in audit / plan
|
|
@@ -154,6 +154,11 @@ export function main(argv) {
|
|
|
154
154
|
const findings = result.findings.map((finding) => ({ ...finding, severity }))
|
|
155
155
|
const filtered = applyIgnores(findings, config.config.ignore ?? [])
|
|
156
156
|
const activeFindings = filtered.findings
|
|
157
|
+
const warnings = []
|
|
158
|
+
if (result.files === 0 && result.findings.length === 0) {
|
|
159
|
+
warnings.push(`0 source files scanned under ${root}; pass --pack app|library or check --root`)
|
|
160
|
+
console.error(`codecraft: warning: ${warnings[0]}`)
|
|
161
|
+
}
|
|
157
162
|
const gates = shouldRunGates(command, args)
|
|
158
163
|
? runGates(root, config.config.gates ?? [], { failFast: args.failFast })
|
|
159
164
|
: []
|
|
@@ -170,6 +175,7 @@ export function main(argv) {
|
|
|
170
175
|
findings: activeFindings.length,
|
|
171
176
|
ignored: filtered.ignored.length,
|
|
172
177
|
expired: filtered.expired.length,
|
|
178
|
+
warnings,
|
|
173
179
|
plan: plan.length,
|
|
174
180
|
gates: gates.length,
|
|
175
181
|
}
|
|
@@ -207,6 +213,7 @@ export function main(argv) {
|
|
|
207
213
|
const failed =
|
|
208
214
|
activeFindings.some((finding) => finding.severity === 'error') ||
|
|
209
215
|
gates.some((gate) => gate.exitCode !== 0)
|
|
216
|
+
if (warnings.length) return 2
|
|
210
217
|
return failed ? 1 : 0
|
|
211
218
|
}
|
|
212
219
|
|
|
@@ -281,13 +288,83 @@ function parseArgs(argv) {
|
|
|
281
288
|
return args
|
|
282
289
|
}
|
|
283
290
|
|
|
284
|
-
function detectPack(root) {
|
|
285
|
-
if (isDir(path.join(root, 'apps'))
|
|
291
|
+
export function detectPack(root) {
|
|
292
|
+
if (isDir(path.join(root, 'apps'))) return 'app'
|
|
293
|
+
if (hasAppMarkers(root)) return 'app'
|
|
286
294
|
if (isDir(path.join(root, 'packages'))) return 'library'
|
|
295
|
+
if (hasNestedApp(root)) return 'app'
|
|
296
|
+
if (isDir(path.join(root, 'src'))) return 'library'
|
|
287
297
|
if (fs.existsSync(path.join(root, 'package.json'))) return 'library'
|
|
288
298
|
return 'app'
|
|
289
299
|
}
|
|
290
300
|
|
|
301
|
+
const APP_DIR_MARKERS = ['src/pages', 'pages', 'app', 'src/app']
|
|
302
|
+
const APP_FILE_MARKERS = [
|
|
303
|
+
'nuxt.config.js',
|
|
304
|
+
'nuxt.config.ts',
|
|
305
|
+
'nuxt.config.mjs',
|
|
306
|
+
'app.config.js',
|
|
307
|
+
'app.config.ts',
|
|
308
|
+
'src/app.config.js',
|
|
309
|
+
'src/app.config.ts',
|
|
310
|
+
'taro.config.js',
|
|
311
|
+
'taro.config.ts',
|
|
312
|
+
]
|
|
313
|
+
const APP_DEPS = ['nuxt', '@tarojs/taro', '@dcloudio/uni-app', 'uni-app', 'next']
|
|
314
|
+
|
|
315
|
+
function hasAppMarkers(root) {
|
|
316
|
+
if (APP_DIR_MARKERS.some((dir) => isDir(path.join(root, dir)))) return true
|
|
317
|
+
if (APP_FILE_MARKERS.some((file) => isFile(path.join(root, file)))) return true
|
|
318
|
+
if (hasUniAppFiles(root)) return true
|
|
319
|
+
return hasAppDependency(root)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function hasNestedApp(root) {
|
|
323
|
+
let entries
|
|
324
|
+
try {
|
|
325
|
+
entries = fs.readdirSync(root, { withFileTypes: true })
|
|
326
|
+
} catch {
|
|
327
|
+
return false
|
|
328
|
+
}
|
|
329
|
+
for (const entry of entries) {
|
|
330
|
+
if (!entry.isDirectory()) continue
|
|
331
|
+
if (['node_modules', 'dist', 'build', 'target', '.git', '.nuxt', '.output', 'coverage'].includes(entry.name)) {
|
|
332
|
+
continue
|
|
333
|
+
}
|
|
334
|
+
const child = path.join(root, entry.name)
|
|
335
|
+
if (APP_DIR_MARKERS.some((dir) => isDir(path.join(child, dir)))) return true
|
|
336
|
+
if (APP_FILE_MARKERS.some((file) => isFile(path.join(child, file)))) return true
|
|
337
|
+
if (hasUniAppFiles(child)) return true
|
|
338
|
+
if (hasAppDependency(child)) return true
|
|
339
|
+
}
|
|
340
|
+
return false
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function hasUniAppFiles(root) {
|
|
344
|
+
const atRoot = isFile(path.join(root, 'manifest.json')) && isFile(path.join(root, 'pages.json'))
|
|
345
|
+
const atSrc = isFile(path.join(root, 'src', 'manifest.json')) && isFile(path.join(root, 'src', 'pages.json'))
|
|
346
|
+
return atRoot || atSrc
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function hasAppDependency(root) {
|
|
350
|
+
let manifest
|
|
351
|
+
try {
|
|
352
|
+
manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'))
|
|
353
|
+
} catch {
|
|
354
|
+
return false
|
|
355
|
+
}
|
|
356
|
+
const deps = { ...(manifest.dependencies ?? {}), ...(manifest.devDependencies ?? {}) }
|
|
357
|
+
return APP_DEPS.some((dep) => dep in deps)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function isFile(target) {
|
|
361
|
+
try {
|
|
362
|
+
return fs.statSync(target).isFile()
|
|
363
|
+
} catch {
|
|
364
|
+
return false
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
291
368
|
function fail(message) {
|
|
292
369
|
console.error(`codecraft: ${message}\n\n${HELP}`)
|
|
293
370
|
process.exit(2)
|
|
@@ -482,6 +559,7 @@ function header(meta) {
|
|
|
482
559
|
`- Findings: ${meta.findings}`,
|
|
483
560
|
`- Ignored: ${meta.ignored ?? 0}`,
|
|
484
561
|
`- Expired exceptions: ${meta.expired ?? 0}`,
|
|
562
|
+
`- Warnings: ${meta.warnings?.length ?? 0}`,
|
|
485
563
|
`- Gates: ${meta.gates}`,
|
|
486
564
|
'',
|
|
487
565
|
]
|
package/src/install.mjs
CHANGED
|
@@ -8,17 +8,19 @@ const SKILL_FILES = ['SKILL.md']
|
|
|
8
8
|
const SKILL_DIRS = ['docs', 'scenarios', 'prompts']
|
|
9
9
|
|
|
10
10
|
const AGENT_DIRS = {
|
|
11
|
+
generic: '.agents/skills',
|
|
11
12
|
claude: '.claude/skills',
|
|
12
|
-
trae: '.trae/skills',
|
|
13
13
|
cursor: '.cursor/skills',
|
|
14
|
-
|
|
14
|
+
trae: '.trae/skills',
|
|
15
|
+
'trae-cn': '.trae-cn/skills',
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
const AGENT_MARKERS = {
|
|
19
|
+
generic: ['.agents'],
|
|
18
20
|
claude: ['.claude', 'CLAUDE.md'],
|
|
19
|
-
trae: ['.trae'],
|
|
20
21
|
cursor: ['.cursor'],
|
|
21
|
-
|
|
22
|
+
trae: ['.trae'],
|
|
23
|
+
'trae-cn': ['.trae-cn'],
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
export const AGENTS = Object.keys(AGENT_DIRS)
|