structuredmerge Logo by GitHub kettle-jem Logo by Aboling0, CC BY-SA 4.0

๐Ÿ”ฎ Kettle::Jem

Version Ruby Users Forum GitHub tag (latest SemVer) License: AGPL-3.0-only OR PolyForm-Small-Business-1.0.0 Total downloads CI Current

if ci_badges.map(&:color).detect { it != "green"} โ˜๏ธ let me know on Discord or RubyForum, as I may have missed the notification.


if ci_badges.map(&:color).all? { it == "green"} ๐Ÿ‘‡๏ธ send money so I can do more of this. FLOSS maintenance is now my full-time job.

Sponsor Me on Github Liberapay Goal Progress Donate on PayPal Buy me a coffee Donate at ko-fi.com

๐Ÿ‘ฃ How will this project approach the September 2025 hostile takeover of RubyGems? ๐Ÿš‘๏ธ

I've summarized my thoughts in this blog post.

๐ŸŒป Synopsis Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0 ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5

Kettle::Jem is an AST-aware gem templating system that keeps hundreds of Ruby gems in sync with a shared template while preserving each project's customizations. Unlike line-based copy/merge tools, Kettle::Jem understands the structure of every file it touches โ€” Ruby via Prism, YAML via Psych, Markdown via Markly, TOML via tree-sitter, and more โ€” so template updates land precisely where they belong, and project-specific additions are never clobbered.

Plugin authors can now use the dedicated plugin authoring guide to build kettle-jem extension gems against the supported plugin seam.

Key Features

  • AST-aware merging โ€” 10 format-specific merge engines (prism, psych, markly, toml, json, jsonc, json5, bash, dotenv, rbs, text)
  • Token substitution โ€” {KJ|TOKEN} patterns resolved from config, ENV, or auto-derived from gemspec
  • Freeze blocks โ€” protect any section from template overwrites with # kettle-jem:freeze / # kettle-jem:unfreeze
  • Per-file strategies โ€” merge, accept_template, keep_destination, or raw_copy
  • Multi-phase pipeline โ€” 11 ordered phases (service_actor-based) from config sync through duplicate checking
  • SHA-pinned GitHub Actions โ€” template uses: always wins, propagating immutable SHAs
  • Convergence in one pass โ€” a single kettle-jem install applies all changes; a second run produces zero diff
  • Selftest divergence check โ€” CI verifies that project drift stays within a configurable threshold

๐Ÿ’ก Info you can shake a stick at

Tokens to Remember Gem name Gem namespace
Works with MRI Ruby 4 Ruby current Compat
Support & Community Join Me on Daily.dev's RubyFriends Get help from RubyForum Live Chat on Discord Get help from me on Upwork Get help from me on Codementor
Source Source on GitLab.com Source on CodeBerg.org Source on GitHub.com The best SHA: dQw4w9WgXcQ!
Documentation Current release on RubyDoc.info YARD on Galtzo.com Maintainer Blog GitLab Wiki GitHub Wiki
Compliance License: AGPL-3.0-only OR PolyForm-Small-Business-1.0.0 Apache license compatibility: Category X ๐Ÿ“„ilo-declaration-img Security Policy Contributor Covenant 2.1 SemVer 2.0.0
Style Enforced Code Style Linter Keep-A-Changelog 1.0.0 Gitmoji Commits Compatibility appraised by: appraisal2
Maintainer ๐ŸŽ–๏ธ Follow Me on LinkedIn Follow Me on Ruby.Social Follow Me on Bluesky Contact Maintainer My technical writing
... ๐Ÿ’– Find Me on WellFound: Find Me on CrunchBase My LinkTree More About Me ๐ŸงŠ ๐Ÿ™ ๐Ÿ›– ๐Ÿงช

Compatibility

Compatible with MRI Ruby 4.0.0+, and concordant releases of JRuby, and TruffleRuby. CI workflows and Appraisals are generated for MRI Ruby 4.0.0+. This test floor is configured by ruby.test_minimum in .kettle-jem.yml and may be higher than the gem's runtime compatibility floor when legacy Rubies are not practical for the current toolchain.

kettle-dev Logo by Aboling0, CC BY-SA 4.0

The amazing test matrix is powered by the kettle-dev stack.

