@ultime/codecraft 1.4.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/CONTRIBUTING.md +3 -0
  3. package/README.md +41 -32
  4. package/SKILL.md +2 -2
  5. package/VERSION +1 -1
  6. package/adapters/README.md +3 -4
  7. package/codecraft.config.example.json +5 -1
  8. package/docs/00-constitution.md +2 -2
  9. package/docs/02-ai-contract.md +12 -1
  10. package/docs/04-gates.md +21 -6
  11. package/docs/07-ai-prompts.md +5 -6
  12. package/docs/08-audit-contract.md +141 -0
  13. package/evals/cases.json +110 -0
  14. package/evals/fixtures/a11-marker/packages/core/dist/tokens.css +4 -0
  15. package/evals/fixtures/a11-marker/packages/core/package.json +1 -0
  16. package/evals/fixtures/a11-marker/packages/core/src/index.ts +1 -0
  17. package/evals/fixtures/a17-boundaries/codecraft.config.json +5 -0
  18. package/evals/fixtures/a17-boundaries/src/pages/home.ts +1 -0
  19. package/evals/fixtures/a17-default/src/pages/home.ts +1 -0
  20. package/evals/fixtures/a17-generated-data/src/icons/icons.ts +2 -0
  21. package/evals/fixtures/a17-generated-data/src/pages/home.ts +3 -0
  22. package/evals/fixtures/a17-generated-header/src/pages/home.ts +5 -0
  23. package/evals/fixtures/a17-generated-header/src/types/project.ts +4 -0
  24. package/evals/fixtures/a17-local/src/pages/home.ts +4 -0
  25. package/evals/fixtures/a17-local/src/types/local.ts +3 -0
  26. package/evals/fixtures/a17-local/src/utils/format.ts +3 -0
  27. package/evals/fixtures/a2-declared/packages/core/package.json +6 -0
  28. package/evals/fixtures/a2-declared/packages/core/src/index.ts +3 -0
  29. package/evals/fixtures/a2-declared/packages/kit/package.json +1 -0
  30. package/evals/fixtures/a2-workspace/packages/core/package.json +1 -0
  31. package/evals/fixtures/a2-workspace/packages/core/src/index.ts +3 -0
  32. package/evals/fixtures/a2-workspace/packages/kit/package.json +1 -0
  33. package/evals/fixtures/a21-doc-paths/packages/ui/README.md +5 -0
  34. package/evals/fixtures/a21-doc-paths/packages/ui/package.json +1 -0
  35. package/evals/fixtures/a21-doc-paths/packages/ui/src/index.ts +1 -0
  36. package/evals/fixtures/a21-placeholders/.env +2 -0
  37. package/evals/fixtures/a21-placeholders/server/application.yml +9 -0
  38. package/evals/fixtures/a21-placeholders/src/pages/home.ts +1 -0
  39. package/evals/fixtures/a21-plaintext/application.yml +5 -0
  40. package/evals/fixtures/a21-plaintext/src/main/java/Ali.java +3 -0
  41. package/evals/fixtures/a21-plaintext/src/pages/home.ts +1 -0
  42. package/evals/fixtures/a6-clean/src/pages/handled.ts +8 -0
  43. package/evals/fixtures/a6-empty-catch/src/pages/empty.ts +3 -0
  44. package/evals/fixtures/ignore-active/codecraft.config.json +5 -0
  45. package/evals/fixtures/ignore-active/src/pages/home.ts +1 -0
  46. package/evals/fixtures/ignore-expired/codecraft.config.json +5 -0
  47. package/evals/fixtures/ignore-expired/src/pages/home.ts +1 -0
  48. package/evals/fixtures/library-a11/dist/tokens.css +3 -0
  49. package/evals/fixtures/library-a11/package.json +4 -0
  50. package/evals/fixtures/library-a11/src/index.ts +1 -0
  51. package/evals/fixtures/zero-files/package.json +4 -0
  52. package/evals/golden-tasks.md +6 -0
  53. package/evals/run.mjs +75 -0
  54. package/package.json +4 -2
  55. package/prompts/output.md +15 -4
  56. package/scenarios/audit-existing.md +15 -8
  57. package/src/check.mjs +290 -40
  58. package/src/cli.mjs +158 -8
  59. package/src/gates.mjs +28 -1
  60. package/src/plan.mjs +2 -0
  61. package/adapters/claude/SKILL.md +0 -42
  62. package/adapters/trae/SKILL.md +0 -42
