qfai 0.3.0 → 0.3.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.
@@ -160,8 +160,8 @@ function report(copied, skipped, dryRun, label) {
160
160
  }
161
161
 
162
162
  // src/cli/commands/report.ts
163
- var import_promises13 = require("fs/promises");
164
- var import_node_path10 = __toESM(require("path"), 1);
163
+ var import_promises15 = require("fs/promises");
164
+ var import_node_path14 = __toESM(require("path"), 1);
165
165
 
166
166
  // src/core/config.ts
167
167
  var import_promises2 = require("fs/promises");
@@ -169,13 +169,11 @@ var import_node_path4 = __toESM(require("path"), 1);
169
169
  var import_yaml = require("yaml");
170
170
  var defaultConfig = {
171
171
  paths: {
172
- specDir: ".qfai/spec",
173
- decisionsDir: ".qfai/spec/decisions",
174
- scenariosDir: ".qfai/spec/scenarios",
175
172
  contractsDir: ".qfai/contracts",
176
- uiContractsDir: ".qfai/contracts/ui",
177
- apiContractsDir: ".qfai/contracts/api",
178
- dataContractsDir: ".qfai/contracts/db",
173
+ specsDir: ".qfai/specs",
174
+ rulesDir: ".qfai/rules",
175
+ outDir: ".qfai/out",
176
+ promptsDir: ".qfai/prompts",
179
177
  srcDir: "src",
180
178
  testsDir: "tests"
181
179
  },
@@ -200,8 +198,7 @@ var defaultConfig = {
200
198
  }
201
199
  },
202
200
  output: {
203
- format: "text",
204
- jsonPath: ".qfai/out/validate.json"
201
+ validateJsonPath: ".qfai/out/validate.json"
205
202
  }
206
203
  };
