qfai 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +28 -0
  2. package/assets/init/qfai/README.md +6 -0
  3. package/assets/init/qfai/contracts/api/api-0001-sample.yaml +14 -0
  4. package/assets/init/qfai/contracts/db/db-0001-sample.sql +5 -0
  5. package/assets/init/qfai/contracts/ui/ui-0001-sample.yaml +4 -0
  6. package/assets/init/qfai/prompts/makeBusinessFlow.md +34 -0
  7. package/assets/init/qfai/prompts/makeOverview.md +27 -0
  8. package/assets/init/qfai/spec/decisions/ADR-0001.md +7 -0
  9. package/assets/init/qfai/spec/scenarios.feature +6 -0
  10. package/assets/init/qfai/spec/spec-0001-sample.md +29 -0
  11. package/assets/init/root/.github/workflows/qfai.yml +22 -0
  12. package/assets/init/root/qfai.config.yaml +29 -0
  13. package/dist/cli/commands/init.d.ts +8 -0
  14. package/dist/cli/commands/init.d.ts.map +1 -0
  15. package/dist/cli/commands/init.js +30 -0
  16. package/dist/cli/commands/init.js.map +1 -0
  17. package/dist/cli/commands/report.d.ts +8 -0
  18. package/dist/cli/commands/report.d.ts.map +1 -0
  19. package/dist/cli/commands/report.js +83 -0
  20. package/dist/cli/commands/report.js.map +1 -0
  21. package/dist/cli/commands/validate.d.ts +10 -0
  22. package/dist/cli/commands/validate.d.ts.map +1 -0
  23. package/dist/cli/commands/validate.js +66 -0
  24. package/dist/cli/commands/validate.js.map +1 -0
  25. package/dist/cli/index.cjs +2003 -0
  26. package/dist/cli/index.cjs.map +1 -0
  27. package/dist/cli/index.d.cts +1 -0
  28. package/dist/cli/index.d.ts +3 -0
  29. package/dist/cli/index.d.ts.map +1 -0
  30. package/dist/cli/index.js +7 -0
  31. package/dist/cli/index.js.map +1 -0
  32. package/dist/cli/index.mjs +1980 -0
  33. package/dist/cli/index.mjs.map +1 -0
  34. package/dist/cli/lib/args.d.ts +19 -0
  35. package/dist/cli/lib/args.d.ts.map +1 -0
  36. package/dist/cli/lib/args.js +107 -0
  37. package/dist/cli/lib/args.js.map +1 -0
  38. package/dist/cli/lib/assets.d.ts +2 -0
  39. package/dist/cli/lib/assets.d.ts.map +1 -0
  40. package/dist/cli/lib/assets.js +8 -0
  41. package/dist/cli/lib/assets.js.map +1 -0
  42. package/dist/cli/lib/failOn.d.ts +5 -0
  43. package/dist/cli/lib/failOn.d.ts.map +1 -0
  44. package/dist/cli/lib/failOn.js +10 -0
  45. package/dist/cli/lib/failOn.js.map +1 -0
  46. package/dist/cli/lib/fs.d.ts +11 -0
  47. package/dist/cli/lib/fs.d.ts.map +1 -0
  48. package/dist/cli/lib/fs.js +91 -0
  49. package/dist/cli/lib/fs.js.map +1 -0
  50. package/dist/cli/lib/logger.d.ts +4 -0
  51. package/dist/cli/lib/logger.d.ts.map +1 -0
  52. package/dist/cli/lib/logger.js +10 -0
  53. package/dist/cli/lib/logger.js.map +1 -0
  54. package/dist/cli/main.d.ts +2 -0
  55. package/dist/cli/main.d.ts.map +1 -0
  56. package/dist/cli/main.js +73 -0
  57. package/dist/cli/main.js.map +1 -0
  58. package/dist/core/config.d.ts +46 -0
  59. package/dist/core/config.d.ts.map +1 -0
  60. package/dist/core/config.js +224 -0
  61. package/dist/core/config.js.map +1 -0
  62. package/dist/core/discovery.d.ts +11 -0
  63. package/dist/core/discovery.d.ts.map +1 -0
  64. package/dist/core/discovery.js +31 -0
  65. package/dist/core/discovery.js.map +1 -0
  66. package/dist/core/fs.d.ts +6 -0
  67. package/dist/core/fs.d.ts.map +1 -0
  68. package/dist/core/fs.js +55 -0
  69. package/dist/core/fs.js.map +1 -0
  70. package/dist/core/ids.d.ts +5 -0
  71. package/dist/core/ids.d.ts.map +1 -0
  72. package/dist/core/ids.js +49 -0
  73. package/dist/core/ids.js.map +1 -0
  74. package/dist/core/index.d.ts +11 -0
  75. package/dist/core/index.d.ts.map +1 -0
  76. package/dist/core/index.js +11 -0
  77. package/dist/core/index.js.map +1 -0
  78. package/dist/core/report.d.ts +41 -0
  79. package/dist/core/report.d.ts.map +1 -0
  80. package/dist/core/report.js +238 -0
  81. package/dist/core/report.js.map +1 -0
  82. package/dist/core/types.d.ts +27 -0
  83. package/dist/core/types.d.ts.map +1 -0
  84. package/dist/core/types.js +2 -0
  85. package/dist/core/types.js.map +1 -0
  86. package/dist/core/validate.d.ts +4 -0
  87. package/dist/core/validate.d.ts.map +1 -0
  88. package/dist/core/validate.js +32 -0
  89. package/dist/core/validate.js.map +1 -0
  90. package/dist/core/validators/contracts.d.ts +5 -0
  91. package/dist/core/validators/contracts.d.ts.map +1 -0
  92. package/dist/core/validators/contracts.js +157 -0
  93. package/dist/core/validators/contracts.js.map +1 -0
  94. package/dist/core/validators/scenario.d.ts +5 -0
  95. package/dist/core/validators/scenario.d.ts.map +1 -0
  96. package/dist/core/validators/scenario.js +82 -0
  97. package/dist/core/validators/scenario.js.map +1 -0
  98. package/dist/core/validators/spec.d.ts +5 -0
  99. package/dist/core/validators/spec.d.ts.map +1 -0
  100. package/dist/core/validators/spec.js +69 -0
  101. package/dist/core/validators/spec.js.map +1 -0
  102. package/dist/core/validators/traceability.d.ts +4 -0
  103. package/dist/core/validators/traceability.d.ts.map +1 -0
  104. package/dist/core/validators/traceability.js +148 -0
  105. package/dist/core/validators/traceability.js.map +1 -0
  106. package/dist/core/version.d.ts +2 -0
  107. package/dist/core/version.d.ts.map +1 -0
  108. package/dist/core/version.js +25 -0
  109. package/dist/core/version.js.map +1 -0
  110. package/dist/index.cjs +1579 -0
  111. package/dist/index.cjs.map +1 -0
  112. package/dist/index.d.cts +132 -0
  113. package/dist/index.d.ts +2 -0
  114. package/dist/index.d.ts.map +1 -0
  115. package/dist/index.js +2 -0
  116. package/dist/index.js.map +1 -0
  117. package/dist/index.mjs +1523 -0
  118. package/dist/index.mjs.map +1 -0
  119. package/dist/tsconfig.tsbuildinfo +1 -0
  120. package/package.json +38 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/core/config.ts","../src/core/ids.ts","../src/core/report.ts","../src/core/discovery.ts","../src/core/fs.ts","../src/core/types.ts","../src/core/version.ts","../src/core/validators/contracts.ts","../src/core/validators/scenario.ts","../src/core/validators/spec.ts","../src/core/validators/traceability.ts","../src/core/validate.ts"],"sourcesContent":["export * from \"./core/index.js\";\n","import { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { parse as parseYaml } from \"yaml\";\n\nimport type { Issue } from \"./types.js\";\n\nexport type FailOn = \"never\" | \"warning\" | \"error\";\nexport type OutputFormat = \"text\" | \"json\" | \"github\";\n\nexport type QfaiPaths = {\n specDir: string;\n decisionsDir: string;\n scenariosDir: string;\n rulesDir: string;\n contractsDir: string;\n uiContractsDir: string;\n apiContractsDir: string;\n dataContractsDir: string;\n srcDir: string;\n testsDir: string;\n};\n\nexport type QfaiValidationConfig = {\n failOn: FailOn;\n require: {\n specSections: string[];\n };\n traceability: {\n brMustHaveSc: boolean;\n scMustTouchContracts: boolean;\n allowOrphanContracts: boolean;\n };\n};\n\nexport type QfaiOutputConfig = {\n format: OutputFormat;\n jsonPath: string;\n};\n\nexport type QfaiConfig = {\n paths: QfaiPaths;\n validation: QfaiValidationConfig;\n output: QfaiOutputConfig;\n};\n\nexport type ConfigPathKey = keyof QfaiPaths;\n\nexport type ConfigLoadResult = {\n config: QfaiConfig;\n issues: Issue[];\n configPath: string;\n};\n\nexport const defaultConfig: QfaiConfig = {\n paths: {\n specDir: \"qfai/spec\",\n decisionsDir: \"qfai/spec/decisions\",\n scenariosDir: \"qfai/spec\",\n rulesDir: \"qfai/rules\",\n contractsDir: \"qfai/contracts\",\n uiContractsDir: \"qfai/contracts/ui\",\n apiContractsDir: \"qfai/contracts/api\",\n dataContractsDir: \"qfai/contracts/db\",\n srcDir: \"src\",\n testsDir: \"tests\",\n },\n validation: {\n failOn: \"error\",\n require: {\n specSections: [\n \"背景\",\n \"スコープ\",\n \"非ゴール\",\n \"用語\",\n \"前提\",\n \"決定事項\",\n \"業務ルール\",\n ],\n },\n traceability: {\n brMustHaveSc: true,\n scMustTouchContracts: true,\n allowOrphanContracts: false,\n },\n },\n output: {\n format: \"text\",\n jsonPath: \".qfai/out/validate.json\",\n },\n};\n\nexport function getConfigPath(root: string): string {\n return path.join(root, \"qfai.config.yaml\");\n}\n\nexport async function loadConfig(root: string): Promise<ConfigLoadResult> {\n const configPath = getConfigPath(root);\n const issues: Issue[] = [];\n\n let parsed: unknown;\n try {\n const raw = await readFile(configPath, \"utf-8\");\n parsed = parseYaml(raw);\n } catch (error) {\n if (isMissingFile(error)) {\n return { config: defaultConfig, issues, configPath };\n }\n issues.push(configIssue(configPath, formatError(error)));\n return { config: defaultConfig, issues, configPath };\n }\n\n const normalized = normalizeConfig(parsed, configPath, issues);\n return { config: normalized, issues, configPath };\n}\n\nexport function resolvePath(\n root: string,\n config: QfaiConfig,\n key: ConfigPathKey,\n): string {\n return path.resolve(root, config.paths[key]);\n}\n\nfunction normalizeConfig(\n raw: unknown,\n configPath: string,\n issues: Issue[],\n): QfaiConfig {\n if (!isRecord(raw)) {\n issues.push(configIssue(configPath, \"設定ファイルの形式が不正です。\"));\n return defaultConfig;\n }\n\n return {\n paths: normalizePaths(raw.paths, configPath, issues),\n validation: normalizeValidation(raw.validation, configPath, issues),\n output: normalizeOutput(raw.output, configPath, issues),\n };\n}\n\nfunction normalizePaths(\n raw: unknown,\n configPath: string,\n issues: Issue[],\n): QfaiPaths {\n const base = defaultConfig.paths;\n if (!raw) {\n return base;\n }\n if (!isRecord(raw)) {\n issues.push(\n configIssue(configPath, \"paths はオブジェクトである必要があります。\"),\n );\n return base;\n }\n\n return {\n specDir: readString(\n raw.specDir,\n base.specDir,\n \"paths.specDir\",\n configPath,\n issues,\n ),\n decisionsDir: readString(\n raw.decisionsDir,\n base.decisionsDir,\n \"paths.decisionsDir\",\n configPath,\n issues,\n ),\n scenariosDir: readString(\n raw.scenariosDir,\n base.scenariosDir,\n \"paths.scenariosDir\",\n configPath,\n issues,\n ),\n rulesDir: readString(\n raw.rulesDir,\n base.rulesDir,\n \"paths.rulesDir\",\n configPath,\n issues,\n ),\n contractsDir: readString(\n raw.contractsDir,\n base.contractsDir,\n \"paths.contractsDir\",\n configPath,\n issues,\n ),\n uiContractsDir: readString(\n raw.uiContractsDir,\n base.uiContractsDir,\n \"paths.uiContractsDir\",\n configPath,\n issues,\n ),\n apiContractsDir: readString(\n raw.apiContractsDir,\n base.apiContractsDir,\n \"paths.apiContractsDir\",\n configPath,\n issues,\n ),\n dataContractsDir: readString(\n raw.dataContractsDir,\n base.dataContractsDir,\n \"paths.dataContractsDir\",\n configPath,\n issues,\n ),\n srcDir: readString(\n raw.srcDir,\n base.srcDir,\n \"paths.srcDir\",\n configPath,\n issues,\n ),\n testsDir: readString(\n raw.testsDir,\n base.testsDir,\n \"paths.testsDir\",\n configPath,\n issues,\n ),\n };\n}\n\nfunction normalizeValidation(\n raw: unknown,\n configPath: string,\n issues: Issue[],\n): QfaiValidationConfig {\n const base = defaultConfig.validation;\n if (!raw) {\n return base;\n }\n if (!isRecord(raw)) {\n issues.push(\n configIssue(\n configPath,\n \"validation はオブジェクトである必要があります。\",\n ),\n );\n return base;\n }\n\n let requireRaw: Record<string, unknown> | undefined;\n if (raw.require === undefined) {\n requireRaw = undefined;\n } else if (isRecord(raw.require)) {\n requireRaw = raw.require;\n } else {\n issues.push(\n configIssue(\n configPath,\n \"validation.require はオブジェクトである必要があります。\",\n ),\n );\n requireRaw = undefined;\n }\n\n let traceabilityRaw: Record<string, unknown> | undefined;\n if (raw.traceability === undefined) {\n traceabilityRaw = undefined;\n } else if (isRecord(raw.traceability)) {\n traceabilityRaw = raw.traceability;\n } else {\n issues.push(\n configIssue(\n configPath,\n \"validation.traceability はオブジェクトである必要があります。\",\n ),\n );\n traceabilityRaw = undefined;\n }\n\n return {\n failOn: readFailOn(\n raw.failOn,\n base.failOn,\n \"validation.failOn\",\n configPath,\n issues,\n ),\n require: {\n specSections: readStringArray(\n requireRaw?.specSections,\n base.require.specSections,\n \"validation.require.specSections\",\n configPath,\n issues,\n ),\n },\n traceability: {\n brMustHaveSc: readBoolean(\n traceabilityRaw?.brMustHaveSc,\n base.traceability.brMustHaveSc,\n \"validation.traceability.brMustHaveSc\",\n configPath,\n issues,\n ),\n scMustTouchContracts: readBoolean(\n traceabilityRaw?.scMustTouchContracts,\n base.traceability.scMustTouchContracts,\n \"validation.traceability.scMustTouchContracts\",\n configPath,\n issues,\n ),\n allowOrphanContracts: readBoolean(\n traceabilityRaw?.allowOrphanContracts,\n base.traceability.allowOrphanContracts,\n \"validation.traceability.allowOrphanContracts\",\n configPath,\n issues,\n ),\n },\n };\n}\n\nfunction normalizeOutput(\n raw: unknown,\n configPath: string,\n issues: Issue[],\n): QfaiOutputConfig {\n const base = defaultConfig.output;\n if (!raw) {\n return base;\n }\n if (!isRecord(raw)) {\n issues.push(\n configIssue(configPath, \"output はオブジェクトである必要があります。\"),\n );\n return base;\n }\n\n return {\n format: readOutputFormat(\n raw.format,\n base.format,\n \"output.format\",\n configPath,\n issues,\n ),\n jsonPath: readString(\n raw.jsonPath,\n base.jsonPath,\n \"output.jsonPath\",\n configPath,\n issues,\n ),\n };\n}\n\nfunction readString(\n value: unknown,\n fallback: string,\n label: string,\n configPath: string,\n issues: Issue[],\n): string {\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value;\n }\n if (value !== undefined) {\n issues.push(\n configIssue(configPath, `${label} は文字列である必要があります。`),\n );\n }\n return fallback;\n}\n\nfunction readStringArray(\n value: unknown,\n fallback: string[],\n label: string,\n configPath: string,\n issues: Issue[],\n): string[] {\n if (Array.isArray(value) && value.every((item) => typeof item === \"string\")) {\n return value;\n }\n if (value !== undefined) {\n issues.push(\n configIssue(configPath, `${label} は文字列配列である必要があります。`),\n );\n }\n return fallback;\n}\n\nfunction readBoolean(\n value: unknown,\n fallback: boolean,\n label: string,\n configPath: string,\n issues: Issue[],\n): boolean {\n if (typeof value === \"boolean\") {\n return value;\n }\n if (value !== undefined) {\n issues.push(\n configIssue(configPath, `${label} は真偽値である必要があります。`),\n );\n }\n return fallback;\n}\n\nfunction readFailOn(\n value: unknown,\n fallback: FailOn,\n label: string,\n configPath: string,\n issues: Issue[],\n): FailOn {\n if (value === \"never\" || value === \"warning\" || value === \"error\") {\n return value;\n }\n if (value !== undefined) {\n issues.push(\n configIssue(\n configPath,\n `${label} は never|warning|error のいずれかである必要があります。`,\n ),\n );\n }\n return fallback;\n}\n\nfunction readOutputFormat(\n value: unknown,\n fallback: OutputFormat,\n label: string,\n configPath: string,\n issues: Issue[],\n): OutputFormat {\n if (value === \"text\" || value === \"json\" || value === \"github\") {\n return value;\n }\n if (value !== undefined) {\n issues.push(\n configIssue(\n configPath,\n `${label} は text|json|github のいずれかである必要があります。`,\n ),\n );\n }\n return fallback;\n}\n\nfunction configIssue(file: string, message: string): Issue {\n return {\n code: \"QFAI_CONFIG_INVALID\",\n severity: \"error\",\n message,\n file,\n rule: \"config.invalid\",\n };\n}\n\nfunction isMissingFile(error: unknown): boolean {\n if (error && typeof error === \"object\" && \"code\" in error) {\n return (error as { code?: string }).code === \"ENOENT\";\n }\n return false;\n}\n\nfunction formatError(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n","export type IdPrefix = \"SPEC\" | \"BR\" | \"SC\" | \"UI\" | \"API\" | \"DATA\";\n\nconst ID_PATTERNS: Record<IdPrefix, RegExp> = {\n SPEC: /\\bSPEC-[A-Z0-9-]+\\b/g,\n BR: /\\bBR-[A-Z0-9-]+\\b/g,\n SC: /\\bSC-[A-Z0-9-]+\\b/g,\n UI: /\\bUI-[A-Z0-9-]+\\b/g,\n API: /\\bAPI-[A-Z0-9-]+\\b/g,\n DATA: /\\bDATA-[A-Z0-9-]+\\b/g,\n};\n\nconst LOOSE_ID_PATTERNS: Record<IdPrefix, RegExp> = {\n SPEC: /\\bSPEC-[A-Za-z0-9_-]+\\b/gi,\n BR: /\\bBR-[A-Za-z0-9_-]+\\b/gi,\n SC: /\\bSC-[A-Za-z0-9_-]+\\b/gi,\n UI: /\\bUI-[A-Za-z0-9_-]+\\b/gi,\n API: /\\bAPI-[A-Za-z0-9_-]+\\b/gi,\n DATA: /\\bDATA-[A-Za-z0-9_-]+\\b/gi,\n};\n\nexport function extractIds(text: string, prefix: IdPrefix): string[] {\n const pattern = ID_PATTERNS[prefix];\n const matches = text.match(pattern);\n return unique(matches ?? []);\n}\n\nexport function extractAllIds(text: string): string[] {\n const all: string[] = [];\n (Object.keys(ID_PATTERNS) as IdPrefix[]).forEach((prefix) => {\n all.push(...extractIds(text, prefix));\n });\n return unique(all);\n}\n\nexport function extractInvalidIds(\n text: string,\n prefixes: IdPrefix[],\n): string[] {\n const invalid: string[] = [];\n for (const prefix of prefixes) {\n const candidates = text.match(LOOSE_ID_PATTERNS[prefix]) ?? [];\n for (const candidate of candidates) {\n if (!isValidId(candidate, prefix)) {\n invalid.push(candidate);\n }\n }\n }\n return unique(invalid);\n}\n\nfunction unique(values: string[]): string[] {\n return Array.from(new Set(values));\n}\n\nfunction isValidId(value: string, prefix: IdPrefix): boolean {\n const pattern = ID_PATTERNS[prefix];\n const strict = new RegExp(pattern.source);\n return strict.test(value);\n}\n","import { readFile } from \"node:fs/promises\";\nimport { loadConfig, resolvePath, type ConfigLoadResult } from \"./config.js\";\nimport { collectContractFiles, collectSpecFiles } from \"./discovery.js\";\nimport { collectFiles } from \"./fs.js\";\nimport { extractAllIds, extractIds, type IdPrefix } from \"./ids.js\";\nimport type { Issue, ValidationCounts, ValidationResult } from \"./types.js\";\nimport { validateProject } from \"./validate.js\";\nimport { resolveToolVersion } from \"./version.js\";\n\nexport type ReportSummary = {\n specs: number;\n scenarios: number;\n decisions: number;\n rules: number;\n contracts: {\n api: number;\n ui: number;\n db: number;\n };\n counts: ValidationCounts;\n};\n\nexport type ReportIds = {\n spec: string[];\n br: string[];\n sc: string[];\n ui: string[];\n api: string[];\n data: string[];\n};\n\nexport type ReportTraceability = {\n upstreamIdsFound: number;\n referencedInCodeOrTests: boolean;\n};\n\nexport type ReportData = {\n tool: \"qfai\";\n version: string;\n generatedAt: string;\n root: string;\n configPath: string;\n summary: ReportSummary;\n ids: ReportIds;\n traceability: ReportTraceability;\n issues: Issue[];\n};\n\nconst ID_PREFIXES: IdPrefix[] = [\"SPEC\", \"BR\", \"SC\", \"UI\", \"API\", \"DATA\"];\n\nexport async function createReportData(\n root: string,\n validation?: ValidationResult,\n configResult?: ConfigLoadResult,\n): Promise<ReportData> {\n const resolved = configResult ?? (await loadConfig(root));\n const config = resolved.config;\n const configPath = resolved.configPath;\n\n const specRoot = resolvePath(root, config, \"specDir\");\n const decisionsRoot = resolvePath(root, config, \"decisionsDir\");\n const scenariosRoot = resolvePath(root, config, \"scenariosDir\");\n const rulesRoot = resolvePath(root, config, \"rulesDir\");\n const apiRoot = resolvePath(root, config, \"apiContractsDir\");\n const uiRoot = resolvePath(root, config, \"uiContractsDir\");\n const dbRoot = resolvePath(root, config, \"dataContractsDir\");\n const srcRoot = resolvePath(root, config, \"srcDir\");\n const testsRoot = resolvePath(root, config, \"testsDir\");\n\n const specFiles = await collectSpecFiles(specRoot);\n const scenarioFiles = await collectFiles(scenariosRoot, {\n extensions: [\".feature\"],\n });\n const decisionFiles = await collectFiles(decisionsRoot, {\n extensions: [\".md\"],\n });\n const ruleFiles = await collectFiles(rulesRoot, { extensions: [\".md\"] });\n const {\n api: apiFiles,\n ui: uiFiles,\n db: dbFiles,\n } = await collectContractFiles(uiRoot, apiRoot, dbRoot);\n\n const idsByPrefix = await collectIds([\n ...specFiles,\n ...scenarioFiles,\n ...decisionFiles,\n ...ruleFiles,\n ...apiFiles,\n ...uiFiles,\n ...dbFiles,\n ]);\n\n const upstreamIds = await collectUpstreamIds([\n ...specFiles,\n ...scenarioFiles,\n ]);\n const traceability = await evaluateTraceability(\n upstreamIds,\n srcRoot,\n testsRoot,\n );\n\n const resolvedValidation =\n validation ?? (await validateProject(root, resolved));\n const version = await resolveToolVersion();\n\n return {\n tool: \"qfai\",\n version,\n generatedAt: new Date().toISOString(),\n root,\n configPath,\n summary: {\n specs: specFiles.length,\n scenarios: scenarioFiles.length,\n decisions: decisionFiles.length,\n rules: ruleFiles.length,\n contracts: {\n api: apiFiles.length,\n ui: uiFiles.length,\n db: dbFiles.length,\n },\n counts: resolvedValidation.counts,\n },\n ids: {\n spec: idsByPrefix.SPEC,\n br: idsByPrefix.BR,\n sc: idsByPrefix.SC,\n ui: idsByPrefix.UI,\n api: idsByPrefix.API,\n data: idsByPrefix.DATA,\n },\n traceability: {\n upstreamIdsFound: upstreamIds.size,\n referencedInCodeOrTests: traceability,\n },\n issues: resolvedValidation.issues,\n };\n}\n\nexport function formatReportMarkdown(data: ReportData): string {\n const lines: string[] = [];\n\n lines.push(\"# QFAI Report\");\n lines.push(`- 生成日時: ${data.generatedAt}`);\n lines.push(`- ルート: ${data.root}`);\n lines.push(`- 設定: ${data.configPath}`);\n lines.push(`- 版: ${data.version}`);\n lines.push(\"\");\n\n lines.push(\"## 概要\");\n lines.push(`- specs: ${data.summary.specs}`);\n lines.push(`- scenarios: ${data.summary.scenarios}`);\n lines.push(`- decisions: ${data.summary.decisions}`);\n lines.push(`- rules: ${data.summary.rules}`);\n lines.push(\n `- contracts: api ${data.summary.contracts.api} / ui ${data.summary.contracts.ui} / db ${data.summary.contracts.db}`,\n );\n lines.push(\n `- issues: info ${data.summary.counts.info} / warning ${data.summary.counts.warning} / error ${data.summary.counts.error}`,\n );\n lines.push(\"\");\n\n lines.push(\"## ID集計\");\n lines.push(formatIdLine(\"SPEC\", data.ids.spec));\n lines.push(formatIdLine(\"BR\", data.ids.br));\n lines.push(formatIdLine(\"SC\", data.ids.sc));\n lines.push(formatIdLine(\"UI\", data.ids.ui));\n lines.push(formatIdLine(\"API\", data.ids.api));\n lines.push(formatIdLine(\"DATA\", data.ids.data));\n lines.push(\"\");\n\n lines.push(\"## トレーサビリティ\");\n lines.push(`- 上流ID検出数: ${data.traceability.upstreamIdsFound}`);\n lines.push(\n `- コード/テスト参照: ${data.traceability.referencedInCodeOrTests ? \"あり\" : \"なし\"}`,\n );\n lines.push(\"\");\n\n lines.push(\"## Hotspots\");\n const hotspots = buildHotspots(data.issues);\n if (hotspots.length === 0) {\n lines.push(\"- (none)\");\n } else {\n for (const spot of hotspots) {\n lines.push(\n `- ${spot.file}: total ${spot.total} (error ${spot.error} / warning ${spot.warning} / info ${spot.info})`,\n );\n }\n }\n lines.push(\"\");\n\n lines.push(\"## トレーサビリティ(検証)\");\n const traceIssues = data.issues.filter(\n (item) =>\n item.rule?.startsWith(\"traceability.\") ||\n item.code.startsWith(\"QFAI_TRACE\") ||\n item.code === \"QFAI_CONTRACT_ORPHAN\",\n );\n if (traceIssues.length === 0) {\n lines.push(\"- (none)\");\n } else {\n for (const item of traceIssues) {\n const location = item.file ? ` (${item.file})` : \"\";\n lines.push(\n `- ${item.severity.toUpperCase()} [${item.code}] ${item.message}${location}`,\n );\n }\n }\n lines.push(\"\");\n\n lines.push(\"## 検証結果\");\n if (data.issues.length === 0) {\n lines.push(\"- (none)\");\n } else {\n for (const item of data.issues) {\n const location = item.file ? ` (${item.file})` : \"\";\n const refs =\n item.refs && item.refs.length > 0 ? ` refs=${item.refs.join(\",\")}` : \"\";\n lines.push(\n `- ${item.severity.toUpperCase()} [${item.code}] ${item.message}${location}${refs}`,\n );\n }\n }\n\n return lines.join(\"\\n\");\n}\n\nexport function formatReportJson(data: ReportData): string {\n return JSON.stringify(data, null, 2);\n}\n\nasync function collectIds(\n files: string[],\n): Promise<Record<IdPrefix, string[]>> {\n const result: Record<IdPrefix, Set<string>> = {\n SPEC: new Set(),\n BR: new Set(),\n SC: new Set(),\n UI: new Set(),\n API: new Set(),\n DATA: new Set(),\n };\n\n for (const file of files) {\n const text = await readFile(file, \"utf-8\");\n for (const prefix of ID_PREFIXES) {\n const ids = extractIds(text, prefix);\n ids.forEach((id) => result[prefix].add(id));\n }\n }\n\n return {\n SPEC: toSortedArray(result.SPEC),\n BR: toSortedArray(result.BR),\n SC: toSortedArray(result.SC),\n UI: toSortedArray(result.UI),\n API: toSortedArray(result.API),\n DATA: toSortedArray(result.DATA),\n };\n}\n\nasync function collectUpstreamIds(files: string[]): Promise<Set<string>> {\n const ids = new Set<string>();\n for (const file of files) {\n const text = await readFile(file, \"utf-8\");\n extractAllIds(text).forEach((id) => ids.add(id));\n }\n return ids;\n}\n\nasync function evaluateTraceability(\n upstreamIds: Set<string>,\n srcRoot: string,\n testsRoot: string,\n): Promise<boolean> {\n if (upstreamIds.size === 0) {\n return false;\n }\n\n const codeFiles = await collectFiles(srcRoot, {\n extensions: [\".ts\", \".tsx\", \".js\", \".jsx\"],\n });\n const testFiles = await collectFiles(testsRoot, {\n extensions: [\".ts\", \".tsx\", \".js\", \".jsx\"],\n });\n const targetFiles = [...codeFiles, ...testFiles];\n\n if (targetFiles.length === 0) {\n return false;\n }\n\n const pattern = buildIdPattern(Array.from(upstreamIds));\n\n for (const file of targetFiles) {\n const text = await readFile(file, \"utf-8\");\n if (pattern.test(text)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction buildIdPattern(ids: string[]): RegExp {\n const escaped = ids.map((id) => id.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"));\n return new RegExp(`\\\\b(${escaped.join(\"|\")})\\\\b`);\n}\n\nfunction formatIdLine(label: string, values: string[]): string {\n if (values.length === 0) {\n return `- ${label}: (none)`;\n }\n return `- ${label}: ${values.join(\", \")}`;\n}\n\nfunction toSortedArray(values: Set<string>): string[] {\n return Array.from(values).sort((a, b) => a.localeCompare(b));\n}\n\ntype Hotspot = {\n file: string;\n total: number;\n error: number;\n warning: number;\n info: number;\n};\n\nfunction buildHotspots(issues: Issue[]): Hotspot[] {\n const map = new Map<string, Hotspot>();\n for (const issue of issues) {\n if (!issue.file) {\n continue;\n }\n const current =\n map.get(issue.file) ??\n ({\n file: issue.file,\n total: 0,\n error: 0,\n warning: 0,\n info: 0,\n } satisfies Hotspot);\n current.total += 1;\n current[issue.severity] += 1;\n map.set(issue.file, current);\n }\n\n return Array.from(map.values()).sort((a, b) =>\n b.total !== a.total ? b.total - a.total : a.file.localeCompare(b.file),\n );\n}\n","import path from \"node:path\";\n\nimport { collectFiles } from \"./fs.js\";\n\nconst LEGACY_SPEC_NAME = \"spec.md\";\nconst SPEC_NAMED_PATTERN = /^spec-\\d{4}-[^/\\\\]+\\.md$/i;\n\nexport type ContractFiles = {\n api: string[];\n ui: string[];\n db: string[];\n};\n\nexport async function collectSpecFiles(specRoot: string): Promise<string[]> {\n const files = await collectFiles(specRoot, { extensions: [\".md\"] });\n return files.filter((file) => isSpecFile(file));\n}\n\nexport async function collectUiContractFiles(\n uiRoot: string,\n): Promise<string[]> {\n return collectFiles(uiRoot, { extensions: [\".yaml\", \".yml\"] });\n}\n\nexport async function collectApiContractFiles(\n apiRoot: string,\n): Promise<string[]> {\n return collectFiles(apiRoot, { extensions: [\".yaml\", \".yml\", \".json\"] });\n}\n\nexport async function collectDataContractFiles(\n dataRoot: string,\n): Promise<string[]> {\n return collectFiles(dataRoot, { extensions: [\".sql\"] });\n}\n\nexport async function collectContractFiles(\n uiRoot: string,\n apiRoot: string,\n dataRoot: string,\n): Promise<ContractFiles> {\n const [ui, api, db] = await Promise.all([\n collectUiContractFiles(uiRoot),\n collectApiContractFiles(apiRoot),\n collectDataContractFiles(dataRoot),\n ]);\n return { ui, api, db };\n}\n\nfunction isSpecFile(filePath: string): boolean {\n const name = path.basename(filePath).toLowerCase();\n return name === LEGACY_SPEC_NAME || SPEC_NAMED_PATTERN.test(name);\n}\n","import { access, readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nconst DEFAULT_IGNORE_DIRS = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \".pnpm\",\n \"tmp\",\n \".mcp-tools\",\n]);\n\nexport type CollectFilesOptions = {\n extensions?: string[];\n ignoreDirs?: string[];\n};\n\nexport async function collectFiles(\n root: string,\n options: CollectFilesOptions = {},\n): Promise<string[]> {\n const entries: string[] = [];\n if (!(await exists(root))) {\n return entries;\n }\n\n const ignoreDirs = new Set([\n ...DEFAULT_IGNORE_DIRS,\n ...(options.ignoreDirs ?? []),\n ]);\n const extensions = options.extensions?.map((ext) => ext.toLowerCase()) ?? [];\n\n await walk(root, root, ignoreDirs, extensions, entries);\n return entries;\n}\n\nasync function walk(\n base: string,\n current: string,\n ignoreDirs: Set<string>,\n extensions: string[],\n out: string[],\n): Promise<void> {\n const items = await readdir(current, { withFileTypes: true });\n\n for (const item of items) {\n const fullPath = path.join(current, item.name);\n\n if (item.isDirectory()) {\n if (ignoreDirs.has(item.name)) {\n continue;\n }\n await walk(base, fullPath, ignoreDirs, extensions, out);\n continue;\n }\n\n if (item.isFile()) {\n if (extensions.length > 0) {\n const ext = path.extname(item.name).toLowerCase();\n if (!extensions.includes(ext)) {\n continue;\n }\n }\n out.push(fullPath);\n }\n }\n}\n\nasync function exists(target: string): Promise<boolean> {\n try {\n await access(target);\n return true;\n } catch {\n return false;\n }\n}\n","export type IssueSeverity = \"info\" | \"warning\" | \"error\";\n\nexport const VALIDATION_SCHEMA_VERSION = \"0.2\" as const;\n\nexport type IssueLocation = {\n line: number;\n column?: number;\n};\n\nexport type Issue = {\n code: string;\n severity: IssueSeverity;\n message: string;\n file?: string;\n refs?: string[];\n rule?: string;\n loc?: IssueLocation;\n};\n\nexport type ValidationCounts = {\n info: number;\n warning: number;\n error: number;\n};\n\nexport type ValidationResult = {\n schemaVersion: typeof VALIDATION_SCHEMA_VERSION;\n toolVersion: string;\n issues: Issue[];\n counts: ValidationCounts;\n};\n","import { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\ndeclare const __QFAI_TOOL_VERSION__: string | undefined;\n\nexport async function resolveToolVersion(): Promise<string> {\n if (\n typeof __QFAI_TOOL_VERSION__ === \"string\" &&\n __QFAI_TOOL_VERSION__.length > 0\n ) {\n return __QFAI_TOOL_VERSION__;\n }\n\n try {\n const packagePath = resolvePackageJsonPath();\n const raw = await readFile(packagePath, \"utf-8\");\n const parsed = JSON.parse(raw) as { version?: unknown };\n const version = typeof parsed.version === \"string\" ? parsed.version : \"\";\n return version.length > 0 ? version : \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nfunction resolvePackageJsonPath(): string {\n const base = import.meta.url;\n const basePath = base.startsWith(\"file:\") ? fileURLToPath(base) : base;\n return path.resolve(path.dirname(basePath), \"../../package.json\");\n}\n","import { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { parse as parseYaml } from \"yaml\";\n\nimport type { QfaiConfig } from \"../config.js\";\nimport { resolvePath } from \"../config.js\";\nimport {\n collectApiContractFiles,\n collectDataContractFiles,\n collectUiContractFiles,\n} from \"../discovery.js\";\nimport { extractInvalidIds } from \"../ids.js\";\nimport type { Issue, IssueSeverity } from \"../types.js\";\n\nconst SQL_DANGEROUS_PATTERNS: Array<{ pattern: RegExp; label: string }> = [\n { pattern: /\\bDROP\\s+TABLE\\b/i, label: \"DROP TABLE\" },\n { pattern: /\\bDROP\\s+DATABASE\\b/i, label: \"DROP DATABASE\" },\n { pattern: /\\bTRUNCATE\\b/i, label: \"TRUNCATE\" },\n {\n pattern: /\\bALTER\\s+TABLE\\b[\\s\\S]*\\bDROP\\b/i,\n label: \"ALTER TABLE ... DROP\",\n },\n];\n\nexport async function validateContracts(\n root: string,\n config: QfaiConfig,\n): Promise<Issue[]> {\n const issues: Issue[] = [];\n\n issues.push(\n ...(await validateUiContracts(resolvePath(root, config, \"uiContractsDir\"))),\n );\n issues.push(\n ...(await validateApiContracts(\n resolvePath(root, config, \"apiContractsDir\"),\n )),\n );\n issues.push(\n ...(await validateDataContracts(\n resolvePath(root, config, \"dataContractsDir\"),\n )),\n );\n\n return issues;\n}\n\nasync function validateUiContracts(uiRoot: string): Promise<Issue[]> {\n const files = await collectUiContractFiles(uiRoot);\n if (files.length === 0) {\n return [\n issue(\n \"QFAI-UI-000\",\n \"UI 契約ファイルが見つかりません。\",\n \"info\",\n uiRoot,\n \"contracts.ui.files\",\n ),\n ];\n }\n\n const issues: Issue[] = [];\n for (const file of files) {\n const text = await readFile(file, \"utf-8\");\n const invalidIds = extractInvalidIds(text, [\n \"SPEC\",\n \"BR\",\n \"SC\",\n \"UI\",\n \"API\",\n \"DATA\",\n ]);\n if (invalidIds.length > 0) {\n issues.push(\n issue(\n \"QFAI_ID_INVALID_FORMAT\",\n `ID フォーマットが不正です: ${invalidIds.join(\", \")}`,\n \"error\",\n file,\n \"id.format\",\n invalidIds,\n ),\n );\n }\n try {\n const doc = parseYaml(text) as Record<string, unknown>;\n const id = typeof doc.id === \"string\" ? doc.id : \"\";\n if (!id.startsWith(\"UI-\")) {\n issues.push(\n issue(\n \"QFAI-UI-001\",\n \"UI 契約の id は UI- で始まる必要があります。\",\n \"error\",\n file,\n \"contracts.ui.id\",\n ),\n );\n }\n } catch (error) {\n issues.push(\n issue(\n \"QFAI-UI-002\",\n `UI YAML の解析に失敗しました: ${formatError(error)}`,\n \"error\",\n file,\n \"contracts.ui.parse\",\n ),\n );\n }\n }\n\n return issues;\n}\n\nasync function validateApiContracts(apiRoot: string): Promise<Issue[]> {\n const files = await collectApiContractFiles(apiRoot);\n if (files.length === 0) {\n return [\n issue(\n \"QFAI-API-000\",\n \"API 契約ファイルが見つかりません。\",\n \"info\",\n apiRoot,\n \"contracts.api.files\",\n ),\n ];\n }\n\n const issues: Issue[] = [];\n for (const file of files) {\n const text = await readFile(file, \"utf-8\");\n const invalidIds = extractInvalidIds(text, [\n \"SPEC\",\n \"BR\",\n \"SC\",\n \"UI\",\n \"API\",\n \"DATA\",\n ]);\n if (invalidIds.length > 0) {\n issues.push(\n issue(\n \"QFAI_ID_INVALID_FORMAT\",\n `ID フォーマットが不正です: ${invalidIds.join(\", \")}`,\n \"error\",\n file,\n \"id.format\",\n invalidIds,\n ),\n );\n }\n try {\n const doc = parseStructured(file, text);\n if (!doc || !hasOpenApi(doc)) {\n issues.push(\n issue(\n \"QFAI-API-001\",\n \"OpenAPI 定義が見つかりません。\",\n \"error\",\n file,\n \"contracts.api.openapi\",\n ),\n );\n }\n } catch (error) {\n issues.push(\n issue(\n \"QFAI-API-002\",\n `API 定義の解析に失敗しました: ${formatError(error)}`,\n \"error\",\n file,\n \"contracts.api.parse\",\n ),\n );\n }\n }\n\n return issues;\n}\n\nasync function validateDataContracts(dataRoot: string): Promise<Issue[]> {\n const files = await collectDataContractFiles(dataRoot);\n if (files.length === 0) {\n return [\n issue(\n \"QFAI-DATA-000\",\n \"DATA 契約ファイルが見つかりません。\",\n \"info\",\n dataRoot,\n \"contracts.data.files\",\n ),\n ];\n }\n\n const issues: Issue[] = [];\n for (const file of files) {\n const text = await readFile(file, \"utf-8\");\n const invalidIds = extractInvalidIds(text, [\n \"SPEC\",\n \"BR\",\n \"SC\",\n \"UI\",\n \"API\",\n \"DATA\",\n ]);\n if (invalidIds.length > 0) {\n issues.push(\n issue(\n \"QFAI_ID_INVALID_FORMAT\",\n `ID フォーマットが不正です: ${invalidIds.join(\", \")}`,\n \"error\",\n file,\n \"id.format\",\n invalidIds,\n ),\n );\n }\n issues.push(...lintSql(text, file));\n }\n\n return issues;\n}\n\nexport function lintSql(text: string, file: string): Issue[] {\n const issues: Issue[] = [];\n for (const { pattern, label } of SQL_DANGEROUS_PATTERNS) {\n if (pattern.test(text)) {\n issues.push(\n issue(\n \"QFAI-DATA-001\",\n `危険な SQL 操作が含まれています: ${label}`,\n \"warning\",\n file,\n \"contracts.data.sql\",\n ),\n );\n }\n }\n return issues;\n}\n\nfunction parseStructured(\n file: string,\n text: string,\n): Record<string, unknown> | null {\n const ext = path.extname(file).toLowerCase();\n if (ext === \".json\") {\n return JSON.parse(text) as Record<string, unknown>;\n }\n return parseYaml(text) as Record<string, unknown>;\n}\n\nfunction hasOpenApi(doc: Record<string, unknown>): boolean {\n return typeof doc.openapi === \"string\" && doc.openapi.length > 0;\n}\n\nfunction formatError(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\n\nfunction issue(\n code: string,\n message: string,\n severity: IssueSeverity,\n file?: string,\n rule?: string,\n refs?: string[],\n): Issue {\n const issue: Issue = {\n code,\n severity,\n message,\n };\n if (file) {\n issue.file = file;\n }\n if (rule) {\n issue.rule = rule;\n }\n if (refs && refs.length > 0) {\n issue.refs = refs;\n }\n return issue;\n}\n","import { readFile } from \"node:fs/promises\";\n\nimport type { QfaiConfig } from \"../config.js\";\nimport { resolvePath } from \"../config.js\";\nimport { collectFiles } from \"../fs.js\";\nimport { extractIds, extractInvalidIds } from \"../ids.js\";\nimport type { Issue, IssueSeverity } from \"../types.js\";\n\nconst GIVEN_PATTERN = /\\bGiven\\b/;\nconst WHEN_PATTERN = /\\bWhen\\b/;\nconst THEN_PATTERN = /\\bThen\\b/;\n\nexport async function validateScenarios(\n root: string,\n config: QfaiConfig,\n): Promise<Issue[]> {\n const scenariosRoot = resolvePath(root, config, \"scenariosDir\");\n const files = await collectFiles(scenariosRoot, {\n extensions: [\".feature\"],\n });\n\n if (files.length === 0) {\n return [\n issue(\n \"QFAI-SC-000\",\n \"Scenario ファイルが見つかりません。\",\n \"info\",\n scenariosRoot,\n \"scenario.files\",\n ),\n ];\n }\n\n const issues: Issue[] = [];\n for (const file of files) {\n const text = await readFile(file, \"utf-8\");\n issues.push(...validateScenarioContent(text, file));\n }\n\n return issues;\n}\n\nexport function validateScenarioContent(text: string, file: string): Issue[] {\n const issues: Issue[] = [];\n\n const invalidIds = extractInvalidIds(text, [\n \"SPEC\",\n \"BR\",\n \"SC\",\n \"UI\",\n \"API\",\n \"DATA\",\n ]);\n if (invalidIds.length > 0) {\n issues.push(\n issue(\n \"QFAI_ID_INVALID_FORMAT\",\n `ID フォーマットが不正です: ${invalidIds.join(\", \")}`,\n \"error\",\n file,\n \"id.format\",\n invalidIds,\n ),\n );\n }\n\n const scIds = extractIds(text, \"SC\");\n if (scIds.length === 0) {\n issues.push(\n issue(\n \"QFAI-SC-001\",\n \"SC ID が見つかりません。\",\n \"error\",\n file,\n \"scenario.id\",\n ),\n );\n }\n\n const specIds = extractIds(text, \"SPEC\");\n if (specIds.length === 0) {\n issues.push(\n issue(\n \"QFAI-SC-002\",\n \"SC は SPEC を参照する必要があります。\",\n \"error\",\n file,\n \"scenario.spec\",\n ),\n );\n }\n\n const brIds = extractIds(text, \"BR\");\n if (brIds.length === 0) {\n issues.push(\n issue(\n \"QFAI-SC-003\",\n \"SC は BR を参照する必要があります。\",\n \"error\",\n file,\n \"scenario.br\",\n ),\n );\n }\n\n const missingSteps: string[] = [];\n if (!GIVEN_PATTERN.test(text)) {\n missingSteps.push(\"Given\");\n }\n if (!WHEN_PATTERN.test(text)) {\n missingSteps.push(\"When\");\n }\n if (!THEN_PATTERN.test(text)) {\n missingSteps.push(\"Then\");\n }\n if (missingSteps.length > 0) {\n issues.push(\n issue(\n \"QFAI-SC-005\",\n `Given/When/Then が不足しています: ${missingSteps.join(\", \")}`,\n \"warning\",\n file,\n \"scenario.steps\",\n ),\n );\n }\n\n return issues;\n}\n\nfunction issue(\n code: string,\n message: string,\n severity: IssueSeverity,\n file?: string,\n rule?: string,\n refs?: string[],\n): Issue {\n const issue: Issue = {\n code,\n severity,\n message,\n };\n if (file) {\n issue.file = file;\n }\n if (rule) {\n issue.rule = rule;\n }\n if (refs && refs.length > 0) {\n issue.refs = refs;\n }\n return issue;\n}\n","import { readFile } from \"node:fs/promises\";\nimport type { QfaiConfig } from \"../config.js\";\nimport { resolvePath } from \"../config.js\";\nimport { collectSpecFiles } from \"../discovery.js\";\nimport { extractIds, extractInvalidIds } from \"../ids.js\";\nimport type { Issue, IssueSeverity } from \"../types.js\";\n\nexport async function validateSpecs(\n root: string,\n config: QfaiConfig,\n): Promise<Issue[]> {\n const specsRoot = resolvePath(root, config, \"specDir\");\n const files = await collectSpecFiles(specsRoot);\n\n if (files.length === 0) {\n return [\n issue(\n \"QFAI-SPEC-000\",\n \"Spec ファイルが見つかりません。\",\n \"info\",\n specsRoot,\n \"spec.files\",\n ),\n ];\n }\n\n const issues: Issue[] = [];\n for (const file of files) {\n const text = await readFile(file, \"utf-8\");\n issues.push(\n ...validateSpecContent(\n text,\n file,\n config.validation.require.specSections,\n ),\n );\n }\n\n return issues;\n}\n\nexport function validateSpecContent(\n text: string,\n file: string,\n requiredSections: string[],\n): Issue[] {\n const issues: Issue[] = [];\n\n const invalidIds = extractInvalidIds(text, [\n \"SPEC\",\n \"BR\",\n \"SC\",\n \"UI\",\n \"API\",\n \"DATA\",\n ]);\n if (invalidIds.length > 0) {\n issues.push(\n issue(\n \"QFAI_ID_INVALID_FORMAT\",\n `ID フォーマットが不正です: ${invalidIds.join(\", \")}`,\n \"error\",\n file,\n \"id.format\",\n invalidIds,\n ),\n );\n }\n\n const specIds = extractIds(text, \"SPEC\");\n if (specIds.length === 0) {\n issues.push(\n issue(\n \"QFAI-SPEC-001\",\n \"SPEC ID が見つかりません。\",\n \"error\",\n file,\n \"spec.id\",\n ),\n );\n }\n\n const brIds = extractIds(text, \"BR\");\n if (brIds.length === 0) {\n issues.push(\n issue(\n \"QFAI-SPEC-002\",\n \"BR ID が見つかりません。\",\n \"error\",\n file,\n \"spec.br\",\n ),\n );\n }\n\n const scIds = extractIds(text, \"SC\");\n if (scIds.length > 0) {\n issues.push(\n issue(\n \"QFAI-SPEC-003\",\n \"Spec は SC を参照しないルールです。\",\n \"warning\",\n file,\n \"spec.noSc\",\n scIds,\n ),\n );\n }\n\n for (const section of requiredSections) {\n if (!text.includes(section)) {\n issues.push(\n issue(\n \"QFAI-SPEC-004\",\n `必須セクションが不足しています: ${section}`,\n \"error\",\n file,\n \"spec.requiredSection\",\n ),\n );\n }\n }\n\n return issues;\n}\n\nfunction issue(\n code: string,\n message: string,\n severity: IssueSeverity,\n file?: string,\n rule?: string,\n refs?: string[],\n): Issue {\n const issue: Issue = {\n code,\n severity,\n message,\n };\n if (file) {\n issue.file = file;\n }\n if (rule) {\n issue.rule = rule;\n }\n if (refs && refs.length > 0) {\n issue.refs = refs;\n }\n return issue;\n}\n","import { readFile } from \"node:fs/promises\";\n\nimport type { QfaiConfig } from \"../config.js\";\nimport { resolvePath } from \"../config.js\";\nimport {\n collectApiContractFiles,\n collectDataContractFiles,\n collectSpecFiles,\n collectUiContractFiles,\n} from \"../discovery.js\";\nimport { collectFiles } from \"../fs.js\";\nimport { extractAllIds, extractIds, type IdPrefix } from \"../ids.js\";\nimport type { Issue, IssueSeverity } from \"../types.js\";\n\nexport async function validateTraceability(\n root: string,\n config: QfaiConfig,\n): Promise<Issue[]> {\n const issues: Issue[] = [];\n const specsRoot = resolvePath(root, config, \"specDir\");\n const decisionsRoot = resolvePath(root, config, \"decisionsDir\");\n const scenariosRoot = resolvePath(root, config, \"scenariosDir\");\n const srcRoot = resolvePath(root, config, \"srcDir\");\n const testsRoot = resolvePath(root, config, \"testsDir\");\n\n const specFiles = await collectSpecFiles(specsRoot);\n const decisionFiles = await collectFiles(decisionsRoot, {\n extensions: [\".md\"],\n });\n const scenarioFiles = await collectFiles(scenariosRoot, {\n extensions: [\".feature\"],\n });\n\n const upstreamIds = new Set<string>();\n const brIdsInSpecs = new Set<string>();\n const brIdsInScenarios = new Set<string>();\n const scIdsInScenarios = new Set<string>();\n const scenarioContractIds = new Set<string>();\n const scWithContracts = new Set<string>();\n\n for (const file of [...specFiles, ...decisionFiles]) {\n const text = await readFile(file, \"utf-8\");\n extractAllIds(text).forEach((id) => upstreamIds.add(id));\n extractIds(text, \"BR\").forEach((id) => brIdsInSpecs.add(id));\n }\n\n for (const file of scenarioFiles) {\n const text = await readFile(file, \"utf-8\");\n extractAllIds(text).forEach((id) => upstreamIds.add(id));\n\n const brIds = extractIds(text, \"BR\");\n brIds.forEach((id) => brIdsInScenarios.add(id));\n\n const scIds = extractIds(text, \"SC\");\n scIds.forEach((id) => scIdsInScenarios.add(id));\n\n const contractIds = [\n ...extractIds(text, \"UI\"),\n ...extractIds(text, \"API\"),\n ...extractIds(text, \"DATA\"),\n ];\n contractIds.forEach((id) => scenarioContractIds.add(id));\n\n if (contractIds.length > 0) {\n scIds.forEach((id) => scWithContracts.add(id));\n }\n }\n\n if (upstreamIds.size === 0) {\n return [\n issue(\n \"QFAI-TRACE-000\",\n \"上流 ID が見つかりません。\",\n \"info\",\n specsRoot,\n \"traceability.upstream\",\n ),\n ];\n }\n\n if (config.validation.traceability.brMustHaveSc && brIdsInSpecs.size > 0) {\n const orphanBrIds = Array.from(brIdsInSpecs).filter(\n (id) => !brIdsInScenarios.has(id),\n );\n if (orphanBrIds.length > 0) {\n issues.push(\n issue(\n \"QFAI_TRACE_BR_ORPHAN\",\n `BR が SC に紐づいていません: ${orphanBrIds.join(\", \")}`,\n \"error\",\n specsRoot,\n \"traceability.brMustHaveSc\",\n orphanBrIds,\n ),\n );\n }\n }\n\n if (\n config.validation.traceability.scMustTouchContracts &&\n scIdsInScenarios.size > 0\n ) {\n const scWithoutContracts = Array.from(scIdsInScenarios).filter(\n (id) => !scWithContracts.has(id),\n );\n if (scWithoutContracts.length > 0) {\n issues.push(\n issue(\n \"QFAI_TRACE_SC_NO_CONTRACT\",\n `SC が契約(UI/API/DATA)に接続していません: ${scWithoutContracts.join(\n \", \",\n )}`,\n \"error\",\n scenariosRoot,\n \"traceability.scMustTouchContracts\",\n scWithoutContracts,\n ),\n );\n }\n }\n\n if (!config.validation.traceability.allowOrphanContracts) {\n const contractIds = await collectContractIds(root, config);\n if (contractIds.size > 0) {\n const orphanContracts = Array.from(contractIds).filter(\n (id) => !scenarioContractIds.has(id),\n );\n if (orphanContracts.length > 0) {\n issues.push(\n issue(\n \"QFAI_CONTRACT_ORPHAN\",\n `契約が SC から参照されていません: ${orphanContracts.join(\", \")}`,\n \"error\",\n scenariosRoot,\n \"traceability.allowOrphanContracts\",\n orphanContracts,\n ),\n );\n }\n }\n }\n\n issues.push(\n ...(await validateCodeReferences(upstreamIds, srcRoot, testsRoot)),\n );\n return issues;\n}\n\nasync function collectContractIds(\n root: string,\n config: QfaiConfig,\n): Promise<Set<string>> {\n const contractIds = new Set<string>();\n const uiRoot = resolvePath(root, config, \"uiContractsDir\");\n const apiRoot = resolvePath(root, config, \"apiContractsDir\");\n const dataRoot = resolvePath(root, config, \"dataContractsDir\");\n\n const uiFiles = await collectUiContractFiles(uiRoot);\n const apiFiles = await collectApiContractFiles(apiRoot);\n const dataFiles = await collectDataContractFiles(dataRoot);\n\n await collectIdsFromFiles(uiFiles, [\"UI\"], contractIds);\n await collectIdsFromFiles(apiFiles, [\"API\"], contractIds);\n await collectIdsFromFiles(dataFiles, [\"DATA\"], contractIds);\n\n return contractIds;\n}\n\nasync function collectIdsFromFiles(\n files: string[],\n prefixes: IdPrefix[],\n out: Set<string>,\n): Promise<void> {\n for (const file of files) {\n const text = await readFile(file, \"utf-8\");\n for (const prefix of prefixes) {\n extractIds(text, prefix).forEach((id) => out.add(id));\n }\n }\n}\n\nasync function validateCodeReferences(\n upstreamIds: Set<string>,\n srcRoot: string,\n testsRoot: string,\n): Promise<Issue[]> {\n const issues: Issue[] = [];\n const codeFiles = await collectFiles(srcRoot, {\n extensions: [\".ts\", \".tsx\", \".js\", \".jsx\"],\n });\n const testFiles = await collectFiles(testsRoot, {\n extensions: [\".ts\", \".tsx\", \".js\", \".jsx\"],\n });\n const targetFiles = [...codeFiles, ...testFiles];\n\n if (targetFiles.length === 0) {\n issues.push(\n issue(\n \"QFAI-TRACE-001\",\n \"参照対象のコード/テストが見つかりません。\",\n \"info\",\n srcRoot,\n \"traceability.codeReferences\",\n ),\n );\n return issues;\n }\n\n const pattern = buildIdPattern(Array.from(upstreamIds));\n let found = false;\n\n for (const file of targetFiles) {\n const text = await readFile(file, \"utf-8\");\n if (pattern.test(text)) {\n found = true;\n break;\n }\n }\n\n if (!found) {\n issues.push(\n issue(\n \"QFAI-TRACE-002\",\n \"上流 ID がコード/テストに参照されていません。\",\n \"warning\",\n srcRoot,\n \"traceability.codeReferences\",\n ),\n );\n }\n\n return issues;\n}\n\nfunction buildIdPattern(ids: string[]): RegExp {\n const escaped = ids.map((id) => id.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"));\n return new RegExp(`\\\\b(${escaped.join(\"|\")})\\\\b`);\n}\n\nfunction issue(\n code: string,\n message: string,\n severity: IssueSeverity,\n file?: string,\n rule?: string,\n refs?: string[],\n): Issue {\n const issue: Issue = {\n code,\n severity,\n message,\n };\n if (file) {\n issue.file = file;\n }\n if (rule) {\n issue.rule = rule;\n }\n if (refs && refs.length > 0) {\n issue.refs = refs;\n }\n return issue;\n}\n","import { loadConfig, type ConfigLoadResult } from \"./config.js\";\nimport type { Issue, ValidationCounts, ValidationResult } from \"./types.js\";\nimport { VALIDATION_SCHEMA_VERSION } from \"./types.js\";\nimport { resolveToolVersion } from \"./version.js\";\nimport { validateContracts } from \"./validators/contracts.js\";\nimport { validateScenarios } from \"./validators/scenario.js\";\nimport { validateSpecs } from \"./validators/spec.js\";\nimport { validateTraceability } from \"./validators/traceability.js\";\n\nexport async function validateProject(\n root: string,\n configResult?: ConfigLoadResult,\n): Promise<ValidationResult> {\n const resolved = configResult ?? (await loadConfig(root));\n const { config, issues: configIssues } = resolved;\n const issues = [\n ...configIssues,\n ...(await validateSpecs(root, config)),\n ...(await validateScenarios(root, config)),\n ...(await validateContracts(root, config)),\n ...(await validateTraceability(root, config)),\n ];\n\n const toolVersion = await resolveToolVersion();\n return {\n schemaVersion: VALIDATION_SCHEMA_VERSION,\n toolVersion,\n issues,\n counts: countIssues(issues),\n };\n}\n\nfunction countIssues(issues: Issue[]): ValidationCounts {\n return issues.reduce<ValidationCounts>(\n (acc, issue) => {\n acc[issue.severity] += 1;\n return acc;\n },\n { info: 0, warning: 0, error: 0 },\n );\n}\n"],"mappings":"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","names":["path","parseYaml","import_promises","import_node_path","import_promises","import_node_path","path","path","import_promises","import_node_path","path","import_promises","import_node_path","import_yaml","parseYaml","formatError","path","issue","import_promises","issue","import_promises","issue","import_promises","issue","issue","buildIdPattern","issue"]}
