{
  "$schema": "https://raw.githubusercontent.com/YawLabs/ctxlint/main/schemas/ctxlint-catalog.schema.json",
  "specVersion": "1.0.0-draft",
  "specDate": "2026-04-07",
  "repository": "https://github.com/YawLabs/ctxlint",
  "categories": [
    {
      "id": "paths",
      "name": "File Reference Validation",
      "description": "Validates that file paths referenced in context files exist in the project.",
      "scope": "per-file"
    },
    {
      "id": "commands",
      "name": "Build/Script Validation",
      "description": "Validates that build and test commands referenced in context files match project scripts.",
      "scope": "per-file"
    },
    {
      "id": "staleness",
      "name": "Freshness Detection",
      "description": "Detects context files that haven't been updated while referenced code has changed.",
      "scope": "per-file",
      "requires": "git"
    },
    {
      "id": "tokens",
      "name": "Context Window Budget",
      "description": "Monitors context file size to help teams manage context window consumption.",
      "scope": "per-file and cross-file"
    },
    {
      "id": "tier-tokens",
      "name": "Tier-Aware Token Accounting",
      "description": "Reports per-section token cost for always-loaded context files and flags demotion candidates for on-demand tiers (skills, subagents, memory).",
      "scope": "per-file"
    },
    {
      "id": "redundancy",
      "name": "Inferable Content",
      "description": "Detects content that agents can already infer from project metadata.",
      "scope": "per-file and cross-file"
    },
    {
      "id": "contradictions",
      "name": "Cross-File Conflicts",
      "description": "Detects conflicting directives across multiple context files.",
      "scope": "cross-file"
    },
    {
      "id": "frontmatter",
      "name": "Client Metadata Validation",
      "description": "Validates YAML frontmatter required by specific AI coding clients.",
      "scope": "per-file"
    },
    {
      "id": "ci-coverage",
      "name": "CI Workflow Documentation",
      "description": "Checks that release/deploy CI workflows are documented in context files.",
      "scope": "cross-file"
    },
    {
      "id": "ci-secrets",
      "name": "CI Secrets Documentation",
      "description": "Checks that CI secrets used in workflows are documented in context files.",
      "scope": "cross-file"
    },
    {
      "id": "hook-coverage",
      "name": "Hook Enforcement Coverage",
      "description": "Validates that PreToolUse hooks and permissions entries in .claude/settings.json reference scripts that exist on disk. The inverse of tier-tokens/hard-enforcement-missing.",
      "scope": "cross-file"
    },
    {
      "id": "content-secrets",
      "name": "Inline Secret Detection",
      "description": "Detects inline-pasted secrets (API keys, tokens, private-key headers) in context files. Complements mcp-security, which covers MCP config files; this covers the context-file paste surface (CLAUDE.md, AGENTS.md, .cursorrules, etc.).",
      "scope": "per-file"
    }
  ],
  "rules": [
    {
      "id": "paths/not-found",
      "category": "paths",
      "severity": "error",
      "description": "Referenced file or directory does not exist at the specified path.",
      "trigger": "Path reference in context file resolves to a non-existent file or directory.",
      "message": "{path} does not exist",
      "fixable": true,
      "fixDescription": "Replace broken path with suggestion from git rename detection or fuzzy matching.",
      "stability": "stable"
    },
    {
      "id": "paths/glob-no-match",
      "category": "paths",
      "severity": "error",
      "description": "Glob pattern matches zero files in the project.",
      "trigger": "Path contains wildcard characters (*, ?) but matches no files.",
      "message": "{pattern} matches no files",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "paths/directory-not-found",
      "category": "paths",
      "severity": "error",
      "description": "Referenced directory does not exist.",
      "trigger": "Path ending with / does not resolve to an existing directory.",
      "message": "{path} directory does not exist",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "commands/script-not-found",
      "category": "commands",
      "severity": "error",
      "description": "Package manager script name is not found in package.json scripts.",
      "trigger": "npm run, pnpm, yarn, or bun script reference does not match a key in package.json#scripts.",
      "message": "\"{cmd}\" — script \"{name}\" not found in package.json",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "commands/make-target-not-found",
      "category": "commands",
      "severity": "error",
      "description": "Make target is not found in Makefile.",
      "trigger": "make command references a target not declared in the project's Makefile.",
      "message": "\"{cmd}\" — target \"{name}\" not found in Makefile",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "commands/no-makefile",
      "category": "commands",
      "severity": "error",
      "description": "Make command used but no Makefile exists in the project.",
      "trigger": "Context file references a make command but no Makefile is found at the project root.",
      "message": "\"{cmd}\" — no Makefile found in project",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "commands/npx-not-in-deps",
      "category": "commands",
      "severity": "warning",
      "description": "npx package is not in project dependencies.",
      "trigger": "npx package is not in package.json dependencies/devDependencies and not in node_modules/.bin.",
      "message": "\"{cmd}\" — \"{pkg}\" not found in dependencies",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "commands/tool-not-found",
      "category": "commands",
      "severity": "warning",
      "description": "Common development tool is not in project dependencies.",
      "trigger": "Tool name (vitest, jest, eslint, prettier, tsc, etc.) not found in dependencies or node_modules/.bin.",
      "message": "\"{cmd}\" — \"{tool}\" not found in dependencies or node_modules/.bin",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "commands/package-json-missing",
      "category": "commands",
      "severity": "info",
      "description": "package.json is missing or unparseable, so all script/npx/tool command validation silently skips. Emitted once per context file that contains at least one command reference that would otherwise have been validated, so the skip isn't invisible.",
      "trigger": "package.json cannot be loaded AND the context file references at least one npm-run/shorthand/npx/tool command.",
      "message": "package.json missing or unparseable — command checks skipped",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "commands/exit-status-masked",
      "category": "commands",
      "severity": "warning",
      "description": "A verification command's exit status is discarded by a pipe into a filter, so the success claim that follows cannot fail.",
      "trigger": "A verifier (tsc, eslint, biome, vitest, pytest, cargo test, or an npm/pnpm/yarn/bun script resolving to one) heads a pipeline whose last stage is a filter or pager (head, tail, grep, sed, awk, less, cat, tee, wc), and the pipeline is followed by && plus a success-announcing echo/printf, or by an echo of $?, with no set -o pipefail in scope.",
      "message": "\"{cmd}\" — exit status comes from \"{filter}\", not \"{verifier}\"; the success claim cannot fail",
      "fixable": false,
      "stability": "experimental"
    },
    {
      "id": "commands/unknown-subcommand",
      "category": "commands",
      "severity": "error",
      "description": "A documented invocation of the project's own binary uses a subcommand the CLI does not implement.",
      "trigger": "A command invokes a name declared in package.json#bin with a leading non-flag argument that is not in the CLI's known subcommand set, where that set was statically resolved as closed from the entry file.",
      "message": "\"{cmd}\" — \"{sub}\" is not a subcommand of {bin} (known: {known})",
      "fixable": false,
      "stability": "experimental"
    },
    {
      "id": "staleness/stale",
      "category": "staleness",
      "severity": "warning",
      "description": "Context file has not been updated in 30+ days while referenced code has changed.",
      "trigger": "File last modified 30+ days ago AND paths it references have git commits since.",
      "message": "Last updated {days} days ago. {path} has {N} commits since.",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "staleness/aging",
      "category": "staleness",
      "severity": "info",
      "description": "Context file has not been updated in 14-30 days while referenced code has changed.",
      "trigger": "File last modified 14-30 days ago AND paths it references have git commits since.",
      "message": "Last updated {days} days ago. {path} has {N} commits since.",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "tokens/excessive",
      "category": "tokens",
      "severity": "error",
      "description": "Single context file consumes excessive context window space.",
      "trigger": "File uses 8000+ tokens (configurable).",
      "message": "{N} tokens — consumes significant context window space",
      "fixable": false,
      "configurable": true,
      "defaultThreshold": 8000,
      "stability": "stable"
    },
    {
      "id": "tokens/large",
      "category": "tokens",
      "severity": "warning",
      "description": "Single context file is large.",
      "trigger": "File uses 3000-7999 tokens (configurable).",
      "message": "{N} tokens — large context file",
      "fixable": false,
      "configurable": true,
      "defaultThreshold": 3000,
      "stability": "stable"
    },
    {
      "id": "tokens/info",
      "category": "tokens",
      "severity": "info",
      "description": "Reports per-file token count.",
      "trigger": "File uses 1000+ tokens (configurable).",
      "message": "Uses ~{N} tokens per session",
      "fixable": false,
      "configurable": true,
      "defaultThreshold": 1000,
      "stability": "stable"
    },
    {
      "id": "tokens/aggregate",
      "category": "tokens",
      "severity": "warning",
      "description": "Combined context files exceed recommended total token budget.",
      "trigger": "All context files combined use 5000+ tokens (configurable) AND there are multiple files.",
      "message": "{count} context files consume {N} tokens combined",
      "fixable": false,
      "configurable": true,
      "defaultThreshold": 5000,
      "stability": "stable"
    },
    {
      "id": "tier-tokens/section-breakdown",
      "category": "tier-tokens",
      "severity": "info",
      "description": "Reports the heaviest top-level sections in an always-loaded context file and suggests demoting the largest to an on-demand tier (skill, subagent, or memory).",
      "trigger": "An always-loaded context file (CLAUDE.md, AGENTS.md, .cursorrules, etc. — excluding path-scoped rules files) reaches or exceeds the tierBreakdown threshold (default 1000 tokens; the boundary is inclusive) and contains H1 or H2 sections.",
      "message": "{N} tokens loaded every session — heaviest top-level section(s): ...",
      "fixable": false,
      "configurable": true,
      "configKey": "tokenThresholds.tierBreakdown",
      "defaultThreshold": 1000,
      "stability": "stable"
    },
    {
      "id": "tier-tokens/aggregate",
      "category": "tier-tokens",
      "severity": "warning",
      "description": "Combined token cost of always-loaded context files exceeds the recommended budget.",
      "trigger": "Two or more always-loaded files total the tierAggregate threshold (default 4000 tokens) or more; the boundary is inclusive.",
      "message": "{count} always-loaded files total {N} tokens — loaded every session",
      "fixable": false,
      "configurable": true,
      "configKey": "tokenThresholds.tierAggregate",
      "defaultThreshold": 4000,
      "stability": "stable"
    },
    {
      "id": "tier-tokens/hard-enforcement-missing",
      "category": "tier-tokens",
      "severity": "info",
      "description": "An always-loaded file uses inviolable framing (NEVER / ALWAYS / DO NOT / MUST NOT) paired with a concrete command, but no corresponding PreToolUse or Stop hook, permissions.deny entry, or permissions.ask entry exists in settings.json. Rules in always-loaded files are advisory — without a hook the agent may still run the command.",
      "trigger": "Line in an always-loaded file matches NEVER|ALWAYS|DO NOT|MUST NOT with a backticked command, and the project's .claude/settings.json / .claude/settings.local.json does not contain a matching PreToolUse or Stop hook, permissions.deny entry, or permissions.ask entry (ask gates the command behind a human prompt, so it counts as enforcement). The user-global ~/.claude/settings.json is consulted only on explicit opt-in (the same gate as hook-coverage's --hooks-global).",
      "message": "Inviolable framing without a hook to back it up",
      "fixable": false,
      "source": "code.claude.com/docs/en/hooks-guide",
      "stability": "experimental"
    },
    {
      "id": "redundancy/tech-mention",
      "category": "redundancy",
      "severity": "info",
      "description": "Context file mentions a technology that is already in package.json dependencies.",
      "trigger": "File contains phrases like 'use React' or 'built with Express' and that package is in dependencies.",
      "message": "\"{tech}\" is in package.json {depType} — agent can infer this",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "redundancy/discoverable-dir",
      "category": "redundancy",
      "severity": "info",
      "description": "Context file describes the location of a directory that the agent can discover by listing files.",
      "trigger": "File says files 'are in' or 'go in' a directory that exists in the project.",
      "message": "Directory \"{dir}\" exists and is discoverable — agent can find this by listing files",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "redundancy/duplicate-content",
      "category": "redundancy",
      "severity": "warning",
      "description": "Two context files have significant content overlap.",
      "trigger": "Two files share 60%+ of their lines (by trimmed content, lines >10 chars).",
      "message": "{file1} and {file2} have {N}% content overlap",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "contradictions/conflict",
      "category": "contradictions",
      "severity": "warning",
      "description": "Two context files specify mutually exclusive options in the same category.",
      "trigger": "File A says 'use Jest' while File B says 'use Vitest' (or similar conflicts across 8 categories).",
      "message": "{category} conflict: \"{optionA}\" in {fileA} vs \"{optionB}\" in {fileB}",
      "fixable": false,
      "contradictionCategories": [
        "testing framework",
        "package manager",
        "indentation style",
        "semicolons",
        "quote style",
        "naming convention",
        "CSS approach",
        "state management"
      ],
      "stability": "stable"
    },
    {
      "id": "frontmatter/missing",
      "category": "frontmatter",
      "severity": "warning",
      "description": "File format requires or recommends frontmatter but none is present. Severity is per-format: warning for Cursor .mdc (frontmatter is required), info for Copilot instructions and Windsurf rules (frontmatter is optional/recommended).",
      "trigger": "Cursor .mdc, Copilot instruction, or Windsurf rule file has no --- frontmatter block.",
      "message": "{format} file is missing frontmatter",
      "fixable": false,
      "appliesTo": [
        "cursor-mdc",
        "copilot-instructions",
        "windsurf-rules"
      ],
      "severityByFormat": {
        "cursor-mdc": "warning",
        "copilot-instructions": "info",
        "windsurf-rules": "info"
      },
      "stability": "stable"
    },
    {
      "id": "frontmatter/missing-field",
      "category": "frontmatter",
      "severity": "warning",
      "description": "A required or recommended frontmatter field is absent.",
      "trigger": "Frontmatter exists but is missing a key field (description for Cursor, applyTo for Copilot, trigger for Windsurf).",
      "message": "Missing \"{field}\" field in {format} frontmatter",
      "fixable": false,
      "appliesTo": [
        "cursor-mdc",
        "copilot-instructions",
        "windsurf-rules"
      ],
      "stability": "stable"
    },
    {
      "id": "frontmatter/invalid-value",
      "category": "frontmatter",
      "severity": "error",
      "description": "A frontmatter field has an invalid value. The error severity applies to the unambiguous branches (alwaysApply not a boolean, Windsurf trigger outside the enum); a possibly-malformed globs value (unbalanced brackets or quotes) is reported at warning severity because Cursor accepts bare directory/extension globs.",
      "trigger": "alwaysApply is not true/false (error), trigger is not a valid enum value (error), or globs has unbalanced brackets/quotes (warning).",
      "message": "Invalid {field} value: \"{value}\"",
      "fixable": false,
      "appliesTo": [
        "cursor-mdc",
        "windsurf-rules"
      ],
      "stability": "stable"
    },
    {
      "id": "frontmatter/unclosed",
      "category": "frontmatter",
      "severity": "error",
      "description": "Frontmatter opens with --- but is never closed, so the host loads the file with no frontmatter at all and every parsed field is suspect. Reported instead of frontmatter/missing because the user clearly tried to write frontmatter.",
      "trigger": "File opens with a --- line but no matching closing --- line follows (typical cause: the closing fence typo'd as -- or ___, or indented).",
      "message": "Frontmatter opens with `---` but is never closed",
      "fixable": false,
      "appliesTo": [
        "cursor-mdc",
        "copilot-instructions",
        "windsurf-rules"
      ],
      "stability": "stable"
    },
    {
      "id": "frontmatter/no-activation",
      "category": "frontmatter",
      "severity": "info",
      "description": "File has frontmatter but no activation mechanism.",
      "trigger": "Cursor .mdc has frontmatter with neither globs nor alwaysApply set.",
      "message": "No activation field — rule may not be applied automatically",
      "fixable": false,
      "appliesTo": [
        "cursor-mdc"
      ],
      "stability": "stable"
    },
    {
      "id": "ci/no-release-docs",
      "category": "ci-coverage",
      "severity": "info",
      "description": "Release/deploy CI workflow exists but no context file documents the process.",
      "trigger": ".github/workflows/ contains release/deploy/publish workflow but no context file mentions the release process.",
      "message": "Release workflow found but no context file documents the release process",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "ci/undocumented-secret",
      "category": "ci-secrets",
      "severity": "info",
      "description": "CI workflow references a secret not mentioned in any context file.",
      "trigger": "${{ secrets.NAME }} found in workflow YAML but NAME not mentioned in any context file. GitHub-provided secrets (GITHUB_TOKEN, etc.) are excluded.",
      "message": "CI secret \"{name}\" is used in {workflow} but not mentioned in any context file",
      "fixable": false,
      "stability": "stable"
    },
    {
      "id": "hook-coverage/dead-hook",
      "category": "hook-coverage",
      "severity": "warning",
      "description": "A PreToolUse (or other) hook command, or a permissions entry, in .claude/settings.json references a script/path that does not exist on disk. The gate silently no-ops -- Claude Code cannot run a missing script, so the protection the user believes is in place does nothing. Two distinct findings share this rule: the target is genuinely absent (restore or remove the entry), or the target EXISTS but the path form does not resolve (rewrite the entry -- do not delete it). The wording and suggestion differ so the two opposite remedies are never confused.",
      "trigger": "A hooks.<Event>[].hooks[].command or permissions.{allow,deny,ask}[] entry contains a path-shaped token (resolved through ~, $CLAUDE_PROJECT_DIR, $CLAUDE_CONFIG_DIR, $HOME) that does not exist on disk. Entries with unresolvable env vars are skipped (cannot be verified). Before reporting, alternate readings of the token are tried (MSYS drive paths with one OR two leading slashes, a stray leading slash on a repo-relative path, backslash/forward-slash flips); if one of them exists, the finding is reported as a path-form problem rather than a missing target. Scans project .claude/settings.json + settings.local.json; the user-global ~/.claude/settings.json is scanned only on explicit opt-in (--hooks-global).",
      "message": "{origin} references \"{path}\" which does not exist on disk -- the gate silently no-ops | {origin} references \"{path}\" which does not resolve, but \"{variant}\" exists -- the path form is wrong, so the gate silently no-ops",
      "fixable": false,
      "stability": "experimental"
    },
    {
      "id": "content-secrets/private-key-header",
      "category": "content-secrets",
      "severity": "error",
      "description": "A PEM private-key header is pasted into a context file. Context files usually end up committed to git, so an inline private key is a leak.",
      "trigger": "A line contains -----BEGIN [RSA |EC |DSA |OPENSSH |PGP ]PRIVATE KEY-----. Lines containing placeholder tokens (example, redacted, xxxx, ...), commented fake/example lines, and explicitly illustrative code fences (text, txt, example, pseudocode, none) are suppressed.",
      "message": "Private key header detected in {file}",
      "fixable": false,
      "stability": "experimental"
    },
    {
      "id": "content-secrets/aws-access-key",
      "category": "content-secrets",
      "severity": "error",
      "description": "An AWS access key ID is pasted into a context file. Emitted messages never include the full secret — only a 6-char redacted prefix.",
      "trigger": "AKIA (long-lived) or ASIA (STS temporary) prefix followed by 16 uppercase alphanumeric characters, outside placeholder/illustrative contexts.",
      "message": "AWS access key detected in {file} ({redacted-prefix}...)",
      "fixable": false,
      "stability": "experimental"
    },
    {
      "id": "content-secrets/github-pat",
      "category": "content-secrets",
      "severity": "error",
      "description": "A GitHub token is pasted into a context file. Covers classic PATs (ghp_), fine-grained PATs (github_pat_), and server-to-server / OAuth / user / refresh tokens (ghs_ / gho_ / ghu_ / ghr_); the non-PAT variants are emitted with the label \"GitHub token\".",
      "trigger": "ghp_{36,}, github_pat_{82,}, or gh[soru]_{36,} token shapes, outside placeholder/illustrative contexts.",
      "message": "GitHub personal access token detected in {file} ({redacted-prefix}...)",
      "fixable": false,
      "stability": "experimental"
    },
    {
      "id": "content-secrets/anthropic-key",
      "category": "content-secrets",
      "severity": "error",
      "description": "An Anthropic API key is pasted into a context file.",
      "trigger": "sk-ant- prefix followed by 20+ key characters, outside placeholder/illustrative contexts. Checked before the OpenAI sk- pattern so the same substring isn't flagged twice.",
      "message": "Anthropic API key detected in {file} ({redacted-prefix}...)",
      "fixable": false,
      "stability": "experimental"
    },
    {
      "id": "content-secrets/openai-key",
      "category": "content-secrets",
      "severity": "error",
      "description": "An OpenAI API key is pasted into a context file.",
      "trigger": "sk- or sk-proj- prefix followed by 20+ key characters (the length floor avoids flagging sk-... ellipses in docs), outside placeholder/illustrative contexts.",
      "message": "OpenAI API key detected in {file} ({redacted-prefix}...)",
      "fixable": false,
      "stability": "experimental"
    },
    {
      "id": "content-secrets/npm-token",
      "category": "content-secrets",
      "severity": "error",
      "description": "An npm automation token is pasted into a context file.",
      "trigger": "npm_ prefix followed by 36+ alphanumeric characters, outside placeholder/illustrative contexts.",
      "message": "npm token detected in {file} ({redacted-prefix}...)",
      "fixable": false,
      "stability": "experimental"
    },
    {
      "id": "content-secrets/slack-token",
      "category": "content-secrets",
      "severity": "error",
      "description": "A Slack token is pasted into a context file (bot, user, app, admin, or refresh token).",
      "trigger": "xox[bpoasr]- prefix followed by 10+ token characters, outside placeholder/illustrative contexts.",
      "message": "Slack token detected in {file} ({redacted-prefix}...)",
      "fixable": false,
      "stability": "experimental"
    },
    {
      "id": "content-secrets/google-api-key",
      "category": "content-secrets",
      "severity": "error",
      "description": "A Google API key is pasted into a context file.",
      "trigger": "AIza prefix followed by exactly 35 key characters, outside placeholder/illustrative contexts.",
      "message": "Google API key detected in {file} ({redacted-prefix}...)",
      "fixable": false,
      "stability": "experimental"
    },
    {
      "id": "content-secrets/stripe-secret",
      "category": "content-secrets",
      "severity": "error",
      "description": "A Stripe live secret key is pasted into a context file.",
      "trigger": "sk_live_ prefix followed by 24+ alphanumeric characters, outside placeholder/illustrative contexts.",
      "message": "Stripe live secret key detected in {file} ({redacted-prefix}...)",
      "fixable": false,
      "stability": "experimental"
    }
  ],
  "formats": [
    {
      "id": "claude-code",
      "client": "Claude Code",
      "patterns": [
        "CLAUDE.md",
        "CLAUDE.local.md",
        ".claude/rules/*.md"
      ],
      "type": "markdown",
      "frontmatter": "none",
      "scope": "project-wide and rule-based"
    },
    {
      "id": "aaif",
      "client": "AAIF / Multi-agent standard",
      "patterns": [
        "AGENTS.md",
        "AGENT.md",
        "AGENTS.override.md"
      ],
      "type": "markdown",
      "frontmatter": "none",
      "scope": "project-wide"
    },
    {
      "id": "cursor",
      "client": "Cursor",
      "patterns": [
        ".cursorrules",
        ".cursor/rules/*.md",
        ".cursor/rules/*.mdc",
        ".cursor/rules/*/RULE.md"
      ],
      "type": "markdown",
      "frontmatter": "required for .mdc",
      "frontmatterFields": {
        "description": {
          "required": true,
          "type": "string"
        },
        "globs": {
          "required": false,
          "type": "string or string[]"
        },
        "alwaysApply": {
          "required": false,
          "type": "boolean"
        }
      },
      "scope": "project-wide and rule-based"
    },
    {
      "id": "copilot",
      "client": "GitHub Copilot",
      "patterns": [
        ".github/copilot-instructions.md",
        ".github/instructions/*.md",
        ".github/git-commit-instructions.md"
      ],
      "type": "markdown",
      "frontmatter": "optional",
      "frontmatterFields": {
        "applyTo": {
          "required": false,
          "type": "string (glob)"
        }
      },
      "scope": "project-wide and scoped"
    },
    {
      "id": "windsurf",
      "client": "Windsurf",
      "patterns": [
        ".windsurfrules",
        ".windsurf/rules/*.md"
      ],
      "type": "markdown",
      "frontmatter": "recommended for rules",
      "frontmatterFields": {
        "trigger": {
          "required": true,
          "type": "enum",
          "values": [
            "always_on",
            "glob",
            "manual",
            "model",
            "model_decision"
          ]
        }
      },
      "scope": "project-wide and rule-based"
    },
    {
      "id": "gemini",
      "client": "Gemini CLI",
      "patterns": [
        "GEMINI.md"
      ],
      "type": "markdown",
      "frontmatter": "none",
      "scope": "project-wide"
    },
    {
      "id": "cline",
      "client": "Cline",
      "patterns": [
        ".clinerules"
      ],
      "type": "plain text",
      "frontmatter": "none",
      "scope": "project-wide"
    },
    {
      "id": "aider",
      "client": "Aider",
      "patterns": [
        ".aiderules"
      ],
      "type": "plain text",
      "frontmatter": "none",
      "scope": "project-wide"
    },
    {
      "id": "aide",
      "client": "Aide / Codestory",
      "patterns": [
        ".aide/rules/*.md"
      ],
      "type": "markdown",
      "frontmatter": "none",
      "scope": "rule-based"
    },
    {
      "id": "amazonq",
      "client": "Amazon Q Developer",
      "patterns": [
        ".amazonq/rules/*.md"
      ],
      "type": "markdown",
      "frontmatter": "none",
      "scope": "rule-based"
    },
    {
      "id": "goose",
      "client": "Goose (Block)",
      "patterns": [
        ".goose/instructions.md",
        ".goosehints"
      ],
      "type": "markdown",
      "frontmatter": "none",
      "scope": "project-wide"
    },
    {
      "id": "junie",
      "client": "JetBrains Junie",
      "patterns": [
        ".junie/guidelines.md",
        ".junie/AGENTS.md"
      ],
      "type": "markdown",
      "frontmatter": "none",
      "scope": "project-wide"
    },
    {
      "id": "jetbrains-ai",
      "client": "JetBrains AI Assistant",
      "patterns": [
        ".aiassistant/rules/*.md"
      ],
      "type": "markdown",
      "frontmatter": "none",
      "scope": "rule-based"
    },
    {
      "id": "continue",
      "client": "Continue",
      "patterns": [
        ".continuerules",
        ".continue/rules/*.md"
      ],
      "type": "markdown",
      "frontmatter": "none",
      "scope": "project-wide and rule-based"
    },
    {
      "id": "zed",
      "client": "Zed",
      "patterns": [
        ".rules"
      ],
      "type": "plain text",
      "frontmatter": "none",
      "scope": "project-wide"
    },
    {
      "id": "replit",
      "client": "Replit",
      "patterns": [
        "replit.md"
      ],
      "type": "markdown",
      "frontmatter": "none",
      "scope": "project-wide"
    }
  ]
}
