@ultime/codecraft 1.3.0 → 1.5.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 +31 -0
- package/CONTRIBUTING.md +3 -0
- package/README.md +51 -81
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/adapters/README.md +3 -4
- package/codecraft.config.example.json +5 -1
- package/docs/00-constitution.md +2 -2
- package/docs/02-ai-contract.md +12 -1
- package/docs/04-gates.md +21 -6
- package/docs/07-ai-prompts.md +9 -8
- package/docs/08-audit-contract.md +141 -0
- package/evals/cases.json +110 -0
- package/evals/fixtures/a11-marker/packages/core/dist/tokens.css +4 -0
- package/evals/fixtures/a11-marker/packages/core/package.json +1 -0
- package/evals/fixtures/a11-marker/packages/core/src/index.ts +1 -0
- package/evals/fixtures/a17-boundaries/codecraft.config.json +5 -0
- package/evals/fixtures/a17-boundaries/src/pages/home.ts +1 -0
- package/evals/fixtures/a17-default/src/pages/home.ts +1 -0
- package/evals/fixtures/a17-generated-data/src/icons/icons.ts +2 -0
- package/evals/fixtures/a17-generated-data/src/pages/home.ts +3 -0
- package/evals/fixtures/a17-generated-header/src/pages/home.ts +5 -0
- package/evals/fixtures/a17-generated-header/src/types/project.ts +4 -0
- package/evals/fixtures/a17-local/src/pages/home.ts +4 -0
- package/evals/fixtures/a17-local/src/types/local.ts +3 -0
- package/evals/fixtures/a17-local/src/utils/format.ts +3 -0
- package/evals/fixtures/a2-declared/packages/core/package.json +6 -0
- package/evals/fixtures/a2-declared/packages/core/src/index.ts +3 -0
- package/evals/fixtures/a2-declared/packages/kit/package.json +1 -0
- package/evals/fixtures/a2-workspace/packages/core/package.json +1 -0
- package/evals/fixtures/a2-workspace/packages/core/src/index.ts +3 -0
- package/evals/fixtures/a2-workspace/packages/kit/package.json +1 -0
- package/evals/fixtures/a21-doc-paths/packages/ui/README.md +5 -0
- package/evals/fixtures/a21-doc-paths/packages/ui/package.json +1 -0
- package/evals/fixtures/a21-doc-paths/packages/ui/src/index.ts +1 -0
- package/evals/fixtures/a21-placeholders/.env +2 -0
- package/evals/fixtures/a21-placeholders/server/application.yml +9 -0
- package/evals/fixtures/a21-placeholders/src/pages/home.ts +1 -0
- package/evals/fixtures/a21-plaintext/application.yml +5 -0
- package/evals/fixtures/a21-plaintext/src/main/java/Ali.java +3 -0
- package/evals/fixtures/a21-plaintext/src/pages/home.ts +1 -0
- package/evals/fixtures/a6-clean/src/pages/handled.ts +8 -0
- package/evals/fixtures/a6-empty-catch/src/pages/empty.ts +3 -0
- package/evals/fixtures/ignore-active/codecraft.config.json +5 -0
- package/evals/fixtures/ignore-active/src/pages/home.ts +1 -0
- package/evals/fixtures/ignore-expired/codecraft.config.json +5 -0
- package/evals/fixtures/ignore-expired/src/pages/home.ts +1 -0
- package/evals/fixtures/library-a11/dist/tokens.css +3 -0
- package/evals/fixtures/library-a11/package.json +4 -0
- package/evals/fixtures/library-a11/src/index.ts +1 -0
- package/evals/fixtures/zero-files/package.json +4 -0
- package/evals/golden-tasks.md +9 -5
- package/evals/run.mjs +75 -0
- package/package.json +6 -4
- package/prompts/output.md +15 -4
- package/scenarios/audit-existing.md +15 -8
- package/src/check.mjs +290 -40
- package/src/cli.mjs +159 -143
- package/src/gates.mjs +28 -1
- package/src/plan.mjs +2 -0
- package/adapters/claude/SKILL.md +0 -42
- package/adapters/trae/SKILL.md +0 -42
- package/src/install.mjs +0 -172
package/evals/cases.json
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "a17-default",
|
|
4
|
+
"root": "a17-default",
|
|
5
|
+
"args": ["--pack", "app", "--format", "json"],
|
|
6
|
+
"expect": { "exit": 0, "findings": { "A17": 1 } }
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "a17-boundaries",
|
|
10
|
+
"root": "a17-boundaries",
|
|
11
|
+
"args": ["--pack", "app", "--format", "json"],
|
|
12
|
+
"expect": { "exit": 0, "findings": { "A17": 1 } }
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "a6-empty-catch",
|
|
16
|
+
"root": "a6-empty-catch",
|
|
17
|
+
"args": ["--pack", "app", "--format", "json"],
|
|
18
|
+
"expect": { "exit": 0, "findings": { "A6": 1 } }
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "ignore-active",
|
|
22
|
+
"root": "ignore-active",
|
|
23
|
+
"args": ["--pack", "app", "--format", "json"],
|
|
24
|
+
"expect": { "exit": 0, "findings": {}, "ignored": 1 }
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "ignore-expired",
|
|
28
|
+
"root": "ignore-expired",
|
|
29
|
+
"args": ["--pack", "app", "--format", "json", "--ci"],
|
|
30
|
+
"expect": { "exit": 1, "findings": { "A17": 1 }, "expired": 1 }
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "library-a11",
|
|
34
|
+
"root": "library-a11",
|
|
35
|
+
"args": ["--pack", "library", "--format", "json"],
|
|
36
|
+
"expect": { "exit": 0, "findings": { "A11": 1 } }
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "zero-files",
|
|
40
|
+
"root": "zero-files",
|
|
41
|
+
"args": ["--pack", "library", "--format", "json"],
|
|
42
|
+
"expect": { "exit": 2, "findings": {}, "warnings": 1 }
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "a17-generated-header",
|
|
46
|
+
"root": "a17-generated-header",
|
|
47
|
+
"args": ["--pack", "app", "--format", "json"],
|
|
48
|
+
"expect": { "exit": 0, "findings": { "A17": 1 } }
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "a21-plaintext",
|
|
52
|
+
"root": "a21-plaintext",
|
|
53
|
+
"args": ["--pack", "app", "--format", "json"],
|
|
54
|
+
"expect": { "exit": 0, "findings": { "A21": 2 } }
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "a2-workspace",
|
|
58
|
+
"root": "a2-workspace",
|
|
59
|
+
"args": ["--pack", "library", "--format", "json"],
|
|
60
|
+
"expect": { "exit": 0, "findings": { "A2": 1 } }
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "a2-declared",
|
|
64
|
+
"root": "a2-declared",
|
|
65
|
+
"args": ["--pack", "library", "--format", "json"],
|
|
66
|
+
"expect": { "exit": 0, "findings": {} }
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "a6-clean",
|
|
70
|
+
"root": "a6-clean",
|
|
71
|
+
"args": ["--pack", "app", "--format", "json"],
|
|
72
|
+
"expect": { "exit": 0, "findings": {} }
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "a11-marker",
|
|
76
|
+
"root": "a11-marker",
|
|
77
|
+
"args": ["--pack", "library", "--format", "json"],
|
|
78
|
+
"expect": { "exit": 0, "findings": {} }
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "a17-local",
|
|
82
|
+
"root": "a17-local",
|
|
83
|
+
"args": ["--pack", "app", "--format", "json"],
|
|
84
|
+
"expect": { "exit": 0, "findings": {} }
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"name": "a17-generated-data",
|
|
88
|
+
"root": "a17-generated-data",
|
|
89
|
+
"args": ["--pack", "app", "--format", "json"],
|
|
90
|
+
"expect": { "exit": 0, "findings": {} }
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "a21-placeholders",
|
|
94
|
+
"root": "a21-placeholders",
|
|
95
|
+
"args": ["--pack", "app", "--format", "json"],
|
|
96
|
+
"expect": { "exit": 0, "findings": {} }
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "a21-doc-paths",
|
|
100
|
+
"root": "a21-doc-paths",
|
|
101
|
+
"args": ["--pack", "design-system", "--format", "json"],
|
|
102
|
+
"expect": { "exit": 0, "findings": { "A21": 2 } }
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"name": "sarif",
|
|
106
|
+
"root": "a17-default",
|
|
107
|
+
"args": ["--pack", "app", "--format", "sarif"],
|
|
108
|
+
"expect": { "exit": 0, "sarif": { "version": "2.1.0", "rule": "A17" } }
|
|
109
|
+
}
|
|
110
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "name": "@x/core" }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const version = '1.0.0'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import type { Project } from '@/types/project'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { User } from '@/external/user'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "name": "@x/kit" }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "name": "@x/core" }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "name": "@x/kit" }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "name": "@x/ui" }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Button } from './button'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const ready = true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const ready = true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { User } from '@/external/user'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { User } from '@/external/user'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const value = 1
|
package/evals/golden-tasks.md
CHANGED
|
@@ -109,12 +109,16 @@
|
|
|
109
109
|
- 期望:命中 A21;只报文件与行号,不回显密钥;提示轮换。
|
|
110
110
|
- 失败信号:漏报;把密钥原文打印到报告;把普通变量名误报为密钥。
|
|
111
111
|
|
|
112
|
-
## 16.
|
|
112
|
+
## 16. 安装(已移出本 skill)
|
|
113
113
|
|
|
114
|
-
-
|
|
115
|
-
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
- 安装由 `skills` CLI 负责:`npx skills add ultime-space/skills --skill codecraft`。
|
|
115
|
+
- 不在 Codecraft 规则评估范围内;本文件不再评估安装器行为。
|
|
116
|
+
|
|
117
|
+
## 17. 审计结论一致性
|
|
118
|
+
|
|
119
|
+
- 输入:一个项目同时存在「多处 `code === 200`」「生成物无再生成 diff」「CLI `0 findings`」。
|
|
120
|
+
- 期望:A4 判 `warn`;A11 判 `warn`;报告写明 `0 findings` 只是静态未命中;计数带检索命令。
|
|
121
|
+
- 失败信号:A4 / A11 判 `pass`;把静态检查当全通过;结论没有文件行号或命令。
|
|
118
122
|
|
|
119
123
|
## 评分
|
|
120
124
|
|
package/evals/run.mjs
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import { spawnSync } from 'node:child_process'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
|
|
5
|
+
const BIN = fileURLToPath(new URL('../bin/codecraft.mjs', import.meta.url))
|
|
6
|
+
const CASES = JSON.parse(fs.readFileSync(new URL('./cases.json', import.meta.url), 'utf8'))
|
|
7
|
+
|
|
8
|
+
let failed = 0
|
|
9
|
+
|
|
10
|
+
for (const testCase of CASES) {
|
|
11
|
+
const root = fileURLToPath(new URL(`./fixtures/${testCase.root}/`, import.meta.url))
|
|
12
|
+
const args = ['check', '--root', root, ...(testCase.args ?? [])]
|
|
13
|
+
const result = spawnSync(process.execPath, [BIN, ...args], { encoding: 'utf8' })
|
|
14
|
+
const problems = []
|
|
15
|
+
|
|
16
|
+
if (result.status !== testCase.expect.exit) {
|
|
17
|
+
problems.push(`exit ${result.status}, expected ${testCase.expect.exit}`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (testCase.expect.sarif) {
|
|
21
|
+
try {
|
|
22
|
+
const report = JSON.parse(result.stdout)
|
|
23
|
+
if (report.version !== testCase.expect.sarif.version) {
|
|
24
|
+
problems.push(`sarif version ${report.version}, expected ${testCase.expect.sarif.version}`)
|
|
25
|
+
}
|
|
26
|
+
if (report.runs?.[0]?.results?.[0]?.ruleId !== testCase.expect.sarif.rule) {
|
|
27
|
+
problems.push(`sarif result ${report.runs?.[0]?.results?.[0]?.ruleId}, expected ${testCase.expect.sarif.rule}`)
|
|
28
|
+
}
|
|
29
|
+
} catch (error) {
|
|
30
|
+
problems.push(`invalid sarif: ${error.message}`)
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
let report
|
|
34
|
+
try {
|
|
35
|
+
report = JSON.parse(result.stdout)
|
|
36
|
+
} catch (error) {
|
|
37
|
+
problems.push(`invalid json: ${error.message}`)
|
|
38
|
+
}
|
|
39
|
+
if (report) {
|
|
40
|
+
const expected = testCase.expect.findings ?? {}
|
|
41
|
+
const total = Object.values(expected).reduce((sum, count) => sum + count, 0)
|
|
42
|
+
if (report.findings.length !== total) {
|
|
43
|
+
problems.push(`findings ${report.findings.length}, expected ${total}`)
|
|
44
|
+
}
|
|
45
|
+
const counts = {}
|
|
46
|
+
for (const finding of report.findings) counts[finding.rule] = (counts[finding.rule] ?? 0) + 1
|
|
47
|
+
for (const [rule, count] of Object.entries(expected)) {
|
|
48
|
+
if ((counts[rule] ?? 0) !== count) problems.push(`${rule} ${counts[rule] ?? 0}, expected ${count}`)
|
|
49
|
+
}
|
|
50
|
+
for (const key of ['ignored', 'expired']) {
|
|
51
|
+
const actual = Array.isArray(report[key]) ? report[key].length : report[key]
|
|
52
|
+
if (testCase.expect[key] != null && actual !== testCase.expect[key]) {
|
|
53
|
+
problems.push(`${key} ${actual}, expected ${testCase.expect[key]}`)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (testCase.expect.warnings != null && report.warnings?.length !== testCase.expect.warnings) {
|
|
57
|
+
problems.push(`warnings ${report.warnings?.length ?? 0}, expected ${testCase.expect.warnings}`)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (problems.length) {
|
|
63
|
+
failed += 1
|
|
64
|
+
console.error(`✗ ${testCase.name}: ${problems.join('; ')}`)
|
|
65
|
+
if (result.stderr.trim()) console.error(result.stderr.trim().split('\n').slice(0, 4).join('\n'))
|
|
66
|
+
} else {
|
|
67
|
+
console.log(`✓ ${testCase.name}`)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (failed) {
|
|
72
|
+
console.error(`\n${failed}/${CASES.length} eval cases failed`)
|
|
73
|
+
process.exit(1)
|
|
74
|
+
}
|
|
75
|
+
console.log(`\n${CASES.length}/${CASES.length} eval cases passed`)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultime/codecraft",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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
|
},
|
|
@@ -50,7 +50,9 @@
|
|
|
50
50
|
"type": "module",
|
|
51
51
|
"scripts": {
|
|
52
52
|
"test": "node --test tests/*.test.mjs",
|
|
53
|
-
"
|
|
53
|
+
"eval": "node evals/run.mjs",
|
|
54
|
+
"self-check": "node bin/codecraft.mjs check --root . --pack design-system --exclude \"evals/fixtures/**\" --ci",
|
|
55
|
+
"prepublishOnly": "npm test && npm run self-check"
|
|
54
56
|
},
|
|
55
57
|
"engines": {
|
|
56
58
|
"node": ">=20"
|
package/prompts/output.md
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
# Output Format(输出格式)
|
|
2
2
|
|
|
3
|
-
默认 Markdown;JSON 仅用于 CLI / CI。
|
|
3
|
+
默认 Markdown;JSON 仅用于 CLI / CI。Audit 还必须满足 `docs/08-audit-contract.md`。
|
|
4
4
|
|
|
5
5
|
## Markdown
|
|
6
6
|
|
|
7
7
|
```md
|
|
8
8
|
## 结论
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## 范围与方法
|
|
11
|
+
|
|
12
|
+
## 逐条判定(A1–A21)
|
|
13
|
+
|
|
14
|
+
| ID | 判定 | 证据 | 局限 |
|
|
15
|
+
|---|---|---|---|
|
|
16
|
+
| A1 | pass | `src/pages/home/index.vue:1` | 仅抽样 |
|
|
11
17
|
|
|
12
18
|
## 差距 / 风险
|
|
13
19
|
|
|
@@ -18,10 +24,15 @@
|
|
|
18
24
|
## 下一步
|
|
19
25
|
```
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
## 规则
|
|
22
28
|
|
|
23
|
-
-
|
|
29
|
+
- A1–A21 全部出现;不适用写 `na` 并说明原因。
|
|
30
|
+
- 每条结论引用 A-ID,并给文件行号或可复现命令;计数必须带检索式。
|
|
31
|
+
- `pass` 必须有证据;只有静态检查通过时写 `pass(静态)`,不能替代人工抽样。
|
|
32
|
+
- `codecraft check` 的 0 findings 只能写「静态检查未命中」;同时写明 A17 / A21 的覆盖盲区。
|
|
33
|
+
- 出结论前做一致性检查:A4 / A8 / A11 / A17 / A21 与证据矛盾时必须降级。
|
|
24
34
|
- 不写敏感信息;示例用占位符。
|
|
25
35
|
- 英文术语首次出现用 `English(中文)`。
|
|
26
36
|
- 不把“计划”写成“已完成”。
|
|
27
37
|
- Audit / Plan 模式只输出报告,不改文件。
|
|
38
|
+
- 计划每阶段可回滚,验收用命令或检索结果,不写时间估算。
|
|
@@ -15,17 +15,21 @@
|
|
|
15
15
|
|
|
16
16
|
## 步骤
|
|
17
17
|
|
|
18
|
-
1.
|
|
19
|
-
2.
|
|
20
|
-
3.
|
|
21
|
-
4.
|
|
22
|
-
5.
|
|
23
|
-
6.
|
|
18
|
+
1. 确认范围:仓库 / 分支 / 目录、只读声明、能否跑命令。
|
|
19
|
+
2. 盘点:技术栈、目录、页面、请求层、状态、类型、测试。
|
|
20
|
+
3. 先跑静态检查,记录 `files / findings` 与覆盖范围;写明 A17 / A21 盲区。
|
|
21
|
+
4. 逐条对照 A1–A21;标 `pass` / `pass(静态)` / `warn` / `fail` / `na`,每条带证据。
|
|
22
|
+
5. 做一致性检查:A4 / A8 / A11 / A17 / A21 与证据矛盾时降级。
|
|
23
|
+
6. 找“历史实现”与“环节缺失”:哪些是习惯,哪些是必需。
|
|
24
|
+
7. 出差距清单:影响、成本、优先级(P0–P3)。
|
|
25
|
+
8. 出升级计划:分阶段、可回滚、每阶段验收;不写时间估算。
|
|
26
|
+
9. 风险:兼容、数据、权限、发布。
|
|
24
27
|
|
|
25
28
|
## 输出
|
|
26
29
|
|
|
27
|
-
-
|
|
28
|
-
-
|
|
30
|
+
- 体检报告(按 `docs/08-audit-contract.md`)
|
|
31
|
+
- 逐条判定表(A1–A21,含证据与局限)
|
|
32
|
+
- 差距清单(P0–P3)
|
|
29
33
|
- 升级计划
|
|
30
34
|
- 回滚方案
|
|
31
35
|
|
|
@@ -38,4 +42,7 @@
|
|
|
38
42
|
## 验收
|
|
39
43
|
|
|
40
44
|
- 只读;不改代码
|
|
45
|
+
- A1–A21 全覆盖或写明 `na`
|
|
41
46
|
- 每条结论有文件路径 / 命令 / A-ID
|
|
47
|
+
- `0 findings` 已注明“仅静态未命中”,没有写成通过
|
|
48
|
+
- 计数可用文中命令复现
|