or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

case-validation.mdconfiguration.mdformatting.mdindex.mdlinting.mdparsing.mdplugins.mdprompts.mdrules-config.md
tile.json

prompts.mddocs/

Interactive Prompts

The interactive prompts system provides types for creating guided commit message creation interfaces. This enables building interactive CLI tools that help users write valid commit messages through step-by-step prompts.

Field Types

Rule Fields

type RuleField =
  | "header"
  | "type"
  | "scope"
  | "subject"
  | "body"
  | "footer";

Core commit message components that can be prompted:

  • header: Complete first line (type + scope + subject)
  • type: Commit type (feat, fix, docs, etc.)
  • scope: Optional scope in parentheses
  • subject: Brief description of changes
  • body: Optional detailed description
  • footer: Optional footer with breaking changes, issues, etc.

Extended Prompt Names

type PromptName =
  | RuleField
  | "isBreaking"
  | "breakingBody"
  | "breaking"
  | "isIssueAffected"
  | "issuesBody"
  | "issues";

Extended set of prompt fields including breaking change and issue handling:

  • isBreaking: Boolean prompt for breaking changes
  • breakingBody: Description of breaking changes
  • breaking: Combined breaking change information
  • isIssueAffected: Boolean prompt for issue references
  • issuesBody: Description of affected issues
  • issues: Combined issue reference information

Prompt Configuration

Prompt Config Structure

interface PromptConfig {
  settings: {
    scopeEnumSeparator: string;
    enableMultipleScopes: boolean;
  };
  messages: PromptMessages;
  questions: Partial<
    Record<
      PromptName,
      {
        description?: string;
        messages?: { [K: string]: string };
        enum?: {
          [enumName: string]: {
            description?: string;
            title?: string;
            emoji?: string;
          };
        };
      }
    >
  >;
}

Complete prompt configuration structure:

  • settings: Global prompt behavior settings
  • messages: Localized text messages for prompts
  • questions: Configuration for each prompt field

Prompt Settings

interface PromptSettings {
  scopeEnumSeparator: string;
  enableMultipleScopes: boolean;
}

Global settings for prompt behavior:

  • scopeEnumSeparator: Character used to separate multiple scopes
  • enableMultipleScopes: Whether to allow selecting multiple scopes

Prompt Messages

interface PromptMessages {
  skip: string;
  max: string;
  min: string;
  emptyWarning: string;
  upperLimitWarning: string;
  lowerLimitWarning: string;
  [_key: string]: string;
}

Localized messages used throughout the prompt interface:

  • skip: Text for skipping optional fields
  • max: Message for maximum length warnings
  • min: Message for minimum length warnings
  • emptyWarning: Warning when required field is empty
  • upperLimitWarning: Warning when input exceeds maximum
  • lowerLimitWarning: Warning when input is below minimum
  • [_key: string]: Extensible for custom messages

User Prompt Configuration

type UserPromptConfig = DeepPartial<PromptConfig>;

type DeepPartial<T> = {
  [P in keyof T]?: {
    [K in keyof T[P]]?: T[P][K];
  };
};

User-provided partial configuration that overrides defaults:

  • UserPromptConfig: Deep partial of PromptConfig for user customization
  • DeepPartial: Utility type making all nested properties optional

Usage Examples

Basic Prompt Configuration

import { PromptConfig, PromptMessages } from "@commitlint/types";

const messages: PromptMessages = {
  skip: "Skip this field",
  max: "Maximum {0} characters",
  min: "Minimum {0} characters", 
  emptyWarning: "This field cannot be empty",
  upperLimitWarning: "Input too long",
  lowerLimitWarning: "Input too short"
};

const basicPromptConfig: PromptConfig = {
  settings: {
    scopeEnumSeparator: ",",
    enableMultipleScopes: true
  },
  messages,
  questions: {
    type: {
      description: "Select the type of change you're committing:",
      enum: {
        feat: {
          description: "A new feature",
          title: "Features",
          emoji: "✨"
        },
        fix: {
          description: "A bug fix", 
          title: "Bug Fixes",
          emoji: "🐛"
        },
        docs: {
          description: "Documentation only changes",
          title: "Documentation",
          emoji: "📚"
        }
      }
    },
    scope: {
      description: "What is the scope of this change (e.g. component, filename):",
      messages: {
        empty: "Scope can be empty"
      }
    },
    subject: {
      description: "Write a short, imperative tense description of the change:",
      messages: {
        empty: "Subject is required",
        maxLength: "Subject must be no more than 50 characters"
      }
    }
  }
};

User Customization

import { UserPromptConfig } from "@commitlint/types";

// Partial configuration that extends defaults
const userConfig: UserPromptConfig = {
  settings: {
    enableMultipleScopes: false // Override default
  },
  messages: {
    skip: "Press Enter to skip", // Custom message
    emptyWarning: "⚠️  This field is required"
  },
  questions: {
    type: {
      description: "Choose commit type:",
      enum: {
        feat: { title: "Feature", emoji: "🚀" },
        fix: { title: "Bug Fix", emoji: "🔧" },
        chore: { title: "Chore", emoji: "🧹" }
      }
    },
    breaking: {
      description: "Describe the breaking changes:",
      messages: {
        skip: "No breaking changes"
      }
    }
  }
};

Advanced Prompt Configuration

import { PromptConfig, PromptName } from "@commitlint/types";

