All files / src/rules/converters prompt-builder.ts

88.16% Statements 380/431
100% Branches 0/0
0% Functions 0/5
88.16% Lines 380/431

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 4321x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x           1x                                                                                              
import type { CustomRule } from '../../types';
import { getLanguage } from '../../i18n/messages';
import { buildKnownRulesSection } from './known-rules';
 
export type PromptInputType = 'freeform' | 'spreadsheet';
 
type Lang = 'zh-CN' | 'en' | 'ja';
 
interface PromptStrings {
  role: (inputDesc: string) => string;
  inputToleranceTitle: string;
  inputToleranceLines: string[];
  nonRuleFilterTitle: string;
  nonRuleFilterLines: string[];
  fieldDefsTitle: string;
  fieldDefsLines: string[];
  languageRulesTitle: string;
  languageRulesLines: string[];
  exampleTitle: string;
  example1Input: string;
  example1Output: string;
  example2Input: string;
  example2Output: string;
  staticAnalysisTitle: string;
  staticAnalysisLines: string[];
  finalInstruction: string;
  outputLang: string;
}
 
const p: Record<Lang, PromptStrings> = {
  'zh-CN': {
    role: (inputDesc) => `你是一个代码审查规则转换器。将用户提供的${inputDesc},转换为结构化的 YAML 格式,用于代码审查工具。`,
    inputToleranceTitle: '## 输入容忍说明',
    inputToleranceLines: [
      '用户输入可能有多种形态,你必须接受并处理以下任一形式:',
      '- 自然语言段落(一段或多段话描述规则)',
      '- 无序列表(每条规则一行或一段)',
      '- 表格(列名不固定,从语义推断)',
      '- 混合形式(段落 + 列表 + 表格组合)',
      '',
      '不得因输入格式非标准而拒绝转换。应主动从松散描述中提取规则语义。',
      '',
      '- 按输入文档的段落或列表项顺序处理,保持原文顺序输出',
      '- 不要合并或拆分原文中已是独立条目的规则',
      '- 一个段落包含多条规则时才拆分,单条规则不要拆成多条',
    ],
    nonRuleFilterTitle: '## 非规则内容过滤',
    nonRuleFilterLines: [
      '用户输入中可能混入项目介绍、背景说明、代码示例、章节标题等非规则内容。你必须:',
      '- 识别并跳过非规则内容,仅将真正的编码规则转为 YAML 条目',
      '- 代码示例、项目介绍等仅作为理解规则语义的上下文,自身不输出为规则',
      '- 若某段内容无法判断为规则(既无规则意图也无违反提示),直接忽略,不强行转换',
    ],
    fieldDefsTitle: '## 字段定义',
    fieldDefsLines: [
      '每条规则需要包含以下字段:',
      '- id: 规则唯一标识(kebab-case 英文,语义化、简短,如 no-console-log、avoid-magic-number)',
      '  **必须**基于规则描述内容自动生成语义化的 id',
      '  即使输入中无显式 id 标识,也必须根据 description/message 的语义推断出合适的 id',
      '  多条规则之间 id 不得重复',
      '  id 只能使用 description/message 中已有的英文单词或短语,转换为 kebab-case',
      '  不要自行创造原文中没有的英文词汇',
      '  如果输入全中文,从语义提取核心关键词翻译为简短英文(2-4 个词)',
      '- severity: 严重级别(error / warning / info)',
      '  **必须**输出。按规则语义推断:',
      '  error: 会导致 bug / 安全问题 / 数据损坏',
      '  warning: 潜在问题 / 不良实践',
      '  info: 风格 / 可读性建议',
      '  即使输入中无显式严重级别,也必须根据规则后果的严重程度推断',
      '  如果无法从输入中确定严重级别,默认填 warning',
      '  只有明确涉及安全、数据泄露、崩溃风险时才填 error',
      '- description: 规则简短描述',
      '  **必须**输出。若输入中不明显,从 message 的内容反向推导出简短描述',
      '- message: 违反时的提示消息',
      '  **必须**输出。若输入中不明显,从 description 的内容推导出违反提示',
      '  description 与 message 语义可相近,无需强行区分口吻,但两者都必须填写',
      '- languages: 适用语言数组(可选,如 [javascript, typescript])',
      '- excludeLanguages: 明确排除的语言数组(可选,如 [css, sql])',
      '- duplicateOf: 重复的规则 ID(linter 如 eslint/no-console;自定义如 custom/my-rule)',
      '- duplicateLevel: 重复程度(exact / overlap / none)',
      '- duplicateReason: 重复/重叠原因说明(overlap 档必填)',
    ],
    languageRulesTitle: '## 语言字段规则(严格遵守)',
    languageRulesLines: [
      '对于每条规则的 languages 字段:',
      '1. 规则描述中含明确语言关键词(如 "Java"、"JavaScript"、"CSS")→ 使用 languages 白名单',
      '2. 规则适用于大多数语言,只有少数不适用 → 使用 excludeLanguages 黑名单',
      '3. 无法确定适用语言,或规则为通用规范 → languages 与 excludeLanguages 均留空',
      '4. languages 和 excludeLanguages 不可同时非空',
      '5. 语言名使用小写:java, javascript, typescript, css, sql, plsql, jsp',
      '6. 严禁猜测。留空比猜测错误更安全。',
    ],
    exampleTitle: '## 输入输出示例',
    example1Input: '不要用 console.log,生产环境会泄露信息。还有不要留下未使用的变量,看着乱。',
    example1Output: [
      '- id: no-console-log',
      '  severity: warning',
      '  description: 禁止使用 console.log',
      '  message: 请使用 logger 工具替代 console.log',
      '  duplicateLevel: none',
      '- id: no-unused-vars',
      '  severity: warning',
      '  description: 禁止未使用的变量',
      '  message: 未使用的变量应删除或注释',
      '  duplicateOf: eslint/no-unused-vars',
      '  duplicateLevel: exact',
    ].join('\n'),
    example2Input: [
      '本项目是一个电商后台管理系统,主要使用 Java + Spring Boot 开发。',
      '代码规范要求:Service 层方法必须有日志记录,方便排查问题。',
      '示例代码:',
      '  public void createOrder(Order order) { ... }',
      '另外,Controller 层返回值统一用 Result 包装,不要直接返回 Map。',
    ].join('\n'),
    example2Output: [
      '- id: require-service-logging',
      '  severity: warning',
      '  description: Service 层方法必须有日志记录',
      '  message: Service 方法缺少日志记录,请补充以便排查问题',
      '  languages: [java]',
      '  duplicateLevel: none',
      '- id: require-result-wrapper',
      '  severity: warning',
      '  description: Controller 返回值必须用 Result 包装',
      '  message: 请用 Result 包装返回值,不要直接返回 Map',
      '  languages: [java]',
      '  duplicateLevel: none',
    ].join('\n'),
    staticAnalysisTitle: '## 静态分析重复检测',
    staticAnalysisLines: [
      '对于每条规则,判断其检测目标与触发条件是否与上述某个 linter 规则或自定义规则重复:',
      '',
      '- **exact**:检测目标与触发条件完全一致(会报出同样的问题行)→ 输出 duplicateOf + duplicateLevel: exact',
      '- **overlap**:检测目标相同,但本规则有额外要求或更窄范围 → 输出 duplicateOf + duplicateLevel: overlap + duplicateReason',
      '- **none**:检测目标不同 → 输出 duplicateLevel: none',
      '',
      '仅"话题相似"不算重复。例如:',
      '- "未使用变量应删除" → exact(重复 eslint/no-unused-vars)',
      '- "禁止在 console.log 中输出敏感信息" → none(检测目标不同)',
      '',
      '仅输出 YAML,不要额外说明。',
    ],
    finalInstruction: '只输出 YAML 内容,不要输出 markdown 代码块标记,不要输出解释性文字',
    outputLang: '输出语言:zh-CN',
  },
 
  en: {
    role: (inputDesc) => `You are a code review rule converter. Convert the ${inputDesc} provided by the user into structured YAML format for a code review tool. All descriptions and messages must be in English.`,
    inputToleranceTitle: '## Input Tolerance',
    inputToleranceLines: [
      'User input may come in various forms. You must accept and process any of the following:',
      '- Natural language paragraphs (one or more paragraphs describing rules)',
      '- Unordered lists (one rule per line or paragraph)',
      '- Tables (column names may vary; infer from semantics)',
      '- Mixed forms (paragraphs + lists + tables)',
      '',
      'Do not reject conversion due to non-standard input format. Actively extract rule semantics from loose descriptions.',
      '',
      '- Process in the order of the input document paragraphs or list items, preserving original order',
      '- Do not merge or split entries that are already independent rules in the original text',
      '- Only split when a single paragraph contains multiple rules; do not split a single rule into multiple',
    ],
    nonRuleFilterTitle: '## Non-Rule Content Filtering',
    nonRuleFilterLines: [
      'User input may contain project introductions, background info, code examples, section titles, etc. You must:',
      '- Identify and skip non-rule content; only convert actual coding rules into YAML entries',
      '- Code examples, project descriptions etc. serve only as context for understanding rule semantics; do not output them as rules',
      '- If content cannot be identified as a rule (no rule intent or violation hint), ignore it; do not force conversion',
    ],
    fieldDefsTitle: '## Field Definitions',
    fieldDefsLines: [
      'Each rule must include the following fields:',
      '- id: Unique rule identifier (kebab-case English, semantic and concise, e.g., no-console-log, avoid-magic-number)',
      '  **Must** generate a semantic id based on the rule description content',
      '  Even if no explicit id is present in the input, infer a suitable id from the description/message semantics',
      '  IDs must not be duplicated across rules',
      '  id must use only English words or phrases already present in description/message, converted to kebab-case',
      '  Do not invent English words not found in the original text',
      '  If input is entirely in Chinese, extract core semantic keywords and translate to short English (2-4 words)',
      '- severity: Severity level (error / warning / info)',
      '  **Must** output. Infer based on rule semantics:',
      '  error: causes bugs / security issues / data corruption',
      '  warning: potential issues / bad practices',
      '  info: style / readability suggestions',
      '  Even if no explicit severity is given, infer from the rule\'s impact',
      '  If severity cannot be determined from input, default to warning',
      '  Only use error when the rule clearly involves security, data leakage, or crash risk',
      '- description: Short rule description',
      '  **Must** output. If not obvious from input, derive from message content',
      '- message: Violation message',
      '  **Must** output. If not obvious from input, derive from description content',
      '  description and message may be semantically similar; no need to force different tones, but both must be filled',
      '- languages: Applicable language array (optional, e.g., [javascript, typescript])',
      '- excludeLanguages: Explicitly excluded language array (optional, e.g., [css, sql])',
      '- duplicateOf: Duplicate rule ID (linter e.g., eslint/no-console; custom e.g., custom/my-rule)',
      '- duplicateLevel: Duplicate level (exact / overlap / none)',
      '- duplicateReason: Duplicate/overlap reason (required for overlap)',
    ],
    languageRulesTitle: '## Language Field Rules (Strict)',
    languageRulesLines: [
      'For each rule\'s languages field:',
      '1. If rule description mentions specific languages (e.g., "Java", "JavaScript", "CSS") → use languages whitelist',
      '2. If rule applies to most languages, with few exceptions → use excludeLanguages blacklist',
      '3. If applicable language cannot be determined, or rule is general → leave both languages and excludeLanguages empty',
      '4. languages and excludeLanguages must not both be non-empty simultaneously',
      '5. Use lowercase language names: java, javascript, typescript, css, sql, plsql, jsp',
      '6. Never guess. Leaving empty is safer than guessing incorrectly.',
    ],
    exampleTitle: '## Input/Output Examples',
    example1Input: 'Do not use console.log, it leaks information in production. Also do not leave unused variables, they look messy.',
    example1Output: [
      '- id: no-console-log',
      '  severity: warning',
      '  description: Forbid using console.log',
      '  message: Use a logger tool instead of console.log',
      '  duplicateLevel: none',
      '- id: no-unused-vars',
      '  severity: warning',
      '  description: Forbid unused variables',
      '  message: Unused variables should be deleted or commented out',
      '  duplicateOf: eslint/no-unused-vars',
      '  duplicateLevel: exact',
    ].join('\n'),
    example2Input: [
      'This project is an e-commerce backend, mainly using Java + Spring Boot.',
      'Coding rules: Service layer methods must have logging for debugging.',
      'Example code:',
      '  public void createOrder(Order order) { ... }',
      'Also, Controller layer return values should use Result wrapper, do not return Map directly.',
    ].join('\n'),
    example2Output: [
      '- id: require-service-logging',
      '  severity: warning',
      '  description: Service layer methods must have logging',
      '  message: Service method missing logging, add for debugging',
      '  languages: [java]',
      '  duplicateLevel: none',
      '- id: require-result-wrapper',
      '  severity: warning',
      '  description: Controller return values must use Result wrapper',
      '  message: Use Result wrapper for return values, do not return Map directly',
      '  languages: [java]',
      '  duplicateLevel: none',
    ].join('\n'),
    staticAnalysisTitle: '## Static Analysis Duplicate Detection',
    staticAnalysisLines: [
      'For each rule, determine whether its detection target and trigger conditions duplicate any linter rule or custom rule above:',
      '',
      '- **exact**: Detection target and trigger conditions are completely identical (would flag the same line) → output duplicateOf + duplicateLevel: exact',
      '- **overlap**: Same detection target but this rule has additional requirements or narrower scope → output duplicateOf + duplicateLevel: overlap + duplicateReason',
      '- **none**: Different detection targets → output duplicateLevel: none',
      '',
      '"Same topic" alone does not count as duplicate. For example:',
      '- "Unused variables should be deleted" → exact (duplicate of eslint/no-unused-vars)',
      '- "Do not output sensitive info in console.log" → none (different detection target)',
      '',
      'Output YAML only, no extra explanation.',
    ],
    finalInstruction: 'All descriptions and messages must be written in English.\nOutput YAML only, no markdown code fences, no explanatory text',
    outputLang: 'Output language: en',
  },
 
  ja: {
    role: (inputDesc) => `あなたはコードレビュールール変換ツールです。ユーザーが提供した${inputDesc}を、コードレビューツール用の構造化YAML形式に変換してください。すべての説明とメッセージは日本語で出力してください。`,
    inputToleranceTitle: '## 入力許容について',
    inputToleranceLines: [
      'ユーザー入力は様々な形式である可能性があります。以下の形式を受け入れ、処理する必要があります:',
      '- 自然言語の段落(1つ以上の段落でルールを記述)',
      '- 順不同リスト(各ルールが1行または1段落)',
      '- テーブル(列名は固定されていません。意味から推測してください)',
      '- 混合形式(段落 + リスト + テーブルの組み込み)',
      '',
      '非標準的な入力形式であっても変換を拒否してはいけません。緩やかな記述からルールの意味を積極的に抽出してください。',
      '',
      '- 入力ドキュメントの段落またはリスト項目の順序で処理し、原文の順序を保持する',
      '- 原文ですでに独立したエントリであるルールを結合または分割しない',
      '- 単一の段落に複数のルールが含まれる場合のみ分割し、単一ルールを複数に分割しない',
    ],
    nonRuleFilterTitle: '## 非ルールコンテンツのフィルタリング',
    nonRuleFilterLines: [
      'ユーザー入力には、プロジェクト紹介、背景説明、コード例、セクションタイトルなどの非ルールコンテンツが混入している可能性があります。以下を行う必要があります:',
      '- 非ルールコンテンツを識別してスキップし、実際のコーディングルールのみをYAMLエントリに変換する',
      '- コード例やプロジェクト紹介などはルール意味理解のコンテキストとしてのみ使用し、これら自体をルールとして出力しない',
      '- ルールと判断できない内容(ルール意図も違反のヒントもない場合)は無視し、無理に変換しない',
    ],
    fieldDefsTitle: '## フィールド定義',
    fieldDefsLines: [
      '各ルールには以下のフィールドが必要です:',
      '- id: ルールの一意識別子(kebab-caseの英語、意味的で簡潔、例:no-console-log、avoid-magic-number)',
      '  **必須** ルール説明内容に基づいて意味的なidを自動生成する',
      '  入力に明示的なidがない場合でも、description/messageの意味から適切なidを推測する',
      '  複数ルール間でidが重複してはいけない',
      '  idはdescription/messageにすでに存在する英単語またはフレーズのみを使用し、kebab-caseに変換する',
      '  原文にない英単語を独自に作成しない',
      '  入力がすべて日本語の場合は、セマンティクスから核心キーワードを抽出し、短い英語(2〜4語)に翻訳する',
      '- severity: 重大度(error / warning / info)',
      '  **必須**で出力。ルールの意味に従って推測:',
      '  error: バグ/セキュリティ問題/データ破損を引き起こす',
      '  warning: 潜在的な問題/悪い慣行',
      '  info: スタイル/可読性の提案',
      '  入力に明示的な重大度がない場合でも、ルールの影響の重大さから推測する',
      '  入力から重大度を判断できない場合は、デフォルトでwarningとする',
      '  セキュリティ、データ漏洩、クラッシュリスクに明確に関連する場合のみerrorとする',
      '- description: ルールの簡単な説明',
      '  **必須**で出力。入力で不明確な場合、messageの内容から逆算して短い説明を導出',
      '- message: 違反時のメッセージ',
      '  **必須**で出力。入力で不明確な場合、descriptionの内容から違反メッセージを導出',
      '  descriptionとmessageは意味的に近くても構いません。口調を無理に区別する必要はありませんが、両方とも必須です',
      '- languages: 対象言語配列(オプション、例:[javascript, typescript])',
      '- excludeLanguages: 明示的に除外する言語配列(オプション、例:[css, sql])',
      '- duplicateOf: 重複するルールID(リンター例:eslint/no-console、カスタム例:custom/my-rule)',
      '- duplicateLevel: 重複レベル(exact / overlap / none)',
      '- duplicateReason: 重複/重複理由の説明(overlapの場合は必須)',
    ],
    languageRulesTitle: '## 言語フィールドルール(厳守)',
    languageRulesLines: [
      '各ルールのlanguagesフィールドについて:',
      '1. ルール説明に明確な言語キーワードがある場合(例:「Java」「JavaScript」「CSS」)→ languagesにホワイトリストを使用',
      '2. ルールがほとんどの言語に適用され、一部のみ適用外の場合 → excludeLanguagesにブラックリストを使用',
      '3. 適用言語が判断できない場合、またはルールが汎用の場合 → languagesとexcludeLanguagesの両方を空にする',
      '4. languagesとexcludeLanguagesは同時に空であってはいけない',
      '5. 言語名は小文字を使用:java, javascript, typescript, css, sql, plsql, jsp',
      '6. 推測は厳禁。空のままにする方が誤った推測より安全です。',
    ],
    exampleTitle: '## 入出力例',
    example1Input: 'console.logは本番環境で情報漏洩するため使用しないでください。また、未使用の変数は残さないでください。散らかって見えます。',
    example1Output: [
      '- id: no-console-log',
      '  severity: warning',
      '  description: console.logの使用禁止',
      '  message: loggerツールを使用してconsole.logを代替してください',
      '  duplicateLevel: none',
      '- id: no-unused-vars',
      '  severity: warning',
      '  description: 未使用変数の禁止',
      '  message: 未使用の変数は削除またはコメントアウトしてください',
      '  duplicateOf: eslint/no-unused-vars',
      '  duplicateLevel: exact',
    ].join('\n'),
    example2Input: [
      '本プロジェクトはECサイト管理システムで、主にJava + Spring Bootを使用しています。',
      'コード規約:Service層のメソッドには必ずログ記録が必要です。問題調査のためです。',
      'コード例:',
      '  public void createOrder(Order order) { ... }',
      'また、Controller層の戻り値は統一してResultでラップし、Mapを直接返さないでください。',
    ].join('\n'),
    example2Output: [
      '- id: require-service-logging',
      '  severity: warning',
      '  description: Service層メソッドにはログ記録が必須',
      '  message: Serviceメソッドにログ記録がありません。問題調査のため追加してください',
      '  languages: [java]',
      '  duplicateLevel: none',
      '- id: require-result-wrapper',
      '  severity: warning',
      '  description: Controllerの戻り値はResultでラップすること',
      '  message: Resultで戻り値をラップし、Mapを直接返さないでください',
      '  languages: [java]',
      '  duplicateLevel: none',
    ].join('\n'),
    staticAnalysisTitle: '## 静的解析重複検出',
    staticAnalysisLines: [
      '各ルールについて、その検出対象とトリガー条件が上記のリンタールールまたはカスタムルールと重複するか判断:',
      '',
      '- **exact**: 検出対象とトリガー条件が完全に一致(同じ問題行を報告する)→ duplicateOf + duplicateLevel: exact を出力',
      '- **overlap**: 検出対象は同じだが、このルールに追加要件やより狭い範囲がある → duplicateOf + duplicateLevel: overlap + duplicateReason を出力',
      '- **none**: 検出対象が異なる → duplicateLevel: none を出力',
      '',
      '単に「トピックが類似している」だけでは重複とみなされません。例:',
      '- 「未使用変数は削除すべき」→ exact(eslint/no-unused-varsと重複)',
      '- 「console.logで機密情報を出力しない」→ none(検出対象が異なる)',
      '',
      'YAMLのみを出力し、追加説明は不要です。',
    ],
    finalInstruction: 'すべてのdescriptionとmessageは日本語で出力してください。\nYAML のみ出力、マークダウンコードブロックなし、説明テキストなし',
    outputLang: '出力言語:ja',
  },
};
 
