#!/usr/bin/env nu # Blog Post Creator / Editor # # Orchestrates the typedialog-web nickel-roundtrip workflow for creating # or editing post metadata (.ncl files). # # Modes: # new — load form defaults → typedialog-web → write to target .ncl # edit -- load existing .ncl → typedialog-web → overwrite in place # agent — write prescribed values directly to .ncl (no browser, no TUI) # # Usage: # nu new-post.nu --type blog --lang en --category devops --slug my-post # nu new-post.nu --edit site/content/blog/en/devops/my-post.ncl # nu new-post.nu --type blog --lang en --slug my-post --agent values.json def main [ --type: string = "blog" # Content type --lang: string = "en" # Language directory --category: string = "" # Category slug (matches parent dir) --slug: string = "" # Post slug (becomes filename stem) --edit: string = "" # Path to existing .ncl to edit (skips new) --agent: string = "" # JSON file with prescribed field values (agent mode) --form: string = "" # Override form template path --content-dir: string = "" # Override SITE_CONTENT_PATH --dry-run # Print resolved paths without opening typedialog --verbose ] { let content_root = resolve_env "SITE_CONTENT_PATH" $content_dir "site/content" let form_base = if $form != "" { $form } else { $"rustelo/forms/content/($type)/post.ncl" } if not ($form_base | path exists) { error make { msg: $"Form template not found: ($form_base)" } } # Determine mode let mode = if $edit != "" { "edit" } else if $agent != "" { "agent" } else { "new" } # Resolve working .ncl path let ncl_path = if $mode == "edit" { $edit } else { if $slug == "" { error make { msg: "--slug is required for new posts" } } if $category != "" { $"($content_root)/($type)/($lang)/($category)/($slug).ncl" } else { $"($content_root)/($type)/($lang)/($slug).ncl" } } if $verbose or $dry_run { print $" mode : ($mode)" print $" form : ($form_base)" print $" output : ($ncl_path)" } if $dry_run { return } # Prepare working copy for typedialog let work_ncl = if $mode == "edit" { $ncl_path } else { # Copy form template to a temp file — typedialog edits it in place let tmp = $"/tmp/new-post-(random chars).ncl" cp ($form_base | path expand) $tmp $tmp } match $mode { "new" | "edit" => { print $"(ansi cyan)Opening typedialog form(ansi reset) — ($work_ncl)" let result = do { typedialog-web nickel-roundtrip ($work_ncl | path expand) } | complete if $result.exit_code != 0 { error make { msg: $"typedialog failed: ($result.stderr)" } } if $mode == "new" { mkdir ($ncl_path | path dirname) cp $work_ncl $ncl_path rm $work_ncl print $"(ansi green)Created(ansi reset) → ($ncl_path)" } else { print $"(ansi green)Updated(ansi reset) → ($ncl_path)" } } "agent" => { # Agent mode: merge prescribed JSON values into the form template if not ($agent | path exists) { error make { msg: $"Agent values file not found: ($agent)" } } let values = open $agent let base = open ($form_base | path expand) # Render filled .ncl by patching fields in the base form mut ncl_lines = $base | lines for entry in ($values | items { |k, v| { key: $k, val: $v } }) { let pat = $" ($entry.key) =" let replacement = $" ($entry.key) = ($entry.val | to nuon)," $ncl_lines = $ncl_lines | each { |l| if ($l | str starts-with $pat) { $replacement } else { $l } } } mkdir ($ncl_path | path dirname) $ncl_lines | str join "\n" | save --force $ncl_path print $"(ansi green)Agent wrote(ansi reset) → ($ncl_path)" } _ => { error make { msg: $"Unknown mode: ($mode)" } } } # Validate the result print " validating .ncl..." let validate = do { nickel-export ($ncl_path | path expand) } | complete if $validate.exit_code != 0 { print $"(ansi yellow)WARNING(ansi reset) .ncl validation failed — check fields before publishing" print $validate.stderr } else { print $" (ansi green)✓(ansi reset) valid" } } def resolve_env [var_name: string, cli_arg: string, fallback: string] { if $cli_arg != "" { return $cli_arg } try { $env | get $var_name } catch { $fallback } }