@epoint-testtech/ep-stage-skill 0.0.9 → 0.5.0-alpha.2

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 (158) hide show
  1. package/SKILL.md +27 -27
  2. package/codex-skill/ep-stage/glue-create-project/SKILL.md +223 -223
  3. package/codex-skill/ep-stage/glue-generate-testcase/SKILL.md +275 -275
  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 -95
  6. package/codex-skill/ep-stage/glue-run-test/SKILL.md +273 -296
  7. package/codex-skill/ep-stage/glue-run-test/references/ai-runtime-loop.md +30 -30
  8. package/codex-skill/ep-stage/glue-run-test/references/crud-pipeline.md +132 -132
  9. package/codex-skill/ep-stage/glue-run-test/references/gap-review-protocol.md +44 -44
  10. package/codex-skill/ep-stage/glue-run-test/references/harness-principles.md +46 -46
  11. package/codex-skill/ep-stage/glue-run-test/references/playwright-cli-odav.md +127 -166
  12. package/codex-skill/ep-stage/glue-run-test/scripts/generate-crud-spec.mjs +149 -149
  13. package/codex-skill/ep-stage/recording-to-glue/SKILL.md +28 -28
  14. package/codex-skill/ep-stage/scripts/validate-skill.mjs +95 -98
  15. package/dist/src/cli/generate-testcase.d.ts +0 -1
  16. package/dist/src/cli/generate-testcase.d.ts.map +1 -1
  17. package/dist/src/cli/generate-testcase.js +1 -6
  18. package/dist/src/cli/index.d.ts.map +1 -1
  19. package/dist/src/cli/index.js +14 -38
  20. package/dist/src/cli/run.d.ts +0 -2
  21. package/dist/src/cli/run.d.ts.map +1 -1
  22. package/dist/src/cli/run.js +21 -70
  23. package/dist/src/cli/spec.d.ts.map +1 -1
  24. package/dist/src/cli/spec.js +0 -9
  25. package/dist/src/contracts/glue-contract-v1.d.ts +0 -35
  26. package/dist/src/contracts/glue-contract-v1.d.ts.map +1 -1
  27. package/dist/src/extractors/code-list-json5.d.ts +1 -34
  28. package/dist/src/extractors/code-list-json5.d.ts.map +1 -1
  29. package/dist/src/extractors/code-list-json5.js +0 -1
  30. package/dist/src/extractors/html-page.d.ts +0 -9
  31. package/dist/src/extractors/html-page.d.ts.map +1 -1
  32. package/dist/src/extractors/html-page.js +2 -26
  33. package/dist/src/generators/agent-inferred-workflow-script.js +66 -66
  34. package/dist/src/generators/stage-skeleton-script.d.ts +0 -50
  35. package/dist/src/generators/stage-skeleton-script.d.ts.map +1 -1
  36. package/dist/src/generators/stage-skeleton-script.js +116 -449
  37. package/dist/src/runtime/ai-runtime-trace.d.ts.map +1 -1
  38. package/dist/src/runtime/ai-runtime-trace.js +0 -8
  39. package/dist/src/testcase/contract-testcase-assembler.d.ts +0 -28
  40. package/dist/src/testcase/contract-testcase-assembler.d.ts.map +1 -1
  41. package/dist/src/testcase/contract-testcase-assembler.js +75 -329
  42. package/dist/src/validation/contract.d.ts.map +1 -1
  43. package/dist/src/validation/contract.js +0 -29
  44. package/dist/test/cli/generate-testcase-json5.test.d.ts +2 -0
  45. package/dist/test/cli/generate-testcase-json5.test.d.ts.map +1 -0
  46. package/dist/test/cli/generate-testcase-json5.test.js +154 -0
  47. package/dist/test/cli/index.test.d.ts +2 -0
  48. package/dist/test/cli/index.test.d.ts.map +1 -0
  49. package/dist/test/cli/index.test.js +114 -0
  50. package/dist/test/cli/run.test.d.ts +2 -0
  51. package/dist/test/cli/run.test.d.ts.map +1 -0
  52. package/dist/test/cli/run.test.js +429 -0
  53. package/dist/test/cli/spec.test.d.ts +2 -0
  54. package/dist/test/cli/spec.test.d.ts.map +1 -0
  55. package/dist/test/cli/spec.test.js +112 -0
  56. package/dist/test/code-list-json5.test.d.ts +2 -0
  57. package/dist/test/code-list-json5.test.d.ts.map +1 -0
  58. package/dist/test/code-list-json5.test.js +84 -0
  59. package/dist/test/code-list.test.d.ts +2 -0
  60. package/dist/test/code-list.test.d.ts.map +1 -0
  61. package/dist/test/code-list.test.js +39 -0
  62. package/dist/test/gap-inference.test.d.ts +2 -0
  63. package/dist/test/gap-inference.test.d.ts.map +1 -0
  64. package/dist/test/gap-inference.test.js +597 -0
  65. package/dist/test/generalization.test.d.ts +2 -0
  66. package/dist/test/generalization.test.d.ts.map +1 -0
  67. package/dist/test/generalization.test.js +73 -0
  68. package/dist/test/material-inventory.test.d.ts +2 -0
  69. package/dist/test/material-inventory.test.d.ts.map +1 -0
  70. package/dist/test/material-inventory.test.js +155 -0
  71. package/dist/test/observable-chain.test.d.ts +2 -0
  72. package/dist/test/observable-chain.test.d.ts.map +1 -0
  73. package/dist/test/observable-chain.test.js +123 -0
  74. package/dist/test/page-structure.test.d.ts +2 -0
  75. package/dist/test/page-structure.test.d.ts.map +1 -0
  76. package/dist/test/page-structure.test.js +45 -0
  77. package/dist/test/runtime/ai-runtime-trace.test.d.ts +2 -0
  78. package/dist/test/runtime/ai-runtime-trace.test.d.ts.map +1 -0
  79. package/dist/test/runtime/ai-runtime-trace.test.js +78 -0
  80. package/dist/test/scenario-inference.test.d.ts +2 -0
  81. package/dist/test/scenario-inference.test.d.ts.map +1 -0
  82. package/dist/test/scenario-inference.test.js +73 -0
  83. package/dist/test/stage-context.test.d.ts +2 -0
  84. package/dist/test/stage-context.test.d.ts.map +1 -0
  85. package/dist/test/stage-context.test.js +395 -0
  86. package/dist/test/testcase/testcase-markdown.test.d.ts +2 -0
  87. package/dist/test/testcase/testcase-markdown.test.d.ts.map +1 -0
  88. package/dist/test/testcase/testcase-markdown.test.js +48 -0
  89. package/dist/test/testcase-generator.test.d.ts +2 -0
  90. package/dist/test/testcase-generator.test.d.ts.map +1 -0
  91. package/dist/test/testcase-generator.test.js +276 -0
  92. package/dist/test/testcase-skeleton.test.d.ts +2 -0
  93. package/dist/test/testcase-skeleton.test.d.ts.map +1 -0
  94. package/dist/test/testcase-skeleton.test.js +185 -0
  95. package/dist/test/testcase-spec-assembly.test.d.ts +2 -0
  96. package/dist/test/testcase-spec-assembly.test.d.ts.map +1 -0
  97. package/dist/test/testcase-spec-assembly.test.js +105 -0
  98. package/dist/test/util/credentials.test.d.ts +2 -0
  99. package/dist/test/util/credentials.test.d.ts.map +1 -0
  100. package/dist/test/util/credentials.test.js +64 -0
  101. package/dist/test/util/i18n-testcase.test.d.ts +2 -0
  102. package/dist/test/util/i18n-testcase.test.d.ts.map +1 -0
  103. package/dist/test/util/i18n-testcase.test.js +119 -0
  104. package/dist/test/util/softlink.test.d.ts +2 -0
  105. package/dist/test/util/softlink.test.d.ts.map +1 -0
  106. package/dist/test/util/softlink.test.js +82 -0
  107. package/dist/test/validation/contract.test.d.ts +2 -0
  108. package/dist/test/validation/contract.test.d.ts.map +1 -0
  109. package/dist/test/validation/contract.test.js +116 -0
  110. package/dist/test/validation/credentials.test.d.ts +2 -0
  111. package/dist/test/validation/credentials.test.d.ts.map +1 -0
  112. package/dist/test/validation/credentials.test.js +74 -0
  113. package/dist/test/validation/projects-index.test.d.ts +2 -0
  114. package/dist/test/validation/projects-index.test.d.ts.map +1 -0
  115. package/dist/test/validation/projects-index.test.js +48 -0
  116. package/dist/test/validation/testcase-v1-draft.test.d.ts +2 -0
  117. package/dist/test/validation/testcase-v1-draft.test.d.ts.map +1 -0
  118. package/dist/test/validation/testcase-v1-draft.test.js +173 -0
  119. package/dist/vitest.config.d.ts +3 -0
  120. package/dist/vitest.config.d.ts.map +1 -0
  121. package/dist/vitest.config.js +7 -0
  122. package/docs/README.md +124 -124
  123. package/examples/schemeresource-observable-docs/schemeresource.context.md +20 -20
  124. package/examples/schemeresource.module-hints.json +38 -38
  125. package/examples/schemeresource.observable.code_list.md +37 -37
  126. package/examples/zwplace-observable-docs/zwplace.context.md +16 -16
  127. package/examples/zwplace-placecategory-validation.json +29 -29
  128. package/examples/zwplace.module-hints.json +69 -69
  129. package/examples/zwplace.observable.code_list.md +37 -37
  130. package/package.json +19 -13
  131. package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-tpl.md +0 -72
  132. package/codex-skill/ep-stage/glue-run-test/scripts/extract-page-elements-eval.mjs +0 -233
  133. package/codex-skill/ep-stage/page-element-scan/SKILL.md +0 -91
  134. package/codex-skill/ep-stage/page-element-scan/evals/evals.json +0 -37
  135. package/codex-skill/ep-stage/page-element-scan/evals/fixtures/sample-list-page.html +0 -85
  136. package/codex-skill/ep-stage/page-element-scan/references/pagination-controls.md +0 -153
  137. package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements-eval.mjs +0 -233
  138. package/codex-skill/ep-stage/page-element-scan/scripts/extract-page-elements.mjs +0 -298
  139. package/codex-skill/ep-stage/page-element-scan/scripts/scan-page.mjs +0 -749
  140. package/dist/src/cli/probe.d.ts +0 -46
  141. package/dist/src/cli/probe.d.ts.map +0 -1
  142. package/dist/src/cli/probe.js +0 -447
  143. package/dist/src/cli/run-report.d.ts +0 -46
  144. package/dist/src/cli/run-report.d.ts.map +0 -1
  145. package/dist/src/cli/run-report.js +0 -316
  146. package/dist/src/extractors/prd-extractor.d.ts +0 -70
  147. package/dist/src/extractors/prd-extractor.d.ts.map +0 -1
  148. package/dist/src/extractors/prd-extractor.js +0 -248
  149. package/dist/src/testcase/button-interaction-inference.d.ts +0 -37
  150. package/dist/src/testcase/button-interaction-inference.d.ts.map +0 -1
  151. package/dist/src/testcase/button-interaction-inference.js +0 -385
  152. package/dist/src/testcase/prd-button-mapper.d.ts +0 -72
  153. package/dist/src/testcase/prd-button-mapper.d.ts.map +0 -1
  154. package/dist/src/testcase/prd-button-mapper.js +0 -148
  155. package/dist/src/testcase/testcase-v2.d.ts +0 -50
  156. package/dist/src/testcase/testcase-v2.d.ts.map +0 -1
  157. package/dist/src/testcase/testcase-v2.js +0 -1
  158. package/docs/mvp-usage-guide.md +0 -298