How kettle-dev manages complexity in tests
Gem Source Role Total downloads
appraisal2 GitHub multi-dependency Appraisal matrix generation Total downloads for appraisal2
appraisal2-rubocop GitHub RuboCop Appraisal generator integration Total downloads for appraisal2-rubocop
kettle-dev GitHub development, release, and CI workflow tooling Total downloads for kettle-dev
kettle-soup-cover GitHub SimpleCov coverage policy and reporting Total downloads for kettle-soup-cover
kettle-test GitHub standard test runner and coverage harness Total downloads for kettle-test
rubocop-lts GitHub Ruby-version-aware linting Total downloads for rubocop-lts
turbo_tests2 GitHub parallel test execution Total downloads for turbotests2
StructuredMerge package family

This gem is part of the StructuredMerge Ruby package family. The implementation inventory, layering model, and backend notes live in the root package-family guide. Shared behavior is defined by the StructuredMerge fixtures and implemented by the Go, Ruby, Rust, and TypeScript repositories.

Merge analysis must enter parsing through tree_haver. Parser-specific gems register concrete TreeHaver backends; substrate gems register grammar mappings and keep shared format or language merge behavior in one place. Missing backends fail closed instead of falling back to direct parser-library calls.

Federated DVCS

Find this repo on federated forges (Coming soon!)
Federated DVCS Repository Status Issues PRs Wiki CI
๐Ÿงช structuredmerge/structuredmerge-ruby on GitLab The Truth ๐Ÿ’š ๐Ÿ’š ๐Ÿ’š ๐Ÿญ Tiny Matrix
๐ŸงŠ structuredmerge/structuredmerge-ruby on CodeBerg An Ethical Mirror (Donate) ๐Ÿ’š ๐Ÿ’š โž– โญ•๏ธ No Matrix
๐Ÿ™ structuredmerge/structuredmerge-ruby on GitHub Another Mirror ๐Ÿ’š ๐Ÿ’š ๐Ÿ’š ๐Ÿ’ฏ Full Matrix

Enterprise Support Tidelift

Available as part of the Tidelift Subscription.

Need enterprise-level guarantees?

The maintainers of this and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use.

Get help from me on Tidelift

  • ๐Ÿ’กSubscribe for support guarantees covering all your FLOSS dependencies
  • ๐Ÿ’กTidelift is part of Sonar
  • ๐Ÿ’กTidelift pays maintainers to maintain the software you depend on!
    ๐Ÿ“Š@Pointy Haired Boss: An enterprise support subscription is "never gonna let you down", and supports open source maintainers

Alternatively:

  • Ruby Users Forum
  • Live Chat on Discord
  • Get help from me on Upwork
  • Get help from me on Codementor

โœจ Installation

Install the gem and add to the application's Gemfile by executing:

bundle add kettle-jem

If bundler is not being used to manage dependencies, install the gem by executing:

gem install kettle-jem

โš™๏ธ Configuration

Each gem that uses Kettle::Jem has a .kettle-jem.yml file at its root. This file controls every aspect of how the template is applied.

Minimal Configuration

project_emoji: "๐Ÿ”ฎ"
engines:
  - ruby
licenses:
  - MIT
tokens:
  forge:
    gh_user: "your-username"
  author:
    name: "Your Name"
    email: "you@example.com"

Full Configuration Reference

# REQUIRED โ€” unique emoji used in badges and gemspec summary
project_emoji: "๐Ÿ”ฎ"               # ENV override: KJ_PROJECT_EMOJI

# Ruby engines to include in CI matrix (remove to skip)
engines:
  - ruby
  - jruby
  - truffleruby

# The generated direct dependency-heads workflow derives its Bundler lockfile
# platform from `engines`. JRuby gets an isolated temporary lockfile with the
# `universal-java` platform; MRI and TruffleRuby use the checked-in lock
# context. This avoids making one shared lockfile satisfy incompatible engine
# platforms.

# SPDX license identifiers
licenses:
  - MIT

# Logo layout in README header: org | project | org_and_project
readme:
  top_logo_mode: org

# Bot accounts to exclude from contributor lists
machine_users:
  - dependabot

# Maximum allowed divergence (%) for selftest CI check
min_divergence_threshold: 5       # ENV override: KJ_MIN_DIVERGENCE_THRESHOLD

# Default merge behavior applied to all files
defaults:
  preference: "template"           # template | destination
  add_template_only_nodes: true    # add nodes that only exist in template
  freeze_token: "kettle-jem"       # marker for frozen sections