207
204
  function getConfigPath(root) {
@@ -250,27 +247,6 @@ function normalizePaths(raw, configPath, issues) {
250
247
  return base;
251
248
  }
252
249
  return {
253
- specDir: readString(
254
- raw.specDir,
255
- base.specDir,
256
- "paths.specDir",
257
- configPath,
258
- issues
259
- ),
260
- decisionsDir: readString(
261
- raw.decisionsDir,
262
- base.decisionsDir,
263
- "paths.decisionsDir",
264
- configPath,
265
- issues
266
- ),
267
- scenariosDir: readString(
268
- raw.scenariosDir,
269
- base.scenariosDir,
270
- "paths.scenariosDir",
271
- configPath,
272
- issues
273
- ),
274
250
  contractsDir: readString(
275
251
  raw.contractsDir,
276
252
  base.contractsDir,
@@ -278,24 +254,31 @@ function normalizePaths(raw, configPath, issues) {
278
254
  configPath,
279
255
  issues
280
256
  ),
281
- uiContractsDir: readString(
282
- raw.uiContractsDir,
283
- base.uiContractsDir,
284
- "paths.uiContractsDir",
257
+ specsDir: readString(
258
+ raw.specsDir,
259
+ base.specsDir,
260
+ "paths.specsDir",
285
261
  configPath,
286
262
  issues
287
263
  ),
288
- apiContractsDir: readString(
289
- raw.apiContractsDir,
290
- base.apiContractsDir,
291
- "paths.apiContractsDir",
264
+ rulesDir: readString(
265
+ raw.rulesDir,
266
+ base.rulesDir,
267
+ "paths.rulesDir",
292
268
  configPath,
293
269
  issues
294
270
  ),
295
- dataContractsDir: readString(
296
- raw.dataContractsDir,
297
- base.dataContractsDir,
298
- "paths.dataContractsDir",
271
+ outDir: readString(
272
+ raw.outDir,
273
+ base.outDir,
274
+ "paths.outDir",
275
+ configPath,
276
+ issues
277
+ ),
278
+ promptsDir: readString(
279
+ raw.promptsDir,
280
+ base.promptsDir,
281
+ "paths.promptsDir",
299
282
  configPath,
300
283
  issues
301
284
  ),
@@ -418,17 +401,10 @@ function normalizeOutput(raw, configPath, issues) {
418
401
  return base;
419
402
  }
420
403
  return {
421
- format: readOutputFormat(
422
- raw.format,
423
- base.format,
424
- "output.format",
425
- configPath,
426
- issues
427
- ),
428
- jsonPath: readString(
429
- raw.jsonPath,
430
- base.jsonPath,
431
- "output.jsonPath",
404
+ validateJsonPath: readString(
405
+ raw.validateJsonPath,
406
+ base.validateJsonPath,
407
+ "output.validateJsonPath",
432
408
  configPath,
433
409
  issues
434
410
  )
@@ -495,20 +471,6 @@ function readTraceabilitySeverity(value, fallback, label, configPath, issues) {
495
471
  }
496
472
  return fallback;
497
473
  }
498
- function readOutputFormat(value, fallback, label, configPath, issues) {
499
- if (value === "text" || value === "json" || value === "github") {
500
- return value;
501
- }
502
- if (value !== void 0) {
503
- issues.push(
504
- configIssue(
505
- configPath,
506
- `${label} \u306F text|json|github \u306E\u3044\u305A\u308C\u304B\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002`
507
- )
508
- );
509
- }
510
- return fallback;
511
- }
512
474
  function configIssue(file, message) {
513
475
  return {
514
476
  code: "QFAI_CONFIG_INVALID",
@@ -535,10 +497,11 @@ function isRecord(value) {
535
497
  }
536
498
 
537
499
  // src/core/report.ts
538
- var import_promises12 = require("fs/promises");
500
+ var import_promises14 = require("fs/promises");
501
+ var import_node_path13 = __toESM(require("path"), 1);
539
502
 
540
503
  // src/core/discovery.ts
541
- var import_node_path6 = __toESM(require("path"), 1);
504
+ var import_promises5 = require("fs/promises");
542
505
 
543
506
  // src/core/fs.ts
544
507
  var import_promises3 = require("fs/promises");
@@ -595,11 +558,50 @@ async function exists2(target) {
595
558
  }
596
559
  }
597
560
 
561
+ // src/core/specLayout.ts
562
+ var import_promises4 = require("fs/promises");
563
+ var import_node_path6 = __toESM(require("path"), 1);
564
+ var SPEC_DIR_RE = /^spec-\d{4}$/;
565
+ async function collectSpecEntries(specsRoot) {
566
+ const dirs = await listSpecDirs(specsRoot);
567
+ const entries = dirs.map((dir) => ({
568
+ dir,
569
+ specPath: import_node_path6.default.join(dir, "spec.md"),
570
+ deltaPath: import_node_path6.default.join(dir, "delta.md"),
571
+ scenarioPath: import_node_path6.default.join(dir, "scenario.md")
572
+ }));
573
+ return entries.sort((a, b) => a.dir.localeCompare(b.dir));
574
+ }
575
+ async function listSpecDirs(specsRoot) {
576
+ try {
577
+ const items = await (0, import_promises4.readdir)(specsRoot, { withFileTypes: true });
578
+ return items.filter((item) => item.isDirectory()).map((item) => item.name).filter((name) => SPEC_DIR_RE.test(name.toLowerCase())).map((name) => import_node_path6.default.join(specsRoot, name));
579
+ } catch (error2) {
580
+ if (isMissingFileError(error2)) {
581
+ return [];
582
+ }
583
+ throw error2;
584
+ }
585
+ }
586
+ function isMissingFileError(error2) {
587
+ if (!error2 || typeof error2 !== "object") {
588
+ return false;
589
+ }
590
+ return error2.code === "ENOENT";
591
+ }
592
+
598
593
  // src/core/discovery.ts
599
- var SPEC_NAMED_PATTERN = /^spec-\d{4}-[^/\\]+\.md$/;
600
- async function collectSpecFiles(specRoot) {
601
- const files = await collectFiles(specRoot, { extensions: [".md"] });
602
- return files.filter((file) => isSpecFile(file));
594
+ async function collectSpecPackDirs(specsRoot) {
595
+ const entries = await collectSpecEntries(specsRoot);
596
+ return entries.map((entry) => entry.dir);
597
+ }
598
+ async function collectSpecFiles(specsRoot) {
599
+ const entries = await collectSpecEntries(specsRoot);
600
+ return filterExisting(entries.map((entry) => entry.specPath));
601
+ }
602
+ async function collectScenarioFiles(specsRoot) {
603
+ const entries = await collectSpecEntries(specsRoot);
604
+ return filterExisting(entries.map((entry) => entry.scenarioPath));
603
605
  }
604
606
  async function collectUiContractFiles(uiRoot) {
605
607
  return collectFiles(uiRoot, { extensions: [".yaml", ".yml"] });
@@ -618,9 +620,22 @@ async function collectContractFiles(uiRoot, apiRoot, dataRoot) {
618
620
  ]);
619
621
  return { ui, api, db };
620
622
  }
621
- function isSpecFile(filePath) {
622
- const name = import_node_path6.default.basename(filePath).toLowerCase();
623
- return SPEC_NAMED_PATTERN.test(name);
623
+ async function filterExisting(files) {
624
+ const existing = [];
625
+ for (const file of files) {
626
+ if (await exists3(file)) {
627
+ existing.push(file);
628
+ }
629
+ }
630
+ return existing;
631
+ }
632
+ async function exists3(target) {
633
+ try {
634
+ await (0, import_promises5.access)(target);
635
+ return true;
636
+ } catch {
637
+ return false;
638
+ }
624
639
  }
625
640
 
626
641
  // src/core/ids.ts
@@ -680,16 +695,16 @@ function isValidId(value, prefix) {
680
695
  var VALIDATION_SCHEMA_VERSION = "0.2";
681
696
 
682
697
  // src/core/version.ts
683
- var import_promises4 = require("fs/promises");
698
+ var import_promises6 = require("fs/promises");
684
699
  var import_node_path7 = __toESM(require("path"), 1);
685
700
  var import_node_url2 = require("url");
686
701
  async function resolveToolVersion() {
687
- if ("0.3.0".length > 0) {
688
- return "0.3.0";
702
+ if ("0.3.2".length > 0) {
703
+ return "0.3.2";
689
704
  }
690
705
  try {
691
706
  const packagePath = resolvePackageJsonPath();
692
- const raw = await (0, import_promises4.readFile)(packagePath, "utf-8");
707
+ const raw = await (0, import_promises6.readFile)(packagePath, "utf-8");
693
708
  const parsed = JSON.parse(raw);
694
709
  const version = typeof parsed.version === "string" ? parsed.version : "";
695
710
  return version.length > 0 ? version : "unknown";
@@ -704,7 +719,8 @@ function resolvePackageJsonPath() {
704
719
  }
705
720
 
706
721
  // src/core/validators/contracts.ts
707
- var import_promises5 = require("fs/promises");
722
+ var import_promises7 = require("fs/promises");
723
+ var import_node_path9 = __toESM(require("path"), 1);
708
724
 
709
725
  // src/core/contracts.ts
710
726
  var import_node_path8 = __toESM(require("path"), 1);
@@ -760,19 +776,10 @@ var SQL_DANGEROUS_PATTERNS = [
760
776
  ];
761
777
  async function validateContracts(root, config) {
762
778
  const issues = [];
763
- issues.push(
764
- ...await validateUiContracts(resolvePath(root, config, "uiContractsDir"))
765
- );
766
- issues.push(
767
- ...await validateApiContracts(
768
- resolvePath(root, config, "apiContractsDir")
769
- )
770
- );
771
- issues.push(
772
- ...await validateDataContracts(
773
- resolvePath(root, config, "dataContractsDir")
774
- )
775
- );
779
+ const contractsRoot = resolvePath(root, config, "contractsDir");
780
+ issues.push(...await validateUiContracts(import_node_path9.default.join(contractsRoot, "ui")));
781
+ issues.push(...await validateApiContracts(import_node_path9.default.join(contractsRoot, "api")));
782
+ issues.push(...await validateDataContracts(import_node_path9.default.join(contractsRoot, "db")));
776
783
  return issues;
777
784
  }
778
785
  async function validateUiContracts(uiRoot) {
@@ -790,7 +797,7 @@ async function validateUiContracts(uiRoot) {
790
797
  }
791
798
  const issues = [];
792
799
  for (const file of files) {
793
- const text = await (0, import_promises5.readFile)(file, "utf-8");
800
+ const text = await (0, import_promises7.readFile)(file, "utf-8");
794
801
  const invalidIds = extractInvalidIds(text, [
795
802
  "SPEC",
796
803
  "BR",
@@ -857,7 +864,7 @@ async function validateApiContracts(apiRoot) {
857
864
  }
858
865
  const issues = [];
859
866
  for (const file of files) {
860
- const text = await (0, import_promises5.readFile)(file, "utf-8");
867
+ const text = await (0, import_promises7.readFile)(file, "utf-8");
861
868
  const invalidIds = extractInvalidIds(text, [
862
869
  "SPEC",
863
870
  "BR",
@@ -935,7 +942,7 @@ async function validateDataContracts(dataRoot) {
935
942
  }
936
943
  const issues = [];
937
944
  for (const file of files) {
938
- const text = await (0, import_promises5.readFile)(file, "utf-8");
945
+ const text = await (0, import_promises7.readFile)(file, "utf-8");
939
946
  const invalidIds = extractInvalidIds(text, [
940
947
  "SPEC",
941
948
  "BR",
@@ -1005,72 +1012,78 @@ function issue(code, message, severity, file, rule, refs) {
1005
1012
  return issue7;
1006
1013
  }
1007
1014
 
1008
- // src/core/validators/decisions.ts
1009
- var import_promises6 = require("fs/promises");
1010
-
1011
- // src/core/parse/adr.ts
1012
- var ADR_ID_RE = /\bADR-\d{4}\b/;
1013
- function extractField(md, key) {
1014
- const pattern = new RegExp(`^\\s*-\\s*${key}:\\s*(.+)\\s*$`, "m");
1015
- return md.match(pattern)?.[1]?.trim();
1016
- }
1017
- function parseAdr(md, file) {
1018
- const adrId = md.match(ADR_ID_RE)?.[0];
1019
- const fields = {};
1020
- const status = extractField(md, "Status");
1021
- const context = extractField(md, "Context");
1022
- const decision = extractField(md, "Decision");
1023
- const consequences = extractField(md, "Consequences");
1024
- const related = extractField(md, "Related");
1025
- if (status) fields.status = status;
1026
- if (context) fields.context = context;
1027
- if (decision) fields.decision = decision;
1028
- if (consequences) fields.consequences = consequences;
1029
- if (related) fields.related = related;
1030
- const parsed = {
1031
- file,
1032
- fields
1033
- };
1034
- if (adrId) {
1035
- parsed.adrId = adrId;
1036
- }
1037
- return parsed;
1038
- }
1039
-
1040
- // src/core/validators/decisions.ts
1041
- var REQUIRED_FIELDS = [
1042
- { key: "status", label: "Status" },
1043
- { key: "context", label: "Context" },
1044
- { key: "decision", label: "Decision" },
1045
- { key: "consequences", label: "Consequences" }
1046
- ];
1047
- async function validateDecisions(root, config) {
1048
- const decisionsRoot = resolvePath(root, config, "decisionsDir");
1049
- const files = await collectFiles(decisionsRoot, { extensions: [".md"] });
1050
- if (files.length === 0) {
1015
+ // src/core/validators/delta.ts
1016
+ var import_promises8 = require("fs/promises");
1017
+ var import_node_path10 = __toESM(require("path"), 1);
1018
+ var SECTION_RE = /^##\s+変更区分/m;
1019
+ var COMPAT_LINE_RE = /^\s*-\s*\[[ xX]\]\s*Compatibility\b/m;
1020
+ var CHANGE_LINE_RE = /^\s*-\s*\[[ xX]\]\s*Change\/Improvement\b/m;
1021
+ var COMPAT_CHECKED_RE = /^\s*-\s*\[[xX]\]\s*Compatibility\b/m;
1022
+ var CHANGE_CHECKED_RE = /^\s*-\s*\[[xX]\]\s*Change\/Improvement\b/m;
1023
+ async function validateDeltas(root, config) {
1024
+ const specsRoot = resolvePath(root, config, "specsDir");
1025
+ const packs = await collectSpecPackDirs(specsRoot);
1026
+ if (packs.length === 0) {
1051
1027
  return [];
1052
1028
  }
1053
1029
  const issues = [];
1054
- for (const file of files) {
1055
- const text = await (0, import_promises6.readFile)(file, "utf-8");
1056
- const parsed = parseAdr(text, file);
1057
- const missing = REQUIRED_FIELDS.filter(
1058
- (field) => !parsed.fields[field.key]
1059
- );
1060
- if (missing.length > 0) {
1030
+ for (const pack of packs) {
1031
+ const deltaPath = import_node_path10.default.join(pack, "delta.md");
1032
+ let text;
1033
+ try {
1034
+ text = await (0, import_promises8.readFile)(deltaPath, "utf-8");
1035
+ } catch (error2) {
1036
+ if (isMissingFileError2(error2)) {
1037
+ issues.push(
1038
+ issue2(
1039
+ "QFAI-DELTA-001",
1040
+ "delta.md \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002",
1041
+ "error",
1042
+ deltaPath,
1043
+ "delta.exists"
1044
+ )
1045
+ );
1046
+ continue;
1047
+ }
1048
+ throw error2;
1049
+ }
1050
+ const hasSection = SECTION_RE.test(text);
1051
+ const hasCompatibility = COMPAT_LINE_RE.test(text);
1052
+ const hasChange = CHANGE_LINE_RE.test(text);
1053
+ if (!hasSection || !hasCompatibility || !hasChange) {
1061
1054
  issues.push(
1062
1055
  issue2(
1063
- "QFAI-ADR-001",
1064
- `ADR \u5FC5\u9808\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059: ${missing.map((field) => field.label).join(", ")}`,
1056
+ "QFAI-DELTA-002",
1057
+ "delta.md \u306E\u5909\u66F4\u533A\u5206\u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059\u3002",
1065
1058
  "error",
1066
- file,
1067
- "adr.requiredFields"
1059
+ deltaPath,
1060
+ "delta.section"
1061
+ )
1062
+ );
1063
+ continue;
1064
+ }
1065
+ const compatibilityChecked = COMPAT_CHECKED_RE.test(text);
1066
+ const changeChecked = CHANGE_CHECKED_RE.test(text);
1067
+ if (compatibilityChecked === changeChecked) {
1068
+ issues.push(
1069
+ issue2(
1070
+ "QFAI-DELTA-003",
1071
+ "delta.md \u306E\u5909\u66F4\u533A\u5206\u306F\u3069\u3061\u3089\u304B1\u3064\u3060\u3051\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\uFF08\u4E21\u65B9ON/\u4E21\u65B9OFF\u306F\u7121\u52B9\u3067\u3059\uFF09\u3002",
1072
+ "error",
1073
+ deltaPath,
1074
+ "delta.classification"
1068
1075
  )
1069
1076
  );
1070
1077
  }
1071
1078
  }
1072
1079
  return issues;
1073
1080
  }
1081
+ function isMissingFileError2(error2) {
1082
+ if (!error2 || typeof error2 !== "object") {
1083
+ return false;
1084
+ }
1085
+ return error2.code === "ENOENT";
1086
+ }
1074
1087
  function issue2(code, message, severity, file, rule, refs) {
1075
1088
  const issue7 = {
1076
1089
  code,
@@ -1090,15 +1103,17 @@ function issue2(code, message, severity, file, rule, refs) {
1090
1103
  }
1091
1104
 
1092
1105
  // src/core/validators/ids.ts
1093
- var import_promises8 = require("fs/promises");
1094
- var import_node_path9 = __toESM(require("path"), 1);
1106
+ var import_promises10 = require("fs/promises");
1107
+ var import_node_path12 = __toESM(require("path"), 1);
1095
1108
 
1096
1109
  // src/core/contractIndex.ts
1097
- var import_promises7 = require("fs/promises");
1110
+ var import_promises9 = require("fs/promises");
1111
+ var import_node_path11 = __toESM(require("path"), 1);
1098
1112
  async function buildContractIndex(root, config) {
1099
- const uiRoot = resolvePath(root, config, "uiContractsDir");
1100
- const apiRoot = resolvePath(root, config, "apiContractsDir");
1101
- const dataRoot = resolvePath(root, config, "dataContractsDir");
1113
+ const contractsRoot = resolvePath(root, config, "contractsDir");
1114
+ const uiRoot = import_node_path11.default.join(contractsRoot, "ui");
1115
+ const apiRoot = import_node_path11.default.join(contractsRoot, "api");
1116
+ const dataRoot = import_node_path11.default.join(contractsRoot, "db");
1102
1117
  const [uiFiles, apiFiles, dataFiles] = await Promise.all([
1103
1118
  collectUiContractFiles(uiRoot),
1104
1119
  collectApiContractFiles(apiRoot),
@@ -1117,7 +1132,7 @@ async function buildContractIndex(root, config) {
1117
1132
  }
1118
1133
  async function indexUiContracts(files, index) {
1119
1134
  for (const file of files) {
1120
- const text = await (0, import_promises7.readFile)(file, "utf-8");
1135
+ const text = await (0, import_promises9.readFile)(file, "utf-8");
1121
1136
  try {
1122
1137
  const doc = parseStructuredContract(file, text);
1123
1138
  extractUiContractIds(doc).forEach((id) => record(index, id, file));
@@ -1129,7 +1144,7 @@ async function indexUiContracts(files, index) {
1129
1144
  }
1130
1145
  async function indexApiContracts(files, index) {
1131
1146
  for (const file of files) {
1132
- const text = await (0, import_promises7.readFile)(file, "utf-8");
1147
+ const text = await (0, import_promises9.readFile)(file, "utf-8");
1133
1148
  try {
1134
1149
  const doc = parseStructuredContract(file, text);
1135
1150
  extractApiContractIds(doc).forEach((id) => record(index, id, file));
@@ -1141,7 +1156,7 @@ async function indexApiContracts(files, index) {
1141
1156
  }
1142
1157
  async function indexDataContracts(files, index) {
1143
1158
  for (const file of files) {
1144
- const text = await (0, import_promises7.readFile)(file, "utf-8");
1159
+ const text = await (0, import_promises9.readFile)(file, "utf-8");
1145
1160
  extractIds(text, "DATA").forEach((id) => record(index, id, file));
1146
1161
  }
1147
1162
  }
@@ -1152,38 +1167,6 @@ function record(index, id, file) {
1152
1167
  index.idToFiles.set(id, current);
1153
1168
  }
1154
1169
 
1155
- // src/core/parse/gherkin.ts
1156
- var FEATURE_RE = /^\s*Feature:\s+/;
1157
- var SCENARIO_RE = /^\s*Scenario:\s*(.+)\s*$/;
1158
- var TAG_LINE_RE = /^\s*@/;
1159
- function parseTags(line) {
1160
- return line.trim().split(/\s+/).filter((tag) => tag.startsWith("@")).map((tag) => tag.replace(/^@/, ""));
1161
- }
1162
- function parseGherkinFeature(text, file) {
1163
- const lines = text.split(/\r?\n/);
1164
- const scenarios = [];
1165
- let featurePresent = false;
1166
- for (let i = 0; i < lines.length; i++) {
1167
- const line = lines[i] ?? "";
1168
- if (FEATURE_RE.test(line)) {
1169
- featurePresent = true;
1170
- }
1171
- const match = line.match(SCENARIO_RE);
1172
- if (!match) continue;
1173
- const scenarioName = match[1];
1174
- if (!scenarioName) continue;
1175
- const tags = [];
1176
- for (let j = i - 1; j >= 0; j--) {
1177
- const previous = lines[j] ?? "";
1178
- if (previous.trim() === "") continue;
1179
- if (!TAG_LINE_RE.test(previous)) break;
1180
- tags.unshift(...parseTags(previous));
1181
- }
1182
- scenarios.push({ name: scenarioName, line: i + 1, tags });
1183
- }
1184
- return { file, featurePresent, scenarios };
1185
- }
1186
-
1187
1170
  // src/core/parse/markdown.ts
1188
1171
  var HEADING_RE = /^(#{1,6})\s+(.+?)\s*$/;
1189
1172
  function parseHeadings(md) {
@@ -1227,9 +1210,9 @@ function extractH2Sections(md) {
1227
1210
 
1228
1211
  // src/core/parse/spec.ts
1229
1212
  var SPEC_ID_RE = /\bSPEC-\d{4}\b/;
1230
- var BR_LINE_RE = /^\s*-\s*\[?(BR-\d{4})\]?\s*\((P[0-3])\)\s*(.+)$/;
1231
- var BR_LINE_ANY_PRIORITY_RE = /^\s*-\s*\[?(BR-\d{4})\]?\s*\((P[^)]+)\)\s*(.+)$/;
1232
- var BR_LINE_NO_PRIORITY_RE = /^\s*-\s*\[?(BR-\d{4})\]?\s+(?!\()(.*\S.*)$/;
1213
+ var BR_LINE_RE = /^\s*(?:[-*]\s*)?\[(BR-\d{4})\]\[(P[0-3])\]\s*(.+)$/;
1214
+ var BR_LINE_ANY_PRIORITY_RE = /^\s*(?:[-*]\s*)?\[(BR-\d{4})\]\[(P[^\]]+)\]\s*(.+)$/;
1215
+ var BR_LINE_NO_PRIORITY_RE = /^\s*(?:[-*]\s*)?\[(BR-\d{4})\](?!\s*\[P)\s*(.*\S.*)$/;
1233
1216
  var BR_SECTION_TITLE = "\u696D\u52D9\u30EB\u30FC\u30EB";
1234
1217
  var VALID_PRIORITIES = /* @__PURE__ */ new Set(["P0", "P1", "P2", "P3"]);
1235
1218
  function parseSpec(md, file) {
@@ -1302,16 +1285,167 @@ function parseSpec(md, file) {
1302
1285
  return parsed;
1303
1286
  }
1304
1287
 
1305
- // src/core/validators/ids.ts
1288
+ // src/core/gherkin/parse.ts
1289
+ var import_gherkin = require("@cucumber/gherkin");
1290
+ var import_node_crypto = require("crypto");
1291
+ function parseGherkin(source, uri) {
1292
+ const errors = [];
1293
+ const uuidFn = () => (0, import_node_crypto.randomUUID)();
1294
+ const builder = new import_gherkin.AstBuilder(uuidFn);
1295
+ const matcher = new import_gherkin.GherkinClassicTokenMatcher();
1296
+ const parser = new import_gherkin.Parser(builder, matcher);
1297
+ try {
1298
+ const gherkinDocument = parser.parse(source);
1299
+ gherkinDocument.uri = uri;
1300
+ return { gherkinDocument, errors };
1301
+ } catch (error2) {
1302
+ errors.push(formatError3(error2));
1303
+ return { gherkinDocument: null, errors };
1304
+ }
1305
+ }
1306
+ function formatError3(error2) {
1307
+ if (error2 instanceof Error) {
1308
+ return error2.message;
1309
+ }
1310
+ return String(error2);
1311
+ }
1312
+
1313
+ // src/core/scenarioModel.ts
1314
+ var SPEC_TAG_RE = /^SPEC-\d{4}$/;
1306
1315
  var SC_TAG_RE = /^SC-\d{4}$/;
1316
+ var BR_TAG_RE = /^BR-\d{4}$/;
1317
+ var UI_TAG_RE = /^UI-\d{4}$/;
1318
+ var API_TAG_RE = /^API-\d{4}$/;
1319
+ var DATA_TAG_RE = /^DATA-\d{4}$/;
1320
+ function parseScenarioDocument(text, uri) {
1321
+ const { gherkinDocument, errors } = parseGherkin(text, uri);
1322
+ if (!gherkinDocument) {
1323
+ return { document: null, errors };
1324
+ }
1325
+ const feature = gherkinDocument.feature;
1326
+ if (!feature) {
1327
+ return {
1328
+ document: { uri, featureTags: [], scenarios: [] },
1329
+ errors
1330
+ };
1331
+ }
1332
+ const featureTags = collectTagNames(feature.tags);
1333
+ const scenarios = collectScenarioNodes(feature, featureTags);
1334
+ return {
1335
+ document: {
1336
+ uri,
1337
+ featureName: feature.name,
1338
+ featureTags,
1339
+ scenarios
1340
+ },
1341
+ errors
1342
+ };
1343
+ }
1344
+ function buildScenarioAtoms(document) {
1345
+ return document.scenarios.map((scenario) => {
1346
+ const specIds = scenario.tags.filter((tag) => SPEC_TAG_RE.test(tag));
1347
+ const scIds = scenario.tags.filter((tag) => SC_TAG_RE.test(tag));
1348
+ const brIds = unique2(scenario.tags.filter((tag) => BR_TAG_RE.test(tag)));
1349
+ const contractIds = /* @__PURE__ */ new Set();
1350
+ scenario.tags.forEach((tag) => {
1351
+ if (UI_TAG_RE.test(tag) || API_TAG_RE.test(tag) || DATA_TAG_RE.test(tag)) {
1352
+ contractIds.add(tag);
1353
+ }
1354
+ });
1355
+ for (const step of scenario.steps) {
1356
+ for (const text of collectStepTexts(step)) {
1357
+ extractIds(text, "UI").forEach((id) => contractIds.add(id));
1358
+ extractIds(text, "API").forEach((id) => contractIds.add(id));
1359
+ extractIds(text, "DATA").forEach((id) => contractIds.add(id));
1360
+ }
1361
+ }
1362
+ const atom = {
1363
+ uri: document.uri,
1364
+ featureName: document.featureName ?? "",
1365
+ scenarioName: scenario.name,
1366
+ kind: scenario.kind,
1367
+ brIds,
1368
+ contractIds: Array.from(contractIds).sort()
1369
+ };
1370
+ if (scenario.line !== void 0) {
1371
+ atom.line = scenario.line;
1372
+ }
1373
+ if (specIds.length === 1) {
1374
+ const specId = specIds[0];
1375
+ if (specId) {
1376
+ atom.specId = specId;
1377
+ }
1378
+ }
1379
+ if (scIds.length === 1) {
1380
+ const scId = scIds[0];
1381
+ if (scId) {
1382
+ atom.scId = scId;
1383
+ }
1384
+ }
1385
+ return atom;
1386
+ });
1387
+ }
1388
+ function collectScenarioNodes(feature, featureTags) {
1389
+ const scenarios = [];
1390
+ for (const child of feature.children) {
1391
+ if (child.scenario) {
1392
+ scenarios.push(buildScenarioNode(child.scenario, featureTags, []));
1393
+ }
1394
+ if (child.rule) {
1395
+ const ruleTags = collectTagNames(child.rule.tags);
1396
+ for (const ruleChild of child.rule.children) {
1397
+ if (ruleChild.scenario) {
1398
+ scenarios.push(
1399
+ buildScenarioNode(ruleChild.scenario, featureTags, ruleTags)
1400
+ );
1401
+ }
1402
+ }
1403
+ }
1404
+ }
1405
+ return scenarios;
1406
+ }
1407
+ function buildScenarioNode(scenario, featureTags, ruleTags) {
1408
+ const tags = [...featureTags, ...ruleTags, ...collectTagNames(scenario.tags)];
1409
+ const kind = scenario.examples.length > 0 ? "ScenarioOutline" : "Scenario";
1410
+ return {
1411
+ name: scenario.name,
1412
+ kind,
1413
+ line: scenario.location?.line,
1414
+ tags,
1415
+ steps: scenario.steps
1416
+ };
1417
+ }
1418
+ function collectTagNames(tags) {
1419
+ return tags.map((tag) => tag.name.replace(/^@/, ""));
1420
+ }
1421
+ function collectStepTexts(step) {
1422
+ const texts = [];
1423
+ if (step.text) {
1424
+ texts.push(step.text);
1425
+ }
1426
+ if (step.docString?.content) {
1427
+ texts.push(step.docString.content);
1428
+ }
1429
+ if (step.dataTable?.rows) {
1430
+ for (const row of step.dataTable.rows) {
1431
+ for (const cell of row.cells) {
1432
+ texts.push(cell.value);
1433
+ }
1434
+ }
1435
+ }
1436
+ return texts;
1437
+ }
1438
+ function unique2(values) {
1439
+ return Array.from(new Set(values));
1440
+ }
1441
+
1442
+ // src/core/validators/ids.ts
1443
+ var SC_TAG_RE2 = /^SC-\d{4}$/;
1307
1444
  async function validateDefinedIds(root, config) {
1308
1445
  const issues = [];
1309
- const specRoot = resolvePath(root, config, "specDir");
1310
- const scenarioRoot = resolvePath(root, config, "scenariosDir");
1311
- const specFiles = await collectSpecFiles(specRoot);
1312
- const scenarioFiles = await collectFiles(scenarioRoot, {
1313
- extensions: [".feature"]
1314
- });
1446
+ const specsRoot = resolvePath(root, config, "specsDir");
1447
+ const specFiles = await collectSpecFiles(specsRoot);
1448
+ const scenarioFiles = await collectScenarioFiles(specsRoot);
1315
1449
  const defined = /* @__PURE__ */ new Map();
1316
1450
  await collectSpecDefinitionIds(specFiles, defined);
1317
1451
  await collectScenarioDefinitionIds(scenarioFiles, defined);
@@ -1340,7 +1474,7 @@ async function validateDefinedIds(root, config) {
1340
1474
  }
1341
1475
  async function collectSpecDefinitionIds(files, out) {
1342
1476
  for (const file of files) {
1343
- const text = await (0, import_promises8.readFile)(file, "utf-8");
1477
+ const text = await (0, import_promises10.readFile)(file, "utf-8");
1344
1478
  const parsed = parseSpec(text, file);
1345
1479
  if (parsed.specId) {
1346
1480
  recordId(out, parsed.specId, file);
@@ -1350,11 +1484,14 @@ async function collectSpecDefinitionIds(files, out) {
1350
1484
  }
1351
1485
  async function collectScenarioDefinitionIds(files, out) {
1352
1486
  for (const file of files) {
1353
- const text = await (0, import_promises8.readFile)(file, "utf-8");
1354
- const parsed = parseGherkinFeature(text, file);
1355
- for (const scenario of parsed.scenarios) {
1487
+ const text = await (0, import_promises10.readFile)(file, "utf-8");
1488
+ const { document, errors } = parseScenarioDocument(text, file);
1489
+ if (!document || errors.length > 0) {
1490
+ continue;
1491
+ }
1492
+ for (const scenario of document.scenarios) {
1356
1493
  for (const tag of scenario.tags) {
1357
- if (SC_TAG_RE.test(tag)) {
1494
+ if (SC_TAG_RE2.test(tag)) {
1358
1495
  recordId(out, tag, file);
1359
1496
  }
1360
1497
  }
@@ -1368,7 +1505,7 @@ function recordId(out, id, file) {
1368
1505
  }
1369
1506
  function formatFileList(files, root) {
1370
1507
  return files.map((file) => {
1371
- const relative = import_node_path9.default.relative(root, file);
1508
+ const relative = import_node_path12.default.relative(root, file);
1372
1509
  return relative.length > 0 ? relative : file;
1373
1510
  }).join(", ");
1374
1511
  }
@@ -1391,39 +1528,55 @@ function issue3(code, message, severity, file, rule, refs) {
1391
1528
  }
1392
1529
 
1393
1530
  // src/core/validators/scenario.ts
1394
- var import_promises9 = require("fs/promises");
1531
+ var import_promises11 = require("fs/promises");
1395
1532
  var GIVEN_PATTERN = /\bGiven\b/;
1396
1533
  var WHEN_PATTERN = /\bWhen\b/;
1397
1534
  var THEN_PATTERN = /\bThen\b/;
1398
- var SC_TAG_RE2 = /^SC-\d{4}$/;
1399
- var SPEC_TAG_RE = /^SPEC-\d{4}$/;
1400
- var BR_TAG_RE = /^BR-\d{4}$/;
1535
+ var SC_TAG_RE3 = /^SC-\d{4}$/;
1536
+ var SPEC_TAG_RE2 = /^SPEC-\d{4}$/;
1537
+ var BR_TAG_RE2 = /^BR-\d{4}$/;
1401
1538
  async function validateScenarios(root, config) {
1402
- const scenariosRoot = resolvePath(root, config, "scenariosDir");
1403
- const files = await collectFiles(scenariosRoot, {
1404
- extensions: [".feature"]
1405
- });
1406
- if (files.length === 0) {
1539
+ const specsRoot = resolvePath(root, config, "specsDir");
1540
+ const entries = await collectSpecEntries(specsRoot);
1541
+ if (entries.length === 0) {
1542
+ const expected = "spec-0001/scenario.md";
1543
+ const legacy = "spec-001/scenario.md";
1407
1544
  return [
1408
1545
  issue4(
1409
1546
  "QFAI-SC-000",
1410
- "Scenario \u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002",
1547
+ `Scenario \u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u914D\u7F6E\u5834\u6240: ${config.paths.specsDir} / \u671F\u5F85\u30D1\u30BF\u30FC\u30F3: ${expected} (${legacy} \u306F\u975E\u5BFE\u5FDC)`,
1411
1548
  "info",
1412
- scenariosRoot,
1549
+ specsRoot,
1413
1550
  "scenario.files"
1414
1551
  )
1415
1552
  ];
1416
1553
  }
1417
1554
  const issues = [];
1418
- for (const file of files) {
1419
- const text = await (0, import_promises9.readFile)(file, "utf-8");
1420
- issues.push(...validateScenarioContent(text, file));
1555
+ for (const entry of entries) {
1556
+ let text;
1557
+ try {
1558
+ text = await (0, import_promises11.readFile)(entry.scenarioPath, "utf-8");
1559
+ } catch (error2) {
1560
+ if (isMissingFileError3(error2)) {
1561
+ issues.push(
1562
+ issue4(
1563
+ "QFAI-SC-001",
1564
+ "scenario.md \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002",
1565
+ "error",
1566
+ entry.scenarioPath,
1567
+ "scenario.exists"
1568
+ )
1569
+ );
1570
+ continue;
1571
+ }
1572
+ throw error2;
1573
+ }
1574
+ issues.push(...validateScenarioContent(text, entry.scenarioPath));
1421
1575
  }
1422
1576
  return issues;
1423
1577
  }
1424
1578
  function validateScenarioContent(text, file) {
1425
1579
  const issues = [];
1426
- const parsed = parseGherkinFeature(text, file);
1427
1580
  const invalidIds = extractInvalidIds(text, [
1428
1581
  "SPEC",
1429
1582
  "BR",
@@ -1445,9 +1598,47 @@ function validateScenarioContent(text, file) {
1445
1598
  )
1446
1599
  );
1447
1600
  }
1601
+ const { document, errors } = parseScenarioDocument(text, file);
1602
+ if (!document || errors.length > 0) {
1603
+ issues.push(
1604
+ issue4(
1605
+ "QFAI-SC-010",
1606
+ `Gherkin \u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${errors.join(", ") || "unknown"}`,
1607
+ "error",
1608
+ file,
1609
+ "scenario.parse"
1610
+ )
1611
+ );
1612
+ return issues;
1613
+ }
1614
+ const featureSpecTags = document.featureTags.filter(
1615
+ (tag) => SPEC_TAG_RE2.test(tag)
1616
+ );
1617
+ if (featureSpecTags.length === 0) {
1618
+ issues.push(
1619
+ issue4(
1620
+ "QFAI-SC-009",
1621
+ "Feature \u30BF\u30B0\u306B SPEC \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002",
1622
+ "error",
1623
+ file,
1624
+ "scenario.featureSpec"
1625
+ )
1626
+ );
1627
+ } else if (featureSpecTags.length > 1) {
1628
+ issues.push(
1629
+ issue4(
1630
+ "QFAI-SC-009",
1631
+ `Feature \u306E SPEC \u30BF\u30B0\u304C\u8907\u6570\u3042\u308A\u307E\u3059: ${featureSpecTags.join(", ")}`,
1632
+ "error",
1633
+ file,
1634
+ "scenario.featureSpec",
1635
+ featureSpecTags
1636
+ )
1637
+ );
1638
+ }
1448
1639
  const missingStructure = [];
1449
- if (!parsed.featurePresent) missingStructure.push("Feature");
1450
- if (parsed.scenarios.length === 0) missingStructure.push("Scenario");
1640
+ if (!document.featureName) missingStructure.push("Feature");
1641
+ if (document.scenarios.length === 0) missingStructure.push("Scenario");
1451
1642
  if (missingStructure.length > 0) {
1452
1643
  issues.push(
1453
1644
  issue4(
@@ -1461,7 +1652,7 @@ function validateScenarioContent(text, file) {
1461
1652
  )
1462
1653
  );
1463
1654
  }
1464
- for (const scenario of parsed.scenarios) {
1655
+ for (const scenario of document.scenarios) {
1465
1656
  if (scenario.tags.length === 0) {
1466
1657
  issues.push(
1467
1658
  issue4(
@@ -1475,13 +1666,16 @@ function validateScenarioContent(text, file) {
1475
1666
  continue;
1476
1667
  }
1477
1668
  const missingTags = [];
1478
- if (!scenario.tags.some((tag) => SC_TAG_RE2.test(tag))) {
1479
- missingTags.push("SC");
1669
+ const scTags = scenario.tags.filter((tag) => SC_TAG_RE3.test(tag));
1670
+ if (scTags.length === 0) {
1671
+ missingTags.push("SC(0\u4EF6)");
1672
+ } else if (scTags.length > 1) {
1673
+ missingTags.push(`SC(${scTags.length}\u4EF6/1\u4EF6\u5FC5\u9808)`);
1480
1674
  }
1481
- if (!scenario.tags.some((tag) => SPEC_TAG_RE.test(tag))) {
1675
+ if (!scenario.tags.some((tag) => SPEC_TAG_RE2.test(tag))) {
1482
1676
  missingTags.push("SPEC");
1483
1677
  }
1484
- if (!scenario.tags.some((tag) => BR_TAG_RE.test(tag))) {
1678
+ if (!scenario.tags.some((tag) => BR_TAG_RE2.test(tag))) {
1485
1679
  missingTags.push("BR");
1486
1680
  }
1487
1681
  if (missingTags.length > 0) {
@@ -1496,26 +1690,29 @@ function validateScenarioContent(text, file) {
1496
1690
  );
1497
1691
  }
1498
1692
  }
1499
- const missingSteps = [];
1500
- if (!GIVEN_PATTERN.test(text)) {
1501
- missingSteps.push("Given");
1502
- }
1503
- if (!WHEN_PATTERN.test(text)) {
1504
- missingSteps.push("When");
1505
- }
1506
- if (!THEN_PATTERN.test(text)) {
1507
- missingSteps.push("Then");
1508
- }
1509
- if (missingSteps.length > 0) {
1510
- issues.push(
1511
- issue4(
1512
- "QFAI-SC-005",
1513
- `Given/When/Then \u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059: ${missingSteps.join(", ")}`,
1514
- "warning",
1515
- file,
1516
- "scenario.steps"
1517
- )
1518
- );
1693
+ for (const scenario of document.scenarios) {
1694
+ const missingSteps = [];
1695
+ const keywords = scenario.steps.map((step) => step.keyword.trim());
1696
+ if (!keywords.some((keyword) => GIVEN_PATTERN.test(keyword))) {
1697
+ missingSteps.push("Given");
1698
+ }
1699
+ if (!keywords.some((keyword) => WHEN_PATTERN.test(keyword))) {
1700
+ missingSteps.push("When");
1701
+ }
1702
+ if (!keywords.some((keyword) => THEN_PATTERN.test(keyword))) {
1703
+ missingSteps.push("Then");
1704
+ }
1705
+ if (missingSteps.length > 0) {
1706
+ issues.push(
1707
+ issue4(
1708
+ "QFAI-SC-005",
1709
+ `Given/When/Then \u304C\u4E0D\u8DB3\u3057\u3066\u3044\u307E\u3059: ${missingSteps.join(", ")} (${scenario.name})`,
1710
+ "warning",
1711
+ file,
1712
+ "scenario.steps"
1713
+ )
1714
+ );
1715
+ }
1519
1716
  }
1520
1717
  return issues;
1521
1718
  }
@@ -1536,18 +1733,25 @@ function issue4(code, message, severity, file, rule, refs) {
1536
1733
  }
1537
1734
  return issue7;
1538
1735
  }
1736
+ function isMissingFileError3(error2) {
1737
+ if (!error2 || typeof error2 !== "object") {
1738
+ return false;
1739
+ }
1740
+ return error2.code === "ENOENT";
1741
+ }
1539
1742
 
1540
1743
  // src/core/validators/spec.ts
1541
- var import_promises10 = require("fs/promises");
1744
+ var import_promises12 = require("fs/promises");
1542
1745
  async function validateSpecs(root, config) {
1543
- const specsRoot = resolvePath(root, config, "specDir");
1544
- const files = await collectSpecFiles(specsRoot);
1545
- if (files.length === 0) {
1546
- const expected = "spec-0001-<slug>.md";
1746
+ const specsRoot = resolvePath(root, config, "specsDir");
1747
+ const entries = await collectSpecEntries(specsRoot);
1748
+ if (entries.length === 0) {
1749
+ const expected = "spec-0001/spec.md";
1750
+ const legacy = "spec-001/spec.md";
1547
1751
  return [
1548
1752
  issue5(
1549
1753
  "QFAI-SPEC-000",
1550
- `Spec \u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u914D\u7F6E\u5834\u6240: ${config.paths.specDir} / \u671F\u5F85\u30D1\u30BF\u30FC\u30F3: ${expected}`,
1754
+ `Spec \u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u914D\u7F6E\u5834\u6240: ${config.paths.specsDir} / \u671F\u5F85\u30D1\u30BF\u30FC\u30F3: ${expected} (${legacy} \u306F\u975E\u5BFE\u5FDC)`,
1551
1755
  "info",
1552
1756
  specsRoot,
1553
1757
  "spec.files"
@@ -1555,12 +1759,29 @@ async function validateSpecs(root, config) {
1555
1759
  ];
1556
1760
  }
1557
1761
  const issues = [];
1558
- for (const file of files) {
1559
- const text = await (0, import_promises10.readFile)(file, "utf-8");
1762
+ for (const entry of entries) {
1763
+ let text;
1764
+ try {
1765
+ text = await (0, import_promises12.readFile)(entry.specPath, "utf-8");
1766
+ } catch (error2) {
1767
+ if (isMissingFileError4(error2)) {
1768
+ issues.push(
1769
+ issue5(
1770
+ "QFAI-SPEC-005",
1771
+ "spec.md \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002",
1772
+ "error",
1773
+ entry.specPath,
1774
+ "spec.exists"
1775
+ )
1776
+ );
1777
+ continue;
1778
+ }
1779
+ throw error2;
1780
+ }
1560
1781
  issues.push(
1561
1782
  ...validateSpecContent(
1562
1783
  text,
1563
- file,
1784
+ entry.specPath,
1564
1785
  config.validation.require.specSections
1565
1786
  )
1566
1787
  );
@@ -1682,29 +1903,25 @@ function issue5(code, message, severity, file, rule, refs) {
1682
1903
  }
1683
1904
  return issue7;
1684
1905
  }
1906
+ function isMissingFileError4(error2) {
1907
+ if (!error2 || typeof error2 !== "object") {
1908
+ return false;
1909
+ }
1910
+ return error2.code === "ENOENT";
1911
+ }
1685
1912
 
1686
1913
  // src/core/validators/traceability.ts
1687
- var import_promises11 = require("fs/promises");
1688
- var SC_TAG_RE3 = /^SC-\d{4}$/;
1689
- var SPEC_TAG_RE2 = /^SPEC-\d{4}$/;
1690
- var BR_TAG_RE2 = /^BR-\d{4}$/;
1691
- var UI_TAG_RE = /^UI-\d{4}$/;
1692
- var API_TAG_RE = /^API-\d{4}$/;
1693
- var DATA_TAG_RE = /^DATA-\d{4}$/;
1914
+ var import_promises13 = require("fs/promises");
1915
+ var SC_TAG_RE4 = /^SC-\d{4}$/;
1916
+ var SPEC_TAG_RE3 = /^SPEC-\d{4}$/;
1917
+ var BR_TAG_RE3 = /^BR-\d{4}$/;
1694
1918
  async function validateTraceability(root, config) {
1695
1919
  const issues = [];
1696
- const specsRoot = resolvePath(root, config, "specDir");
1697
- const decisionsRoot = resolvePath(root, config, "decisionsDir");
1698
- const scenariosRoot = resolvePath(root, config, "scenariosDir");
1920
+ const specsRoot = resolvePath(root, config, "specsDir");
1699
1921
  const srcRoot = resolvePath(root, config, "srcDir");
1700
1922
  const testsRoot = resolvePath(root, config, "testsDir");
1701
1923
  const specFiles = await collectSpecFiles(specsRoot);
1702
- const decisionFiles = await collectFiles(decisionsRoot, {
1703
- extensions: [".md"]
1704
- });
1705
- const scenarioFiles = await collectFiles(scenariosRoot, {
1706
- extensions: [".feature"]
1707
- });
1924
+ const scenarioFiles = await collectScenarioFiles(specsRoot);
1708
1925
  const upstreamIds = /* @__PURE__ */ new Set();
1709
1926
  const specIds = /* @__PURE__ */ new Set();
1710
1927
  const brIdsInSpecs = /* @__PURE__ */ new Set();
@@ -1716,7 +1933,7 @@ async function validateTraceability(root, config) {
1716
1933
  const contractIndex = await buildContractIndex(root, config);
1717
1934
  const contractIds = contractIndex.ids;
1718
1935
  for (const file of specFiles) {
1719
- const text = await (0, import_promises11.readFile)(file, "utf-8");
1936
+ const text = await (0, import_promises13.readFile)(file, "utf-8");
1720
1937
  extractAllIds(text).forEach((id) => upstreamIds.add(id));
1721
1938
  const parsed = parseSpec(text, file);
1722
1939
  if (parsed.specId) {
@@ -1752,111 +1969,100 @@ async function validateTraceability(root, config) {
1752
1969
  specToBrIds.set(parsed.specId, current);
1753
1970
  }
1754
1971
  }
1755
- for (const file of decisionFiles) {
1756
- const text = await (0, import_promises11.readFile)(file, "utf-8");
1757
- extractAllIds(text).forEach((id) => upstreamIds.add(id));
1758
- }
1759
1972
  for (const file of scenarioFiles) {
1760
- const text = await (0, import_promises11.readFile)(file, "utf-8");
1973
+ const text = await (0, import_promises13.readFile)(file, "utf-8");
1761
1974
  extractAllIds(text).forEach((id) => upstreamIds.add(id));
1762
- const parsed = parseGherkinFeature(text, file);
1763
- const specIdsInScenario = /* @__PURE__ */ new Set();
1764
- const brIds = /* @__PURE__ */ new Set();
1765
- const scIds = /* @__PURE__ */ new Set();
1766
- const scenarioIds = /* @__PURE__ */ new Set();
1767
- for (const scenario of parsed.scenarios) {
1768
- for (const tag of scenario.tags) {
1769
- if (SPEC_TAG_RE2.test(tag)) {
1770
- specIdsInScenario.add(tag);
1771
- }
1772
- if (BR_TAG_RE2.test(tag)) {
1773
- brIds.add(tag);
1774
- }
1775
- if (SC_TAG_RE3.test(tag)) {
1776
- scIds.add(tag);
1777
- }
1778
- if (UI_TAG_RE.test(tag) || API_TAG_RE.test(tag) || DATA_TAG_RE.test(tag)) {
1779
- scenarioIds.add(tag);
1780
- }
1781
- }
1782
- }
1783
- const specIdsList = Array.from(specIdsInScenario);
1784
- const brIdsList = Array.from(brIds);
1785
- const scIdsList = Array.from(scIds);
1786
- const scenarioIdsList = Array.from(scenarioIds);
1787
- brIdsList.forEach((id) => brIdsInScenarios.add(id));
1788
- scIdsList.forEach((id) => scIdsInScenarios.add(id));
1789
- scenarioIdsList.forEach((id) => scenarioContractIds.add(id));
1790
- if (scenarioIdsList.length > 0) {
1791
- scIdsList.forEach((id) => scWithContracts.add(id));
1792
- }
1793
- const unknownSpecIds = specIdsList.filter((id) => !specIds.has(id));
1794
- if (unknownSpecIds.length > 0) {
1795
- issues.push(
1796
- issue6(
1797
- "QFAI-TRACE-005",
1798
- `Scenario \u304C\u5B58\u5728\u3057\u306A\u3044 SPEC \u3092\u53C2\u7167\u3057\u3066\u3044\u307E\u3059: ${unknownSpecIds.join(", ")}`,
1799
- "error",
1800
- file,
1801
- "traceability.scenarioSpecExists",
1802
- unknownSpecIds
1803
- )
1804
- );
1805
- }
1806
- const unknownBrIds = brIdsList.filter((id) => !brIdsInSpecs.has(id));
1807
- if (unknownBrIds.length > 0) {
1808
- issues.push(
1809
- issue6(
1810
- "QFAI-TRACE-006",
1811
- `Scenario \u304C\u5B58\u5728\u3057\u306A\u3044 BR \u3092\u53C2\u7167\u3057\u3066\u3044\u307E\u3059: ${unknownBrIds.join(", ")}`,
1812
- "error",
1813
- file,
1814
- "traceability.scenarioBrExists",
1815
- unknownBrIds
1816
- )
1817
- );
1818
- }
1819
- const unknownContractIds = scenarioIdsList.filter(
1820
- (id) => !contractIds.has(id)
1821
- );
1822
- if (unknownContractIds.length > 0) {
1823
- issues.push(
1824
- issue6(
1825
- "QFAI-TRACE-008",
1826
- `Scenario \u304C\u5B58\u5728\u3057\u306A\u3044\u5951\u7D04 ID \u3092\u53C2\u7167\u3057\u3066\u3044\u307E\u3059: ${unknownContractIds.join(
1827
- ", "
1828
- )}`,
1829
- config.validation.traceability.unknownContractIdSeverity,
1830
- file,
1831
- "traceability.scenarioContractExists",
1832
- unknownContractIds
1833
- )
1834
- );
1975
+ const { document, errors } = parseScenarioDocument(text, file);
1976
+ if (!document || errors.length > 0) {
1977
+ continue;
1835
1978
  }
1836
- if (specIdsList.length > 0) {
1837
- const allowedBrIds = /* @__PURE__ */ new Set();
1838
- for (const specId of specIdsList) {
1839
- const brIdsForSpec = specToBrIds.get(specId);
1840
- if (!brIdsForSpec) {
1841
- continue;
1842
- }
1843
- brIdsForSpec.forEach((id) => allowedBrIds.add(id));
1979
+ const atoms = buildScenarioAtoms(document);
1980
+ for (const [index, scenario] of document.scenarios.entries()) {
1981
+ const atom = atoms[index];
1982
+ if (!atom) {
1983
+ continue;
1984
+ }
1985
+ const specTags = scenario.tags.filter((tag) => SPEC_TAG_RE3.test(tag));
1986
+ const brTags = scenario.tags.filter((tag) => BR_TAG_RE3.test(tag));
1987
+ const scTags = scenario.tags.filter((tag) => SC_TAG_RE4.test(tag));
1988
+ brTags.forEach((id) => brIdsInScenarios.add(id));
1989
+ scTags.forEach((id) => scIdsInScenarios.add(id));
1990
+ atom.contractIds.forEach((id) => scenarioContractIds.add(id));
1991
+ if (atom.contractIds.length > 0) {
1992
+ scTags.forEach((id) => scWithContracts.add(id));
1844
1993
  }
1845
- const invalidBrIds = brIdsList.filter((id) => !allowedBrIds.has(id));
1846
- if (invalidBrIds.length > 0) {
1994
+ const unknownSpecIds = specTags.filter((id) => !specIds.has(id));
1995
+ if (unknownSpecIds.length > 0) {
1847
1996
  issues.push(
1848
1997
  issue6(
1849
- "QFAI-TRACE-007",
1850
- `Scenario \u306E BR \u304C\u53C2\u7167 SPEC \u306B\u5C5E\u3057\u3066\u3044\u307E\u305B\u3093: ${invalidBrIds.join(
1998
+ "QFAI-TRACE-005",
1999
+ `Scenario \u304C\u5B58\u5728\u3057\u306A\u3044 SPEC \u3092\u53C2\u7167\u3057\u3066\u3044\u307E\u3059: ${unknownSpecIds.join(
1851
2000
  ", "
1852
- )} (SPEC: ${specIdsList.join(", ")})`,
2001
+ )} (${scenario.name})`,
1853
2002
  "error",
1854
2003
  file,
1855
- "traceability.scenarioBrUnderSpec",
1856
- invalidBrIds
2004
+ "traceability.scenarioSpecExists",
2005
+ unknownSpecIds
1857
2006
  )
1858
2007
  );
1859
2008
  }
2009
+ const unknownBrIds = brTags.filter((id) => !brIdsInSpecs.has(id));
2010
+ if (unknownBrIds.length > 0) {
2011
+ issues.push(
2012
+ issue6(
2013
+ "QFAI-TRACE-006",
2014
+ `Scenario \u304C\u5B58\u5728\u3057\u306A\u3044 BR \u3092\u53C2\u7167\u3057\u3066\u3044\u307E\u3059: ${unknownBrIds.join(
2015
+ ", "
2016
+ )} (${scenario.name})`,
2017
+ "error",
2018
+ file,
2019
+ "traceability.scenarioBrExists",
2020
+ unknownBrIds
2021
+ )
2022
+ );
2023
+ }
2024
+ const unknownContractIds = atom.contractIds.filter(
2025
+ (id) => !contractIds.has(id)
2026
+ );
2027
+ if (unknownContractIds.length > 0) {
2028
+ issues.push(
2029
+ issue6(
2030
+ "QFAI-TRACE-008",
2031
+ `Scenario \u304C\u5B58\u5728\u3057\u306A\u3044\u5951\u7D04 ID \u3092\u53C2\u7167\u3057\u3066\u3044\u307E\u3059: ${unknownContractIds.join(
2032
+ ", "
2033
+ )} (${scenario.name})`,
2034
+ config.validation.traceability.unknownContractIdSeverity,
2035
+ file,
2036
+ "traceability.scenarioContractExists",
2037
+ unknownContractIds
2038
+ )
2039
+ );
2040
+ }
2041
+ if (specTags.length > 0 && brTags.length > 0) {
2042
+ const allowedBrIds = /* @__PURE__ */ new Set();
2043
+ for (const specId of specTags) {
2044
+ const brIdsForSpec = specToBrIds.get(specId);
2045
+ if (!brIdsForSpec) {
2046
+ continue;
2047
+ }
2048
+ brIdsForSpec.forEach((id) => allowedBrIds.add(id));
2049
+ }
2050
+ const invalidBrIds = brTags.filter((id) => !allowedBrIds.has(id));
2051
+ if (invalidBrIds.length > 0) {
2052
+ issues.push(
2053
+ issue6(
2054
+ "QFAI-TRACE-007",
2055
+ `Scenario \u306E BR \u304C\u53C2\u7167 SPEC \u306B\u5C5E\u3057\u3066\u3044\u307E\u305B\u3093: ${invalidBrIds.join(
2056
+ ", "
2057
+ )} (SPEC: ${specTags.join(", ")}) (${scenario.name})`,
2058
+ "error",
2059
+ file,
2060
+ "traceability.scenarioBrUnderSpec",
2061
+ invalidBrIds
2062
+ )
2063
+ );
2064
+ }
2065
+ }
1860
2066
  }
1861
2067
  }
1862
2068
  if (upstreamIds.size === 0) {
@@ -1899,7 +2105,7 @@ async function validateTraceability(root, config) {
1899
2105
  ", "
1900
2106
  )}`,
1901
2107
  "error",
1902
- scenariosRoot,
2108
+ specsRoot,
1903
2109
  "traceability.scMustTouchContracts",
1904
2110
  scWithoutContracts
1905
2111
  )
@@ -1917,7 +2123,7 @@ async function validateTraceability(root, config) {
1917
2123
  "QFAI_CONTRACT_ORPHAN",
1918
2124
  `\u5951\u7D04\u304C SC \u304B\u3089\u53C2\u7167\u3055\u308C\u3066\u3044\u307E\u305B\u3093: ${orphanContracts.join(", ")}`,
1919
2125
  "error",
1920
- scenariosRoot,
2126
+ specsRoot,
1921
2127
  "traceability.allowOrphanContracts",
1922
2128
  orphanContracts
1923
2129
  )
@@ -1954,7 +2160,7 @@ async function validateCodeReferences(upstreamIds, srcRoot, testsRoot) {
1954
2160
  const pattern = buildIdPattern(Array.from(upstreamIds));
1955
2161
  let found = false;
1956
2162
  for (const file of targetFiles) {
1957
- const text = await (0, import_promises11.readFile)(file, "utf-8");
2163
+ const text = await (0, import_promises13.readFile)(file, "utf-8");
1958
2164
  if (pattern.test(text)) {
1959
2165
  found = true;
1960
2166
  break;
@@ -2002,8 +2208,8 @@ async function validateProject(root, configResult) {
2002
2208
  const issues = [
2003
2209
  ...configIssues,
2004
2210
  ...await validateSpecs(root, config),
2211
+ ...await validateDeltas(root, config),
2005
2212
  ...await validateScenarios(root, config),
2006
- ...await validateDecisions(root, config),
2007
2213
  ...await validateContracts(root, config),
2008
2214
  ...await validateDefinedIds(root, config),
2009
2215
  ...await validateTraceability(root, config)
@@ -2032,21 +2238,15 @@ async function createReportData(root, validation, configResult) {
2032
2238
  const resolved = configResult ?? await loadConfig(root);
2033
2239
  const config = resolved.config;
2034
2240
  const configPath = resolved.configPath;
2035
- const specRoot = resolvePath(root, config, "specDir");
2036
- const decisionsRoot = resolvePath(root, config, "decisionsDir");
2037
- const scenariosRoot = resolvePath(root, config, "scenariosDir");
2038
- const apiRoot = resolvePath(root, config, "apiContractsDir");
2039
- const uiRoot = resolvePath(root, config, "uiContractsDir");
2040
- const dbRoot = resolvePath(root, config, "dataContractsDir");
2241
+ const specsRoot = resolvePath(root, config, "specsDir");
2242
+ const contractsRoot = resolvePath(root, config, "contractsDir");
2243
+ const apiRoot = import_node_path13.default.join(contractsRoot, "api");
2244
+ const uiRoot = import_node_path13.default.join(contractsRoot, "ui");
2245
+ const dbRoot = import_node_path13.default.join(contractsRoot, "db");
2041
2246
  const srcRoot = resolvePath(root, config, "srcDir");
2042
2247
  const testsRoot = resolvePath(root, config, "testsDir");
2043
- const specFiles = await collectSpecFiles(specRoot);
2044
- const scenarioFiles = await collectFiles(scenariosRoot, {
2045
- extensions: [".feature"]
2046
- });
2047
- const decisionFiles = await collectFiles(decisionsRoot, {
2048
- extensions: [".md"]
2049
- });
2248
+ const specFiles = await collectSpecFiles(specsRoot);
2249
+ const scenarioFiles = await collectScenarioFiles(specsRoot);
2050
2250
  const {
2051
2251
  api: apiFiles,
2052
2252
  ui: uiFiles,
@@ -2055,7 +2255,6 @@ async function createReportData(root, validation, configResult) {
2055
2255
  const idsByPrefix = await collectIds([
2056
2256
  ...specFiles,
2057
2257
  ...scenarioFiles,
2058
- ...decisionFiles,
2059
2258
  ...apiFiles,
2060
2259
  ...uiFiles,
2061
2260
  ...dbFiles
@@ -2080,7 +2279,6 @@ async function createReportData(root, validation, configResult) {
2080
2279
  summary: {
2081
2280
  specs: specFiles.length,
2082
2281
  scenarios: scenarioFiles.length,
2083
- decisions: decisionFiles.length,
2084
2282
  contracts: {
2085
2283
  api: apiFiles.length,
2086
2284
  ui: uiFiles.length,
@@ -2114,7 +2312,6 @@ function formatReportMarkdown(data) {
2114
2312
  lines.push("## \u6982\u8981");
2115
2313
  lines.push(`- specs: ${data.summary.specs}`);
2116
2314
  lines.push(`- scenarios: ${data.summary.scenarios}`);
2117
- lines.push(`- decisions: ${data.summary.decisions}`);
2118
2315
  lines.push(
2119
2316
  `- contracts: api ${data.summary.contracts.api} / ui ${data.summary.contracts.ui} / db ${data.summary.contracts.db}`
2120
2317
  );
@@ -2190,7 +2387,7 @@ async function collectIds(files) {
2190
2387
  DATA: /* @__PURE__ */ new Set()
2191
2388
  };
2192
2389
  for (const file of files) {
2193
- const text = await (0, import_promises12.readFile)(file, "utf-8");
2390
+ const text = await (0, import_promises14.readFile)(file, "utf-8");
2194
2391
  for (const prefix of ID_PREFIXES2) {
2195
2392
  const ids = extractIds(text, prefix);
2196
2393
  ids.forEach((id) => result[prefix].add(id));
@@ -2208,7 +2405,7 @@ async function collectIds(files) {
2208
2405
  async function collectUpstreamIds(files) {
2209
2406
  const ids = /* @__PURE__ */ new Set();
2210
2407
  for (const file of files) {
2211
- const text = await (0, import_promises12.readFile)(file, "utf-8");
2408
+ const text = await (0, import_promises14.readFile)(file, "utf-8");
2212
2409
  extractAllIds(text).forEach((id) => ids.add(id));
2213
2410
  }
2214
2411
  return ids;
@@ -2229,7 +2426,7 @@ async function evaluateTraceability(upstreamIds, srcRoot, testsRoot) {
2229
2426
  }
2230
2427
  const pattern = buildIdPattern2(Array.from(upstreamIds));
2231
2428
  for (const file of targetFiles) {
2232
- const text = await (0, import_promises12.readFile)(file, "utf-8");
2429
+ const text = await (0, import_promises14.readFile)(file, "utf-8");
2233
2430
  if (pattern.test(text)) {
2234
2431
  return true;
2235
2432
  }
@@ -2273,21 +2470,22 @@ function buildHotspots(issues) {
2273
2470
 
2274
2471
  // src/cli/commands/report.ts
2275
2472
  async function runReport(options) {
2276
- const root = import_node_path10.default.resolve(options.root);
2473
+ const root = import_node_path14.default.resolve(options.root);
2277
2474
  const configResult = await loadConfig(root);
2278
- const input = options.jsonPath ?? configResult.config.output.jsonPath;
2279
- const inputPath = import_node_path10.default.isAbsolute(input) ? input : import_node_path10.default.resolve(root, input);
2475
+ const input = configResult.config.output.validateJsonPath;
2476
+ const inputPath = import_node_path14.default.isAbsolute(input) ? input : import_node_path14.default.resolve(root, input);
2280
2477
  let validation;
2281
2478
  try {
2282
2479
  validation = await readValidationResult(inputPath);
2283
2480
  } catch (err) {
2284
- if (isMissingFileError(err)) {
2481
+ if (isMissingFileError5(err)) {
2285
2482
  error(
2286
2483
  [
2287
2484
  `qfai report: \u5165\u529B\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: ${inputPath}`,
2288
2485
  "",
2289
- "\u307E\u305A validate.json \u3092\u751F\u6210\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u4F8B:",
2290
- ` qfai validate --json-path ${input}`,
2486
+ "\u307E\u305A qfai validate \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u4F8B:",
2487
+ " qfai validate",
2488
+ "\uFF08\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u51FA\u529B\u5148: .qfai/out/validate.json\uFF09",
2291
2489
  "",
2292
2490
  "GitHub Actions \u30C6\u30F3\u30D7\u30EC\u3092\u4F7F\u3063\u3066\u3044\u308B\u5834\u5408\u306F\u3001workflow \u306E validate \u30B8\u30E7\u30D6\u3092\u5148\u306B\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
2293
2491
  ].join("\n")
@@ -2299,11 +2497,12 @@ async function runReport(options) {
2299
2497
  }
2300
2498
  const data = await createReportData(root, validation, configResult);
2301
2499
  const output = options.format === "json" ? formatReportJson(data) : formatReportMarkdown(data);
2302
- const defaultOut = options.format === "json" ? ".qfai/out/report.json" : ".qfai/out/report.md";
2500
+ const outRoot = resolvePath(root, configResult.config, "outDir");
2501
+ const defaultOut = options.format === "json" ? import_node_path14.default.join(outRoot, "report.json") : import_node_path14.default.join(outRoot, "report.md");
2303
2502
  const out = options.outPath ?? defaultOut;
2304
- const outPath = import_node_path10.default.isAbsolute(out) ? out : import_node_path10.default.resolve(root, out);
2305
- await (0, import_promises13.mkdir)(import_node_path10.default.dirname(outPath), { recursive: true });
2306
- await (0, import_promises13.writeFile)(outPath, `${output}
2503
+ const outPath = import_node_path14.default.isAbsolute(out) ? out : import_node_path14.default.resolve(root, out);
2504
+ await (0, import_promises15.mkdir)(import_node_path14.default.dirname(outPath), { recursive: true });
2505
+ await (0, import_promises15.writeFile)(outPath, `${output}
2307
2506
  `, "utf-8");
2308
2507
  info(
2309
2508
  `report: info=${validation.counts.info} warning=${validation.counts.warning} error=${validation.counts.error}`
@@ -2311,7 +2510,7 @@ async function runReport(options) {
2311
2510
  info(`wrote report: ${outPath}`);
2312
2511
  }
2313
2512
  async function readValidationResult(inputPath) {
2314
- const raw = await (0, import_promises13.readFile)(inputPath, "utf-8");
2513
+ const raw = await (0, import_promises15.readFile)(inputPath, "utf-8");
2315
2514
  const parsed = JSON.parse(raw);
2316
2515
  if (!isValidationResult(parsed)) {
2317
2516
  throw new Error(`validate.json \u306E\u5F62\u5F0F\u304C\u4E0D\u6B63\u3067\u3059: ${inputPath}`);
@@ -2343,7 +2542,7 @@ function isValidationResult(value) {
2343
2542
  }
2344
2543
  return typeof counts.info === "number" && typeof counts.warning === "number" && typeof counts.error === "number";
2345
2544
  }
2346
- function isMissingFileError(error2) {
2545
+ function isMissingFileError5(error2) {
2347
2546
  if (!error2 || typeof error2 !== "object") {
2348
2547
  return false;
2349
2548
  }
@@ -2352,8 +2551,8 @@ function isMissingFileError(error2) {
2352
2551
  }
2353
2552
 
2354
2553
  // src/cli/commands/validate.ts
2355
- var import_promises14 = require("fs/promises");
2356
- var import_node_path11 = __toESM(require("path"), 1);
2554
+ var import_promises16 = require("fs/promises");
2555
+ var import_node_path15 = __toESM(require("path"), 1);
2357
2556
 
2358
2557
  // src/cli/lib/failOn.ts
2359
2558
  function shouldFail(result, failOn) {
@@ -2368,24 +2567,17 @@ function shouldFail(result, failOn) {
2368
2567
 
2369
2568
  // src/cli/commands/validate.ts
2370
2569
  async function runValidate(options) {
2371
- const root = import_node_path11.default.resolve(options.root);
2570
+ const root = import_node_path15.default.resolve(options.root);
2372
2571
  const configResult = await loadConfig(root);
2373
2572
  const result = await validateProject(root, configResult);
2374
- const format = options.format ?? configResult.config.output.format;
2375
- const explicitJsonPath = options.jsonPath;
2573
+ const format = options.format ?? "text";
2376
2574
  if (format === "text") {
2377
2575
  emitText(result);
2378
2576
  }
2379
2577
  if (format === "github") {
2380
2578
  result.issues.forEach(emitGitHub);
2381
2579
  }
2382
- const shouldWriteJson = format === "json" || explicitJsonPath !== void 0;
2383
- if (shouldWriteJson) {
2384
- const jsonPath = format === "json" ? options.jsonPath ?? configResult.config.output.jsonPath : explicitJsonPath;
2385
- if (jsonPath) {
2386
- await emitJson(result, root, jsonPath);
2387
- }
2388
- }
2580
+ await emitJson(result, root, configResult.config.output.validateJsonPath);
2389
2581
  const failOn = resolveFailOn(options, configResult.config.validation.failOn);
2390
2582
  return shouldFail(result, failOn) ? 1 : 0;
2391
2583
  }
@@ -2424,9 +2616,9 @@ function emitGitHub(issue7) {
2424
2616
  );
2425
2617
  }
2426
2618
  async function emitJson(result, root, jsonPath) {
2427
- const abs = import_node_path11.default.isAbsolute(jsonPath) ? jsonPath : import_node_path11.default.resolve(root, jsonPath);
2428
- await (0, import_promises14.mkdir)(import_node_path11.default.dirname(abs), { recursive: true });
2429
- await (0, import_promises14.writeFile)(abs, `${JSON.stringify(result, null, 2)}
2619
+ const abs = import_node_path15.default.isAbsolute(jsonPath) ? jsonPath : import_node_path15.default.resolve(root, jsonPath);
2620
+ await (0, import_promises16.mkdir)(import_node_path15.default.dirname(abs), { recursive: true });
2621
+ await (0, import_promises16.writeFile)(abs, `${JSON.stringify(result, null, 2)}
2430
2622
  `, "utf-8");
2431
2623
  }
2432
2624
 
@@ -2486,15 +2678,6 @@ function parseArgs(argv, cwd) {
2486
2678
  i += 1;
2487
2679
  break;
2488
2680
  }
2489
- case "--json-path":
2490
- {
2491
- const next = args[i + 1];
2492
- if (next) {
2493
- options.jsonPath = next;
2494
- }
2495
- }
2496
- i += 1;
2497
- break;
2498
2681
  case "--out":
2499
2682
  {
2500
2683
  const next = args[i + 1];
@@ -2525,7 +2708,7 @@ function applyFormatOption(command, value, options) {
2525
2708
  return;
2526
2709
  }
2527
2710
  if (command === "validate") {
2528
- if (value === "text" || value === "json" || value === "github") {
2711
+ if (value === "text" || value === "github") {
2529
2712
  options.validateFormat = value;
2530
2713
  }
2531
2714
  return;
@@ -2533,7 +2716,7 @@ function applyFormatOption(command, value, options) {
2533
2716
  if (value === "md" || value === "json") {
2534
2717
  options.reportFormat = value;
2535
2718
  }
2536
- if (value === "text" || value === "json" || value === "github") {
2719
+ if (value === "text" || value === "github") {
2537
2720
  options.validateFormat = value;
2538
2721
  }
2539
2722
  }
@@ -2559,15 +2742,13 @@ async function run(argv, cwd) {
2559
2742
  root: options.root,
2560
2743
  strict: options.strict,
2561
2744
  format: options.validateFormat,
2562
- ...options.failOn !== void 0 ? { failOn: options.failOn } : {},
2563
- ...options.jsonPath !== void 0 ? { jsonPath: options.jsonPath } : {}
2745
+ ...options.failOn !== void 0 ? { failOn: options.failOn } : {}
2564
2746
  });
2565
2747
  return;
2566
2748
  case "report":
2567
2749
  await runReport({
2568
2750
  root: options.root,
2569
2751
  format: options.reportFormat,
2570
- ...options.jsonPath !== void 0 ? { jsonPath: options.jsonPath } : {},
2571
2752
  ...options.reportOut !== void 0 ? { outPath: options.reportOut } : {}
2572
2753
  });
2573
2754
  return;
@@ -2589,14 +2770,13 @@ Options:
2589
2770
  --root <path> \u5BFE\u8C61\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA
2590
2771
  --dir <path> init \u306E\u51FA\u529B\u5148
2591
2772
  --force \u65E2\u5B58\u30D5\u30A1\u30A4\u30EB\u3092\u4E0A\u66F8\u304D
2592
- --yes init: \u975E\u5BFE\u8A71\u3067\u30C7\u30D5\u30A9\u30EB\u30C8\u3092\u63A1\u7528\uFF08\u73FE\u5728\u306F\u975E\u5BFE\u8A71\u304C\u65E2\u5B9A\u3001\u5C06\u6765\u306E\u5BFE\u8A71\u5C0E\u5165\u6642\u3082\u81EA\u52D5Yes\uFF09
2773
+ --yes init: \u4E88\u7D04\u30D5\u30E9\u30B0\uFF08\u73FE\u72B6\u306F\u975E\u5BFE\u8A71\u306E\u305F\u3081\u6319\u52D5\u5DEE\u306A\u3057\u3002\u5C06\u6765\u306E\u5BFE\u8A71\u5C0E\u5165\u6642\u306B\u81EA\u52D5Yes\uFF09
2593
2774
  --dry-run \u5909\u66F4\u3092\u884C\u308F\u305A\u8868\u793A\u306E\u307F
2594
- --format <text|json|github> validate \u306E\u51FA\u529B\u5F62\u5F0F
2775
+ --format <text|github> validate \u306E\u51FA\u529B\u5F62\u5F0F
2595
2776
  --format <md|json> report \u306E\u51FA\u529B\u5F62\u5F0F
2596
- --strict validate: warning \u4EE5\u4E0A\u3067 exit 1
2777
+ --strict validate: warning \u4EE5\u4E0A\u3067 exit 1
2597
2778
  --fail-on <error|warning|never> validate: \u5931\u6557\u6761\u4EF6
2598
- --json-path <path> validate: JSON \u51FA\u529B\u5148 / report: validate JSON \u5165\u529B
2599
- --out <path> report: \u51FA\u529B\u5148
2779
+ --out <path> report: \u51FA\u529B\u5148
2600
2780
  -h, --help \u30D8\u30EB\u30D7\u8868\u793A
2601
2781
  `;
2602
2782
  }