@@ -1,28 +1,28 @@
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
+ ---
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,98 +1,95 @@
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
+ #!/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(/^\s*version:\s*["']?([^"'\n]+)["']?\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));
@@ -12,7 +12,6 @@ export type GenerateTestcaseArgs = {
12
12
  projectDir?: string;
13
13
  requirementDir?: string;
14
14
  directUrl?: string;
15
- sourceRoot?: string;
16
15
  };
17
16
  /**
18
17
  * testcase 子命令产物路径(JSON5 新产线,Task 3)。
@@ -1 +1 @@
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"}
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;CACpB,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,CAoB9E;AAqGD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,sBAAsB,CA8DtF"}
@@ -37,7 +37,6 @@ export function parseGenerateTestcaseArgs(argv) {
37
37
  projectDir: result['project-dir'],
38
38
  requirementDir: result['requirement-dir'],
39
39
  directUrl: result['direct-url'],
40
- sourceRoot: result['source-root'],
41
40
  };
42
41
  }
43
42
  /**
@@ -145,11 +144,7 @@ export function runGenerateTestcase(args) {
145
144
  if (!directUrl) {
146
145
  throw new Error('缺少待测页面 directUrl:请在 /glue-generate-testcase 交互中提供列表页直达地址,或在 code_list.json5 的 pages[role=list].directUrl 中提供。');
147
146
  }
148
- const { contract, testcase, inferenceTrace } = assembleJson5ContractAndTestcase({
149
- codeList,
150
- directUrl,
151
- sourceRoot: args.sourceRoot,
152
- });
147
+ const { contract, testcase, inferenceTrace } = assembleJson5ContractAndTestcase({ codeList, directUrl });
153
148
  if (args.projectDir) {
154
149
  testcase.runtimeContext = {
155
150
  testProjectDir: args.projectDir,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":";AAOA;;;;;;;;;;;GAWG;AACH,wBAAsB,IAAI,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,CAuD1E;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAchC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAS3F"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/index.ts"],"names":[],"mappings":";AAOA;;;;;;;;;;;GAWG;AACH,wBAAsB,IAAI,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,CA+B1E;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAchC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAS3F"}
@@ -34,32 +34,8 @@ export async function main(argv = process.argv) {
34
34
  ].join('\n'));
35
35
  return result;
36
36
  }
37
- case 'run': {
38
- const result = await runRun(parseRunArgs(args));
39
- // 结果摘要回显(此前 run 静默退出,自动化无法感知成败)。
40
- const lines = [
41
- `spec: ${result.specPath}`,
42
- `report: ${result.reportDir}`,
43
- `trace: ${result.aiRuntimeTracePath}`,
44
- ];
45
- if (result.comprehensiveReportPath) {
46
- lines.push(`综合报告: ${result.comprehensiveReportPath}`);
47
- }
48
- if (result.repairReportPath) {
49
- lines.push(`repair: ${result.repairReportPath}`);
50
- }
51
- if (result.credentialsNotice) {
52
- lines.push(result.credentialsNotice);
53
- }
54
- console.log(lines.join('\n'));
55
- // 执行失败(已落修补报告)以非 0 退出,对齐 skill 文档「CLI 非 0 退出 → 复述 stderr」契约;
56
- // 修补流程本身不受影响,人审确认方向后重跑即可。
57
- if (result.repairReportPath) {
58
- console.error(`执行失败:已落修补报告 ${result.repairReportPath},待人审确认修补方向`);
59
- process.exitCode = 1;
60
- }
61
- return result;
62
- }
37
+ case 'run':
38
+ return runRun(parseRunArgs(args));
63
39
  case undefined:
64
40
  case '--help':
65
41
  case '-h':
@@ -74,18 +50,18 @@ export async function main(argv = process.argv) {
74
50
  * 打印 CLI help(仅 testcase / spec / run 3 子命令,无 contract)。
75
51
  */