const advancedConfig: PromptConfig = {
  settings: {
    scopeEnumSeparator: "|",
    enableMultipleScopes: true
  },
  messages: {
    skip: "Leave empty to skip",
    max: "Maximum {0} chars allowed",
    min: "At least {0} chars required",
    emptyWarning: "❌ Required field",
    upperLimitWarning: "📏 Too long!",
    lowerLimitWarning: "📏 Too short!"
  },
  questions: {
    type: {
      description: "🏷️  Select commit type:",
      enum: {
        feat: { description: "New feature", title: "✨ Feature", emoji: "✨" },
        fix: { description: "Bug fix", title: "🐛 Fix", emoji: "🐛" },
        docs: { description: "Documentation", title: "📝 Docs", emoji: "📝" },
        style: { description: "Code style", title: "💄 Style", emoji: "💄" },
        refactor: { description: "Code refactor", title: "♻️ Refactor", emoji: "♻️" },
        test: { description: "Tests", title: "🧪 Test", emoji: "🧪" },
        chore: { description: "Maintenance", title: "🔧 Chore", emoji: "🔧" }
      }
    },
    scope: {
      description: "📦 Scope (component/module affected):",
      messages: {
        empty: "Scope helps identify what changed",
        invalidFormat: "Use lowercase, no spaces"
      }
    },
    subject: {
      description: "📋 Brief description (imperative mood):",
      messages: {
        empty: "Subject describes what the commit does",
        maxLength: "Keep it under 50 characters",
        invalidFormat: "Start with lowercase verb"
      }
    },
    body: {
      description: "📖 Detailed description (optional):",
      messages: {
        skip: "Leave empty if subject is sufficient"
      }
    },
    isBreaking: {
      description: "💥 Are there breaking changes?",
      messages: {
        confirmation: "This will increment major version"
      }
    },
    breakingBody: {
      description: "💥 Describe the breaking changes:",
      messages: {
        empty: "Explain what breaks and how to migrate"
      }
    },
    isIssueAffected: {
      description: "🐛 Does this close any issues?",
      messages: {
        help: "Reference issues with fixes #123"
      }
    },
    issuesBody: {
      description: "🐛 List the issues (e.g., fixes #123, closes #456):",
      messages: {
        empty: "Use format: fixes #123, closes #456",
        invalidFormat: "Use keywords: fixes, closes, resolves"
      }
    }
  }
};

Internationalization

import { PromptMessages, UserPromptConfig } from "@commitlint/types";

// Spanish messages
const spanishMessages: PromptMessages = {
  skip: "Presiona Enter para omitir",
  max: "Máximo {0} caracteres",
  min: "Mínimo {0} caracteres",
  emptyWarning: "Este campo no puede estar vacío",
  upperLimitWarning: "Entrada demasiado larga",
  lowerLimitWarning: "Entrada demasiado corta"
};

const spanishConfig: UserPromptConfig = {
  messages: spanishMessages,
  questions: {
    type: {
      description: "Selecciona el tipo de cambio:",
      enum: {
        feat: { description: "Nueva funcionalidad", title: "Funcionalidad" },
        fix: { description: "Corrección de error", title: "Corrección" },
        docs: { description: "Solo documentación", title: "Documentación" }
      }
    },
    subject: {
      description: "Escribe una descripción breve en modo imperativo:",
      messages: {
        empty: "El asunto es requerido",
        maxLength: "El asunto no debe exceder 50 caracteres"
      }
    }
  }
};

Dynamic Prompt Configuration

import { PromptConfig, UserPromptConfig } from "@commitlint/types";

// Function to generate configuration based on project type
function createPromptConfig(projectType: "library" | "application" | "monorepo"): UserPromptConfig {
  const baseConfig: UserPromptConfig = {
    settings: {
      scopeEnumSeparator: ",",
      enableMultipleScopes: projectType === "monorepo"
    },
    questions: {
      type: {
        description: "Select commit type:",
        enum: {
          feat: { title: "Feature", emoji: "✨" },
          fix: { title: "Bug Fix", emoji: "🐛" },
          docs: { title: "Documentation", emoji: "📝" }
        }
      }
    }
  };
  
  // Add project-specific customizations
  switch (projectType) {
    case "library":
      baseConfig.questions!.type!.enum!.perf = {
        title: "Performance",
        emoji: "⚡",
        description: "Performance improvement"
      };
      break;
      
    case "application":
      baseConfig.questions!.type!.enum!.deploy = {
        title: "Deployment",
        emoji: "🚀",
        description: "Deployment related changes"
      };
      break;
      
    case "monorepo":
      baseConfig.questions!.scope = {
        description: "Which package/workspace is affected:",
        messages: {
          empty: "Scope is required for monorepo"
        }
      };
      break;
  }
  
  return baseConfig;
}

// Usage
const libraryPrompts = createPromptConfig("library");
const appPrompts = createPromptConfig("application");
const monorepoPrompts = createPromptConfig("monorepo");

Prompt Validation Integration

import { PromptConfig, RulesConfig } from "@commitlint/types";

// Generate prompt config from commitlint rules
function generatePromptsFromRules(rules: Partial<RulesConfig>): UserPromptConfig {
  const config: UserPromptConfig = {
    questions: {}
  };
  
  // Extract type enum from rules
  const typeEnumRule = rules["type-enum"];
  if (typeEnumRule && typeEnumRule[2]) {
    const types = typeEnumRule[2] as string[];
    config.questions!.type = {
      description: "Select commit type:",
      enum: types.reduce((acc, type) => {
        acc[type] = { title: type.charAt(0).toUpperCase() + type.slice(1) };
        return acc;
      }, {} as Record<string, { title: string }>)
    };
  }
  
  // Extract subject max length
  const subjectMaxRule = rules["subject-max-length"];
  if (subjectMaxRule && subjectMaxRule[2]) {
    const maxLength = subjectMaxRule[2] as number;
    config.questions!.subject = {
      description: `Brief description (max ${maxLength} chars):`,
      messages: {
        maxLength: `Subject must be no more than ${maxLength} characters`
      }
    };
  }
  
  return config;
}