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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/SKILL.md +2 -2
  2. package/codex-skill/ep-stage/glue-create-project/SKILL.md +186 -0
  3. package/codex-skill/ep-stage/glue-generate-testcase/SKILL.md +199 -0
  4. package/codex-skill/ep-stage/glue-generate-testcase/references/testcase-schema.md +112 -0
  5. package/codex-skill/ep-stage/glue-run-test/SKILL.md +249 -0
  6. package/codex-skill/ep-stage/glue-run-test/references/crud-pipeline.md +145 -0
  7. package/codex-skill/ep-stage/{glue-test → glue-run-test}/scripts/generate-crud-spec.mjs +3 -3
  8. package/codex-skill/ep-stage/recording-to-glue/SKILL.md +1 -0
  9. package/codex-skill/ep-stage/scripts/validate-skill.mjs +29 -7
  10. package/dist/src/cli/dev/extract-contract.d.ts +14 -0
  11. package/dist/src/cli/dev/extract-contract.d.ts.map +1 -0
  12. package/dist/src/cli/dev/extract-contract.js +114 -0
  13. package/dist/src/cli/generate-crud-contract.js +7 -77
  14. package/dist/src/cli/generate-playwright-tests.d.ts +0 -28
  15. package/dist/src/cli/generate-playwright-tests.d.ts.map +1 -1
  16. package/dist/src/cli/generate-playwright-tests.js +4 -81
  17. package/dist/src/cli/generate-testcase.d.ts +83 -0
  18. package/dist/src/cli/generate-testcase.d.ts.map +1 -0
  19. package/dist/src/cli/generate-testcase.js +197 -0
  20. package/dist/src/cli/index.d.ts +18 -0
  21. package/dist/src/cli/index.d.ts.map +1 -0
  22. package/dist/src/cli/index.js +55 -0
  23. package/dist/src/cli/probe.d.ts +44 -0
  24. package/dist/src/cli/probe.d.ts.map +1 -0
  25. package/dist/src/cli/probe.js +221 -0
  26. package/dist/src/cli/run-gap-pipeline.js +4 -0
  27. package/dist/src/cli/run.d.ts +63 -0
  28. package/dist/src/cli/run.d.ts.map +1 -0
  29. package/dist/src/cli/run.js +116 -0
  30. package/dist/src/cli/spec.d.ts +45 -0
  31. package/dist/src/cli/spec.d.ts.map +1 -0
  32. package/dist/src/cli/spec.js +74 -0
  33. package/dist/src/context/stage-context.d.ts +72 -8
  34. package/dist/src/context/stage-context.d.ts.map +1 -1
  35. package/dist/src/context/stage-context.js +61 -15
  36. package/dist/src/index.d.ts +2 -2
  37. package/dist/src/index.d.ts.map +1 -1
  38. package/dist/src/index.js +1 -1
  39. package/dist/src/testcase/testcase-generator.d.ts.map +1 -1
  40. package/dist/src/testcase/testcase-generator.js +4 -0
  41. package/dist/src/testcase/testcase-v2.d.ts +50 -0
  42. package/dist/src/testcase/testcase-v2.d.ts.map +1 -0
  43. package/dist/src/testcase/testcase-v2.js +1 -0
  44. package/dist/src/util/credentials.d.ts +12 -0
  45. package/dist/src/util/credentials.d.ts.map +1 -0
  46. package/dist/src/util/credentials.js +19 -0
  47. package/dist/src/util/i18n-testcase.d.ts +8 -0
  48. package/dist/src/util/i18n-testcase.d.ts.map +1 -0
  49. package/dist/src/util/i18n-testcase.js +55 -0
  50. package/dist/src/util/softlink.d.ts +33 -0
  51. package/dist/src/util/softlink.d.ts.map +1 -0
  52. package/dist/src/util/softlink.js +43 -0
  53. package/dist/src/validation/credentials.d.ts +19 -0
  54. package/dist/src/validation/credentials.d.ts.map +1 -0
  55. package/dist/src/validation/credentials.js +38 -0
  56. package/dist/src/validation/index.d.ts +5 -0
  57. package/dist/src/validation/index.d.ts.map +1 -0
  58. package/dist/src/validation/index.js +3 -0
  59. package/dist/src/validation/projects-index.d.ts +13 -0
  60. package/dist/src/validation/projects-index.d.ts.map +1 -0
  61. package/dist/src/validation/projects-index.js +37 -0
  62. package/dist/src/validation/testcase.d.ts +13 -0
  63. package/dist/src/validation/testcase.d.ts.map +1 -0
  64. package/dist/src/validation/testcase.js +53 -0
  65. package/dist/test/cli/extract-contract.test.d.ts +2 -0
  66. package/dist/test/cli/extract-contract.test.d.ts.map +1 -0
  67. package/dist/test/cli/extract-contract.test.js +32 -0
  68. package/dist/test/cli/generate-testcase.test.d.ts +2 -0
  69. package/dist/test/cli/generate-testcase.test.d.ts.map +1 -0
  70. package/dist/test/cli/generate-testcase.test.js +130 -0
  71. package/dist/test/cli/index.test.d.ts +2 -0
  72. package/dist/test/cli/index.test.d.ts.map +1 -0
  73. package/dist/test/cli/index.test.js +93 -0
  74. package/dist/test/cli/run.test.d.ts +2 -0
  75. package/dist/test/cli/run.test.d.ts.map +1 -0
  76. package/dist/test/cli/run.test.js +149 -0
  77. package/dist/test/cli/spec.test.d.ts +2 -0
  78. package/dist/test/cli/spec.test.d.ts.map +1 -0
  79. package/dist/test/cli/spec.test.js +196 -0
  80. package/dist/test/stage-context.test.js +145 -13
  81. package/dist/test/util/credentials.test.d.ts +2 -0
  82. package/dist/test/util/credentials.test.d.ts.map +1 -0
  83. package/dist/test/util/credentials.test.js +64 -0
  84. package/dist/test/util/i18n-testcase.test.d.ts +2 -0
  85. package/dist/test/util/i18n-testcase.test.d.ts.map +1 -0
  86. package/dist/test/util/i18n-testcase.test.js +119 -0
  87. package/dist/test/util/softlink.test.d.ts +2 -0
  88. package/dist/test/util/softlink.test.d.ts.map +1 -0
  89. package/dist/test/util/softlink.test.js +82 -0
  90. package/dist/test/validation/credentials.test.d.ts +2 -0
  91. package/dist/test/validation/credentials.test.d.ts.map +1 -0
  92. package/dist/test/validation/credentials.test.js +72 -0
  93. package/dist/test/validation/projects-index.test.d.ts +2 -0
  94. package/dist/test/validation/projects-index.test.d.ts.map +1 -0
  95. package/dist/test/validation/projects-index.test.js +48 -0
  96. package/dist/test/validation/testcase.test.d.ts +2 -0
  97. package/dist/test/validation/testcase.test.d.ts.map +1 -0
  98. package/dist/test/validation/testcase.test.js +129 -0
  99. package/docs/README.md +6 -6
  100. package/docs/mvp-usage-guide.md +3 -3
  101. package/package.json +9 -4
  102. package/codex-skill/ep-stage/create-project/SKILL.md +0 -59
  103. package/codex-skill/ep-stage/glue-test/SKILL.md +0 -258
  104. package/codex-skill/ep-stage/glue-test/references/crud-pipeline.md +0 -139
  105. package/codex-skill/ep-stage/glue-testcase/SKILL.md +0 -31
  106. package/codex-skill/ep-stage/glue-testcase/references/testcase-schema.md +0 -67
  107. /package/codex-skill/ep-stage/{glue-testcase → glue-generate-testcase}/examples/observable-testcase.json +0 -0
  108. /package/codex-skill/ep-stage/{glue-test → glue-run-test}/references/gap-review-protocol.md +0 -0
  109. /package/codex-skill/ep-stage/{glue-test → glue-run-test}/references/harness-principles.md +0 -0
