소스 검색

wiki updates and file cleanup

xcad 2 달 전
부모
커밋
f64137a60f

+ 0 - 26
.editorconfig

@@ -1,26 +0,0 @@
-# https://editorconfig.org/
-root = true
-
-[*]
-charset = utf-8
-end_of_line = lf
-indent_size = 4
-indent_style = space
-insert_final_newline = true
-trim_trailing_whitespace = true
-
-[*.json]
-indent_size = 2
-
-[*.{js,jsx,ts,tsx}]
-indent_size = 2
-
-[*.md]
-indent_size = unset
-trim_trailing_whitespace = false
-
-[*.py]
-indent_size = 4
-
-[{*.{yaml,yml},.yamllint}]
-indent_size = 2

+ 0 - 252
.github/scripts/generate_wiki_docs.py

@@ -1,252 +0,0 @@
-#!/usr/bin/env python3
-"""Generate GitHub Wiki documentation for module variables.
-
-This script auto-generates variable documentation in GitHub Wiki markdown format
-for all registered modules, using the latest schema version for each.
-"""
-
-import sys
-from pathlib import Path
-
-# Add project root to path (script is in .github/scripts, so go up twice)
-project_root = Path(__file__).parent.parent.parent
-sys.path.insert(0, str(project_root))
-
-# ruff: noqa: E402
-# Import all modules to register them
-import cli.modules.ansible
-import cli.modules.compose
-import cli.modules.helm
-import cli.modules.kubernetes
-import cli.modules.packer
-import cli.modules.terraform  # noqa: F401
-from cli.core.registry import registry  # Module import after path manipulation
-
-
-def format_value(value):
-    """Format value for markdown display."""
-    if value is None or value == "":
-        return "_none_"
-    if isinstance(value, bool):
-        return "✓" if value else "✗"
-    if isinstance(value, list):
-        return ", ".join(f"`{v}`" for v in value)
-    return f"`{value}`"
-
-
-def generate_module_docs(module_name: str, output_dir: Path):  # noqa: PLR0912, PLR0915
-    """Generate wiki documentation for a single module."""
-    # Get module class from registry
-    module_classes = dict(registry.iter_module_classes())
-
-    if module_name not in module_classes:
-        sys.stderr.write(f"Warning: Module '{module_name}' not found, skipping\n")
-        return False
-
-    module_cls = module_classes[module_name]
-    schema_version = module_cls.schema_version
-
-    # Get the spec for the latest schema version
-    if hasattr(module_cls, "schemas") and schema_version in module_cls.schemas:
-        spec = module_cls.schemas[schema_version]
-    elif hasattr(module_cls, "spec"):
-        spec = module_cls.spec
-    else:
-        sys.stderr.write(f"Warning: No spec found for module '{module_name}', skipping\n")
-        return False
-
-    # Generate markdown content
-    lines = []
-
-    # Header
-    lines.append(f"# {module_name.title()} Variables")
-    lines.append("")
-    lines.append(f"**Module:** `{module_name}`  ")
-    lines.append(f"**Schema Version:** `{schema_version}`  ")
-    lines.append(f"**Description:** {module_cls.description}")
-    lines.append("")
-    lines.append("---")
-    lines.append("")
-    lines.append(
-        "This page documents all available variables for the "
-        + f"{module_name} module. Variables are organized into sections "
-        + "that can be enabled/disabled based on your configuration needs."
-    )
-    lines.append("")
-
-    # Table of contents
-    lines.append("## Table of Contents")
-    lines.append("")
-    for section_key, section_data in spec.items():
-        section_title = section_data.get("title", section_key)
-        anchor = section_title.lower().replace(" ", "-").replace("/", "")
-        lines.append(f"- [{section_title}](#{anchor})")
-    lines.append("")
-    lines.append("---")
-    lines.append("")
-
-    # Process each section
-    for section_key, section_data in spec.items():
-        section_title = section_data.get("title", section_key)
-        section_desc = section_data.get("description", "")
-        section_toggle = section_data.get("toggle", "")
-        section_needs = section_data.get("needs", "")
-        section_required = section_data.get("required", False)
-        section_vars = section_data.get("vars", {})
-
-        # Section header
-        lines.append(f"## {section_title}")
-        lines.append("")
-
-        # Section metadata
-        metadata = []
-        if section_required:
-            metadata.append("**Required:** Yes")
-        if section_toggle:
-            metadata.append(f"**Toggle Variable:** `{section_toggle}`")
-        if section_needs:
-            if isinstance(section_needs, list):
-                needs_str = ", ".join(f"`{n}`" for n in section_needs)
-            else:
-                needs_str = f"`{section_needs}`"
-            metadata.append(f"**Depends On:** {needs_str}")
-
-        if metadata:
-            lines.append("  \n".join(metadata))
-            lines.append("")
-
-        if section_desc:
-            lines.append(section_desc)
-            lines.append("")
-
-        # Skip sections with no variables
-        if not section_vars:
-            lines.append("_No variables defined in this section._")
-            lines.append("")
-            continue
-
-        # Variables table
-        lines.append("| Variable | Type | Default | Description |")
-        lines.append("|----------|------|---------|-------------|")
-
-        for var_name, var_data in section_vars.items():
-            var_type = var_data.get("type", "str")
-            var_default = format_value(var_data.get("default"))
-            var_description = var_data.get("description", "").replace("\n", " ")
-
-            # Add extra metadata to description
-            extra_parts = []
-            if var_data.get("sensitive"):
-                extra_parts.append("**Sensitive**")
-            if var_data.get("autogenerated"):
-                extra_parts.append("**Auto-generated**")
-            if "options" in var_data:
-                opts = ", ".join(f"`{o}`" for o in var_data["options"])
-                extra_parts.append(f"**Options:** {opts}")
-            if "needs" in var_data:
-                extra_parts.append(f"**Needs:** `{var_data['needs']}`")
-            if "extra" in var_data:
-                extra_parts.append(var_data["extra"])
-
-            if extra_parts:
-                var_description += "<br>" + " • ".join(extra_parts)
-
-            lines.append(f"| `{var_name}` | `{var_type}` | {var_default} | {var_description} |")
-
-        lines.append("")
-        lines.append("---")
-        lines.append("")
-
-    # Footer
-    lines.append("## Notes")
-    lines.append("")
-    lines.append("- **Required sections** must be configured")
-    lines.append("- **Toggle variables** enable/disable entire sections")
-    lines.append("- **Dependencies** (`needs`) control when sections/variables are available")
-    lines.append("- **Sensitive variables** are masked during prompts")
-    lines.append("- **Auto-generated variables** are populated automatically if not provided")
-    lines.append("")
-    lines.append("---")
-    lines.append("")
-    lines.append(f"_Last updated: Schema version {schema_version}_")
-
-    # Write to file
-    output_file = output_dir / f"Variables-{module_name.title()}.md"
-    output_file.write_text("\n".join(lines))
-
-    sys.stdout.write(f"Generated: {output_file.name}\n")
-    return True
-
-
-def generate_variables_index(modules: list[str], output_dir: Path):
-    """Generate index page for all variable documentation."""
-    lines = []
-
-    lines.append("# Variables Documentation")
-    lines.append("")
-    lines.append("This section contains auto-generated documentation for all " + "available variables in each module.")
-    lines.append("")
-    lines.append("## Available Modules")
-    lines.append("")
-
-    for module_name in sorted(modules):
-        lines.append(f"- [{module_name.title()}](Variables-{module_name.title()})")
-
-    lines.append("")
-    lines.append("---")
-    lines.append("")
-    lines.append("Each module page includes:")
-    lines.append("")
-    lines.append("- Schema version information")
-    lines.append("- Complete list of sections and variables")
-    lines.append("- Variable types, defaults, and descriptions")
-    lines.append("- Section dependencies and toggle configurations")
-    lines.append("")
-    lines.append("---")
-    lines.append("")
-    lines.append("_This documentation is auto-generated from module schemas._")
-
-    output_file = output_dir / "Variables.md"
-    output_file.write_text("\n".join(lines))
-
-    sys.stdout.write(f"Generated: {output_file.name}\n")
-
-
-# Minimum required arguments
-MIN_ARGS = 2
-
-
-def main():
-    """Main entry point."""
-    if len(sys.argv) < MIN_ARGS:
-        sys.stderr.write("Usage: python3 scripts/generate_wiki_docs.py <output_directory>\n")
-        sys.exit(1)
-
-    output_dir = Path(sys.argv[1])
-    output_dir.mkdir(parents=True, exist_ok=True)
-
-    sys.stdout.write(f"Generating wiki documentation in: {output_dir}\n")
-    sys.stdout.write("\n")
-
-    # Get all registered modules
-    module_classes = dict(registry.iter_module_classes())
-    successful_modules = []
-
-    for module_name in sorted(module_classes.keys()):
-        if generate_module_docs(module_name, output_dir):
-            successful_modules.append(module_name)
-
-    sys.stdout.write("\n")
-
-    # Generate index page
-    if successful_modules:
-        generate_variables_index(successful_modules, output_dir)
-        sys.stdout.write("\n")
-        sys.stdout.write(f"✓ Successfully generated documentation for {len(successful_modules)} module(s)\n")
-    else:
-        sys.stderr.write("Error: No documentation generated\n")
-        sys.exit(1)
-
-
-if __name__ == "__main__":
-    main()

