@epoint-testtech/ep-stage-skill 0.0.4-alpha.0 → 0.0.5

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 (206) hide show
  1. package/SKILL.md +27 -27
  2. package/codex-skill/ep-stage/glue-create-project/SKILL.md +223 -186
  3. package/codex-skill/ep-stage/glue-generate-testcase/SKILL.md +275 -199
  4. package/codex-skill/ep-stage/glue-generate-testcase/examples/observable-testcase.json +40 -40
  5. package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-schema.md +95 -112
  6. package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-tpl.md +72 -0
  7. package/codex-skill/ep-stage/glue-run-test/SKILL.md +292 -249
  8. package/codex-skill/ep-stage/glue-run-test/references/ai-runtime-loop.md +30 -0
  9. package/codex-skill/ep-stage/glue-run-test/references/crud-pipeline.md +132 -145
  10. package/codex-skill/ep-stage/glue-run-test/references/gap-review-protocol.md +44 -43
  11. package/codex-skill/ep-stage/glue-run-test/references/harness-principles.md +46 -46
  12. package/codex-skill/ep-stage/glue-run-test/references/playwright-cli-odav.md +166 -0
  13. package/codex-skill/ep-stage/glue-run-test/scripts/extract-page-elements-eval.mjs +233 -0
  14. package/codex-skill/ep-stage/glue-run-test/scripts/generate-crud-spec.mjs +149 -149
  15. package/codex-skill/ep-stage/page-element-scan/SKILL.md +91 -0
  16. package/codex-skill/ep-stage/page-element-scan/evals/evals.json +37 -0
  17. package/codex-skill/ep-stage/page-element-scan/evals/fixtures/sample-list-page.html +85 -0
  18. package/codex-skill/ep-stage/page-element-scan/references/pagination-controls.md +153 -0
  19. package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements-eval.mjs +233 -0
  20. package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements.mjs +298 -0
  21. package/codex-skill/ep-stage/page-element-scan/scripts/scan-page.mjs +749 -0
  22. package/codex-skill/ep-stage/recording-to-glue/SKILL.md +28 -28
  23. package/codex-skill/ep-stage/scripts/validate-skill.mjs +98 -95
  24. package/dist/src/cli/generate-testcase.d.ts +18 -51
  25. package/dist/src/cli/generate-testcase.d.ts.map +1 -1
  26. package/dist/src/cli/generate-testcase.js +127 -131
  27. package/dist/src/cli/index.d.ts +11 -2
  28. package/dist/src/cli/index.d.ts.map +1 -1
  29. package/dist/src/cli/index.js +49 -16
  30. package/dist/src/cli/probe.d.ts +2 -0
  31. package/dist/src/cli/probe.d.ts.map +1 -1
  32. package/dist/src/cli/probe.js +239 -13
  33. package/dist/src/cli/run.d.ts +136 -29
  34. package/dist/src/cli/run.d.ts.map +1 -1
  35. package/dist/src/cli/run.js +494 -65
  36. package/dist/src/cli/spec.d.ts +18 -11
  37. package/dist/src/cli/spec.d.ts.map +1 -1
  38. package/dist/src/cli/spec.js +74 -20
  39. package/dist/src/contracts/crud-business-module.d.ts +6 -0
  40. package/dist/src/contracts/crud-business-module.d.ts.map +1 -1
  41. package/dist/src/contracts/glue-contract-v1.d.ts +98 -0
  42. package/dist/src/contracts/glue-contract-v1.d.ts.map +1 -0
  43. package/dist/src/contracts/glue-contract-v1.js +9 -0
  44. package/dist/src/contracts/inference-trace.d.ts +223 -0
  45. package/dist/src/contracts/inference-trace.d.ts.map +1 -0
  46. package/dist/src/contracts/inference-trace.js +12 -0
  47. package/dist/src/contracts/observable-chain.d.ts +4 -0
  48. package/dist/src/contracts/observable-chain.d.ts.map +1 -1
  49. package/dist/src/extractors/code-list-json5.d.ts +192 -0
  50. package/dist/src/extractors/code-list-json5.d.ts.map +1 -0
  51. package/dist/src/extractors/code-list-json5.js +91 -0
  52. package/dist/src/extractors/code-list.d.ts +3 -3
  53. package/dist/src/extractors/code-list.d.ts.map +1 -1
  54. package/dist/src/extractors/code-list.js +46 -11
  55. package/dist/src/extractors/html-page.d.ts +9 -0
  56. package/dist/src/extractors/html-page.d.ts.map +1 -1
  57. package/dist/src/extractors/html-page.js +26 -2
  58. package/dist/src/extractors/prd-extractor.d.ts +70 -0
  59. package/dist/src/extractors/prd-extractor.d.ts.map +1 -0
  60. package/dist/src/extractors/prd-extractor.js +248 -0
  61. package/dist/src/generators/agent-inferred-workflow-script.js +66 -66
  62. package/dist/src/generators/stage-skeleton-script.d.ts +87 -20
  63. package/dist/src/generators/stage-skeleton-script.d.ts.map +1 -1
  64. package/dist/src/generators/stage-skeleton-script.js +656 -238
  65. package/dist/src/index.d.ts +7 -2
  66. package/dist/src/index.d.ts.map +1 -1
  67. package/dist/src/index.js +3 -1
  68. package/dist/src/inference/module-hints-inference.d.ts +30 -0
  69. package/dist/src/inference/module-hints-inference.d.ts.map +1 -0
  70. package/dist/src/inference/module-hints-inference.js +363 -0
  71. package/dist/src/inference/source-analysis.d.ts +26 -0
  72. package/dist/src/inference/source-analysis.d.ts.map +1 -0
  73. package/dist/src/inference/source-analysis.js +258 -0
  74. package/dist/src/normalizers/crud-contract.d.ts +3 -0
  75. package/dist/src/normalizers/crud-contract.d.ts.map +1 -1
  76. package/dist/src/normalizers/crud-contract.js +3 -0
  77. package/dist/src/runtime/ai-runtime-trace.d.ts +88 -0
  78. package/dist/src/runtime/ai-runtime-trace.d.ts.map +1 -0
  79. package/dist/src/runtime/ai-runtime-trace.js +119 -0
  80. package/dist/src/testcase/button-interaction-inference.d.ts +37 -0
  81. package/dist/src/testcase/button-interaction-inference.d.ts.map +1 -0
  82. package/dist/src/testcase/button-interaction-inference.js +385 -0
  83. package/dist/src/testcase/contract-testcase-assembler.d.ts +44 -0
  84. package/dist/src/testcase/contract-testcase-assembler.d.ts.map +1 -0
  85. package/dist/src/testcase/contract-testcase-assembler.js +806 -0
  86. package/dist/src/testcase/prd-button-mapper.d.ts +72 -0
  87. package/dist/src/testcase/prd-button-mapper.d.ts.map +1 -0
  88. package/dist/src/testcase/prd-button-mapper.js +148 -0
  89. package/dist/src/testcase/runtime-context.d.ts +9 -0
  90. package/dist/src/testcase/runtime-context.d.ts.map +1 -0
  91. package/dist/src/testcase/runtime-context.js +1 -0
  92. package/dist/src/testcase/test-access.d.ts +38 -0
  93. package/dist/src/testcase/test-access.d.ts.map +1 -0
  94. package/dist/src/testcase/test-access.js +106 -0
  95. package/dist/src/testcase/testcase-execution-fields.d.ts +10 -0
  96. package/dist/src/testcase/testcase-execution-fields.d.ts.map +1 -0
  97. package/dist/src/testcase/testcase-execution-fields.js +1 -0
  98. package/dist/src/testcase/testcase-generator.d.ts.map +1 -1
  99. package/dist/src/testcase/testcase-generator.js +6 -0
  100. package/dist/src/testcase/testcase-markdown.d.ts +11 -0
  101. package/dist/src/testcase/testcase-markdown.d.ts.map +1 -0
  102. package/dist/src/testcase/testcase-markdown.js +181 -0
  103. package/dist/src/testcase/testcase-skeleton.d.ts +4 -0
  104. package/dist/src/testcase/testcase-skeleton.d.ts.map +1 -1
  105. package/dist/src/testcase/testcase-spec-assembly.d.ts +4 -0
  106. package/dist/src/testcase/testcase-spec-assembly.d.ts.map +1 -1
  107. package/dist/src/testcase/testcase-v1-draft.d.ts +58 -0
  108. package/dist/src/testcase/testcase-v1-draft.d.ts.map +1 -0
  109. package/dist/src/testcase/testcase-v1-draft.js +13 -0
  110. package/dist/src/validation/contract.d.ts +14 -0
  111. package/dist/src/validation/contract.d.ts.map +1 -0
  112. package/dist/src/validation/contract.js +132 -0
  113. package/dist/src/validation/credentials.d.ts +7 -2
  114. package/dist/src/validation/credentials.d.ts.map +1 -1
  115. package/dist/src/validation/credentials.js +15 -4
  116. package/dist/src/validation/index.d.ts +2 -0
  117. package/dist/src/validation/index.d.ts.map +1 -1
  118. package/dist/src/validation/index.js +2 -0
  119. package/dist/src/validation/inference-trace.d.ts +13 -0
  120. package/dist/src/validation/inference-trace.d.ts.map +1 -0
  121. package/dist/src/validation/inference-trace.js +31 -0
  122. package/dist/src/validation/testcase.d.ts +9 -7
  123. package/dist/src/validation/testcase.d.ts.map +1 -1
  124. package/dist/src/validation/testcase.js +181 -34
  125. package/docs/README.md +124 -134
  126. package/docs/mvp-usage-guide.md +298 -298
  127. package/examples/schemeresource-observable-docs/schemeresource.context.md +20 -20
  128. package/examples/schemeresource.module-hints.json +38 -38
  129. package/examples/schemeresource.observable.code_list.md +37 -37
  130. package/examples/zwplace-observable-docs/zwplace.context.md +16 -16
  131. package/examples/zwplace-placecategory-validation.json +29 -29
  132. package/examples/zwplace.module-hints.json +69 -69
  133. package/examples/zwplace.observable.code_list.md +37 -37
  134. package/package.json +2 -2
  135. package/dist/test/cli/extract-contract.test.d.ts +0 -2
  136. package/dist/test/cli/extract-contract.test.d.ts.map +0 -1
  137. package/dist/test/cli/extract-contract.test.js +0 -32
  138. package/dist/test/cli/generate-testcase.test.d.ts +0 -2
  139. package/dist/test/cli/generate-testcase.test.d.ts.map +0 -1
  140. package/dist/test/cli/generate-testcase.test.js +0 -130
  141. package/dist/test/cli/index.test.d.ts +0 -2
  142. package/dist/test/cli/index.test.d.ts.map +0 -1
  143. package/dist/test/cli/index.test.js +0 -93
  144. package/dist/test/cli/run.test.d.ts +0 -2
  145. package/dist/test/cli/run.test.d.ts.map +0 -1
  146. package/dist/test/cli/run.test.js +0 -149
  147. package/dist/test/cli/spec.test.d.ts +0 -2
  148. package/dist/test/cli/spec.test.d.ts.map +0 -1
  149. package/dist/test/cli/spec.test.js +0 -196
  150. package/dist/test/crud-contract.test.d.ts +0 -2
  151. package/dist/test/crud-contract.test.d.ts.map +0 -1
  152. package/dist/test/crud-contract.test.js +0 -819
  153. package/dist/test/gap-inference.test.d.ts +0 -2
  154. package/dist/test/gap-inference.test.d.ts.map +0 -1
  155. package/dist/test/gap-inference.test.js +0 -597
  156. package/dist/test/generalization.test.d.ts +0 -2
  157. package/dist/test/generalization.test.d.ts.map +0 -1
  158. package/dist/test/generalization.test.js +0 -73
  159. package/dist/test/material-inventory.test.d.ts +0 -2
  160. package/dist/test/material-inventory.test.d.ts.map +0 -1
  161. package/dist/test/material-inventory.test.js +0 -141
  162. package/dist/test/observable-chain.test.d.ts +0 -2
  163. package/dist/test/observable-chain.test.d.ts.map +0 -1
  164. package/dist/test/observable-chain.test.js +0 -123
  165. package/dist/test/observable-pipeline.test.d.ts +0 -2
  166. package/dist/test/observable-pipeline.test.d.ts.map +0 -1
  167. package/dist/test/observable-pipeline.test.js +0 -461
  168. package/dist/test/page-structure.test.d.ts +0 -2
  169. package/dist/test/page-structure.test.d.ts.map +0 -1
  170. package/dist/test/page-structure.test.js +0 -45
  171. package/dist/test/scenario-inference.test.d.ts +0 -2
  172. package/dist/test/scenario-inference.test.d.ts.map +0 -1
  173. package/dist/test/scenario-inference.test.js +0 -73
  174. package/dist/test/stage-context.test.d.ts +0 -2
  175. package/dist/test/stage-context.test.d.ts.map +0 -1
  176. package/dist/test/stage-context.test.js +0 -395
  177. package/dist/test/testcase-generator.test.d.ts +0 -2
  178. package/dist/test/testcase-generator.test.d.ts.map +0 -1
  179. package/dist/test/testcase-generator.test.js +0 -276
  180. package/dist/test/testcase-skeleton.test.d.ts +0 -2
  181. package/dist/test/testcase-skeleton.test.d.ts.map +0 -1
  182. package/dist/test/testcase-skeleton.test.js +0 -185
  183. package/dist/test/testcase-spec-assembly.test.d.ts +0 -2
  184. package/dist/test/testcase-spec-assembly.test.d.ts.map +0 -1
  185. package/dist/test/testcase-spec-assembly.test.js +0 -105
  186. package/dist/test/util/credentials.test.d.ts +0 -2
  187. package/dist/test/util/credentials.test.d.ts.map +0 -1
  188. package/dist/test/util/credentials.test.js +0 -64
  189. package/dist/test/util/i18n-testcase.test.d.ts +0 -2
  190. package/dist/test/util/i18n-testcase.test.d.ts.map +0 -1
  191. package/dist/test/util/i18n-testcase.test.js +0 -119
  192. package/dist/test/util/softlink.test.d.ts +0 -2
  193. package/dist/test/util/softlink.test.d.ts.map +0 -1
  194. package/dist/test/util/softlink.test.js +0 -82
  195. package/dist/test/validation/credentials.test.d.ts +0 -2
  196. package/dist/test/validation/credentials.test.d.ts.map +0 -1
  197. package/dist/test/validation/credentials.test.js +0 -72
  198. package/dist/test/validation/projects-index.test.d.ts +0 -2
  199. package/dist/test/validation/projects-index.test.d.ts.map +0 -1
  200. package/dist/test/validation/projects-index.test.js +0 -48
  201. package/dist/test/validation/testcase.test.d.ts +0 -2
  202. package/dist/test/validation/testcase.test.d.ts.map +0 -1
  203. package/dist/test/validation/testcase.test.js +0 -129
  204. package/dist/vitest.config.d.ts +0 -3
  205. package/dist/vitest.config.d.ts.map +0 -1
  206. package/dist/vitest.config.js +0 -7