@@ -6,6 +6,9 @@ import YAML from 'yaml';
6
6
  /**
7
7
  * 读取项目级 stage-context.md,并将相对路径归一到项目目录。
8
8
  *
9
+ * @deprecated stage-context.md 已砍(REQ-ENV-01 修订);新链路应从 projects.index.json5 反查
10
+ * ({@link lookupProjectByRequirement})。本函数保留仅为零回归兼容,
11
+ * 不要在新代码里调用。
9
12
  * @param stageContextPath - stage-context.md 文件路径。
10
13
  * @returns 归一化后的项目上下文。
11
14
  * @throws 当 frontmatter 缺少必要字段或格式不合法时抛出错误。
@@ -31,20 +34,55 @@ export function readStageContext(stageContextPath) {
31
34
  };
32
35
  }
33
36
  /**
34
- * 写入用户级 ep-stage 项目索引。
37
+ * 写入用户级 ep-stage 项目索引(REQ-DATA-01 修订 / spec §4.1)。
35
38
  *
36
- * @param input - HOME 目录和项目列表。
39
+ * schema:顶层 `{ projects: [...], systems: [...] }`,无 `version` 字段;
40
+ * projects[] 条目不再写入 envPath/knowledgeRoot/codeListPaths/stageContextPath 等 legacy 字段。
41
+ *
42
+ * @param input - HOME 目录、项目列表,以及可选的 systems[](不传按空数组写入)。
37
43
  * @returns 写入后的索引文件路径。
38
44
  */
39
45
  export function writeProjectIndex(input) {
40
46
  const indexPath = getProjectIndexPath(input.homeDir);
41
47
  mkdirSync(path.dirname(indexPath), { recursive: true });
42
- writeFileSync(indexPath, `${JSON5.stringify({ version: 1, projects: input.projects }, null, 2)}\n`, 'utf8');
48
+ writeFileSync(indexPath, `${JSON5.stringify({ projects: input.projects, systems: input.systems ?? [] }, null, 2)}\n`, 'utf8');
43
49
  return indexPath;
44
50
  }