+ 0 - 98
.github/workflows/docs-update-wiki.yaml

@@ -1,98 +0,0 @@
----
-name: Docs - Update Wiki
-
-'on':
-  push:
-    branches:
-      - main
-    paths:
-      - 'cli/core/schema/**/*.json'  # JSON schema files
-      - '.wiki/**'  # Static wiki pages
-      - '.github/scripts/generate_wiki_docs.py'  # Wiki generation script
-      - '.github/workflows/docs-update-wiki.yaml'  # This workflow
-  workflow_dispatch:  # Allow manual trigger
-
-permissions:
-  contents: write
-
-jobs:
-  update-wiki:
-    runs-on: ubuntu-latest
-    steps:
-      - name: Checkout repository
-        uses: actions/checkout@v6
-        with:
-          fetch-depth: 1
-
-      - name: Checkout wiki repository
-        uses: actions/checkout@v6
-        with:
-          repository: ${{ github.repository }}.wiki
-          path: wiki
-          token: ${{ secrets.GITHUB_TOKEN }}
-
-      - name: Set up Python
-        uses: actions/setup-python@v6
-        with:
-          python-version: '3.14'
-
-      - name: Install dependencies
-        run: |
-          python -m pip install --upgrade pip
-          pip install -e .
-
-      - name: Generate variable documentation
-        run: |
-          python3 .github/scripts/generate_wiki_docs.py wiki/
-
-      - name: Sync wiki pages from .wiki directory
-        run: |
-          # Copy all markdown files from .wiki/ to wiki/ (except Variables-*.md which are auto-generated)
-          if [ -d ".wiki" ]; then
-            echo "Syncing wiki pages from .wiki/ directory..."
-            for file in .wiki/*.md; do
-              filename=$(basename "$file")
-              # Skip auto-generated variable documentation files
-              if [[ ! "$filename" =~ ^Variables- ]]; then
-                echo "  Copying $filename"
-                cp "$file" "wiki/$filename"
-              fi
-            done
-          else
-            echo "No .wiki directory found, skipping static wiki pages sync"
-          fi
-
-      - name: Check for changes
-        id: changes
-        working-directory: wiki
-        run: |
-          git add .
-          if git diff --staged --quiet; then
-            echo "has_changes=false" >> $GITHUB_OUTPUT
-            echo "No changes detected in wiki documentation"
-          else
-            echo "has_changes=true" >> $GITHUB_OUTPUT
-            echo "Changes detected in wiki documentation"
-          fi
-
-      - name: Commit and push changes
-        if: steps.changes.outputs.has_changes == 'true'
-        working-directory: wiki
-        run: |
-          git config user.name "github-actions[bot]"
-          git config user.email "github-actions[bot]@users.noreply.github.com"
-          git commit -m "Auto-update wiki pages"
-
-          # Pull with rebase to handle any remote changes, then push
-          # GitHub wikis use master as default branch
-          git pull --rebase origin master
-          git push origin master
-
-      - name: Summary
-        run: |
-          if [ "${{ steps.changes.outputs.has_changes }}" == "true" ]; then
-            echo "Wiki variable documentation updated successfully"
-            echo "View at: https://github.com/${{ github.repository }}/wiki"
-          else
-            echo "No changes to wiki documentation"
-          fi

+ 22 - 15
.wiki/Core-Concepts-Defaults.md

@@ -4,7 +4,7 @@ Save time by setting default values for variables you use frequently. This page
 
 
 ## What are Default Variables?
 ## What are Default Variables?
 
 
-**Default variables** are user-defined values that override module and template defaults. They allow you to:
+**Default variables** are user-defined values that override template manifest values for a module. They allow you to:
 - Avoid repetitive typing during template generation
 - Avoid repetitive typing during template generation
 - Standardize values across multiple templates
 - Standardize values across multiple templates
 - Customize your environment once, use everywhere
 - Customize your environment once, use everywhere
@@ -13,13 +13,13 @@ Save time by setting default values for variables you use frequently. This page
 
 
 Variables are resolved in this order (lowest to highest priority):
 Variables are resolved in this order (lowest to highest priority):
 
 
-1. Module runtime defaults
-2. Template defaults from `template.json`
-3. **User config** (your saved defaults) ← This page
-4. `--var-file`
-5. CLI arguments (`--var`)
+1. Template manifest values from `template.json`
+2. **User config** (your saved defaults) ← This page
+3. `--var-file`
+4. CLI arguments (`--var`)
+5. Interactive prompt answers
 
 
-Your saved defaults override module and template defaults, but they can still be overridden at generation time.
+Your saved defaults override template manifest values, but they can still be overridden at generation time.
 
 
 ## Managing Defaults
 ## Managing Defaults
 
 
@@ -104,7 +104,7 @@ Response:
 Removed default: container_timezone
 Removed default: container_timezone
 ```
 ```
 
 
-The variable will now use module/template defaults again.
+The variable will now use template manifest values again.
 
 
 ### Clear All Defaults
 ### Clear All Defaults
 
 
@@ -123,11 +123,13 @@ Cleared all defaults for compose
 
 
 ## Configuration Storage
 ## Configuration Storage
 
 
-Defaults are stored in:
+Defaults are usually stored in:
 ```
 ```
 ~/.config/boilerplates/config.yaml
 ~/.config/boilerplates/config.yaml
 ```
 ```
 
 
+If a local `./config.yaml` exists in your current working directory, the CLI uses that file instead.
+
 Example content:
 Example content:
 ```yaml
 ```yaml
 libraries:
 libraries:
@@ -153,13 +155,15 @@ defaults:
 You can manually edit the config file:
 You can manually edit the config file:
 
 
 ```bash
 ```bash
-# Edit configuration
+# Edit the global configuration example
 nano ~/.config/boilerplates/config.yaml
 nano ~/.config/boilerplates/config.yaml
 
 
 # Verify defaults
 # Verify defaults
 boilerplates compose defaults list
 boilerplates compose defaults list
 ```
 ```
 
 
+If you are using a local `./config.yaml`, edit that file instead.
+
 ## Common Use Cases
 ## Common Use Cases
 
 
 ### Timezone Configuration
 ### Timezone Configuration
@@ -251,6 +255,8 @@ Defaults don't transfer between modules—they're module-specific.
 
 
 ## Backup and Restore
 ## Backup and Restore
 
 
+The examples below use the global config path. If the CLI is using a local `./config.yaml`, substitute that path instead.
+
 ### Backup Configuration
 ### Backup Configuration
 
 
 Save your configuration:
 Save your configuration:
@@ -332,7 +338,7 @@ cat ~/.config/boilerplates/config.yaml
 If config file is corrupted:
 If config file is corrupted:
 
 
 ```bash
 ```bash
-# Validate YAML syntax
+# Validate global config YAML syntax
 python3 -c "import yaml; yaml.safe_load(open('~/.config/boilerplates/config.yaml'))"
 python3 -c "import yaml; yaml.safe_load(open('~/.config/boilerplates/config.yaml'))"
 
 
 # Or remove and recreate
 # Or remove and recreate
@@ -346,15 +352,16 @@ If wrong values appear:
 
 
 ```bash
 ```bash
 # Check precedence
 # Check precedence
-# 1. Module spec
-# 2. Template spec
-# 3. User defaults ← Check here
+# 1. Template manifest values
+# 2. User defaults ← Check here
+# 3. --var-file
 # 4. CLI --var
 # 4. CLI --var
+# 5. Interactive answers
 
 
 # Verify your defaults
 # Verify your defaults
 boilerplates compose defaults list
 boilerplates compose defaults list
 
 
-# Check template spec
+# Check the loaded template state
 boilerplates compose show <template-name>
 boilerplates compose show <template-name>
 ```
 ```
 
 

+ 26 - 8
.wiki/Core-Concepts-Libraries.md

@@ -40,14 +40,28 @@ libraries:
 
 
 ## Local Storage
 ## Local Storage
 
 
-Git libraries are stored under:
+Git libraries are stored under the active config directory's `libraries/` folder.
+
+Global-config default:
 
 
 ```text
 ```text
 ~/.config/boilerplates/libraries/
 ~/.config/boilerplates/libraries/
 ```
 ```
 
 
+If the CLI is using a local `./config.yaml`, the library checkout location becomes `./libraries/`.
+
 The configured `directory` is applied inside that checkout. For the official library the directory is `.`.
 The configured `directory` is applied inside that checkout. For the official library the directory is `.`.
 
 
+## Configuration Location
+
+By default, Boilerplates uses:
+
+```text
+~/.config/boilerplates/config.yaml
+```
+
+If a local `./config.yaml` exists in your current working directory, the CLI uses that file instead.
+
 ## Discovery Rules
 ## Discovery Rules
 
 
 Boilerplates discovers templates by module directory, for example:
 Boilerplates discovers templates by module directory, for example:
@@ -79,11 +93,21 @@ boilerplates repo update
 Add a custom Git library:
 Add a custom Git library:
 
 
 ```bash
 ```bash
-boilerplates repo add my-templates https://github.com/user/templates \
+boilerplates repo add my-templates \
+  --library-type git \
+  --url https://github.com/user/templates \
   --directory . \
   --directory . \
   --branch main
   --branch main
 ```
 ```
 
 
+Add a static library:
+
+```bash
+boilerplates repo add local \
+  --library-type static \
+  --path ~/my-templates
+```
+
 Remove a library:
 Remove a library:
 
 
 ```bash
 ```bash
@@ -127,12 +151,6 @@ Templates with `metadata.draft: true` are excluded from normal listings and look
 
 
 ## Config File
 ## Config File
 
 
-Library configuration lives in:
-
-```text
-~/.config/boilerplates/config.yaml
-```
-
 Example:
 Example:
 
 
 ```yaml
 ```yaml

+ 70 - 64
.wiki/Core-Concepts-Templates.md

@@ -1,10 +1,8 @@
 # Templates
 # Templates
 
 
-Templates are the core unit of Boilerplates. A template is a directory with a `template.json` manifest and a `files/` directory containing the files to render.
+Templates are the core unit of Boilerplates. A supported template is a directory with a `template.json` manifest and a `files/` directory containing every renderable output file.
 
 
-## Template Structure
-
-Every supported template looks like this:
+## Required Layout
 
 
 ```text
 ```text
 my-template/
 my-template/
@@ -16,11 +14,13 @@ my-template/
         └── app.yaml
         └── app.yaml
 ```
 ```
 
 
-Only `template.json` is a supported manifest format. Legacy `template.yaml` and `template.yml` manifests are not supported.
+Rules:
+- `template.json` is the only supported manifest format
+- all rendered content must live under `files/`
+- legacy `template.yaml`, `template.yml`, and top-level `.j2` layouts are incompatible with the current runtime
 
 
-## Manifest Shape
+## Top-Level Manifest Shape
 
 
-Top-level fields:
 - `slug`
 - `slug`
 - `kind`
 - `kind`
 - `metadata`
 - `metadata`
@@ -35,6 +35,8 @@ Example:
   "metadata": {
   "metadata": {
     "name": "My Template",
     "name": "My Template",
     "description": "Short human description",
     "description": "Short human description",
+    "author": "Your Name",
+    "date": "2026-04-22",
     "tags": ["infra", "dev"],
     "tags": ["infra", "dev"],
     "icon": {
     "icon": {
       "provider": "mdi",
       "provider": "mdi",
@@ -48,7 +50,7 @@ Example:
       "source_dep_version": "1.1.0",
       "source_dep_version": "1.1.0",
       "source_dep_digest": "sha256:abc123def456",
       "source_dep_digest": "sha256:abc123def456",
       "upstream_ref": "release-2026-04-22",
       "upstream_ref": "release-2026-04-22",
-      "notes": "Tracks upstream container release used by this template snapshot"
+      "notes": "Tracks the tested upstream dependency snapshot"
     }
     }
   },
   },
   "variables": [
   "variables": [
@@ -68,24 +70,38 @@ Example:
 }
 }
 ```
 ```
 
 
+## `slug` and Template IDs
+
+`slug` is the canonical template ID exposed by the CLI.
+
+Behavior:
+- if `slug` is present, it wins over the directory name
+- if `slug` ends with `-<kind>`, that suffix is normalized away for CLI use
+- if `slug` is missing, the directory name is used
+
+Example:
+- directory: `portainer/`
+- `kind`: `compose`
+- `slug`: `portainer-compose`
+- CLI ID: `portainer`
+
 ## Metadata
 ## Metadata
 
 
 Common metadata fields:
 Common metadata fields:
 - `name`
 - `name`
 - `description`
 - `description`
+- `author`
+- `date`
 - `tags`
 - `tags`
 - `icon`
 - `icon`
 - `draft`
 - `draft`
-- `author`
-- `date`
-- `guide`
 - `version`
 - `version`
 
 
 ### Version Metadata
 ### Version Metadata
 
 
-`metadata.version` is optional. If present, it must be an object.
+`metadata.version` is optional, but when present it must be an object.
 
 
-Supported version fields:
+Supported fields:
 - `name`
 - `name`
 - `source_dep_name`
 - `source_dep_name`
 - `source_dep_version`
 - `source_dep_version`
@@ -93,57 +109,34 @@ Supported version fields:
 - `upstream_ref`
 - `upstream_ref`
 - `notes`
 - `notes`
 
 
-Important rules:
-- the whole `version` object may be omitted
-- any individual `version` field may be omitted
-- the CLI uses `metadata.version.name` as the visible version label in list/show output
+Important behavior:
+- `metadata.version.name` is the user-facing version label shown in list/show output
+- the rest of the version object is for upstream dependency tracking
+- the full object may be omitted
+- individual fields inside the object may also be omitted
 
 
-This means these are all valid:
+## Variable Declarations Are Mandatory
 
 
-```json
-{
-  "metadata": {
-    "name": "My Template"
-  }
-}
-```
+Any variable used in files under `files/` must be declared in `template.json`.
 
 
-```json
-{
-  "metadata": {
-    "name": "My Template",
-    "version": {
-      "name": "v1.1"
-    }
-  }
-}
-```
+If a file references an undeclared variable, the template fails validation and load/render operations surface a template error.
 
 
-```json
-{
-  "metadata": {
-    "name": "My Template",
-    "version": {
-      "source_dep_name": "ghcr.io/example/my-image",
-      "source_dep_version": "1.1.0"
-    }
-  }
-}
-```
+Use the [Variables](Core-Concepts-Variables) page for the manifest structure.
 
 
 ## Files and Rendering
 ## Files and Rendering
 
 
-All files inside `files/` are part of the template output.
+Everything under `files/` is part of the output tree.
 
 
-Rendering rules:
-- Boilerplates renders every file under `files/`
-- files without template expressions pass through unchanged
-- output paths currently match the relative file paths under `files/`
-- template discovery ignores anything without `template.json`
+Rendering behavior:
+- Boilerplates walks every file under `files/`
+- files are rendered with the custom delimiter set
+- files without template expressions still pass through the render pipeline
+- output paths currently mirror the relative paths inside `files/`
+- rendered output is sanitized to normalize blank lines and trailing whitespace
 
 
 ## Delimiters
 ## Delimiters
 
 
-Templates use custom delimiters, not default Jinja syntax:
+Templates use custom delimiters rather than default Jinja syntax:
 
 
 - variables: `<< value >>`
 - variables: `<< value >>`
 - blocks: `<% if condition %>`
 - blocks: `<% if condition %>`
@@ -165,17 +158,21 @@ Legacy `{{ }}`, `{% %}`, and `{# #}` delimiters are rejected.
 
 
 ## Includes and Imports
 ## Includes and Imports
 
 
-Includes and imports are resolved relative to the template's `files/` directory.
-
-Example:
+Includes and imports resolve relative to the template's `files/` directory.
 
 
 ```jinja
 ```jinja
 <% include 'partials/header.yaml' %>
 <% include 'partials/header.yaml' %>
 ```
 ```
 
 
-## Template Discovery
+## Discovery Rules
+
+Templates are discovered from configured libraries.
 
 
-Templates are discovered from configured libraries. A directory is considered a template only when it contains `template.json`.
+A directory is considered a valid template only when:
+- `template.json` exists
+- `files/` exists
+
+In practice, Boilerplates discovers templates by module directory path such as `compose/<template>/` or `terraform/<template>/`.
 
 
 Useful commands:
 Useful commands:
 
 
@@ -186,11 +183,11 @@ boilerplates compose show nginx
 ```
 ```
 
 
 Draft templates:
 Draft templates:
-- set `metadata.draft` to `true` to hide a template from normal discovery
+- set `metadata.draft` to `true` to hide the template from normal discovery
 
 
-## Generation
+## Generation Workflow
 
 
-Typical generation flow:
+Typical workflow:
 
 
 ```bash
 ```bash
 boilerplates compose show nginx
 boilerplates compose show nginx
@@ -204,6 +201,7 @@ Useful flags:
 - `--var` for direct CLI overrides
 - `--var` for direct CLI overrides
 - `--no-interactive` for non-interactive generation
 - `--no-interactive` for non-interactive generation
 - `--dry-run` to preview generation without writing files
 - `--dry-run` to preview generation without writing files
+- `--show-files` to print rendered files during dry-run
 
 
 ## Validation
 ## Validation
 
 
@@ -219,11 +217,19 @@ Validate all templates in a module:
 boilerplates compose validate
 boilerplates compose validate
 ```
 ```
 
 
+Validation covers:
+- manifest structure
+- variable declaration coverage
+- delimiter compatibility
+- renderability
+- optional semantic validators for module-specific output
+
 ## Best Practices
 ## Best Practices
 
 
 - keep manifests in `template.json`
 - keep manifests in `template.json`
-- keep all generated content under `files/`
+- keep every generated file under `files/`
+- declare every variable that appears in rendered content
 - use the custom delimiter set consistently
 - use the custom delimiter set consistently
-- hardcode tested upstream application versions in rendered files unless a variable is truly required
+- hardcode tested upstream application versions in rendered files unless a variable is actually needed
 - use `metadata.version.name` for the user-facing label
 - use `metadata.version.name` for the user-facing label
-- use the remaining `metadata.version` fields to track upstream dependency context when useful
+- use the remaining `metadata.version` fields for upstream snapshot context when useful

+ 62 - 42
.wiki/Core-Concepts-Variables.md

@@ -1,10 +1,10 @@
 # Variables
 # Variables
 
 
-Variables define the configurable inputs available to a template. In the current runtime they live in `template.json` under `variables`, grouped into sections with `items`.
+Variables are defined per template in `template.json` under the top-level `variables` array. There is no separate schema reference layer anymore, so the manifest is the source of truth.
 
 
-## Variable Layout
+## Runtime Shape
 
 
-Example:
+Each entry in `variables` is a group. Each group contains `items`.
 
 
 ```json
 ```json
 {
 {
@@ -24,7 +24,7 @@ Example:
           "name": "environment",
           "name": "environment",
           "type": "enum",
           "type": "enum",
           "title": "Environment",
           "title": "Environment",
-          "default": "prod",
+          "value": "stage",
           "config": {
           "config": {
             "options": ["prod", "stage", "dev"]
             "options": ["prod", "stage", "dev"]
           }
           }
@@ -35,15 +35,16 @@ Example:
 }
 }
 ```
 ```
 
 
-Each group requires:
+Group fields:
 - `name`
 - `name`
 - `title`
 - `title`
+- `description`
+- `toggle`
+- `needs`
 - `items`
 - `items`
 
 
-Each item requires:
+Item fields:
 - `name`
 - `name`
-
-Common item fields:
 - `type`
 - `type`
 - `title`
 - `title`
 - `description`
 - `description`
@@ -54,11 +55,13 @@ Common item fields:
 - `extra`
 - `extra`
 - `config`
 - `config`
 
 
-`title` is used as the prompt label. If `description` is omitted, the runtime falls back to `title`.
+Runtime mapping notes:
+- `title` is used as the prompt label
+- `description` is used as the display/help text
+- if `description` is omitted, the runtime falls back to `title`
 
 
-## Variable Types
+## Supported Types
 
 
-Supported types:
 - `str`
 - `str`
 - `int`
 - `int`
 - `float`
 - `float`
@@ -69,25 +72,37 @@ Supported types:
 - `hostname`
 - `hostname`
 - `secret`
 - `secret`
 
 
-### Secret Variables
+Type notes:
+- `bool` defaults to `false` when omitted
+- other types default to empty unless a `default` or `value` is provided
+- `secret` values are masked in prompts and display output
+
+## `default` vs `value`
+
+Use `default` for the normal manifest default.
 
 
-Use `type: "secret"` for values that should be masked in prompts and output.
+Use `value` when the template should pin the runtime value directly. If both are present, `value` wins.
 
 
 Example:
 Example:
 
 
 ```json
 ```json
 {
 {
-  "name": "api_token",
-  "type": "secret",
-  "title": "API token"
+  "name": "environment",
+  "type": "enum",
+  "title": "Environment",
+  "default": "prod",
+  "value": "stage",
+  "config": {
+    "options": ["prod", "stage", "dev"]
+  }
 }
 }
 ```
 ```
 
 
 ## Config Object
 ## Config Object
 
 
-Extra behavior is defined under `config`.
+Additional behavior is configured through `config`.
 
 
-Supported config fields:
+Supported fields:
 - `placeholder`
 - `placeholder`
 - `textarea`
 - `textarea`
 - `unit`
 - `unit`
@@ -143,7 +158,7 @@ Supported config fields:
 }
 }
 ```
 ```
 
 
-### Autogenerated Secret Values
+### Autogenerated Secrets
 
 
 `config.autogenerated` is supported only for `secret` variables.
 `config.autogenerated` is supported only for `secret` variables.
 
 
@@ -180,22 +195,12 @@ Base64 example:
 }
 }
 ```
 ```
 
 
-## Default Values and Overrides
-
-Value precedence is:
-
-1. module runtime defaults
-2. template defaults from `template.json`
-3. saved user defaults in `config.yaml`
-4. `--var-file`
-5. `--var`
+Rules:
+- autogenerated secrets cannot define `default`
+- base64 generation uses `bytes`, not `length`
+- `autogenerated` on non-secret variables is rejected
 
 
-Useful notes:
-- `default` defines the standard value
-- `value` lets a template pin a value more directly in the manifest
-- autogenerated secrets cannot also define a default
-
-## Dependencies
+## Dependencies with `needs`
 
 
 Both groups and items support `needs`.
 Both groups and items support `needs`.
 
 
@@ -207,14 +212,18 @@ Examples:
 
 
 Semantics:
 Semantics:
 - semicolon means AND
 - semicolon means AND
-- comma means OR within the same comparison
-- `!=` means the current value must not match
+- comma means OR within a single comparison
+- `=` means the value must match
+- `!=` means the value must not match
 
 
-## Toggles
+Behavior notes:
+- group `needs` controls whether the group is considered available
+- item `needs` controls whether the item is shown and validated
+- disabled boolean items are reset to `false` unless they were explicitly set via CLI
 
 
-Groups may define `toggle`, but the toggle variable must exist as a boolean item in that same group.
+## Group Toggles
 
 
-Example:
+Groups may define `toggle`, but the referenced toggle variable must exist as a boolean item in that same group.
 
 
 ```json
 ```json
 {
 {
@@ -237,14 +246,25 @@ Example:
 }
 }
 ```
 ```
 
 
-If a referenced toggle variable is not present, the runtime drops the toggle behavior.
+If the toggle variable is missing, the runtime ignores the toggle.
+
+## Override Order
+
+For a loaded template, values are applied in this order:
+
+1. `default` / `value` from `template.json`
+2. saved defaults from the active config file
+3. `--var-file`
+4. `--var`
+5. interactive prompt answers
 
 
 ## Inspecting Variables
 ## Inspecting Variables
 
 
-The fastest way to see the actual variables for a template is still:
+Use `show` against the template you care about:
 
 
 ```bash
 ```bash
 boilerplates compose show nginx
 boilerplates compose show nginx
+boilerplates terraform show cloudflare-dns-record
 ```
 ```
 
 
-That reflects the template as the runtime sees it, including defaults, dependencies, and optional sections.
+That view reflects the actual runtime state for that template, including defaults, dependencies, toggle state, and file structure.

+ 0 - 4
.wiki/Home.md

@@ -20,10 +20,6 @@ The current runtime supports:
 - [Libraries](Core-Concepts-Libraries) - official and custom libraries, discovery, priority, and config
 - [Libraries](Core-Concepts-Libraries) - official and custom libraries, discovery, priority, and config
 - [Defaults](Core-Concepts-Defaults) - saved default values and precedence
 - [Defaults](Core-Concepts-Defaults) - saved default values and precedence
 
 
-## Variable Discovery
-
-- [Variables](Variables) - how to inspect the variables a template actually exposes
-
 ## Additional Docs
 ## Additional Docs
 
 
 - [Repository README](https://github.com/ChristianLempa/boilerplates/blob/main/README.md)
 - [Repository README](https://github.com/ChristianLempa/boilerplates/blob/main/README.md)

+ 6 - 1
.wiki/Installation.md

@@ -283,11 +283,16 @@ Sync the default template library:
 boilerplates repo update
 boilerplates repo update
 ```
 ```
 
 
-This downloads all available templates to:
+This syncs the configured Git library checkouts under the active config directory's `libraries/` folder.
+
+Global-config default:
+
 ```
 ```
 ~/.config/boilerplates/libraries/
 ~/.config/boilerplates/libraries/
 ```
 ```
 
 
+If the CLI is using a local `./config.yaml`, the sync location becomes `./libraries/`.
+
 ### Shell Completion (Optional)
 ### Shell Completion (Optional)
 
 
 Enable tab completion for your shell:
 Enable tab completion for your shell:

+ 0 - 35
.wiki/Variables.md

@@ -1,35 +0,0 @@
-# Variables Reference
-
-Boilerplates no longer publishes static schema-version variable reference pages.
-
-The current runtime is template-driven:
-- modules provide baseline variable behavior
-- each template can override defaults and add template-specific variables
-- the exact variable set depends on the template you are inspecting
-
-## How to Inspect Variables
-
-Use the CLI against the template itself:
-
-```bash
-boilerplates compose show nginx
-boilerplates terraform show cloudflare-dns-record
-boilerplates ansible show ubuntu-vm-core
-```
-
-This shows:
-- template metadata
-- rendered version label from `metadata.version.name` when present
-- the template file tree
-- the actual variable groups and items exposed by that template
-
-## Recommended Workflow
-
-1. List templates for a module.
-2. Show the template you want to use.
-3. Review defaults, dependencies, and optional sections.
-4. Generate with `--output`, `--var-file`, and `--var` as needed.
-
-## Why the Old Pages Were Removed
-
-The older wiki pages were generated from schema-version snapshots and no longer matched the current `template.json` runtime. They were removed to avoid documenting variables that may not exist, may have changed shape, or may be template-specific.

+ 0 - 3
.wiki/_Sidebar.md

@@ -11,6 +11,3 @@
 - [Variables](Core-Concepts-Variables)
 - [Variables](Core-Concepts-Variables)
 - [Libraries](Core-Concepts-Libraries)
 - [Libraries](Core-Concepts-Libraries)
 - [Defaults](Core-Concepts-Defaults)
 - [Defaults](Core-Concepts-Defaults)
-
-### Reference
-- [Variables](Variables)

+ 4 - 6
README.md

@@ -6,8 +6,7 @@ Create reusable templates and turn them into configurable workloads for homelabs
 
 
 Create reusable templates for infrastructure expertise like Docker, Kubernetes, Terraform, Ansible, Python, and more. Use the built-in *Jinja2-like* templating syntax with `<< >>` variables, `<% %>` blocks, and `<# #>` comments to keep configuration modular and conditional. Sync with Git in both directions or manage everything locally. Render templates, configure variables through a guided wizard, and wire up secrets. Copy them to remote servers and environments or any local directory.
 Create reusable templates for infrastructure expertise like Docker, Kubernetes, Terraform, Ansible, Python, and more. Use the built-in *Jinja2-like* templating syntax with `<< >>` variables, `<% %>` blocks, and `<# #>` comments to keep configuration modular and conditional. Sync with Git in both directions or manage everything locally. Render templates, configure variables through a guided wizard, and wire up secrets. Copy them to remote servers and environments or any local directory.
 
 
-*Don't want to start from scratch?*
-> Explore 100+ template presets for homelabs and self-hosted infrastructure: https://github.com/ChristianLempa/boilerplates-library
+✨ Explore 100+ template presets for homelabs and self-hosted infrastructure: https://github.com/ChristianLempa/boilerplates-library
 
 
 ## Boilerplates CLI
 ## Boilerplates CLI
 
 
@@ -15,10 +14,9 @@ The Boilerplates CLI is the main interface for working with template libraries l
 
 
 It combines template-defined variables and defaults, guided interactive prompts, CLI variable overrides, and git-backed template libraries into one workflow. In practice, that means you can keep reusable boilerplates in a repository and turn them into concrete, environment-specific configurations with a single command.
 It combines template-defined variables and defaults, guided interactive prompts, CLI variable overrides, and git-backed template libraries into one workflow. In practice, that means you can keep reusable boilerplates in a repository and turn them into concrete, environment-specific configurations with a single command.
 
 
-> **WARNING**
-> Boilerplates `0.2.0` introduced the new template format. Legacy `template.yaml` / `template.yml` manifests and `.j2` template files are no longer supported.
->
-> New templates must use `template.json`, keep renderable content under `files/`, and use the custom *Jinja2*-like delimiters `<< >>`, `<% %>`, and `<# #>` instead of default *Jinja2* syntax.
+⚠️ Boilerplates `0.2.0` introduced the new template format. Legacy `template.yaml` / `template.yml` manifests and `.j2` template files are no longer supported.
+
+ℹ️ New templates must use `template.json`, keep renderable content under `files/`, and use the custom *Jinja2*-like delimiters `<< >>`, `<% %>`, and `<# #>` instead of default *Jinja2* syntax.
 
 
 ### Installation
 ### Installation
 
 

+ 0 - 2
cli/core/template/template.py

@@ -203,7 +203,6 @@ class TemplateMetadata:
     library: str = "unknown"
     library: str = "unknown"
     library_type: str = "git"
     library_type: str = "git"
     draft: bool = False
     draft: bool = False
-    guide: str = ""
     icon: dict[str, Any] = field(default_factory=dict)
     icon: dict[str, Any] = field(default_factory=dict)
 
 
     def __init__(
     def __init__(
@@ -226,7 +225,6 @@ class TemplateMetadata:
         self.library = library_name or "unknown"
         self.library = library_name or "unknown"
         self.library_type = library_type
         self.library_type = library_type
         self.draft = bool(metadata.get("draft", False))
         self.draft = bool(metadata.get("draft", False))
-        self.guide = str(metadata.get("guide", "")).rstrip("\n")
         self.icon = metadata.get("icon", {}) if isinstance(metadata.get("icon"), dict) else {}
         self.icon = metadata.get("icon", {}) if isinstance(metadata.get("icon"), dict) else {}