@@ -1,28 +1,28 @@
1
- ---
2
- name: recording-to-glue
3
- description: 使用时机:当用户讨论或规划「将录制产出的 Playwright 脚本转换为 ep-stage 胶水骨架(glue skeleton)或组件(component)候选代码」时使用。
4
- version: 2026-06-23+a9e7576
5
- ---
6
-
7
- # ep-stage: recording-to-glue
8
-
9
- 本 skill 是一个规划中的能力入口。
10
-
11
- ## 当前状态
12
-
13
- 转换工作流在本期尚未实现。
14
-
15
- ## 预期输入(未来)
16
-
17
- - 来自 `stage-recorder-ext` 或其他录制器的原始 Playwright 脚本。
18
- - 标识页面、iframe(frame)、选择器(selector)以及用户动作意图的录制元数据。
19
-
20
- ## 预期产出(未来)
21
-
22
- - 候选胶水骨架代码(glue skeleton code)。
23
- - 候选组件动作(component actions)。
24
- - 关于代码应归入骨架、组件、测试 spec 还是 `ModuleHints` 的评审意见。
25
-
26
- ## 当前行为
27
-
28
- 当用户调用本 skill 时,应说明实现尚未开始,并提议为该转换工作流创建设计或实施计划,而不是在本期从录制产物生成生产级胶水代码。
1
+ ---
2
+ name: recording-to-glue
3
+ description: 使用时机:当用户讨论或规划「将录制产出的 Playwright 脚本转换为 ep-stage 胶水骨架(glue skeleton)或组件(component)候选代码」时使用。
4
+ version: 2026-07-05+57662df
5
+ ---
6
+
7
+ # ep-stage: recording-to-glue
8
+
9
+ 本 skill 是一个规划中的能力入口。
10
+
11
+ ## 当前状态
12
+
13
+ 转换工作流在本期尚未实现。
14
+
15
+ ## 预期输入(未来)
16
+
17
+ - 来自 `stage-recorder-ext` 或其他录制器的原始 Playwright 脚本。
18
+ - 标识页面、iframe(frame)、选择器(selector)以及用户动作意图的录制元数据。
19
+
20
+ ## 预期产出(未来)
21
+
22
+ - 候选胶水骨架代码(glue skeleton code)。
23
+ - 候选组件动作(component actions)。
24
+ - 关于代码应归入骨架、组件、测试 spec,还是作为待确认槽位进入 `unresolvedSlots` / `sourceTrace` 的评审意见。
25
+
26
+ ## 当前行为
27
+
28
+ 当用户调用本 skill 时,应说明实现尚未开始,并提议为该转换工作流创建设计或实施计划,而不是在本期从录制产物生成生产级胶水代码。
@@ -1,95 +1,98 @@
1
- #!/usr/bin/env node
2
- // validate-skill.mjs — 校验 ep-stage scope 下所有 skill 的 SKILL.md frontmatter
3
- // + 双发现目录(.claude/skills / .agents/skills)symlink 指向。
4
- //
5
- // version 字段维护契约(REQ-VER-003):
6
- // 当 SKILL.md 内容(frontmatter 或正文)发生变更时,维护者必须手动把
7
- // `version:` 更新为 `YYYY-MM-DD+<7位短SHA>`,其中 <短SHA> 取当前 HEAD。
8
- // 快捷生成命令:date +"%Y-%m-%d"+$(git rev-parse --short HEAD)
9
- // pre-commit hook(.husky/pre-commit → check-skill-version-bump.mjs)会
10
- // 自动校验:改了 SKILL.md 但没改 version 行 → 阻塞 commit。
11
- import { lstatSync, readFileSync, realpathSync } from 'node:fs';
12
- import path from 'node:path';
13
- import { fileURLToPath } from 'node:url';
14
-
15
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
- const epStageRoot = path.resolve(__dirname, '..');
17
- const packageRoot = path.resolve(epStageRoot, '../..');
18
- const repoRoot = path.resolve(packageRoot, '../..');
19
-
20
- // version 值正则:YYYY-MM-DD+<7位十六进制短SHA>
21
- const VERSION_PATTERN = /^\d{4}-\d{2}-\d{2}\+[0-9a-f]{7}$/;
22
-
23
- function readFrontmatter(skillRoot) {
24
- const skillPath = path.join(skillRoot, 'SKILL.md');
25
- const text = readFileSync(skillPath, 'utf8');
26
- const match = text.match(/^---\n([\s\S]*?)\n---\n/);
27
- if (!match) throw new Error(`${skillPath} missing YAML frontmatter`);
28
-
29
- const frontmatter = match[1];
30
- return {
31
- name: frontmatter.match(/^name:\s*(.+)$/m)?.[1]?.trim(),
32
- description: frontmatter.match(/^description:\s*(.+)$/m)?.[1]?.trim(),
33
- version: frontmatter.match(/^version:\s*(.+)$/m)?.[1]?.trim(),
34
- skillPath,
35
- };
36
- }
37
-
38
- function assertSkill(skillRoot, expectedName) {
39
- const { name, description, version, skillPath } = readFrontmatter(skillRoot);
40
- if (name !== expectedName) throw new Error(`Expected name ${expectedName} in ${skillPath}, got ${name ?? 'missing'}`);
41
- if (!description?.startsWith('使用时机') && !description?.startsWith('Use when')) throw new Error(`${skillPath} description 必须以「使用时机」或「Use when」开头`);
42
- if (description.length > 1024) throw new Error(`${skillPath} description exceeds 1024 characters`);
43
- if (path.basename(skillRoot) !== name) throw new Error(`${skillPath} directory basename must match name`);
44
- if (!version) throw new Error(`${skillPath} version 字段缺失;维护者改 SKILL.md 时必须同步更新 version(格式 YYYY-MM-DD+<7位短SHA>)`);
45
- if (!VERSION_PATTERN.test(version)) throw new Error(`${skillPath} version 格式非法 "${version}";应为 YYYY-MM-DD+<7位短SHA>,如 2026-06-23+a9e7576`);
46
- return { name, descriptionLength: description.length, version, skillRoot };
47
- }
48
-
49
- function assertSymlink(linkPath, targetPath) {
50
- const linkStat = lstatSync(linkPath);
51
- if (!linkStat.isSymbolicLink()) throw new Error(`${linkPath} must be a symlink`);
52
- if (realpathSync(linkPath) !== realpathSync(targetPath)) {
53
- throw new Error(`${linkPath} does not point at ${targetPath}`);
54
- }
55
- }
56
-
57
- // ep-stage scope 下四个 skill 的包内真相源目录。
58
- // 注:Task 0.2 git mv 改名 3 个 skill 目录(glue-* 前缀化)。
59
- // - glue-create-project:Task 2.1 已同步 SKILL.md frontmatter name 'glue-create-project'。
60
- // - glue-generate-testcase:Task 3.2 已同步 SKILL.md frontmatter name → 'glue-generate-testcase'。
61
- // - glue-run-test:Task 4.3 已同步 SKILL.md frontmatter name → 'glue-run-test'。
62
- const skillSources = [
63
- { name: 'glue-create-project', source: path.join(epStageRoot, 'glue-create-project') },
64
- { name: 'glue-generate-testcase', source: path.join(epStageRoot, 'glue-generate-testcase') },
65
- { name: 'glue-run-test', source: path.join(epStageRoot, 'glue-run-test') },
66
- { name: 'recording-to-glue', source: path.join(epStageRoot, 'recording-to-glue') },
67
- ];
68
-
69
- // 校验每个源 skill 的 SKILL.md frontmatter。
70
- const checks = skillSources.map(({ name, source }) => assertSkill(source, name));
71
-
72
- // 发现目录:Claude Code 扫 .claude/skills/,Codex 扫 .agents/skills/。
73
- // 两个工具都只扫直接子目录,每个子目录是一个 skill;用 symlink 指向包内源。
74
- const discoveryDirs = [
75
- path.join(repoRoot, '.claude/skills'),
76
- path.join(repoRoot, '.agents/skills'),
77
- ];
78
-
79
- // 注:symlink 名总是与包内目录 basename 一致(Phase 0 重建后)。
80
- // Task 4.3 收敛后,3 个 glue-* skill 的 SKILL.md `name` 与目录 basename 完全一致;symlink 仍以 basename 为准。
81
- const symlinks = [];
82
- for (const dir of discoveryDirs) {
83
- for (const { name, source } of skillSources) {
84
- const linkName = path.basename(source);
85
- assertSymlink(path.join(dir, linkName), source);
86
- symlinks.push({ dir: path.relative(repoRoot, dir), name, linkName, pointsTo: path.relative(repoRoot, source) });
87
- }
88
- }
89
-
90
- console.log(JSON.stringify({
91
- ok: true,
92
- packageRoot,
93
- checks,
94
- symlinks,
95
- }, null, 2));
1
+ #!/usr/bin/env node
2
+ // validate-skill.mjs — 校验 ep-stage scope 下所有 skill 的 SKILL.md frontmatter
3
+ // + 双发现目录(.claude/skills / .agents/skills)symlink 指向。
4
+ //
5
+ // version 字段维护契约(REQ-VER-003):
6
+ // 当 SKILL.md 内容(frontmatter 或正文)发生变更时,维护者必须手动把
7
+ // `version:` 更新为 `YYYY-MM-DD+<7位短SHA>`,其中 <短SHA> 取当前 HEAD。
8
+ // 快捷生成命令:date +"%Y-%m-%d"+$(git rev-parse --short HEAD)
9
+ // pre-commit hook(.husky/pre-commit → check-skill-version-bump.mjs)会
10
+ // 自动校验:改了 SKILL.md 但没改 version 行 → 阻塞 commit。
11
+ import { lstatSync, readFileSync, realpathSync } from 'node:fs';
12
+ import path from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+
15
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
+ const epStageRoot = path.resolve(__dirname, '..');
17
+ const packageRoot = path.resolve(epStageRoot, '../..');
18
+ const repoRoot = path.resolve(packageRoot, '../..');
19
+
20
+ // version 值正则:YYYY-MM-DD+<7位十六进制短SHA>
21
+ const VERSION_PATTERN = /^\d{4}-\d{2}-\d{2}\+[0-9a-f]{7}$/;
22
+
23
+ function readFrontmatter(skillRoot) {
24
+ const skillPath = path.join(skillRoot, 'SKILL.md');
25
+ const text = readFileSync(skillPath, 'utf8');
26
+ // Windows 工作树 core.autocrlf=true 时文件是 CRLF,先容忍 \r 再匹配。
27
+ const match = text.replace(/\r/g, '').match(/^---\n([\s\S]*?)\n---\n/);
28
+ if (!match) throw new Error(`${skillPath} missing YAML frontmatter`);
29
+
30
+ const frontmatter = match[1];
31
+ return {
32
+ name: frontmatter.match(/^name:\s*(.+)$/m)?.[1]?.trim(),
33
+ description: frontmatter.match(/^description:\s*(.+)$/m)?.[1]?.trim(),
34
+ version: frontmatter.match(/^version:\s*(.+)$/m)?.[1]?.trim(),
35
+ skillPath,
36
+ };
37
+ }
38
+
39
+ function assertSkill(skillRoot, expectedName) {
40
+ const { name, description, version, skillPath } = readFrontmatter(skillRoot);
41
+ if (name !== expectedName) throw new Error(`Expected name ${expectedName} in ${skillPath}, got ${name ?? 'missing'}`);
42
+ if (!description?.startsWith('使用时机') && !description?.startsWith('Use when')) throw new Error(`${skillPath} description 必须以「使用时机」或「Use when」开头`);
43
+ if (description.length > 1024) throw new Error(`${skillPath} description exceeds 1024 characters`);
44
+ if (path.basename(skillRoot) !== name) throw new Error(`${skillPath} directory basename must match name`);
45
+ if (!version) throw new Error(`${skillPath} version 字段缺失;维护者改 SKILL.md 时必须同步更新 version(格式 YYYY-MM-DD+<7位短SHA>)`);
46
+ if (!VERSION_PATTERN.test(version)) throw new Error(`${skillPath} version 格式非法 "${version}";应为 YYYY-MM-DD+<7位短SHA>,如 2026-06-23+a9e7576`);
47
+ return { name, descriptionLength: description.length, version, skillRoot };
48
+ }
49
+
50
+ function assertSymlink(linkPath, targetPath) {
51
+ const linkStat = lstatSync(linkPath);
52
+ if (!linkStat.isSymbolicLink()) throw new Error(`${linkPath} must be a symlink`);
53
+ if (realpathSync(linkPath) !== realpathSync(targetPath)) {
54
+ throw new Error(`${linkPath} does not point at ${targetPath}`);
55
+ }
56
+ }
57
+
58
+ // ep-stage scope 下四个 skill 的包内真相源目录。
59
+ // 注:Task 0.2 git mv 改名 3 个 skill 目录(glue-* 前缀化)。
60
+ // - glue-create-project:Task 2.1 已同步 SKILL.md frontmatter name → 'glue-create-project'。
61
+ // - glue-generate-testcase:Task 3.2 已同步 SKILL.md frontmatter name → 'glue-generate-testcase'。
62
+ // - glue-run-test:Task 4.3 已同步 SKILL.md frontmatter name → 'glue-run-test'。
63
+ const skillSources = [
64
+ { name: 'glue-create-project', source: path.join(epStageRoot, 'glue-create-project') },
65
+ { name: 'glue-generate-testcase', source: path.join(epStageRoot, 'glue-generate-testcase') },
66
+ { name: 'glue-run-test', source: path.join(epStageRoot, 'glue-run-test') },
67
+ { name: 'recording-to-glue', source: path.join(epStageRoot, 'recording-to-glue') },
68
+ ];
69
+
70
+ // 校验每个源 skill 的 SKILL.md frontmatter。
71
+ const checks = skillSources.map(({ name, source }) => assertSkill(source, name));
72
+
73
+ // 发现目录:Claude Code .claude/skills/,Codex 扫 .agents/skills/。
74
+ // 两个工具都只扫直接子目录,每个子目录是一个 skill;用 symlink 指向包内源。
75
+ const discoveryDirs = [
76
+ path.join(repoRoot, '.claude/skills'),
77
+ path.join(repoRoot, '.agents/skills'),
78
+ ];
79
+
80
+ // 注:symlink 名总是与包内目录 basename 一致(Phase 0 重建后)。
81
+ // Task 4.3 收敛后,3 个 glue-* skill 的 SKILL.md `name` 与目录 basename 完全一致;symlink 仍以 basename 为准。
82
+ const symlinks = [];
83
+ // Windows 工作树 symlink 会物化为普通目录/文本文件,无法做指向校验,跳过(Mac/Linux 仍强制)。
84
+ const skipSymlinkCheck = process.platform === 'win32';
85
+ for (const dir of discoveryDirs) {
86
+ for (const { name, source } of skillSources) {
87
+ const linkName = path.basename(source);
88
+ if (!skipSymlinkCheck) assertSymlink(path.join(dir, linkName), source);
89
+ symlinks.push({ dir: path.relative(repoRoot, dir), name, linkName, pointsTo: path.relative(repoRoot, source), checkSkipped: skipSymlinkCheck });
90
+ }
91
+ }
92
+
93
+ console.log(JSON.stringify({
94
+ ok: true,
95
+ packageRoot,
96
+ checks,
97
+ symlinks,
98
+ }, null, 2));
@@ -1,36 +1,28 @@
1
- import type { TestcaseV2 } from '../testcase/testcase-v2.js';
2
1
  /**
3
- * testcase 子命令解析后的参数(REQ-CHAIN-01 / REQ-SCENE-01)。
2
+ * testcase 子命令解析后的参数(JSON5 新产线,Task 3)。
4
3
  *
5
- * - codeList:上游 code_list.md 绝对路径(必填);
6
- * - testsDir:<projectDir>/src/tests/<需求名> 绝对路径(必填);
7
- * - scenario:①档优先级(显式 CLI 参数);
8
- * - hints:ModuleHints JSON 路径(可选;推断 scenario 用);
9
- * - projectDir / requirementDir:可选覆盖,默认从 codeList 路径推断。
4
+ * - codeList:上游 _docs/code_list.json5 绝对路径(必填,必须是 .json5)
5
+ * - testsDir:<projectDir>/src/tests/<需求名> 绝对路径(必填)
6
+ * - projectDir / requirementDir:可选覆盖,默认从 codeList 路径推断;
7
+ * projectDir 用于 projects.index.json5 回写,新产线不依赖旧 probe 物料解析。
10
8
  */