51
+ /**
52
+ * 从 `~/.ep-stage/projects.index.json5` 反查匹配的 ProjectIndexEntry(REQ-DATA-01 修订 / spec §5.2 §5.3)。
53
+ *
54
+ * 匹配优先级:①精确 requirementDir / testsDir 命中;②精确 projectDir 命中。
55
+ * 用于 glue-generate-testcase / glue-run-test skill 的「反查项目脚手架」步骤——
56
+ * 给定一个需求目录或项目目录,回到对应的 ProjectIndexEntry(不再依赖砍掉的 stage-context.md)。
57
+ *
58
+ * @param input - 需求路径或项目目录 + 可选 homeDir。
59
+ * @returns 匹配的 ProjectIndexEntry,未匹配返回 null。
60
+ */
61
+ export function lookupProjectByRequirement(input) {
62
+ const indexPath = getProjectIndexPath(input.homeDir);
63
+ if (!existsSync(indexPath))
64
+ return null;
65
+ const parsed = JSON5.parse(readFileSync(indexPath, 'utf8'));
66
+ const target = path.resolve(input.requirementPath);
67
+ for (const project of parsed.projects ?? []) {
68
+ for (const requirement of project.requirements ?? []) {
69
+ if (path.resolve(requirement.requirementDir) === target ||
70
+ path.resolve(requirement.testsDir) === target) {
71
+ return project;
72
+ }
73
+ }
74
+ if (path.resolve(project.projectDir) === target) {
75
+ return project;
76
+ }
77
+ }
78
+ return null;
79
+ }
45
80
  /**
46
81
  * 按固定优先级解析 ep-stage 项目上下文,并返回可审阅 trace。
47
82
  *
83
+ * @deprecated 旧链路依赖 stage-context.md 与 codeListPaths 匹配,stage-context.md 已砍
84
+ * (REQ-ENV-01 修订)。保留实现仅为零回归兼容;新代码请使用
85
+ * {@link lookupProjectByRequirement}。
48
86
  * @param input - 显式路径、当前目录、code_list 路径和可选 HOME。
49
87
  * @returns 解析出的项目上下文与 context-resolution trace。
50
88
  * @throws 当无法定位任何上下文来源时抛出错误。
@@ -75,8 +113,8 @@ export function resolveStageContext(input) {
75
113
  };
76
114
  }
77
115
  const indexPath = getProjectIndexPath(input.homeDir);
78
- const index = readProjectIndex(indexPath);
79
- const matched = index.projects.find((entry) => matchesProjectIndexEntry(entry, {
116
+ const indexEntries = readProjectIndexRaw(indexPath);
117
+ const matched = indexEntries.find((entry) => matchesProjectIndexEntry(entry, {
80
118
  projectDir: input.projectDir,
81
119
  cwd: input.cwd,
82
120
  codeListPath: input.codeListPath,
@@ -95,8 +133,9 @@ export function resolveStageContext(input) {
95
133
  }
96
134
  throw new Error(`stage context not found from ${startDir} or ${indexPath}`);
97
135
  }
98
- const context = matched.stageContextPath
99
- ? readStageContext(resolvePath(path.resolve(matched.projectDir), matched.stageContextPath))
136
+ const matchedLegacy = matched;
137
+ const context = matchedLegacy.stageContextPath
138
+ ? readStageContext(resolvePath(path.resolve(matched.projectDir), matchedLegacy.stageContextPath))
100
139
  : normalizeProjectIndexEntry(matched);
101
140
  return {
102
141
  context,
@@ -140,22 +179,29 @@ function parseStageContextFrontmatter(markdown) {
140
179
  return YAML.parse(match[1] ?? '');
141
180
  }
142
181
  /**
143
- * 读取用户级 JSON5 项目索引。
182
+ * 读取用户级 JSON5 项目索引的原始 projects[](含可能残留的 legacy 字段)。
183
+ *
184
+ * 新代码请使用 {@link lookupProjectByRequirement},本函数仅服务于
185
+ * {@link resolveStageContext} 的零回归兼容。
144
186
  *
145
187
  * @param indexPath - 索引文件路径。
146
- * @returns 项目索引对象。
188
+ * @returns 原始 projects[] 数组,缺失返回空数组。
147
189
  */