@@ -0,0 +1,132 @@
1
+ type IssueSeverity = "info" | "warning" | "error";
2
+ declare const VALIDATION_SCHEMA_VERSION: "0.2";
3
+ type IssueLocation = {
4
+ line: number;
5
+ column?: number;
6
+ };
7
+ type Issue = {
8
+ code: string;
9
+ severity: IssueSeverity;
10
+ message: string;
11
+ file?: string;
12
+ refs?: string[];
13
+ rule?: string;
14
+ loc?: IssueLocation;
15
+ };
16
+ type ValidationCounts = {
17
+ info: number;
18
+ warning: number;
19
+ error: number;
20
+ };
21
+ type ValidationResult = {
22
+ schemaVersion: typeof VALIDATION_SCHEMA_VERSION;
23
+ toolVersion: string;
24
+ issues: Issue[];
25
+ counts: ValidationCounts;
26
+ };
27
+
28
+ type FailOn = "never" | "warning" | "error";
29
+ type OutputFormat = "text" | "json" | "github";
30
+ type QfaiPaths = {
31
+ specDir: string;
32
+ decisionsDir: string;
33
+ scenariosDir: string;
34
+ rulesDir: string;
35
+ contractsDir: string;
36
+ uiContractsDir: string;
37
+ apiContractsDir: string;
38
+ dataContractsDir: string;
39
+ srcDir: string;
40
+ testsDir: string;
41
+ };
42
+ type QfaiValidationConfig = {
43
+ failOn: FailOn;
44
+ require: {
45
+ specSections: string[];
46
+ };
47
+ traceability: {
48
+ brMustHaveSc: boolean;
49
+ scMustTouchContracts: boolean;
50
+ allowOrphanContracts: boolean;
51
+ };
52
+ };
53
+ type QfaiOutputConfig = {
54
+ format: OutputFormat;
55
+ jsonPath: string;
56
+ };
57
+ type QfaiConfig = {
58
+ paths: QfaiPaths;
59
+ validation: QfaiValidationConfig;
60
+ output: QfaiOutputConfig;
61
+ };
62
+ type ConfigPathKey = keyof QfaiPaths;
63
+ type ConfigLoadResult = {
64
+ config: QfaiConfig;
65
+ issues: Issue[];
66
+ configPath: string;
67
+ };
68
+ declare const defaultConfig: QfaiConfig;
69
+ declare function getConfigPath(root: string): string;
70
+ declare function loadConfig(root: string): Promise<ConfigLoadResult>;
71
+ declare function resolvePath(root: string, config: QfaiConfig, key: ConfigPathKey): string;
72
+
73
+ type IdPrefix = "SPEC" | "BR" | "SC" | "UI" | "API" | "DATA";
74
+ declare function extractIds(text: string, prefix: IdPrefix): string[];
75
+ declare function extractAllIds(text: string): string[];
76
+ declare function extractInvalidIds(text: string, prefixes: IdPrefix[]): string[];
77
+
78
+ type ReportSummary = {
79
+ specs: number;
80
+ scenarios: number;
81
+ decisions: number;
82
+ rules: number;
83
+ contracts: {
84
+ api: number;
85
+ ui: number;
86
+ db: number;
87
+ };
88
+ counts: ValidationCounts;
89
+ };
90
+ type ReportIds = {
91
+ spec: string[];
92
+ br: string[];
93
+ sc: string[];
94
+ ui: string[];
95
+ api: string[];
96
+ data: string[];
97
+ };
98
+ type ReportTraceability = {
99
+ upstreamIdsFound: number;
100
+ referencedInCodeOrTests: boolean;
101
+ };
102
+ type ReportData = {
103
+ tool: "qfai";
104
+ version: string;
105
+ generatedAt: string;
106
+ root: string;
107
+ configPath: string;
108
+ summary: ReportSummary;
109
+ ids: ReportIds;
110
+ traceability: ReportTraceability;
111
+ issues: Issue[];
112
+ };
113
+ declare function createReportData(root: string, validation?: ValidationResult, configResult?: ConfigLoadResult): Promise<ReportData>;
114
+ declare function formatReportMarkdown(data: ReportData): string;
115
+ declare function formatReportJson(data: ReportData): string;
116
+
117
+ declare function validateProject(root: string, configResult?: ConfigLoadResult): Promise<ValidationResult>;
118
+
119
+ declare function resolveToolVersion(): Promise<string>;
120
+
121
+ declare function validateContracts(root: string, config: QfaiConfig): Promise<Issue[]>;
122
+ declare function lintSql(text: string, file: string): Issue[];
123
+
124
+ declare function validateScenarios(root: string, config: QfaiConfig): Promise<Issue[]>;
125
+ declare function validateScenarioContent(text: string, file: string): Issue[];
126
+
127
+ declare function validateSpecs(root: string, config: QfaiConfig): Promise<Issue[]>;
128
+ declare function validateSpecContent(text: string, file: string, requiredSections: string[]): Issue[];
129
+
130
+ declare function validateTraceability(root: string, config: QfaiConfig): Promise<Issue[]>;
131
+
132
+ export { type ConfigLoadResult, type ConfigPathKey, type FailOn, type IdPrefix, type Issue, type IssueLocation, type IssueSeverity, type OutputFormat, type QfaiConfig, type QfaiOutputConfig, type QfaiPaths, type QfaiValidationConfig, type ReportData, type ReportIds, type ReportSummary, type ReportTraceability, VALIDATION_SCHEMA_VERSION, type ValidationCounts, type ValidationResult, createReportData, defaultConfig, extractAllIds, extractIds, extractInvalidIds, formatReportJson, formatReportMarkdown, getConfigPath, lintSql, loadConfig, resolvePath, resolveToolVersion, validateContracts, validateProject, validateScenarioContent, validateScenarios, validateSpecContent, validateSpecs, validateTraceability };
@@ -0,0 +1,2 @@
1
+ export * from "./core/index.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./core/index.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}