11
9
  export type GenerateTestcaseArgs = {
12
10
  codeList: string;
13
11
  testsDir: string;
14
- scenario?: string;
15
- hints?: string;
16
12
  projectDir?: string;
17
13
  requirementDir?: string;
14
+ directUrl?: string;
15
+ sourceRoot?: string;
18
16
  };
19
17
  /**
20
- * testcase 子命令产物 + 推断结果(供 skill Step 5 交互确认)。
18
+ * testcase 子命令产物路径(JSON5 新产线,Task 3)。
21
19
  */
22
20
  export type GenerateTestcaseResult = {
23
21
  testcasePath: string;
24
22
  testcaseMdPath: string;
25
23
  credentialsTemplatePath: string;
26
- inference: {
27
- scenario: string;
28
- requiredSystems: TestcaseV2['requiredSystems'];
29
- requiredRoles: TestcaseV2['requiredRoles'];
30
- credentialsTemplatePath: string;
31
- credentialsActualPath: string;
32
- cachedSystemsHit: string[];
33
- };
24
+ inferenceTracePath: string;
25
+ contractPath: string;
34
26
  };
35
27
  /**
36
28
  * 解析 testcase 子命令参数。
@@ -41,43 +33,18 @@ export type GenerateTestcaseResult = {
41
33
  */