148
- function readProjectIndex(indexPath) {
190
+ function readProjectIndexRaw(indexPath) {
149
191
  if (!existsSync(indexPath)) {
150
- return { projects: [] };
192
+ return [];
151
193
  }
152
194
  const parsed = JSON5.parse(readFileSync(indexPath, 'utf8'));
153
- return { projects: parsed.projects ?? [] };
195
+ return (parsed.projects ?? []);
154
196
  }
155
197
  /**
156
- * 将用户索引条目归一为 StageContext
198
+ * 将用户索引条目归一为 StageContext(含旧 legacy 字段 graceful read)。
199
+ *
200
+ * 新数据条目(spec §4.1)无 envPath/knowledgeRoot/codeListPaths 字段,此函数会回填默认值
201
+ * (envPath='.env',knowledgeRoot=undefined,codeListPaths=[])。
202
+ * 旧数据条目仍可携带这些字段,按原逻辑归一相对路径。
157
203
  *
158
- * @param entry - 用户索引中的项目条目。
204
+ * @param entry - 用户索引中的项目条目(可能携带 legacy 字段)。
159
205
  * @returns 归一化后的项目上下文。
160
206
  */
161
207
  function normalizeProjectIndexEntry(entry) {
@@ -175,7 +221,7 @@ function normalizeProjectIndexEntry(entry) {
175
221
  /**
176
222
  * 判断用户索引条目是否匹配当前 projectDir、cwd 或 code_list 路径。
177
223
  *
178
- * @param entry - 用户索引中的项目条目。
224
+ * @param entry - 用户索引中的项目条目(可能携带 legacy codeListPaths)。
179
225
  * @param input - 当前解析输入。
180
226
  * @returns 是否匹配该条目。
181
227
  */
@@ -15,8 +15,8 @@ export type { ActionCandidate, AgentInferredWorkflow, GapActionType, GapExplorat
15
15
  export type { ObservableArtifactRef, ObservableGate, ObservableStageName, ObservableStagePayload, ObservableStageSummary, ReasoningEvidence, ReasoningSummary, TraceWriterOptions } from './contracts/observable-chain.js';
16
16
  export { createTraceWriter, } from './trace/trace-writer.js';
17
17
  export type { TraceWriter, } from './trace/trace-writer.js';
18
- export { readStageContext, resolveStageContext, writeProjectIndex, } from './context/stage-context.js';
19
- export type { ProjectIndex, ProjectIndexEntry, ResolveStageContextInput, ResolveStageContextResult, StageContext, StageContextMode, } from './context/stage-context.js';
18
+ export { lookupProjectByRequirement, readStageContext, resolveStageContext, writeProjectIndex, } from './context/stage-context.js';
19
+ export type { LookupProjectByRequirementInput, ProjectIndex, ProjectIndexEntry, ProjectsIndex, RequirementEntry, ResolveStageContextInput, ResolveStageContextResult, StageContext, StageContextMode, SystemEntry, } from './context/stage-context.js';
20
20
  export { renderReviewSummary, } from './trace/review-summary.js';
21
21
  export type { ReviewSummaryInput, } from './trace/review-summary.js';
22
22
  export { collectActionCandidates, } from './gap-planner/action-candidates.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,UAAU,EACV,kBAAkB,EAClB,0BAA0B,EAC1B,eAAe,EACf,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,WAAW,EACX,aAAa,EACb,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,YAAY,EACZ,QAAQ,EACR,gCAAgC,EAChC,kBAAkB,EAClB,uBAAuB,EACvB,gBAAgB,EAChB,cAAc,EACd,UAAU,EACV,SAAS,EACT,6BAA6B,EAC7B,cAAc,EACd,kBAAkB,EACnB,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EACL,eAAe,EAChB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,sBAAsB,EACtB,iBAAiB,EAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,eAAe,EAChB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,yBAAyB,EACzB,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,sBAAsB,EACvB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,wBAAwB,EACzB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,iBAAiB,EAClB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,mBAAmB,EACpB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,+BAA+B,EAChC,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,sBAAsB,EACtB,iBAAiB,EAClB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACL,6BAA6B,EAC7B,mCAAmC,EACnC,4BAA4B,EAC7B,MAAM,uCAAuC,CAAC;AAC/C,YAAY,EACV,kCAAkC,EAClC,6BAA6B,EAC7B,0BAA0B,EAC1B,0BAA0B,EAC3B,MAAM,uCAAuC,CAAC;AAE/C,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,aAAa,EACb,oBAAoB,EACpB,aAAa,EACb,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EACV,qBAAqB,EACrB,cAAc,EACd,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,EACtB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,WAAW,GACZ,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,0BAA0B,EAC1B,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,+BAA+B,EAC/B,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,wBAAwB,EACxB,yBAAyB,EACzB,YAAY,EACZ,gBAAgB,EAChB,WAAW,GACZ,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,uBAAuB,GACxB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EACL,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,GACjB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACL,0BAA0B,GAC3B,MAAM,uCAAuC,CAAC;AAE/C,OAAO,EACL,gBAAgB,GACjB,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EACL,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,gDAAgD,CAAC;AAExD,OAAO,EACL,wBAAwB,GACzB,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EACV,kBAAkB,EAClB,yBAAyB,EACzB,6BAA6B,EAC7B,8BAA8B,EAC9B,eAAe,GAChB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EACL,sBAAsB,GACvB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,oBAAoB,GACrB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,4BAA4B,EAC5B,6BAA6B,GAC9B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,4BAA4B,EAC5B,kBAAkB,GACnB,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EACV,uBAAuB,GACxB,MAAM,oCAAoC,CAAC;AAC5C,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,uBAAuB,GACxB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,qBAAqB,GACtB,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EACV,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,8BAA8B,EAC9B,0BAA0B,EAC1B,gCAAgC,GACjC,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,2CAA2C,GAC5C,MAAM,sCAAsC,CAAC;AAC9C,YAAY,EACV,iBAAiB,EACjB,mCAAmC,EACnC,yBAAyB,GAC1B,MAAM,iCAAiC,CAAC;AACzC,YAAY,EACV,YAAY,EACZ,oBAAoB,GACrB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,sBAAsB,EACtB,mCAAmC,EACnC,+BAA+B,EAC/B,2BAA2B,EAC3B,2BAA2B,GAC5B,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,sBAAsB,EACtB,8BAA8B,EAC9B,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,2BAA2B,EAC3B,gCAAgC,EAChC,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,iBAAiB,EACjB,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,yCAAyC,CAAC;AACjD,YAAY,EACV,wBAAwB,EACxB,wBAAwB,EACxB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,yCAAyC,CAAC"}
package/dist/src/index.js CHANGED
@@ -5,7 +5,7 @@ export { extractJavaAction } from './extractors/java-action.js';
5
5
  export { buildCrudBusinessModuleContract } from './normalizers/crud-contract.js';
6
6
  export { generateStageSkeletonCrudSpec, mapContractToStageSkeletonCrudSlots, renderStageSkeletonCrudSlots } from './generators/stage-skeleton-script.js';
7
7
  export { createTraceWriter, } from './trace/trace-writer.js';
8
- export { readStageContext, resolveStageContext, writeProjectIndex, } from './context/stage-context.js';
8
+ export { lookupProjectByRequirement, readStageContext, resolveStageContext, writeProjectIndex, } from './context/stage-context.js';
9
9
  export { renderReviewSummary, } from './trace/review-summary.js';
10
10
  export { collectActionCandidates, } from './gap-planner/action-candidates.js';
11
11
  export { createSkeletonCoverage, } from './gap-planner/skeleton-coverage.js';
@@ -1 +1 @@
1
- {"version":3,"file":"testcase-generator.d.ts","sourceRoot":"","sources":["../../../src/testcase/testcase-generator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EAEb,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAOvF;;;;;;;;;GASG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,0BAA0B,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,kBAAkB,CAAC,EAAE,YAAY,EAAE,CAAC;IACpC,YAAY,EAAE,iBAAiB,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,2BAA2B,CA2GpG"}
1
+ {"version":3,"file":"testcase-generator.d.ts","sourceRoot":"","sources":["../../../src/testcase/testcase-generator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EAEb,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAOvF;;;;;;;;;GASG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,EAAE,0BAA0B,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,kBAAkB,CAAC,EAAE,YAAY,EAAE,CAAC;IACpC,YAAY,EAAE,iBAAiB,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;GAUG;AAKH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,2BAA2B,CA2GpG"}
@@ -11,6 +11,10 @@ import { mapContractToCrudTestcaseSlots, renderGlueTestcaseSkeleton, selectTestc
11
11
  * @param input - 用例生成入参。
12
12
  * @returns 用例 JSON + Markdown。
13
13
  */
14
+ // TODO(Task 3.1 convergence):本函数当前产 v1 形态 testcase 文档(仍被 run-gap-pipeline.ts 使用)。
15
+ // Task 1.6 的新 testcase 子命令改走 src/cli/probe.ts.assembleTestcaseV2 产 v2 形态。两条产线
16
+ // 必然在 Task 3.1(升级 testcase-schema.md 到 v2)合并:届时本函数签名升级为 v2,删除
17
+ // assembleTestcaseV2。在那之前任何对本函数的修改务必同步检视 probe.assembleTestcaseV2。
14
18
  export function generateGlueTestcases(input) {
15
19
  if (!input.contract || !input.menu) {
16
20
  throw new Error('生成胶水测试用例必须提供 contract 和 menu,不能再由 moduleId/moduleName 自由拼装');
@@ -0,0 +1,50 @@
1
+ /** testcase.json5 v2 顶层结构(REQ-DATA-02)。字段名永远英文(机器格式)。 */
2
+ export type TestcaseV2 = {
3
+ schemaVersion: 'v2';
4
+ scenario: string;
5
+ moduleId: string;
6
+ moduleName: string;
7
+ menuPath: string;
8
+ requiredSystems: Array<{
9
+ url: string;
10
+ systemName?: string;
11
+ }>;
12
+ requiredRoles: Array<{
13
+ role: string;
14
+ hint?: string;
15
+ }>;
16
+ coveredActions: Array<{
17
+ actionId: string;
18
+ kind: string;
19
+ evidence: string[];
20
+ }>;
21
+ uncoveredCandidates: Array<{
22
+ actionId: string;
23
+ label: string;
24
+ status: 'planned' | 'candidate' | 'unresolved' | 'resolved';
25
+ requiredRole: string;
26
+ businessIntent: string;
27
+ assertionExpectation: string;
28
+ evidence: string[];
29
+ }>;
30
+ cases: Array<{
31
+ caseId: string;
32
+ title: string;
33
+ scenario: string;
34
+ reviewStatus: 'confirmed' | 'needs_review' | 'blocked';
35
+ requiredRole: string;
36
+ evidence: string[];
37
+ steps: string[];
38
+ assertions: string[];
39
+ unresolved: string[];
40
+ }>;
41
+ reasoningSummary: {
42
+ conclusion: string;
43
+ evidenceChain: string[];
44
+ alternatives: string[];
45
+ confidence: 'high' | 'medium' | 'low';
46
+ risks: string[];
47
+ needsHumanReview: boolean;
48
+ };
49
+ };
50
+ //# sourceMappingURL=testcase-v2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testcase-v2.d.ts","sourceRoot":"","sources":["../../../src/testcase/testcase-v2.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,MAAM,MAAM,UAAU,GAAG;IACvB,aAAa,EAAE,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtD,cAAc,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAC9E,mBAAmB,EAAE,KAAK,CAAC;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,UAAU,CAAC;QAC5D,YAAY,EAAE,MAAM,CAAC;QACrB,cAAc,EAAE,MAAM,CAAC;QACvB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC,CAAC;IACH,KAAK,EAAE,KAAK,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,WAAW,GAAG,cAAc,GAAG,SAAS,CAAC;QACvD,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC,CAAC;IACH,gBAAgB,EAAE;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,aAAa,EAAE,MAAM,EAAE,CAAC;QACxB,YAAY,EAAE,MAAM,EAAE,CAAC;QACvB,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;QACtC,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,gBAAgB,EAAE,OAAO,CAAC;KAC3B,CAAC;CACH,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ import { type Credential } from '../validation/credentials.js';
2
+ /**
3
+ * 从需求目录读取 credentials.json5 数组形态凭据(REQ-ENV-02 / REQ-ENV-03)。
4
+ *
5
+ * 供 run 子命令按 cases[].requiredRole 匹配登录凭据。
6
+ *
7
+ * @param credentialsPath - <需求名>/credentials.json5 绝对路径。
8
+ * @returns 校验后的凭据数组。
9
+ * @throws 文件不存在或 schema 校验失败时抛结构化错误。
10
+ */
11
+ export declare function loadCredentials(credentialsPath: string): Credential[];
12
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../../src/util/credentials.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAEpF;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,eAAe,EAAE,MAAM,GAAG,UAAU,EAAE,CAQrE"}
@@ -0,0 +1,19 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+ import JSON5 from 'json5';
3
+ import { validateCredentials } from '../validation/credentials.js';
4
+ /**
5
+ * 从需求目录读取 credentials.json5 数组形态凭据(REQ-ENV-02 / REQ-ENV-03)。
6
+ *
7
+ * 供 run 子命令按 cases[].requiredRole 匹配登录凭据。
8
+ *
9
+ * @param credentialsPath - <需求名>/credentials.json5 绝对路径。
10
+ * @returns 校验后的凭据数组。
11
+ * @throws 文件不存在或 schema 校验失败时抛结构化错误。
12
+ */
13
+ export function loadCredentials(credentialsPath) {
14
+ if (!existsSync(credentialsPath)) {
15
+ throw new Error(`credentials.json5 不存在: ${credentialsPath}(请先填好凭据,或重跑 glue-generate-testcase 交互输入)`);
16
+ }
17
+ const raw = JSON5.parse(readFileSync(credentialsPath, 'utf8'));
18
+ return validateCredentials(raw);
19
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 把 testcase.json5 中的英文字段名 / 状态值翻译成中文 testcase.md 显示文案。
3
+ *
4
+ * @param value - testcase.json5 中的英文 token(字段名 / 状态值 / 推荐动作 / 场景值)。
5
+ * @returns 对应中文文案;未在映射表中的 token 原样返回(兜底)。
6
+ */
7
+ export declare function i18nTestcase(value: string): string;
8
+ //# sourceMappingURL=i18n-testcase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n-testcase.d.ts","sourceRoot":"","sources":["../../../src/util/i18n-testcase.ts"],"names":[],"mappings":"AA+CA;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * REQ-UX-03:testcase.json5 字段名 / 状态值 → testcase.md 中文显示文案。
3
+ *
4
+ * 设计原则(spec §4.6 约束):
5
+ * - testcase.json5 内部字段名 / 状态值永远英文(TypeScript types 一致 + AI Agent 解析稳定)。
6
+ * - testcase.md 是人类视图,CLI 写 .md 时调用本 util 把英文 token 翻译成中文。
7
+ * - 纯查表函数,无副作用,无 I/O;未知 token 原样返回(兜底保证可显示)。
8
+ *
9
+ * 已知 trade-off:spec §4.6 中 `unresolved` 同时是字段名(未确认项)和状态值(未解决)。
10
+ * 扁平 lookup 表无法同时持有两个映射 —— 当前实现保留「状态值 = 未解决」(后定义覆盖前者)。
11
+ * 字段名场景下 CLI 可在调用前对 key 做硬编码标签替换(如 `'unresolved'` 字段标题写死「未确认项」)。
12
+ */
13
+ const I18N_MAP = {
14
+ // 字段名
15
+ caseId: '用例 ID',
16
+ actionId: '动作 ID',
17
+ scenario: '场景类型',
18
+ menuPath: '菜单路径',
19
+ requiredRole: '操作角色',
20
+ requiredSystems: '需求系统集',
21
+ requiredRoles: '需求角色集',
22
+ coveredActions: '骨架已命中动作',
23
+ uncoveredCandidates: '骨架未命中候选',
24
+ businessIntent: '业务意图',
25
+ assertionExpectation: '预期断言',
26
+ recommendedAction: '推荐动作',
27
+ reviewStatus: '审阅状态',
28
+ evidence: '证据',
29
+ // 状态值
30
+ confirmed: '已确认',
31
+ needs_review: '待审阅',
32
+ blocked: '已阻断',
33
+ planned: '已计划',
34
+ candidate: '候选',
35
+ unresolved: '未解决',
36
+ resolved: '已解决',
37
+ // 推荐动作
38
+ infer: '推理',
39
+ 'supplement-hints': '补充 hints',
40
+ skip: '跳过',
41
+ // 场景值
42
+ 'crud.single-page': '单页 CRUD',
43
+ 'crud.nested': '嵌套 CRUD',
44
+ statistic: '统计视图',
45
+ approval: '审批流',
46
+ };
47
+ /**
48
+ * 把 testcase.json5 中的英文字段名 / 状态值翻译成中文 testcase.md 显示文案。
49
+ *
50
+ * @param value - testcase.json5 中的英文 token(字段名 / 状态值 / 推荐动作 / 场景值)。
51
+ * @returns 对应中文文案;未在映射表中的 token 原样返回(兜底)。
52
+ */
53
+ export function i18nTestcase(value) {
54
+ return I18N_MAP[value] ?? value;
55
+ }
@@ -0,0 +1,33 @@
1
+ import { symlinkSync, linkSync, copyFileSync, existsSync, lstatSync, unlinkSync } from 'node:fs';
2
+ /**
3
+ * 软链 fallback 返回结果。
4
+ * - strategy: 实际采用的链接策略;
5
+ * - warned: 仅在 copy fallback 时为 true,提示「上游更新不会自动同步到链接落点」。
6
+ */
7
+ export type SoftlinkResult = {
8
+ strategy: 'symlink' | 'hardlink' | 'copy';
9
+ warned?: boolean;
10
+ };
11
+ /**
12
+ * 内部 fs 操作集合。抽象为对象是为了在测试中可替换具体实现,
13
+ * 绕开 ESM namespace 不可写带来的 vi.spyOn 限制(REQ-FILE-02 测试需要模拟 EPERM)。
14
+ * 生产代码不应替换此对象。
15
+ */
16
+ export declare const softlinkFs: {
17
+ symlinkSync: typeof symlinkSync;
18
+ linkSync: typeof linkSync;
19
+ copyFileSync: typeof copyFileSync;
20
+ existsSync: typeof existsSync;
21
+ lstatSync: typeof lstatSync;
22
+ unlinkSync: typeof unlinkSync;
23
+ };
24
+ /**
25
+ * 跨平台创建「指向 target 的 link」,三档 fallback(REQ-FILE-02):
26
+ * symlink(macOS/Linux)→ hardlink → copy + 时间戳警示(Windows 无管理员权限)。
27
+ *
28
+ * @param target - 被链接的源文件绝对路径(上游 code_list.md)。
29
+ * @param linkPath - 链接落点(需求目录下的 code_list.md)。
30
+ * @returns 实际采用的策略;copy 时 warned=true。
31
+ */
32
+ export declare function createSoftlinkWithFallback(target: string, linkPath: string): SoftlinkResult;
33
+ //# sourceMappingURL=softlink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"softlink.d.ts","sourceRoot":"","sources":["../../../src/util/softlink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEjG;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU;;;;;;;CAOtB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAiB3F"}
@@ -0,0 +1,43 @@
1
+ import { symlinkSync, linkSync, copyFileSync, existsSync, lstatSync, unlinkSync } from 'node:fs';
2
+ /**
3
+ * 内部 fs 操作集合。抽象为对象是为了在测试中可替换具体实现,
4
+ * 绕开 ESM namespace 不可写带来的 vi.spyOn 限制(REQ-FILE-02 测试需要模拟 EPERM)。
5
+ * 生产代码不应替换此对象。
6
+ */
7
+ export const softlinkFs = {
8
+ symlinkSync,
9
+ linkSync,
10
+ copyFileSync,
11
+ existsSync,
12
+ lstatSync,
13
+ unlinkSync,
14
+ };
15
+ /**
16
+ * 跨平台创建「指向 target 的 link」,三档 fallback(REQ-FILE-02):
17
+ * symlink(macOS/Linux)→ hardlink → copy + 时间戳警示(Windows 无管理员权限)。
18
+ *
19
+ * @param target - 被链接的源文件绝对路径(上游 code_list.md)。
20
+ * @param linkPath - 链接落点(需求目录下的 code_list.md)。
21
+ * @returns 实际采用的策略;copy 时 warned=true。
22
+ */
23
+ export function createSoftlinkWithFallback(target, linkPath) {
24
+ if (softlinkFs.existsSync(linkPath)) {
25
+ const st = softlinkFs.lstatSync(linkPath);
26
+ if (st.isSymbolicLink() || st.isFile())
27
+ softlinkFs.unlinkSync(linkPath);
28
+ }
29
+ try {
30
+ softlinkFs.symlinkSync(target, linkPath, 'file');
31
+ return { strategy: 'symlink' };
32
+ }
33
+ catch {
34
+ try {
35
+ softlinkFs.linkSync(target, linkPath);
36
+ return { strategy: 'hardlink' };
37
+ }
38
+ catch {
39
+ softlinkFs.copyFileSync(target, linkPath);
40
+ return { strategy: 'copy', warned: true };
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,19 @@
1
+ /** credentials.json5 单条凭据(REQ-ENV-03)。 */
2
+ export type Credential = {
3
+ url: string;
4
+ username: string;
5
+ password: string;
6
+ role?: string;
7
+ systemName?: string;
8
+ };
9
+ /**
10
+ * 校验 credentials.json5 数组形态(REQ-ENV-02 / REQ-ENV-03)。
11
+ *
12
+ * 不变量:数组至少 1 条;必填 url/username/password 非空;同 (url, role) 组合唯一。
13
+ *
14
+ * @param obj - 从 JSON5 解析出来的原始对象。
15
+ * @returns 归一化后的凭据数组。
16
+ * @throws 违反不变量时抛结构化错误。
17
+ */
18
+ export declare function validateCredentials(obj: unknown): Credential[];
19
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../../src/validation/credentials.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,EAAE,CAyB9D"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 校验 credentials.json5 数组形态(REQ-ENV-02 / REQ-ENV-03)。
3
+ *
4
+ * 不变量:数组至少 1 条;必填 url/username/password 非空;同 (url, role) 组合唯一。
5
+ *
6
+ * @param obj - 从 JSON5 解析出来的原始对象。
7
+ * @returns 归一化后的凭据数组。
8
+ * @throws 违反不变量时抛结构化错误。
9
+ */
10
+ export function validateCredentials(obj) {
11
+ if (!Array.isArray(obj))
12
+ throw new Error('credentials.json5 顶层必须是数组');
13
+ if (obj.length === 0)
14
+ throw new Error('credentials.json5 至少需要 1 条凭据');
15
+ const seen = new Set();
16
+ const result = [];
17
+ for (let i = 0; i < obj.length; i++) {
18
+ const e = obj[i];
19
+ for (const f of ['url', 'username', 'password']) {
20
+ if (typeof e[f] !== 'string' || !e[f].trim()) {
21
+ throw new Error(`credentials.json5 第 ${i + 1} 条缺必填字段 ${f}`);
22
+ }
23
+ }
24
+ const role = typeof e.role === 'string' ? e.role : '';
25
+ const key = `${e.url}|${role}`;
26
+ if (seen.has(key))
27
+ throw new Error(`credentials.json5 同 (url, role) 重复: ${key}`);
28
+ seen.add(key);
29
+ result.push({
30
+ url: e.url,
31
+ username: e.username,
32
+ password: e.password,
33
+ ...(role ? { role } : {}),
34
+ ...(typeof e.systemName === 'string' ? { systemName: e.systemName } : {}),
35
+ });
36
+ }
37
+ return result;
38
+ }
@@ -0,0 +1,5 @@
1
+ export { validateProjectsIndex } from './projects-index.js';
2
+ export { validateTestcase } from './testcase.js';
3
+ export { validateCredentials } from './credentials.js';
4
+ export type { Credential } from './credentials.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/validation/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { validateProjectsIndex } from './projects-index.js';
2
+ export { validateTestcase } from './testcase.js';
3
+ export { validateCredentials } from './credentials.js';
@@ -0,0 +1,13 @@
1
+ import type { ProjectsIndex } from '../context/stage-context.js';
2
+ /**
3
+ * 校验 ~/.ep-stage/projects.index.json5 的顶层结构(REQ-DATA-01 修订)。
4
+ *
5
+ * 必含 projects[] 与 systems[];projects[] 条目必含 stageCreateVersion;
6
+ * 拒绝 stage-context.md 砍掉后遗留的 stageContextPath/envPath/knowledgeRoot/codeListPaths 字段。
7
+ *
8
+ * @param obj - 从 JSON5 解析出来的原始对象。
9
+ * @returns 归一化后的 ProjectsIndex。
10
+ * @throws 缺字段或含已废弃字段时抛结构化错误。
11
+ */
12
+ export declare function validateProjectsIndex(obj: unknown): ProjectsIndex;
13
+ //# sourceMappingURL=projects-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projects-index.d.ts","sourceRoot":"","sources":["../../../src/validation/projects-index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAkC,MAAM,6BAA6B,CAAC;AAEjG;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAqBjE"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * 校验 ~/.ep-stage/projects.index.json5 的顶层结构(REQ-DATA-01 修订)。
3
+ *
4
+ * 必含 projects[] 与 systems[];projects[] 条目必含 stageCreateVersion;
5
+ * 拒绝 stage-context.md 砍掉后遗留的 stageContextPath/envPath/knowledgeRoot/codeListPaths 字段。
6
+ *
7
+ * @param obj - 从 JSON5 解析出来的原始对象。
8
+ * @returns 归一化后的 ProjectsIndex。
9
+ * @throws 缺字段或含已废弃字段时抛结构化错误。
10
+ */
11
+ export function validateProjectsIndex(obj) {
12
+ if (!obj || typeof obj !== 'object')
13
+ throw new Error('projects.index.json5 顶层必须是对象');
14
+ const raw = obj;
15
+ if (!Array.isArray(raw.projects))
16
+ throw new Error('projects.index.json5 缺 projects[] 字段');
17
+ if (!Array.isArray(raw.systems))
18
+ throw new Error('projects.index.json5 缺 systems[] 字段');
19
+ const removedFields = ['stageContextPath', 'envPath', 'knowledgeRoot', 'codeListPaths'];
20
+ for (const entry of raw.projects) {
21
+ const e = entry;
22
+ if (typeof e.projectName !== 'string')
23
+ throw new Error('projects[] 条目缺 projectName');
24
+ if (typeof e.projectDir !== 'string')
25
+ throw new Error('projects[] 条目缺 projectDir');
26
+ if (e.mode !== 'glue')
27
+ throw new Error('projects[] 条目 mode 必须为 glue');
28
+ if (typeof e.stageCreateVersion !== 'string' || !e.stageCreateVersion) {
29
+ throw new Error('projects[] 条目缺 stageCreateVersion(承接砍掉的 stage-context.md 元信息)');
30
+ }
31
+ for (const f of removedFields) {
32
+ if (f in e)
33
+ throw new Error(`projects[] 条目含已废弃字段 ${f}(stage-context.md 已砍,应 removed)`);
34
+ }
35
+ }
36
+ return { projects: raw.projects, systems: raw.systems };
37
+ }
@@ -0,0 +1,13 @@
1
+ import type { TestcaseV2 } from '../testcase/testcase-v2.js';
2
+ /**
3
+ * 校验 testcase.json5 v2 schema(REQ-DATA-02 / REQ-SCHEMA-01)。
4
+ *
5
+ * 必含 schemaVersion:"v2" + scenario + menuPath + requiredSystems[] + requiredRoles[] +
6
+ * coveredActions[] + uncoveredCandidates[] + cases[];evidence 元素符合 <kind>=<ref>。
7
+ *
8
+ * @param obj - 从 JSON5 解析出来的原始对象。
9
+ * @returns 归一化后的 TestcaseV2。
10
+ * @throws 缺字段 / 版本不匹配 / evidence 格式错时抛结构化错误。
11
+ */
12
+ export declare function validateTestcase(obj: unknown): TestcaseV2;
13
+ //# sourceMappingURL=testcase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testcase.d.ts","sourceRoot":"","sources":["../../../src/validation/testcase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAI7D;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAuCzD"}