# Token values for {KJ|TOKEN} substitution
tokens:
  forge:
    gh_user: "github-username"    # ENV override: KJ_GH_USER
    gl_user: "gitlab-username"    # ENV override: KJ_GL_USER
    cb_user: "codeberg-username"  # ENV override: KJ_CB_USER
    sh_user: "sourcehut-user"     # ENV override: KJ_SH_USER
  author:
    name: "Full Name"             # ENV override: KJ_AUTHOR_NAME
    given_names: "Full"           # ENV override: KJ_AUTHOR_GIVEN_NAMES
    family_names: "Name"          # ENV override: KJ_AUTHOR_FAMILY_NAMES
    email: "you@example.com"      # ENV override: KJ_AUTHOR_EMAIL
    domain: "example.com"         # ENV override: KJ_AUTHOR_DOMAIN
    orcid: "0000-0000-0000-0000"  # ENV override: KJ_AUTHOR_ORCID
  funding:
    kofi: "username"              # ENV override: KJ_FUNDING_KOFI
    paypal: "username"            # ENV override: KJ_FUNDING_PAYPAL
    buymeacoffee: "username"      # ENV override: KJ_FUNDING_BUYMEACOFFEE
    liberapay: "username"         # ENV override: KJ_FUNDING_LIBERAPAY
  social:
    mastodon: "username"          # ENV override: KJ_SOCIAL_MASTODON
    bluesky: "user.bsky.social"   # ENV override: KJ_SOCIAL_BLUESKY
    linktree: "username"          # ENV override: KJ_SOCIAL_LINKTREE
    devto: "username"             # ENV override: KJ_SOCIAL_DEVTO

# Glob-based overrides (first match wins)
patterns:
  - path: "certs/**"
    strategy: raw_copy

# Per-file overrides
files:
  Rakefile:
    strategy: merge
    preference: destination        # preserve local tasks
  AGENTS.md:
    strategy: accept_template      # always use template version

Framework Matrix vs. Appraisals

workflows.preset: framework and workflows.framework_matrix are meant for a simple 2D matrix: Ruby versions ร— one framework gem/version axis. This is a good fit when you want kettle-jem to generate framework-version modular gemfiles, Appraisals entries, and CI matrix entries for a single framework dependency.

If you need a deeper or more complex matrix, prefer kettle-jem-appraisals, which generates Appraisals entries and is the better fit for Appraisals-style combinations.

Strategies

Strategy Behavior
merge Resolve tokens, then AST-merge template + destination (default)
accept_template Resolve tokens, overwrite destination with template result
keep_destination Skip entirely โ€” no merge, no creation
raw_copy Copy bytes as-is โ€” no token resolution, no merge (for binary assets)

raw_copy exists for bootstrap files that may be needed before the full templating stack is available. Because it bypasses normal template processing, do not use it for templates that contain {KJ|...} tokens or require StructuredMerge normalization.

Token Substitution

Tokens use {KJ|TOKEN} syntax and are resolved in priority order:

  1. ENV variables (highest) โ€” e.g., KJ_AUTHOR_NAME
  2. .kettle-jem.yml tokens: section โ€” explicit values
  3. Auto-derived from gemspec (lowest) โ€” author name, email, domain

Common tokens:

Token Source
`GEM_NAME` Gem name from gemspec
`NAMESPACE` Ruby module namespace
`AUTHOR:NAME` Author full name
`AUTHOR:EMAIL` Author email
`GH:USER` GitHub username
`PROJECT_EMOJI` Project emoji from config
`MIN_RUBY` Minimum Ruby version
`FREEZE_TOKEN` Freeze marker name

Freeze Blocks

Protect sections in any file from template overwrites:

# kettle-jem:freeze
gem "my-local-fork", path: "../custom"
# kettle-jem:unfreeze

Content between freeze/unfreeze markers is always preserved from the destination, regardless of what the template contains. Works in all supported formats (Ruby, YAML, Markdown, TOML, JSON, Bash, etc.).

Merge Engine Selection

Kettle::Jem selects the merge engine by file type:

File Pattern Merge Engine Key Behaviors
*.rb, Gemfile, *.gemspec, Rakefile, Appraisals Prism::Merge Three-phase matching, gemspec var renaming
*.yml, *.yaml Psych::Merge SHA-pinned uses:, per-key preferences
*.md, *.markdown Markly::Merge Heading/list matching, inner list merge
*.toml Toml::Merge Sort keys, table matching
*.json Json::Merge Key-based matching
*.jsonc Json::Merge With comment preservation
*.json5 Json::Merge JSON5 keys, strings, comments, and trailing commas
*.sh, *.bash, .envrc Bash::Merge Block matching
.env* Dotenv::Merge KEY=value matching
*.rbs RBS::Merge Type signature matching
.gitignore Text::Merge Intentional line-based merge

No silent fallback: If a tree-sitter grammar is unavailable for a file type that requires AST merging, kettle-jem will fail (default) or skip the file โ€” never silently degrade to text-based merging. See PARSE_ERROR_MODE below.

๐Ÿ”ง Basic Usage

Initial Setup

gem install kettle-jem
cd my-gem
kettle-jem

The setup CLI runs a two-phase bootstrap:

  1. Bootstrap โ€” creates .kettle-jem.yml, installs modular gemfiles, ensures dev dependencies
  2. Bundled โ€” loads the full runtime and runs kettle-jem install

Applying Template Updates

After initial setup, re-run the template process to pull in updates:

K_JEM_TEMPLATING=true bundle exec kettle-jem install

This applies all template phases, then runs the local finishing steps such as bin/setup, curated binstub generation, hooks, and lockfile normalization.

Phase Description Files Affected
0 Config sync .kettle-jem.yml
1 Dev container .devcontainer/
2 GitHub workflows .github/workflows/, FUNDING.yml
3 Quality config .qlty/qlty.toml
4 Modular gemfiles gemfiles/modular/
5 Spec helper spec/spec_helper.rb
6 Environment templates .env.local.example
7 Remaining files gemspec, README, LICENSE, Rakefile, โ€ฆ
8 Git hooks .git-hooks/
9 License files LICENSE*
10 Duplicate check (validation only)

Each phase is implemented as a composable service_actor actor, enabling per-phase statistics (๐Ÿ“„ templates, ๐Ÿ†• created, ๐Ÿ“‹ pre-existing, ๐ŸŸฐ identical, โœ๏ธ changed) and future slice-based workflows.

Checking Divergence

CI can verify that a project hasn't drifted too far from the template:

bundle exec rake kettle:jem:selftest

This re-applies the template in a temporary checkout and measures the diff. Output is condensed to two summary lines after the template run:

[selftest] ๐Ÿ“„  Report - tmp/template_test/report/summary.md
[selftest] โœ…  Score: 100.0% ยท Divergence: 0.0% ยท Threshold: fail when divergence reaches 5.0%

If divergence exceeds min_divergence_threshold (default 5%), the check fails.

Workflow-Specific Options

For GitHub Actions workflows, the template always wins for uses: lines (SHA-pinned action references) while destination wins for job configuration:

# Template updates this SHA automatically:
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0

# Your matrix customizations are preserved:
matrix:
  ruby: ["3.2", "3.3", "3.4"]

Per-File Overrides

Override merge behavior for specific files in .kettle-jem.yml:

files:
  Rakefile:
    strategy: merge
    preference: destination     # keep your custom tasks
  certs/my.pem:
    strategy: raw_copy          # binary file, no merging
  generated/report.md:
    strategy: keep_destination  # never touch this file

Environment Variables & CLI Options

Kettle::Jem behavior is controlled via environment variables (which double as Rake task arguments) and CLI flags passed to kettle-jem setup.

Merge & Error Handling

Variable CLI Flag Default Description
FAILURE_MODE --failure-mode=VAL error How general merge failures are handled. error raises and halts; rescue logs a warning and uses unmerged content.
PARSE_ERROR_MODE โ€” fail How AST parser unavailability is handled. fail raises immediately (recommended); skip warns and preserves the destination file unchanged. There is no text-merge fallback โ€” AST merge or nothing.

Task Control

Variable CLI Flag Default Description
allowed --allowed=VAL true Set to false/0/no to require manual review of env file changes before continuing.
โ€” --interactive (off) Enable interactive prompts (opt-in). Overrides the default non-interactive behavior.
KETTLE_JEM_VERBOSE --verbose false Show detailed output including per-file messages and setup progress. Overrides the default quiet behavior.
only --only=VAL (all) Comma-separated glob patterns โ€” only template files matching at least one pattern are processed.
include --include=VAL (all) Comma-separated glob patterns โ€” additional files to include beyond the default set.
hook_templates --hook_templates=VAL (prompt) Git hook install location: l/local, g/global, or n/none. Also via KETTLE_DEV_HOOK_TEMPLATES.
KETTLE_JEM_CHECKSUMS --checksums=VAL template,ignore-dest Checksum skip mode. See "Checksum Skip Modes" below.
โ€” --ignore-checksums (off) Alias for --checksums=off; disables checksum-based skipping.