76
52
  export function printHelp() {
77
- console.log(`ep-stage-skill — 胶水测试链路 CLI
78
-
79
- 用法:
80
- ep-stage-skill testcase --code-list <path> --tests-dir <path> --direct-url <url> [--project-dir <path>] [--requirement-dir <path>]
81
- ep-stage-skill spec --testcase <path> --out <path>
82
- ep-stage-skill prepare-tasks --testcase <path>
83
- ep-stage-skill run --testcase <path> [--ai-snippets <path>] [--headless false]
84
-
85
- 子命令:
86
- testcase 读取 _docs/code_list.json5 + 生成 contract.json5 / testcase.json5 / inference-trace.json
87
- spec 基于 testcase.json5 + contract.json5 的 glue cases 生成初始版 .spec.ts(只生成不执行)
88
- prepare-tasks 组装 glue 骨架并输出 AI Runtime 任务包
53
+ console.log(`ep-stage-skill — 胶水测试链路 CLI
54
+
55
+ 用法:
56
+ ep-stage-skill testcase --code-list <path> --tests-dir <path> --direct-url <url> [--project-dir <path>] [--requirement-dir <path>]
57
+ ep-stage-skill spec --testcase <path> --out <path>
58
+ ep-stage-skill prepare-tasks --testcase <path>
59
+ ep-stage-skill run --testcase <path> [--ai-snippets <path>] [--headless false]
60
+
61
+ 子命令:
62
+ testcase 读取 _docs/code_list.json5 + 生成 contract.json5 / testcase.json5 / inference-trace.json
63
+ spec 基于 testcase.json5 + contract.json5 的 glue cases 生成初始版 .spec.ts(只生成不执行)
64
+ prepare-tasks 组装 glue 骨架并输出 AI Runtime 任务包
89
65
  run 合并 Agent 产出的 ai-snippets.json5 并执行完整 spec`);
90
66
  }
91
67
  /**
@@ -21,7 +21,6 @@ export type RunArgs = {
21
21
  * @property version - 'v1':已接入 Agent snippets 合并、spec 执行和 repair 回路。
22
22
  * @property aiRuntimeTracePath - 落盘的 .ep-stage/ai-runtime-trace.json5(含 tasks 与 results)。
23
23
  * @property repairReportPath - 执行失败时落盘的 repair-report.md;成功时为 undefined。
24
- * @property comprehensiveReportPath - glue 风格综合报告 <reportDir>/index.html;渲染失败时为 undefined。
25
24
  */
26
25
  export type RunResult = {
27
26
  specPath: string;
@@ -30,7 +29,6 @@ export type RunResult = {
30
29
  version: 'v1';
31
30
  aiRuntimeTracePath?: string;
32
31
  repairReportPath?: string;
33
- comprehensiveReportPath?: string;
34
32
  };
35
33
  /**
36
34
  * runRun 的可注入依赖(测试即契约:允许单测替换推理与执行,避免触发真机)。
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/run.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/run.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,10 @@
1
- import { spawnSync } from 'node:child_process';
1
+ import { execFileSync } from 'node:child_process';
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import JSON5 from 'json5';
5
5
  import { validateTestcase } from '../validation/testcase.js';
6
6
  import { classifyRunFailure, createInitialAiRuntimeTrace, nextRepairRound, writeAiRuntimeTrace, writeRepairReport, } from '../runtime/ai-runtime-trace.js';
7
7
  import { runSpec } from './spec.js';
8
- import { parsePlaywrightJsonResults, renderRunReportHtml } from './run-report.js';
9
8
  /**
10
9
  * AI 追加段分隔标记。重跑时 detect 已有标记则覆盖(保证幂等)。
11
10
  *
@@ -115,18 +114,7 @@ export function resolveRunPaths(testcasePath) {
115
114
  * @returns 命令、参数与 cwd,供 execFileSync 直接消费。
116
115
  */
117
116
  export function buildPlaywrightCommand(input) {
118
- // Playwright 命令行参数按正则表达式匹配测试文件路径;原样传中文/含点号
119
- // 的绝对路径会被误解析为无效正则。取 basename 并转义正则特殊字符,
120
- // 构造 `.*<escaped-basename>` 模式,在 testDir 内稳定命中对应 spec。
121
- const basename = path.basename(input.specPath);
122
- const escapedBasename = basename.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
123
- const specArg = `.*${escapedBasename}`;
124
- // 枚举多个 reporter:list 保证控制台有可被 classifyRunFailure 消费的原生输出;
125
- // json 落机器可读结果(经 PLAYWRIGHT_JSON_OUTPUT_NAME 指定文件)供 glue 风格综合报告渲染;
126
- // html 与 stage-core reporter 让综合报告(playwright-report / stage-report)随 run 一并生成,
127
- // 避免执行完还要手工补跑才能拿到汇总报告。CLI --reporter 会整体覆盖项目 config,
128
- // 所以必须在同一条参数里显式枚举,不能只加 list。
129
- const args = ['playwright', 'test', specArg, '--reporter=list,json,html,@epoint-testtech/stage-core/reporter'];
117
+ const args = ['playwright', 'test', input.specPath];
130
118
  if (!input.headless) {
131
119
  args.push('--headed');
132
120
  }
@@ -142,21 +130,22 @@ export function buildPlaywrightCommand(input) {
142
130
  */
143
131
  async function defaultExecuteFullSpec(input) {
144
132
  const cmd = buildPlaywrightCommand(input);
145
- // Windows execFileSync 调用 npx 经常捕获不到子进程输出(status=null,
146
- // stdout/stderr 为空)。改用 shell 执行 spawnSync,并手动拼接命令字符串
147
- // 避免 Node 的 DEP0190 警告(shell=true 时仍传 args 会被弃用)。
148
- const command = `${cmd.command} ${cmd.args.map((arg) => `"${arg.replace(/"/g, '\\"')}"`).join(' ')}`;
149
- const spawned = spawnSync(command, {
150
- cwd: cmd.cwd,
151
- encoding: 'utf8',
152
- shell: true,
153
- env: input.resultsJsonPath
154
- ? { ...process.env, PLAYWRIGHT_JSON_OUTPUT_NAME: input.resultsJsonPath }
155
- : process.env,
156
- });
157
- const stdout = spawned.stdout ?? '';
158
- const stderr = spawned.stderr ?? '';
159
- const status = spawned.status ?? 1;
133
+ let stdout = '';
134
+ let stderr = '';
135
+ let status = 0;
136
+ try {
137
+ stdout = execFileSync(cmd.command, cmd.args, {
138
+ cwd: cmd.cwd,
139
+ encoding: 'utf8',
140
+ stdio: ['ignore', 'pipe', 'pipe'],
141
+ });
142
+ }
143
+ catch (error) {
144
+ const err = error;
145
+ stdout = err.stdout ?? '';
146
+ stderr = err.stderr ?? '';
147
+ status = err.status ?? 1;
148
+ }
160
149
  return { passed: status === 0, output: `${stdout}\n${stderr}` };
161
150
  }
162
151
  /**
@@ -509,24 +498,17 @@ export async function runRun(args, deps) {
509
498
  }
510
499
  appendAiSnippets(paths.specPath, aiRuntimeTasks, resolvedResults);
511
500
  writeAiRuntimeTrace(tracePath, trace);
512
- // 报告目录先于执行创建:json reporter 结果文件落这里,供 glue 风格综合报告渲染。
513
- const reportDir = path.join(paths.projectDir, 'e2e/glue-code-mvp/glue-report', new Date().toISOString().replace(/[:.]/g, '-'));
514
- mkdirSync(reportDir, { recursive: true });
515
- const resultsJsonPath = path.join(reportDir, 'playwright-results.json');
516
501
  // 执行完整 spec。
517
502
  const executor = deps?.executeFullSpec ?? defaultExecuteFullSpec;
518
503
  const execution = await executor({
519
504
  projectDir: paths.projectDir,
520
505
  specPath: paths.specPath,
521
506
  headless: args.headless,
522
- resultsJsonPath,
523
507
  });
524
508
  // 失败时分类并写 repair report,停止等待人审;成功时 repairReportPath 留空。
525
509
  let repairReportPath;
526
- let failureType;
527
510
  if (!execution.passed) {
528
511
  const classification = classifyRunFailure(execution.output);
529
- failureType = classification.failureType;
530
512
  const round = nextRepairRound(paths.caseDir);
531
513
  repairReportPath = writeRepairReport({
532
514
  caseDir: paths.caseDir,
@@ -543,39 +525,9 @@ export async function runRun(args, deps) {
543
525
  });
544
526
  writeAiRuntimeTrace(tracePath, trace);
545
527
  }
546
- // 报告目录:落执行账本(完整执行输出 + 机器可读 run 信息),
547
- // 修补报告只截失败输出片段,完整证据以这里为准。
548
- writeFileSync(path.join(reportDir, 'run-output.log'), execution.output);
549
- writeFileSync(path.join(reportDir, 'run-info.json'), `${JSON.stringify({
550
- specPath: paths.specPath,
551
- passed: execution.passed,
552
- failureType,
553
- repairReportPath,
554
- executedAt: new Date().toISOString(),
555
- }, null, 2)}\n`);
556
- // glue 风格综合报告(index.html):数据来自 Playwright JSON 结果,
557
- // 覆盖全部用例(含 AI snippet);渲染失败只降级缺省,不阻断主流程。
558
- let comprehensiveReportPath;
559
- try {
560
- if (existsSync(resultsJsonPath)) {
561
- const parsedResults = parsePlaywrightJsonResults(readFileSync(resultsJsonPath, 'utf8'));
562
- const html = renderRunReportHtml({
563
- suiteTitle: parsedResults.suiteTitle,
564
- startedAt: parsedResults.startedAt,
565
- durationMs: parsedResults.durationMs,
566
- specFile: paths.specPath,
567
- items: parsedResults.items,
568
- aiTasks: aiRuntimeTasks.map((task) => ({ title: task.title, caseId: task.caseId })),
569
- playwrightReportHref: '../../../../playwright-report/index.html',
570
- stageReportHref: '../../../../stage-report/report.pdf',
571
- });
572
- comprehensiveReportPath = path.join(reportDir, 'index.html');
573
- writeFileSync(comprehensiveReportPath, html);
574
- }
575
- }
576
- catch {
577
- comprehensiveReportPath = undefined;
578
- }
528
+ // 报告目录。
529
+ const reportDir = path.join(paths.projectDir, 'e2e/glue-code-mvp/glue-report', new Date().toISOString().replace(/[:.]/g, '-'));
530
+ mkdirSync(reportDir, { recursive: true });
579
531
  // credentials.json5 消费边界声明。
580
532
  const credentialsNotice = loadCredentialsNotice(testcase, paths.testcasePath);
581
533
  return {
@@ -585,6 +537,5 @@ export async function runRun(args, deps) {
585
537
  version: 'v1',
586
538
  aiRuntimeTracePath: tracePath,
587
539
  repairReportPath,
588
- comprehensiveReportPath,
589
540
  };
590
541
  }
@@ -1 +1 @@
1
- {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../../../src/cli/spec.ts"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,IAAI,CAAC;CACf,CAAC;AAiCF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,QAAQ,CActD;AAcD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAsDlD"}
1
+ {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../../../src/cli/spec.ts"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,IAAI,CAAC;CACf,CAAC;AAiCF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,QAAQ,CActD;AAcD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CA6ClD"}
@@ -101,7 +101,6 @@ export function runSpec(args) {
101
101
  // skeletonSlots.crud 形态对齐 StageSkeletonCrudFlowSlots(见 glue-contract-v1.ts 注释)。
102
102
  const crudSlots = contract.scenario.skeletonSlots.crud;
103
103
  const exportSlot = contract.scenario.skeletonSlots.export;
104
- const childGridSlots = (contract.scenario.skeletonSlots.childGrids ?? []);
105
104
  const directUrl = contract.base?.navigation?.directUrl ?? '';
106
105
  if (!directUrl) {
107
106
  throw new Error('contract.base.navigation.directUrl 缺失。glue 主链路已切换为直达链接模式,请重跑 `/glue-generate-testcase` 补齐 --direct-url 后再生成 spec。');
@@ -119,14 +118,6 @@ export function runSpec(args) {
119
118
  requirementName,
120
119
  crudSlots,
121
120
  exportSlot,
122
- childGridSlots,
123
- glueCases: (testcase.cases ?? []).map((testCase) => ({
124
- caseId: testCase.caseId,
125
- title: testCase.title,
126
- precondition: testCase.precondition,
127
- steps: testCase.steps,
128
- assertion: testCase.assertion,
129
- })),
130
121
  });
131
122
  const body = `${SKELETON_MARKER_LINE}\n${script}`;
132
123
  mkdirSync(path.dirname(args.out), { recursive: true });
@@ -12,40 +12,6 @@
12
12
  * - ai:AI Agent 推理产出
13
13
  */
14
14
  export type GenerationStrategy = 'glue' | 'ai';
15
- /**
16
- * 子表列槽位。
17
- */
18
- export type ChildGridColumnSlot = {
19
- field: string;
20
- label: string;
21
- component: 'input' | 'listbox';
22
- required: boolean;
23
- modelRef?: string;
24
- };
25
- /**
26
- * 子表槽位。
27
- */
28
- export type ChildGridFlowSlot = {
29
- gridId: string;
30
- label: string;
31
- pageRole: 'add' | 'edit';
32
- editMode: 'cell_edit' | 'row_button_dialog' | 'none';
33
- columns: ChildGridColumnSlot[];
34
- addRowButtonLabel?: string;
35
- deleteSelectedButtonLabel?: string;
36
- locators: {
37
- container: string;
38
- addRowButton: string;
39
- deleteSelectedButton: string;
40
- grid: string;
41
- editWrapInput: string;
42
- editWrapListboxTrigger: string;
43
- editWrapListboxInput: string;
44
- rowCheckboxByIndex: string;
45
- cellByIndexAndField: string;
46
- cellValueByIndexAndField: string;
47
- };
48
- };
49
15
  /**
50
16
  * contract.json5 v1-draft 顶层类型。
51
17
  *
@@ -85,7 +51,6 @@ export type EpStageContractV1Draft = {
85
51
  label: string;
86
52
  mode: string;
87
53
  };
88
- childGrids?: ChildGridFlowSlot[];
89
54
  };
90
55
  gapCandidates: Array<{
91
56
  gapId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"glue-contract-v1.d.ts","sourceRoot":"","sources":["../../../src/contracts/glue-contract-v1.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,IAAI,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,GAAG,MAAM,CAAC;IACzB,QAAQ,EAAE,WAAW,GAAG,mBAAmB,GAAG,MAAM,CAAC;IACrD,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,IAAI,EAAE,MAAM,CAAC;QACb,aAAa,EAAE,MAAM,CAAC;QACtB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,oBAAoB,EAAE,MAAM,CAAC;QAC7B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,mBAAmB,EAAE,MAAM,CAAC;QAC5B,wBAAwB,EAAE,MAAM,CAAC;KAClC,CAAC;CACH,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,aAAa,EAAE,4BAA4B,CAAC;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE;QACJ,MAAM,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QACtC,UAAU,EAAE;YAAE,SAAS,EAAE,WAAW,GAAG,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACvF,WAAW,EAAE;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;KACjC,CAAC;IACF,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,KAAK,CAAC;YAClB,KAAK,EAAE,MAAM,CAAC;YACd,KAAK,EAAE,MAAM,CAAC;YACd,SAAS,EAAE,MAAM,CAAC;YAClB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;SAC1B,CAAC,CAAC;QACH,aAAa,EAAE;YAEb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC9B,MAAM,EAAE;gBAAE,QAAQ,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAA;aAAE,CAAC;YAC1D,UAAU,CAAC,EAAE,iBAAiB,EAAE,CAAC;SAClC,CAAC;QACF,aAAa,EAAE,KAAK,CAAC;YACnB,KAAK,EAAE,MAAM,CAAC;YACd,KAAK,EAAE,MAAM,CAAC;YACd,kBAAkB,EAAE,kBAAkB,CAAC;SACxC,CAAC,CAAC;KACJ,CAAC;IACF,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACjD,CAAC"}
1
+ {"version":3,"file":"glue-contract-v1.d.ts","sourceRoot":"","sources":["../../../src/contracts/glue-contract-v1.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,IAAI,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,aAAa,EAAE,4BAA4B,CAAC;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE;QACJ,MAAM,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QACtC,UAAU,EAAE;YAAE,SAAS,EAAE,WAAW,GAAG,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACvF,WAAW,EAAE;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;KACjC,CAAC;IACF,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,KAAK,CAAC;YAClB,KAAK,EAAE,MAAM,CAAC;YACd,KAAK,EAAE,MAAM,CAAC;YACd,SAAS,EAAE,MAAM,CAAC;YAClB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;SAC1B,CAAC,CAAC;QACH,aAAa,EAAE;YAEb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC9B,MAAM,EAAE;gBAAE,QAAQ,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAA;aAAE,CAAC;SAC3D,CAAC;QACF,aAAa,EAAE,KAAK,CAAC;YACnB,KAAK,EAAE,MAAM,CAAC;YACd,KAAK,EAAE,MAAM,CAAC;YACd,kBAAkB,EAAE,kBAAkB,CAAC;SACxC,CAAC,CAAC;KACJ,CAAC;IACF,eAAe,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACjD,CAAC"}