@@ -0,0 +1,5 @@
1
+ {
2
+ "boundaries": {
3
+ "generated": ["@/types/project"]
4
+ }
5
+ }
@@ -0,0 +1 @@
1
+ import type { Project } from '@/types/project'
@@ -0,0 +1 @@
1
+ import { User } from '@/external/user'
@@ -0,0 +1,2 @@
1
+ /* icons.ts — 本文件由脚本生成,勿手改 */
2
+ export default { search: '<path />' }
@@ -0,0 +1,3 @@
1
+ import icons from '@/icons/icons'
2
+
3
+ export const searchIcon = icons.search
@@ -0,0 +1,5 @@
1
+ import type { Project } from '@/types/project'
2
+
3
+ export function nameOf(project: Project) {
4
+ return project.id
5
+ }
@@ -0,0 +1,4 @@
1
+ /** 本文件由脚本生成,勿手改 */
2
+ export interface Project {
3
+ id: string
4
+ }
@@ -0,0 +1,4 @@
1
+ import type { Local } from '@/types/local'
2
+ import { format } from '@/utils/format'
3
+
4
+ export const label = (value: Local) => format(value.id)
@@ -0,0 +1,3 @@
1
+ export interface Local {
2
+ id: string
3
+ }
@@ -0,0 +1,3 @@
1
+ export function format(value: string): string {
2
+ return value.trim()
3
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "@x/core",
3
+ "dependencies": {
4
+ "@x/kit": "workspace:*"
5
+ }
6
+ }
@@ -0,0 +1,3 @@
1
+ import { token } from '@x/kit'
2
+
3
+ export const value = token
@@ -0,0 +1 @@
1
+ { "name": "@x/kit" }
@@ -0,0 +1 @@
1
+ { "name": "@x/core" }
@@ -0,0 +1,3 @@
1
+ import { token } from '@x/kit'
2
+
3
+ export const value = token
@@ -0,0 +1 @@
1
+ { "name": "@x/kit" }
@@ -0,0 +1,5 @@
1
+ # UI
2
+
3
+ 实现说明见 `docs/开发计划/components/README.md`。
4
+ 上游约定见 `../docs/guide.md`。
5
+ 自带文档:`docs/guide.md`。
@@ -0,0 +1 @@
1
+ { "name": "@x/ui" }
@@ -0,0 +1 @@
1
+ export { Button } from './button'
@@ -0,0 +1,2 @@
1
+ APP_SECRET=${APP_SECRET}
2
+ PASSWORD=
@@ -0,0 +1,9 @@
1
+ spring:
2
+ datasource:
3
+ password: ${DB_PASSWORD}
4
+ redis:
5
+ password: ${REDIS_PASSWORD}
6
+ jwt:
7
+ secret: change-me
8
+ apiKey: your-key-here
9
+ accessKey: <ACCESS_KEY>
@@ -0,0 +1 @@
1
+ export const ready = true
@@ -0,0 +1,5 @@
1
+ spring:
2
+ datasource:
3
+ password: demo-pass-123
4
+ redis:
5
+ password: ${REDIS_PASSWORD}
@@ -0,0 +1,3 @@
1
+ class Ali {
2
+ private static final String ACCESS_KEY_ID = "LTAI5tAbCdEfGhIjKl";
3
+ }
@@ -0,0 +1 @@
1
+ export const ready = true
@@ -0,0 +1,8 @@
1
+ export function load(): string {
2
+ try {
3
+ return read()
4
+ } catch (error) {
5
+ report(error)
6
+ return ''
7
+ }
8
+ }
@@ -0,0 +1,3 @@
1
+ try {
2
+ run()
3
+ } catch {}
@@ -0,0 +1,5 @@
1
+ {
2
+ "ignore": [
3
+ { "rule": "A17", "file": "src/pages/**", "reason": "eval fixture", "expires": "2099-12-31" }
4
+ ]
5
+ }
@@ -0,0 +1 @@
1
+ import { User } from '@/external/user'
@@ -0,0 +1,5 @@
1
+ {
2
+ "ignore": [
3
+ { "rule": "A17", "file": "src/pages/**", "reason": "eval fixture", "expires": "2000-01-01" }
4
+ ]
5
+ }
@@ -0,0 +1 @@
1
+ import { User } from '@/external/user'
@@ -0,0 +1,3 @@
1
+ :root {
2
+ --color-text: #000;
3
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "eval-library-a11",
3
+ "version": "0.0.0"
4
+ }
@@ -0,0 +1 @@
1
+ export const value = 1
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "eval-zero-files",
3
+ "version": "0.0.0"
4
+ }
@@ -114,6 +114,12 @@
114
114
  - 安装由 `skills` CLI 负责:`npx skills add ultime-space/skills --skill codecraft`。
115
115
  - 不在 Codecraft 规则评估范围内;本文件不再评估安装器行为。
116
116
 
117
+ ## 17. 审计结论一致性
118
+
119
+ - 输入:一个项目同时存在「多处 `code === 200`」「生成物无再生成 diff」「CLI `0 findings`」。
120
+ - 期望:A4 判 `warn`;A11 判 `warn`;报告写明 `0 findings` 只是静态未命中;计数带检索命令。
121
+ - 失败信号:A4 / A11 判 `pass`;把静态检查当全通过;结论没有文件行号或命令。
122
+
117
123
  ## 评分
118
124
 
119
125
  - 每条任务:pass / fail。
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.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "面向 JS/TS 的代码规范与架构设计(前端默认,Node 后端可复用结构规则),作为可安装的 skill 包。",
5
5
  "keywords": [
6
6
  "ai",
@@ -50,7 +50,9 @@
50
50
  "type": "module",
51
51
  "scripts": {
52
52
  "test": "node --test tests/*.test.mjs",
53
- "prepublishOnly": "npm test"
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
- - 每条结论引用 A-ID 或文件路径。
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. 逐条对照 A1–A21;标 pass / warn / fail。
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
- - 差距清单(severity)
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
+ - 计数可用文中命令复现