function getLang(): Lang {
  const lang = getLanguage();
  if (lang === 'en' || lang === 'ja') { return lang; }
  return 'zh-CN';
}
 
export function buildSystemPrompt(inputType: PromptInputType, existingRules?: CustomRule[]): string {
  const lang = getLang();
  const s = p[lang];

  const inputDesc = inputType === 'spreadsheet'
    ? (lang === 'zh-CN' ? '表格规则数据' : lang === 'ja' ? '表形式のルールデータ' : 'spreadsheet rule data')
    : (lang === 'zh-CN' ? '自然语言规则描述' : lang === 'ja' ? '自然言語のルール記述' : 'natural language rule description');

  const parts: string[] = [
    s.role(inputDesc),
    '',
    s.inputToleranceTitle,
    ...s.inputToleranceLines,
    '',
    s.nonRuleFilterTitle,
    ...s.nonRuleFilterLines,
    '',
    s.fieldDefsTitle,
    ...s.fieldDefsLines,
    '',
    s.languageRulesTitle,
    ...s.languageRulesLines,
    '',
    s.exampleTitle,
    '',
    `${lang === 'zh-CN' ? '示例 1 — 松散段落输入:' : lang === 'ja' ? '例1 — 緩やかな段落入力:' : 'Example 1 — Loose paragraph input:'}`,
    `${lang === 'zh-CN' ? '输入' : lang === 'ja' ? '入力' : 'Input'}:${s.example1Input}`,
    `${lang === 'zh-CN' ? '输出' : lang === 'ja' ? '出力' : 'Output'}:`,
    s.example1Output,
    '',
    `${lang === 'zh-CN' ? '示例 2 — 含非规则内容的混合输入:' : lang === 'ja' ? '例2 — 非ルールコンテンツを含む混合入力:' : 'Example 2 — Mixed input with non-rule content:'}`,
    `${lang === 'zh-CN' ? '输入' : lang === 'ja' ? '入力' : 'Input'}:${s.example2Input}`,
    `${lang === 'zh-CN' ? '输出' : lang === 'ja' ? '出力' : 'Output'}:`,
    s.example2Output,
    '',
    buildKnownRulesSection(existingRules),
    '',
    s.staticAnalysisTitle,
    ...s.staticAnalysisLines,
    '',
    s.finalInstruction,
    s.outputLang,
  ];

  return parts.join('\n');
}