Checksum Skip Modes

Kettle::Jem writes managed template state to .structuredmerge/kettle-jem.lock. The legacy whole-template checksum inventory now lives under template_state.checksums. That inventory is useful for coarse template drift state, but it is not used to skip individual files. File skipping uses files.*.input_fingerprint, a per-destination digest that includes the selected template source SHA, recipe identity/version, resolved token digest, and kettle-jem implementation SHA. Token values are hashed into the fingerprint and are not written to the lockfile.

--checksums accepts comma-separated modes:

Mode Behavior
template Skip a destination file only when its input_fingerprint matches the lock. This is the default template-input check.
ignore-dest Do not compare the destination file checksum. This is the default destination behavior.
dest Also require the destination file checksum to match the lock before skipping. dest implies template unless ignore-template is present.
ignore-template Do not compare input_fingerprint; useful only with dest when local destination state should be the only skip gate.
off Disable checksum-based skipping. This cannot be combined with other modes.

Common combinations:

Value Result
template,ignore-dest Default. Retemplate when relevant template input changes; ignore local destination edits.
dest,template Retemplate when relevant template input changes or the destination differs from the prior templated result.
dest,ignore-template Skip only when the destination checksum matches; ignore template input changes.
off Run normal templating without checksum skip shortcuts.

Config & Identity (KJ_ prefix)

These map directly to .kettle-jem.yml keys, seed freshly created configs, fill missing keys during config sync, and act as runtime overrides.

Variable Description
KJ_PROJECT_EMOJI Project identifying emoji (e.g. ๐Ÿช™). Required in config.
KJ_MIN_DIVERGENCE_THRESHOLD Selftest divergence threshold for min_divergence_threshold.
KJ_AUTHOR_NAME Gem author full name
KJ_AUTHOR_EMAIL Gem author email
KJ_AUTHOR_DOMAIN Author website domain (derived from email if unset)
KJ_AUTHOR_GIVEN_NAMES First/given names
KJ_AUTHOR_FAMILY_NAMES Last/family names
KJ_AUTHOR_ORCID ORCID identifier
FORGE_ORG Preferred forge organization or owner used for scaffolded repository URLs when no forge remote is available
KJ_GH_ORG GitHub-specific organization or owner fallback for scaffolded repository URLs
KJ_GH_USER GitHub username used for maintainer profile and sponsor links
KJ_GL_USER GitLab username
KJ_CB_USER Codeberg username
KJ_SH_USER SourceHut username

Workspace & Funding

Variable Description
KETTLE_DEV_DEV Workspace root for local sibling gems. true = ~/src/my; a path = that path; unset/false = released gems.
KETTLE_DEV_DEBUG Set to true for verbose debug output.
FUNDING_ORG OpenCollective organization handle for FUNDING.yml. Auto-derived from git remote if unset.
OPENCOLLECTIVE_HANDLE Alternative to FUNDING_ORG for personal OpenCollective pages.
KJ_FUNDING_KOFI Ko-fi handle for FUNDING.yml
KJ_FUNDING_PAYPAL PayPal handle for FUNDING.yml
KJ_FUNDING_BUYMEACOFFEE Buy Me a Coffee handle for funding links
KJ_FUNDING_LIBERAPAY Liberapay handle for funding links
KJ_SOCIAL_MASTODON Mastodon handle for social/profile links
KJ_SOCIAL_BLUESKY Bluesky handle for social/profile links
KJ_SOCIAL_LINKTREE Linktree handle for social/profile links
KJ_SOCIAL_DEVTO DEV Community handle for social/profile links

Templating Examples

# Standard template update (quiet, non-interactive โ€” the default)
K_JEM_TEMPLATING=true bundle exec kettle-jem install

# Verbose output
K_JEM_TEMPLATING=true KETTLE_JEM_VERBOSE=true bundle exec kettle-jem install

# Interactive mode (prompts before each change)
K_JEM_TEMPLATING=true bundle exec kettle-jem install --interactive

