@ultime/codecraft 1.4.0 → 1.6.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 +32 -0
- package/CONTRIBUTING.md +3 -0
- package/README.md +78 -39
- package/SKILL.md +13 -9
- package/VERSION +1 -1
- package/adapters/README.md +3 -4
- package/codecraft.config.example.json +5 -1
- package/docs/00-constitution.md +5 -5
- package/docs/01-family-cards.md +2 -2
- package/docs/02-ai-contract.md +14 -3
- package/docs/04-gates.md +21 -6
- package/docs/06-architecture.md +5 -3
- package/docs/07-ai-prompts.md +16 -7
- package/docs/08-audit-contract.md +141 -0
- package/docs/09-glossary.md +22 -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 +14 -1
- package/evals/run.mjs +75 -0
- package/package.json +4 -2
- package/prompts/output.md +19 -4
- package/scenarios/architecture-report.md +88 -0
- package/scenarios/audit-existing.md +15 -8
- package/scenarios/new-project.md +1 -1
- package/scenarios/refactor-review.md +1 -1
- package/src/check.mjs +290 -40
- package/src/cli.mjs +158 -8
- package/src/gates.mjs +28 -1
- package/src/plan.mjs +2 -0
- package/templates/README.md +3 -1
- package/templates/architecture-report.md +97 -0
- package/adapters/claude/SKILL.md +0 -42
- package/adapters/trae/SKILL.md +0 -42
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# 审计输出契约 v1
|
|
2
|
+
|
|
3
|
+
> 2026-10-27 · v1
|
|
4
|
+
> 用途:约束 Audit 模式的判定、证据、局限与一致性,防止把“静态检查 0 findings”写成“全部通过”。
|
|
5
|
+
> 范围:JS/TS 项目,默认前端;Node 后端只复用结构与安全类判定。
|
|
6
|
+
> 关系:流程见 `docs/02-ai-contract.md`;格式见 `prompts/output.md`;规则正文见 `docs/00-constitution.md` 与 `docs/01-family-cards.md`。
|
|
7
|
+
|
|
8
|
+
## 1. 必填结构
|
|
9
|
+
|
|
10
|
+
Audit 报告必须包含:
|
|
11
|
+
|
|
12
|
+
1. **范围与方法**:仓库 / 分支 / 目录、只读声明、执行的命令、未执行项与原因。
|
|
13
|
+
2. **逐条判定**:A1–A21 全覆盖;不适用写 `na` 并说明原因。
|
|
14
|
+
3. **差距 / 风险**:按优先级分组,每条带证据。
|
|
15
|
+
4. **计划**:阶段、内容、可回滚、验收。
|
|
16
|
+
5. **验证**:命令 + 结果;不能跑要写原因。
|
|
17
|
+
6. **局限**:静态检查覆盖不到的部分,尤其是 A17 / A21。
|
|
18
|
+
|
|
19
|
+
逐条判定表固定为:
|
|
20
|
+
|
|
21
|
+
```md
|
|
22
|
+
| ID | 判定 | 证据 | 局限 |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| A4 | warn | `useList.ts` 6 处、页面 20+ 处 `code === 200` | 未覆盖运行时错误分支 |
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 2. 判定口径
|
|
28
|
+
|
|
29
|
+
| 判定 | 含义 |
|
|
30
|
+
|---|---|
|
|
31
|
+
| `pass` | 有证据覆盖该规则,且没有相反证据 |
|
|
32
|
+
| `pass(静态)` | 只有静态检查通过;必须写清检查范围,不能替代人工抽样 |
|
|
33
|
+
| `warn` | 存在相反证据、覆盖不完整,或只有部分满足 |
|
|
34
|
+
| `fail` | 明确违反,且会造成安全、数据或契约风险;按 P0 处理 |
|
|
35
|
+
| `na` | 该规则不适用;必须写原因 |
|
|
36
|
+
|
|
37
|
+
证据要求:
|
|
38
|
+
|
|
39
|
+
- 文件级:`path/to/file.ts:line`;不要只写目录。
|
|
40
|
+
- 命令级:写出命令与结果;计数必须带可复现的检索式。
|
|
41
|
+
- 禁止「看起来一致」「整体规范」这类无证据结论。
|
|
42
|
+
- 一个规则有多个子项时,逐项给证据;不能用一个子项代表全部。
|
|
43
|
+
|
|
44
|
+
## 3. 静态检查边界
|
|
45
|
+
|
|
46
|
+
内置检查是**抽样工具**,不是审计结论:
|
|
47
|
+
|
|
48
|
+
- **A17**:按三条线索识别生成类型:路径含 `external/` / `generated/`、文件头有只读声明(`本文件由…生成` / `勿手改` / `@generated`)、`boundaries.external` / `boundaries.generated` 配置。只解析相对路径与 `@/` `~/` `#/` 别名;解析不到或头部无标记时仍是盲区,必须人工检查。
|
|
49
|
+
- 生成的数据 / 资源模块(如图标表)不算类型契约,不触发 A17。
|
|
50
|
+
- 约定:生成 / 外部类型放在 `external/` 或 `generated/` 下;迁移前按人工检查,不能因 0 findings 判 `pass`。
|
|
51
|
+
- **A2 / A11**:只覆盖包结构、生成物标记等文本模式;没有类型图、没有运行时验证。
|
|
52
|
+
- **A21**:高置信 token 模式(私钥 / AWS / 阿里云 `LTAI` / GitHub / Slack / API key)+ 配置与 Java / Kotlin 源码中的明文强口令 / 密钥字段;跳过环境变量、占位符与测试目录。没有命中不等于不存在,必须写明扫描范围。
|
|
53
|
+
- **A4 / A5 / A8 / A12 / A13 / A19**:靠人工阅读,不做静态扫描。A4 需要语义判断;A5 需要状态可达性、A8 需要数据流分析,文本级扫描实测误报高;不能因为 CLI 0 findings 就给 `pass`。
|
|
54
|
+
|
|
55
|
+
因此:
|
|
56
|
+
|
|
57
|
+
- 报告必须写「`codecraft check` 的结果与覆盖范围」。
|
|
58
|
+
- 用了 `--exclude` 时必须列出排除路径;否则计数不可复现。
|
|
59
|
+
- `0 findings` 只能写成「静态检查未命中」,不能写成「规则通过」。
|
|
60
|
+
- 审计结论的置信度分为 High / Medium / Low:
|
|
61
|
+
- High:静态 + 人工抽样 + 关键路径验证
|
|
62
|
+
- Medium:静态 + 人工抽样
|
|
63
|
+
- Low:仅静态检查
|
|
64
|
+
|
|
65
|
+
## 4. A1–A21 判定矩阵
|
|
66
|
+
|
|
67
|
+
| ID | 判定方式 | `pass` 条件 | 常见误判 |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| A1 | 人工抽样(结构检索辅助) | 同族实例使用同一模板 / 插槽;抽样覆盖主要分支 | 只看到统一 Wrapper,忽略内部结构分叉 |
|
|
70
|
+
| A2 | 静态依赖 + 人工 | 无反向依赖、无跨层 import | 只看 core 的 import,漏掉传输层 import 状态 |
|
|
71
|
+
| A3 | 人工 | 传输层不解释业务 code、不承担 UI 反馈 | 401 收口一处就判通过 |
|
|
72
|
+
| A4 | 人工 + 计数 | 每种关注点只有一个收口;无散落的同类判断 | 认证 / 提示各一处,但业务码判断散落 30+ 处 |
|
|
73
|
+
| A5 | 静态 + 人工 | 非法状态不可表示;联合类型 / 穷尽分支 | 有类型但大量 `any` / 多布尔并存 |
|
|
74
|
+
| A6 | 人工 + 静态(空 catch) | 错误是值;无静默失败;调用方必须处理 | 成功元组里 err 恒为 `undefined`,业务失败仍走成功分支 |
|
|
75
|
+
| A7 | 人工 | 可枚举变化用表;加 case 只加一行 | 有表但仍有并行 if / switch |
|
|
76
|
+
| A8 | 人工 | 派生状态不落库 / 不重复存储 | 有 computed,同时把结果再存一份 |
|
|
77
|
+
| A9 | 人工 | 注释只写意图 / 契约 / 边界 / 反例 | 注释复述代码,或规范文档本身漂移 |
|
|
78
|
+
| A10 | 人工 + 配置 | 例外有理由 / 主人 / 到期;安全不可豁免 | 只写“历史原因”,没有到期 |
|
|
79
|
+
| A11 | 静态(标记)+ CI 比对 | 生成物有只读标记,且再生成 diff 为空 | 只有“勿手改”头,没有再生成比对 |
|
|
80
|
+
| A12 | 人工 | 复制有母版、版本、分叉点、升级路径 | 多处副本,未指明母版 |
|
|
81
|
+
| A13 | 人工 | 每一层都能一眼定位源头 | `utils/` 成为域名 / 传输 / 海报的混合桶 |
|
|
82
|
+
| A14 | 人工 | 抽象有真实消费方与触发条件 | 以“以后可能用”为由提前抽象 |
|
|
83
|
+
| A15 | 流程 / 输出检查 | 变更说明含改了什么 / 为什么 / 怎么验 / 风险 | 用 A15 去评价代码结构 |
|
|
84
|
+
| A16 | 人工 + 搜索 | 命名标层、可搜索、组合可读 | 单项目内一致,但跨端 / 跨团队歧义 |
|
|
85
|
+
| A17 | 静态(路径 / 文件头 / 配置)+ 人工 | 内部不 import 外部类型;外部契约在包装层转换 | 别名解析不到或头部无标记就判通过;把生成数据模块当成类型契约 |
|
|
86
|
+
| A18 | 人工 | 有档位;默认 L0;按触发升级 | 所有能力默认开启 |
|
|
87
|
+
| A19 | 人工 + 契约测试 | public contract 固定;有可替换性验证 | 边界口头清楚,但没有契约测试 |
|
|
88
|
+
| A20 | 人工 | 记录环节 / 职责 / 契约,不把库当层 | 用框架名代替架构层 |
|
|
89
|
+
| A21 | 静态扫描 + 人工 | 无密钥 / 内部路径外泄;交付白名单一致 | 模式扫描 0 命中就判全局通过;把运行时变量名当明文密钥 |
|
|
90
|
+
|
|
91
|
+
## 5. 一致性检查
|
|
92
|
+
|
|
93
|
+
出结论前,必须做一次交叉检查;发现矛盾就降级:
|
|
94
|
+
|
|
95
|
+
| 场景 | 要求 |
|
|
96
|
+
|---|---|
|
|
97
|
+
| A4 判 `pass`,但存在多处 `code === 200` / 重复错误处理 | 改为 `warn`;单一收口未成立 |
|
|
98
|
+
| A11 判 `pass`,但没有再生成比对 / CI diff | 最多 `warn`;只能写“生成物有只读标记” |
|
|
99
|
+
| A8 判 `pass`,但派生状态被落库 / 重复存储 | 改为 `warn`,或写明可接受理由与主人 |
|
|
100
|
+
| A21 判 `pass`,但只做了模式扫描 | 写 `pass(有限)` 或 `warn`;列扫描范围 |
|
|
101
|
+
| A17 判 `pass`,但存在生成类型直用 | 改为 `warn`;路径不在 `external/` 也算 |
|
|
102
|
+
| A12 判 `pass`,但同族存在多份副本 | 改为 `warn`;必须指明母版 |
|
|
103
|
+
| A3 判 `pass`,但传输层直接弹 Toast / 跳转 | 改为 `warn`;传输层不应承担 UI 反馈 |
|
|
104
|
+
|
|
105
|
+
## 6. 优先级
|
|
106
|
+
|
|
107
|
+
| 级别 | 定义 | 例子 |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| P0 | 安全、数据丢失、不可回滚 | 密钥泄漏、越权、破坏性迁移 |
|
|
110
|
+
| P1 | 架构 / 契约 / 文档真源错误 | 分层失效、规范写错事实、门禁缺失 |
|
|
111
|
+
| P2 | 可维护性 / 重复实现 | 业务错误码散落、上传目录三份真源 |
|
|
112
|
+
| P3 | 测试、清理、体验 | 纯函数无单测、命名不统一 |
|
|
113
|
+
|
|
114
|
+
同一份报告里,P1 不能多于结论总数的一半而不说明取舍;否则先合并根因。
|
|
115
|
+
|
|
116
|
+
## 7. 计划表
|
|
117
|
+
|
|
118
|
+
```md
|
|
119
|
+
| 阶段 | 内容 | 可回滚 | 验收 |
|
|
120
|
+
|---|---|---|---|
|
|
121
|
+
| 0 | 文档纠偏 | 纯文档,单 commit 可回退 | 描述与检索结果一致 |
|
|
122
|
+
| 1 | 门禁补齐 | 移除 config / hook 即恢复 | 故意引入类型错误会被拦 |
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
要求:
|
|
126
|
+
|
|
127
|
+
- 每阶段可独立回滚,不用“重来一遍”。
|
|
128
|
+
- 验收必须是可执行命令或可检索结果,不写“感觉完成”。
|
|
129
|
+
- 不写时间估算;写依赖与顺序。
|
|
130
|
+
- 重构阶段先补基线(typecheck / 单测 / 契约测试),再改行为。
|
|
131
|
+
|
|
132
|
+
## 8. 交付前自检
|
|
133
|
+
|
|
134
|
+
- [ ] A1–A21 全部出现,或写明 `na`
|
|
135
|
+
- [ ] 每条 `pass` / `warn` 都有文件行号或命令
|
|
136
|
+
- [ ] 所有计数都能用文中命令复现
|
|
137
|
+
- [ ] 已做一致性检查,A4 / A8 / A11 / A17 / A21 没有被高估
|
|
138
|
+
- [ ] 已写明静态检查覆盖不到的部分
|
|
139
|
+
- [ ] 没有密钥、内部路径、客户隐私
|
|
140
|
+
- [ ] Audit 只读;未改代码、未起服务
|
|
141
|
+
- [ ] 计划每阶段可回滚、有验收
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Glossary(术语表)
|
|
2
|
+
|
|
3
|
+
> 写法:正文首次出现用 `English(中文)`;名字类不附中文。
|
|
4
|
+
> 名字类 = 产品 / 文件 / 命令 / 语言 / 框架 / 标准名 / 代码标识(如 Codecraft、README.md、npm、JS/TS、JSON、A1–A21)。
|
|
5
|
+
> 本表是术语写法的单一真源;`tests/docs.test.mjs` 检查正文首次出现。
|
|
6
|
+
|
|
7
|
+
| English | 中文 |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Audit | 审计 |
|
|
10
|
+
| Plan | 计划 |
|
|
11
|
+
| Apply | 执行 |
|
|
12
|
+
| Wrapper | 包装层 |
|
|
13
|
+
| Gate | 门禁 |
|
|
14
|
+
| Gates | 门禁 |
|
|
15
|
+
| Drift | 漂移 |
|
|
16
|
+
| ADR | 架构决策记录 |
|
|
17
|
+
| Canonical Link | 规范链接 |
|
|
18
|
+
| canonical link | 规范链接 |
|
|
19
|
+
| Context Engineering | 上下文工程 |
|
|
20
|
+
| Progressive Disclosure | 渐进披露 |
|
|
21
|
+
| Single Source of Truth | 单一真源 |
|
|
22
|
+
| Design System | 设计系统 |
|
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
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
|
|
86
86
|
- 输入:一个单团队、单产品的多页应用,后端契约未知。
|
|
87
87
|
- 模式:Plan(只读)。
|
|
88
|
-
- 期望:先定档位 L0/L1
|
|
88
|
+
- 期望:先定档位 L0/L1;列变化轴;切边界并给依赖规则;Context / Container 用表格描述(本阶段不要求画图);ADR 只记难逆转项;不改文件。
|
|
89
89
|
- 失败信号:直接上微服务 / 微前端;只画图不给依赖规则;没有退出条件。
|
|
90
90
|
|
|
91
91
|
## 13. 架构评审
|
|
@@ -114,6 +114,19 @@
|
|
|
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
|
+
|
|
123
|
+
## 18. 代码架构分析报告
|
|
124
|
+
|
|
125
|
+
- 输入:一个三端项目(client / admin / server),已有 README 与部分 `rules/`。
|
|
126
|
+
- 模式:Audit(只读)。
|
|
127
|
+
- 期望:按 `templates/architecture-report.md` 出文档;结构 / 依赖 / 关键链路 / A1–A21 / 风险 / 演进 / 未知项齐全;每条有证据;明确写「不含业务架构」「本阶段不画图」;不改文件。
|
|
128
|
+
- 失败信号:写成业务能力 / 角色 / 业务流程;没有证据;只给结论;用图代替结构表;改文件。
|
|
129
|
+
|
|
117
130
|
## 评分
|
|
118
131
|
|
|
119
132
|
- 每条任务: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.
|
|
3
|
+
"version": "1.6.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
|
-
"
|
|
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,19 @@
|
|
|
18
24
|
## 下一步
|
|
19
25
|
```
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
## 架构报告
|
|
28
|
+
|
|
29
|
+
代码架构分析报告按 [`../templates/architecture-report.md`](../templates/architecture-report.md) 输出;默认不画图,用表格 / 列表表达结构;证据、局限与 `0 findings` 口径同 Audit。
|
|
30
|
+
|
|
31
|
+
## 规则
|
|
22
32
|
|
|
23
|
-
-
|
|
33
|
+
- A1–A21 全部出现;不适用写 `na` 并说明原因。
|
|
34
|
+
- 每条结论引用 A-ID,并给文件行号或可复现命令;计数必须带检索式。
|
|
35
|
+
- `pass` 必须有证据;只有静态检查通过时写 `pass(静态)`,不能替代人工抽样。
|
|
36
|
+
- `codecraft check` 的 0 findings 只能写「静态检查未命中」;同时写明 A17 / A21 的覆盖盲区。
|
|
37
|
+
- 出结论前做一致性检查:A4 / A8 / A11 / A17 / A21 与证据矛盾时必须降级。
|
|
24
38
|
- 不写敏感信息;示例用占位符。
|
|
25
39
|
- 英文术语首次出现用 `English(中文)`。
|
|
26
40
|
- 不把“计划”写成“已完成”。
|
|
27
41
|
- Audit / Plan 模式只输出报告,不改文件。
|
|
42
|
+
- 计划每阶段可回滚,验收用命令或检索结果,不写时间估算。
|