42
34
  export declare function parseGenerateTestcaseArgs(argv: string[]): GenerateTestcaseArgs;
43
35
  /**
44
- * scenario 3 档决策(REQ-SCENE-01):
45
- * 1. --scenario CLI 参数(最高优先级)
46
- * 2. ModuleHints 文件路径包含 `scenario=<value>` 字串(粗推断)
47
- * 3. 默认 "crud.single-page"(与 i18n 表 / skeleton scenario 一致)
48
- *
49
- * @param args - 解析后的参数。
50
- * @returns 决策结果。
51
- */
52
- export declare function decideScenario(args: GenerateTestcaseArgs): string;
53
- /**
54
- * 把 TestcaseV2 渲染成中文 testcase.md(REQ-UX-03)。
55
- *
56
- * 设计:
57
- * - 字段名 / 状态值通过 i18nTestcase() 翻译;
58
- * - `unresolved` 字段标题硬编码「未确认项」(i18n 表中 `unresolved` 解析为状态值「未解决」);
59
- * - 每次重跑覆盖(REQ-DATA-03 由调用方 writeFileSync 触发)。
60
- *
61
- * @param testcase - TestcaseV2 顶层结构。
62
- * @returns Markdown 文本。
63
- */
64
- export declare function renderTestcaseMarkdown(testcase: TestcaseV2): string;
65
- /**
66
- * testcase 子命令主体(REQ-CHAIN-01 / REQ-CHAIN-02 / REQ-SCENE-01 / REQ-DATA-02)。
36
+ * testcase 子命令主体(JSON5 新产线,Task 3)。
67
37
  *
68
38
  * 流程:
69
- * 1. 决定 scenario(3 档优先级,写入 testcase 顶层 scenario 字段);
70
- * 2. runProbeAndCoverage 钻探上游物料 + 装配 TestcaseV2
71
- * 3. 落盘 testcase.json5 + testcase.md(中文 i18n) + credentials.template.json5;
72
- * 4. 软链上游 code_list.md <tests-dir>/code_list.md;
73
- * 5. 返回推断结果(供 skill Step 5 交互确认 systemUrl / credentialsCache 命中)。
74
- *
75
- * 注意:contract 提取通过 probe 内部完成,不直接复用 dev/extract-contract.ts 的 CLI 入口
76
- * (probe + extract-contract 共享同一组 buildCrudBusinessModuleContract + extractor 依赖,
77
- * 二者各自负责自己的 I/O 边界,保持职责单一)。
39
+ * 1. extractCodeListJson5 解析 code_list.json5;
40
+ * 2. assembleJson5ContractAndTestcase 装配 contract + testcase + inference-trace
41
+ * 3. validateContract / validateTestcase schema 门禁;
42
+ * 4. 落盘 .ep-stage/contract.json5 + testcase.json5 + .ep-stage/inference-trace.json +
43
+ * credentials.template.json5 + testcase.md;软链 code_list.json5;
44
+ * 5. 可选回写 projects.index.json5(仅当显式传入 projectDir)。
78
45
  *
79
46
  * @param args - 已解析参数。
80
- * @returns 产物路径 + 推断结果。
47
+ * @returns 产物路径。
81
48
  */
82
49
  export declare function runGenerateTestcase(args: GenerateTestcaseArgs): GenerateTestcaseResult;
83
50
  //# sourceMappingURL=generate-testcase.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generate-testcase.d.ts","sourceRoot":"","sources":["../../../src/cli/generate-testcase.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7D;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,uBAAuB,EAAE,MAAM,CAAC;IAChC,SAAS,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,eAAe,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC;QAC/C,aAAa,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC;QAC3C,uBAAuB,EAAE,MAAM,CAAC;QAChC,qBAAqB,EAAE,MAAM,CAAC;QAC9B,gBAAgB,EAAE,MAAM,EAAE,CAAC;KAC5B,CAAC;CACH,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,oBAAoB,CAqB9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,oBAAoB,GAAG,MAAM,CAOjE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAgDnE;AAsBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,sBAAsB,CA+CtF"}
1
+ {"version":3,"file":"generate-testcase.d.ts","sourceRoot":"","sources":["../../../src/cli/generate-testcase.ts"],"names":[],"mappings":"AAaA;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,uBAAuB,EAAE,MAAM,CAAC;IAChC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,oBAAoB,CAqB9E;AAqGD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,sBAAsB,CAkEtF"}