# Force re-evaluation of every selected template file
K_JEM_TEMPLATING=true bundle exec kettle-jem install --ignore-checksums

# Retemplate when either template inputs or destination checksums changed
K_JEM_TEMPLATING=true bundle exec kettle-jem install --checksums=dest,template

# Only workflow files, skip unparseable. Scoped template runs skip install
# finishing steps and are intended for surgical file updates.
K_JEM_TEMPLATING=true PARSE_ERROR_MODE=skip bundle exec kettle-jem template --only=".github/**"

# Rescue on merge failure (don't halt)
K_JEM_TEMPLATING=true FAILURE_MODE=rescue bundle exec kettle-jem install

The kettle:jem:* rake tasks are internal targets used by the executable after it prepares the templating environment; call kettle-jem directly for normal templating work.

๐Ÿ” Security

See SECURITY.md.

๐Ÿค Contributing

If you need some ideas of where to help, you could work on adding more code coverage, or if it is already ๐Ÿ’ฏ (see below) check issues or PRs, or use the gem and think about how it could be better.

We Keep A Changelog so if you make changes, remember to update it.

See CONTRIBUTING.md for more detailed instructions.

๐Ÿš€ Release Instructions

See CONTRIBUTING.md.

Code Coverage

Coverage service badges

๐Ÿช‡ Code of Conduct

Everyone interacting with this project's codebases, issue trackers, chat rooms and mailing lists agrees to follow the Contributor Covenant 2.1.

๐ŸŒˆ Contributors

Contributors

Made with contributors-img.

Also see GitLab Contributors: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/graphs/main

โญ๏ธ Star History Star History Chart

๐Ÿ“Œ Versioning

This library follows Semantic Versioning 2.0.0 for its public API where practical. For most applications, prefer the Pessimistic Version Constraint with two digits of precision.

For example:

spec.add_dependency("kettle-jem", "~> 7.0")
๐Ÿ“Œ Is "Platform Support" part of the public API? More details inside.

Dropping support for a platform can be a breaking change for affected users. If a release changes supported platforms, it should be called out clearly in the changelog and versioned with that impact in mind.

To get a better understanding of how SemVer is intended to work over a project's lifetime, read this article from the creator of SemVer:

See CHANGELOG.md for a list of releases.

๐Ÿ“„ License

The gem is available under the following licenses: AGPL-3.0-only, PolyForm-Small-Business-1.0.0. See LICENSE.md for details.

If none of the available licenses suit your use case, please contact us to discuss a custom commercial license.

See LICENSE.md for the official copyright notice.

Copyright holders
  • Required Notice: Copyright (c) 2026 Peter H. Boling

๐Ÿค‘ A request for help

Maintainers have teeth and need to pay their dentists. After getting laid off in an RIF in March, and encountering difficulty finding a new one, I began spending most of my time building open source tools. I'm hoping to be able to pay for my kids' health insurance this month, so if you value the work I am doing, I need your support. Please consider sponsoring me or the project.

To join the community or get help, use the RubyForum or Discord.

Ruby Users Forum Live Chat on Discord

To say "thanks!" โ˜๏ธ Join the community or ๐Ÿ‘‡๏ธ send money.

Sponsor me on GitHub Sponsors ๐Ÿ’Œ Sponsor me on Liberapay ๐Ÿ’Œ Donate on PayPal

Please give the project a star โญ โ™ฅ.

Many parts of this project are actively managed by a kettle-jem smart template utilizing StructuredMerge.org merge contracts.

Thanks for RTFM. โ˜บ๏ธ

Field Value
Package kettle-jem
Description ๐Ÿ”ฎ Kettle::Jem provides gem scaffolding, templating, and setup automation using the *-merge gem family for AST-based file merging and token-resolver for template token resolution. Includes MergerConfig presets, YAML recipes, and a complete gem template scaffold.
Homepage https://github.com/structuredmerge/structuredmerge-ruby
Source https://github.com/structuredmerge/structuredmerge-ruby
License AGPL-3.0-only OR PolyForm-Small-Business-1.0.0
Funding https://github.com/sponsors/pboling, https://ko-fi.com/pboling, https://liberapay.com/pboling/donate, https://thanks.dev/u/gh/pboling, https://tidelift.com/funding/github/rubygems/kettle-jem, https://www.buymeacoffee.com/pboling