{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "pi-lens ast-grep rule",
  "description": "A single ast-grep rule loaded by pi-lens. Place files under rules/ast-grep-rules/rules/. Supports multiple documents per file separated by ---.",
  "type": "object",
  "required": ["id", "rule"],
  "additionalProperties": false,
  "definitions": {
    "ruleCondition": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "pattern": {
          "type": "string",
          "description": "Ast-grep pattern syntax. Avoid single bare metavariables like '$VAR' (too broad, filtered out)."
        },
        "kind": {
          "type": "string",
          "description": "AST node kind name, e.g. 'call_expression', 'import_statement'."
        },
        "regex": {
          "type": "string",
          "description": "Regular expression matched against the node's text."
        },
        "has": {
          "$ref": "#/definitions/ruleCondition",
          "description": "The node must have a descendant matching this condition."
        },
        "not": {
          "$ref": "#/definitions/ruleCondition",
          "description": "Negation — the node must NOT match this condition."
        },
        "any": {
          "type": "array",
          "items": { "$ref": "#/definitions/ruleCondition" },
          "description": "OR — matches if any item matches. Minimum 1 item."
        },
        "all": {
          "type": "array",
          "items": { "$ref": "#/definitions/ruleCondition" },
          "description": "AND — matches only if all items match. Minimum 1 item."
        },
        "inside": {
          "$ref": "#/definitions/ruleCondition",
          "description": "Ancestor must match. Combined with `stopBy: end` it searches ALL ancestors (default `stopBy: neighbor` = direct parent)."
        },
        "follows": {
          "$ref": "#/definitions/ruleCondition",
          "description": "Immediately-preceding sibling must match."
        },
        "precedes": {
          "$ref": "#/definitions/ruleCondition",
          "description": "Immediately-following sibling must match."
        },
        "stopBy": {
          "type": "string",
          "description": "How `inside`/`has`/`follows`/`precedes` walk the tree. `neighbor` (default) = direct parent/child/sibling; `end` = walk all the way to the root / leaves."
        },
        "field": {
          "type": "string",
          "description": "Field name constraint — e.g. `field: name` on an `import_specifier` matches the import's local binding."
        },
        "nthChild": {
          "description": "Match only the Nth child of its parent. Used to disambiguate repeated siblings (e.g. the first vs second argument in a call)."
        }
      }
    }
  },
  "properties": {
    "id": {
      "type": "string",
      "description": "Unique rule identifier. A project rule with the same id as a built-in overrides the built-in."
    },
    "language": {
      "type": "string",
      "description": "Language this rule applies to.",
      "enum": [
        "TypeScript", "JavaScript",
        "Python", "Go", "Rust",
        "Java", "C", "Cpp", "CSharp",
        "Kotlin", "Ruby", "Php"
      ]
    },
    "severity": {
      "type": "string",
      "enum": ["error", "warning", "info"],
      "description": "Diagnostic severity."
    },
    "message": {
      "type": "string",
      "description": "Short diagnostic message shown inline."
    },
    "note": {
      "type": "string",
      "description": "Extended guidance shown in the detail view. Markdown supported. Supports block scalar (|)."
    },
    "fix": {
      "type": "string",
      "description": "Suggested replacement text."
    },
    "rule": {
      "$ref": "#/definitions/ruleCondition",
      "description": "The matching condition. Must contain at least one of: pattern, kind, regex, has, any, all, not."
    },
    "constraints": {
      "type": "object",
      "description": "Metavariable regex constraints — `KEY: { regex: \"...\" }` narrows what `$KEY` will match. Supported by the napi engine (and the ast-grep CLI/LSP).",
      "additionalProperties": {
        "type": "object",
        "properties": {
          "regex": { "type": "string" }
        }
      }
    },
    "metadata": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "weight": {
          "type": "number",
          "description": "Priority weight for ordering results."
        },
        "category": {
          "type": "string",
          "description": "Category label for grouping."
        }
      }
    }
  }
}
