# \[RFC\] crinit: Pluggable filesystem-driven template engine for Crystal (upstream crystal init evolution)

**URL:** <https://forum.crystal-lang.org/t/rfc-crinit-pluggable-filesystem-driven-template-engine-for-crystal-upstream-crystal-init-evolution/9156>\
**Category:** Crystal Contrib\
**Created:** [September 18, 2026, 7:42pm UTC](https://forum.crystal-lang.org/t/rfc-crinit-pluggable-filesystem-driven-template-engine-for-crystal-upstream-crystal-init-evolution/9156 "2026-09-18T19:42:09Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![renich](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/renich/32/2137_2.png) [@renich](https://forum.crystal-lang.org/u/renich)\
**Post date:** [September 18, 2026, 7:42pm UTC](https://forum.crystal-lang.org/t/rfc-crinit-pluggable-filesystem-driven-template-engine-for-crystal-upstream-crystal-init-evolution/9156/1 "2026-09-18T19:42:09Z")

</div>

Hi everyone,

Currently, `crystal init app` and `crystal init lib` generate hardcoded skeletons embedded in compiler macros. While fine for getting started, real-world projects invariably require customized scaffolding: domain-segregated layouts, alternative licenses (GPL, Apache, BSD), container recipes, systemd unit files, CI/CD matrices, and static analysis setups (Ameba, Flaw).

To address this, we developed **`crinit`** as a prototype and working implementation for an upstream RFC to evolve `crystal init` into a pluggable, filesystem-driven scaffolding engine.

### Core Mechanics

1. **Filesystem Tree Mirroring with Macro Safety** :
  - A template is simply a directory containing files, directories, and an optional `template.yml` manifest.
  - Template tokens use double curly braces (`{{name}}`, `{{module_name}}`, `{{author}}`, `{{year}}`, `{{crystal_version}}`).
  - Standard Crystal macros (`{% ... %}`, `{{ ... }}`) inside generated source code pass through untouched without escaping.

2. **Resolution Hierarchy** :  
Templates resolve deterministically with zero configuration:
  - CLI flag: `--template <path>`
  - Environment: `$CRYSTAL_TEMPLATE_PATH`
  - Workspace: `./.crystal/templates/<name>`
  - User directory: `$XDG_DATA_HOME/crystal/templates/<name>` (Linux/BSD) or OS equivalent
  - System directory: `/usr/share/crystal/templates/<name>`
  - Built-in fallback: Embedded `app` and `lib` skeletons (100% backward-compatible)

3. **Remote Assets with Offline Fallback (ADR-004)**:  
Templates can declare static external assets (e.g. CSS/JS libraries or licenses) fetched over HTTPS with SHA-256 integrity validation. A 4-tier pipeline guarantees air-gapped/offline reliability:  
`Local Cache` → `HTTPS Download` → `Bundled Template Fallback` → `Graceful .todo Stub`.

### Reference Examples in Repository

We’ve added three concrete template implementations to [`examples/`](https://github.com/renich/crinit/tree/master/examples):

- **[`cli-tool`](https://github.com/renich/crinit/tree/master/examples/cli-tool)**: OptionParser integration, version module, and isolated specs.
- **[`enterprise-service`](https://github.com/renich/crinit/tree/master/examples/enterprise-service)**: Domain layout (`config/`, `src/{{name}}/cli.cr`), Podman Quadlet container files, RPM `.spec`, and GPLv3 licensing.
- **[`kemal-web`](https://github.com/renich/crinit/tree/master/examples/kemal-web)**: Kemal hypermedia web layout with remote asset declarations (Pico CSS, Datastar) and bundled offline fallbacks.

### Links

- **Repository** : [GitHub](https://github.com/renich/crinit) | [GitLab](https://gitlab.com/renich/crinit)
- **Architecture & ADRs** : [`docs/adrs/`](https://github.com/renich/crinit/tree/master/docs/adrs)
- **Upstream Roadmaps** : [`docs/project/roadmaps/`](https://github.com/renich/crinit/tree/master/docs/project/roadmaps)

Looking forward to feedback on the resolution order, token conventions, and manifest schema before opening the formal RFC against `crystal-lang/rfcs`.

---

<div class="post-metadata">

**Author:** ![Xen](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/xen/32/2288_2.png) [@Xen](https://forum.crystal-lang.org/u/Xen)\
**Post date:** [September 20, 2026, 4:21pm UTC](https://forum.crystal-lang.org/t/rfc-crinit-pluggable-filesystem-driven-template-engine-for-crystal-upstream-crystal-init-evolution/9156/2 "2026-09-20T16:21:52Z")

</div>

I like the idea, but how about remote templates? Something like:

```bash
crystal init https://github.com/kemalcr/kemal my-kemal-project

```

For using a (hypothetical) official kemal template.

---

<div class="post-metadata">

**Author:** ![renich](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/renich/32/2137_2.png) [@renich](https://forum.crystal-lang.org/u/renich)\
**Post date:** [September 20, 2026, 6:40pm UTC](https://forum.crystal-lang.org/t/rfc-crinit-pluggable-filesystem-driven-template-engine-for-crystal-upstream-crystal-init-evolution/9156/3 "2026-09-20T18:40:02Z")

</div>

Not a bad idea to consider. I’ve included the possibility to use remote assets. Remote templates sounds like a logical must-have. :)
