{"id":9844,"date":"2026-09-03T12:05:55","date_gmt":"2026-09-03T17:05:55","guid":{"rendered":"https:\/\/krisbunda.com\/blog\/?p=9844"},"modified":"2026-09-03T12:17:06","modified_gmt":"2026-09-03T17:17:06","slug":"auto-update-your-readmes-ascii-directory-tree-with-github-actions","status":"publish","type":"post","link":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/","title":{"rendered":"Auto-Update Your README&#8217;s ASCII Directory Tree with GitHub Actions"},"content":{"rendered":"\n<h2 class=\"has--font-size wp-block-heading\">How many GitHub repos have outdated directory tree art? <\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I&#8217;ve seen lots of outdated READMEs. Projects with ASCII art visualizations reflecting a bygone era of an app&#8217;s file system. Perhaps developers are sentimental types, always longing for simpler times? But if information is important enough to feature on the main README, shouldn&#8217;t it be accurate? <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">The future is now!<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">We basically live in The Future, sans flying cars (turns out: we can barely handle ground driving). We can automate tasks now. And make every commit push or PR merge regenerate a repo&#8217;s directory tree art. One way to do it is with a GitHub Actions workflow, and the way I do it requires just 2 little files. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What is a GitHub Actions workflow? <\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">It&#8217;s a YAML file. For this example, it&#8217;s a simple file (<code>update-tree.yml<\/code>) that calls a shell script file (<code>generate-tree.ps1<\/code>), which generates and updates the directory tree in the repo&#8217;s <code>README.md<\/code> file. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">To create a workflow: <\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\">Either write your YAML file and place it in your repo&#8217;s <code>.github\/workflows<\/code> folder, <\/li>\n\n\n\n<li class=\"\">Or push the &#8220;New workflow&#8221; button on the Actions tab and write your YAML file in a GitHub text editor, which will then be saved to the <code>.github\/workflows<\/code> folder. <\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"1002\" height=\"859\" loading=\"lazy\" src=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_9_GitHub_Actions_Workflow_file_create.webp\" alt=\"GitHub Actions workflow YAML file creation\" class=\"wp-image-9849\" srcset=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_9_GitHub_Actions_Workflow_file_create.webp 1002w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_9_GitHub_Actions_Workflow_file_create-600x514.webp 600w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_9_GitHub_Actions_Workflow_file_create-150x129.webp 150w\" sizes=\"auto, (max-width: 1002px) 100vw, 1002px\" \/><figcaption class=\"wp-element-caption\">GitHub Actions workflow YAML file creation<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">YAML file<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This is a simple workflow that permits writing to the README and calling the shell script to create and update the dir tree. Note the triggers section that sets conditions for when the job is to run: <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\">on commit from a non-bot user, <\/li>\n\n\n\n<li class=\"\">on Pull Request merge, <\/li>\n\n\n\n<li class=\"\">or manually when the &#8216;Run workflow&#8217; btn is clicked in GitHub.<\/li>\n<\/ul>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers cbp-highlight-hover\" data-code-block-pro-font-family=\"Code-Pro-Fira-Code\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Fira-Code,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#adbac7;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(139, 186, 234, 0.2);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2d333d;color:#9eadbd\">YAML<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#adbac7;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly># GitHub Actions Workflow \n# - Output: Generate &amp; auto-update an ASCII repo directory tree in the README file.\n# - Place in path: .github\/workflows\/update-tree.yml\n\n# Generate ASCII map locally with this command, (run from repo root):\n# pwsh .\\tools\\generate-tree.ps1 -Depth 2\n\nname: Update Directory Tree\n\non:\n  push:\n    branches: &#91;main, master&#93;\n  pull_request:\n    types: &#91;closed&#93;\n  workflow_dispatch:\n\n# permission to update README\npermissions:\n  contents: write\n\n# Conditions that trigger this workflow:\n# push: runs for any push to main\/master by a non-bot actor.\n# pull_request closed: runs only when a PR is closed and merged.\n# workflow_dispatch: keeps manual runs available from GitHub Actions UI (btn push).\njobs:\n  update-tree:\n    if: >\n      (github.event_name == 'push' &amp;&amp; github.actor != 'github-actions&#91;bot&#93;') ||\n      (github.event_name == 'pull_request' &amp;&amp; github.event.pull_request.merged == true) ||\n      github.event_name == 'workflow_dispatch'\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions\/checkout@v4\n        with:\n          persist-credentials: true\n\n      - name: Run tree generator\n        run: pwsh -NoProfile -Command \".\\tools\\generate-tree.ps1 -Depth 2\"\n\n      - name: Commit updated README\n        run: |\n          git config user.name \"github-actions&#91;bot&#93;\"\n          git config user.email \"41898282+github-actions&#91;bot&#93;@users.noreply.github.com\"\n          git add README.md\n          git commit -m \"Auto-update directory tree\" || echo \"No changes to commit\"\n          git push\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6\"><\/path><\/svg><\/span><pre class=\"shiki github-dark-dimmed\" style=\"background-color: #22272e\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #768390\"># GitHub Actions Workflow <\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\"># - Output: Generate &amp; auto-update an ASCII repo directory tree in the README file.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\"># - Place in path: .github\/workflows\/update-tree.yml<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #768390\"># Generate ASCII map locally with this command, (run from repo root):<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\"># pwsh .\\tools\\generate-tree.ps1 -Depth 2<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #8DDB8C\">name<\/span><span style=\"color: #ADBAC7\">: <\/span><span style=\"color: #96D0FF\">Update Directory Tree<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6CB6FF\">on<\/span><span style=\"color: #ADBAC7\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">  <\/span><span style=\"color: #8DDB8C\">push<\/span><span style=\"color: #ADBAC7\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #8DDB8C\">branches<\/span><span style=\"color: #ADBAC7\">: &#91;<\/span><span style=\"color: #96D0FF\">main<\/span><span style=\"color: #ADBAC7\">, <\/span><span style=\"color: #96D0FF\">master<\/span><span style=\"color: #ADBAC7\">&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">  <\/span><span style=\"color: #8DDB8C\">pull_request<\/span><span style=\"color: #ADBAC7\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #8DDB8C\">types<\/span><span style=\"color: #ADBAC7\">: &#91;<\/span><span style=\"color: #96D0FF\">closed<\/span><span style=\"color: #ADBAC7\">&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">  <\/span><span style=\"color: #8DDB8C\">workflow_dispatch<\/span><span style=\"color: #ADBAC7\">:<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #768390\"># permission to update README<\/span><\/span>\n<span class=\"line\"><span style=\"color: #8DDB8C\">permissions<\/span><span style=\"color: #ADBAC7\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">  <\/span><span style=\"color: #8DDB8C\">contents<\/span><span style=\"color: #ADBAC7\">: <\/span><span style=\"color: #96D0FF\">write<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #768390\"># Conditions that trigger this workflow:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\"># push: runs for any push to main\/master by a non-bot actor.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\"># pull_request closed: runs only when a PR is closed and merged.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #768390\"># workflow_dispatch: keeps manual runs available from GitHub Actions UI (btn push).<\/span><\/span>\n<span class=\"line\"><span style=\"color: #8DDB8C\">jobs<\/span><span style=\"color: #ADBAC7\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">  <\/span><span style=\"color: #8DDB8C\">update-tree<\/span><span style=\"color: #ADBAC7\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #8DDB8C\">if<\/span><span style=\"color: #ADBAC7\">: <\/span><span style=\"color: #F47067\">&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #96D0FF\">      (github.event_name == &#39;push&#39; &amp;&amp; github.actor != &#39;github-actions&#91;bot&#93;&#39;) ||<\/span><\/span>\n<span class=\"line\"><span style=\"color: #96D0FF\">      (github.event_name == &#39;pull_request&#39; &amp;&amp; github.event.pull_request.merged == true) ||<\/span><\/span>\n<span class=\"line\"><span style=\"color: #96D0FF\">      github.event_name == &#39;workflow_dispatch&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #8DDB8C\">runs-on<\/span><span style=\"color: #ADBAC7\">: <\/span><span style=\"color: #96D0FF\">ubuntu-latest<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">    <\/span><span style=\"color: #8DDB8C\">steps<\/span><span style=\"color: #ADBAC7\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">      - <\/span><span style=\"color: #8DDB8C\">name<\/span><span style=\"color: #ADBAC7\">: <\/span><span style=\"color: #96D0FF\">Checkout<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #8DDB8C\">uses<\/span><span style=\"color: #ADBAC7\">: <\/span><span style=\"color: #96D0FF\">actions\/checkout@v4<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #8DDB8C\">with<\/span><span style=\"color: #ADBAC7\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">          <\/span><span style=\"color: #8DDB8C\">persist-credentials<\/span><span style=\"color: #ADBAC7\">: <\/span><span style=\"color: #6CB6FF\">true<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">      - <\/span><span style=\"color: #8DDB8C\">name<\/span><span style=\"color: #ADBAC7\">: <\/span><span style=\"color: #96D0FF\">Run tree generator<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #8DDB8C\">run<\/span><span style=\"color: #ADBAC7\">: <\/span><span style=\"color: #96D0FF\">pwsh -NoProfile -Command &quot;.\\tools\\generate-tree.ps1 -Depth 2&quot;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">      - <\/span><span style=\"color: #8DDB8C\">name<\/span><span style=\"color: #ADBAC7\">: <\/span><span style=\"color: #96D0FF\">Commit updated README<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ADBAC7\">        <\/span><span style=\"color: #8DDB8C\">run<\/span><span style=\"color: #ADBAC7\">: <\/span><span style=\"color: #F47067\">|<\/span><\/span>\n<span class=\"line\"><span style=\"color: #96D0FF\">          git config user.name &quot;github-actions&#91;bot&#93;&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #96D0FF\">          git config user.email &quot;41898282+github-actions&#91;bot&#93;@users.noreply.github.com&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #96D0FF\">          git add README.md<\/span><\/span>\n<span class=\"line\"><span style=\"color: #96D0FF\">          git commit -m &quot;Auto-update directory tree&quot; || echo &quot;No changes to commit&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #96D0FF\">          git push<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">PowerShell script<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This file first parses the README. If there are no FOOTER or TREE markers (example below), it appends them to the end of README, along with some &#8216;about&#8217; content. These are required locators, indicating where to insert directory tree info. The ASCII art is then generated into a file named <code>tree.tmp<\/code>. If the file fails to generate, the script removes the FOOTER section entirely to leave a clean README.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!--FOOTER-START--&gt;\n&lt;!-- TREE-START --&gt;\n&lt;!-- TREE-END --&gt;\n&lt;!--FOOTER-END--&gt;<\/code><\/pre>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers cbp-highlight-hover\" data-code-block-pro-font-family=\"Code-Pro-Fira-Code\" style=\"font-size:clamp(14px, .875rem, 21px);font-family:Code-Pro-Fira-Code,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(3 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:clamp(20px, 1.25rem, 30px);--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7\">PowerShell<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#D4D4D4;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>&lt;#\n.SYNOPSIS\n  Generate an ASCII directory tree and inject it into README.md inside a FOOTER block.\n\n.DESCRIPTION\n  - Generates a compact ASCII tree (\u251c\u2500, \u2514\u2500, \u2502) to a temporary file.\n  - Replaces the marker region between &lt;!-- TREE-START --> and &lt;!-- TREE-END --> with a fenced code block (no markers left).\n  - If README lacks the FOOTER block, the script appends a canonical FOOTER (with markers) first.\n  - On success: replace the entire FOOTER block with the Project Structure header + fenced tree (no markers left).\n  - On failure: remove the entire FOOTER block (including markers) when RemoveOnFail is true.\n\n.EXAMPLE\n  - Place in path: tools\/generate-tree.ps1\n  - Temp file: The generated tree is written to a temporary file before being injected into the README.\n  - Excludes: .git, bin, obj, node_modules, tmp, publish, .vs\n  - Usage: pwsh .\\tools\\generate-tree.ps1 -Depth 2\n#>\n\nparam(\n  &#91;string&#93;$Path = '.',\n  &#91;int&#93;$Depth = 2,\n  [string[]]$Exclude = @('.git','bin','obj','node_modules','tmp','publish','.vs'),\n  &#91;string&#93;$TmpFile = 'tree.tmp',\n  &#91;string&#93;$ReadmeFile = 'README.md',\n  &#91;switch&#93;$RemoveOnFail\n)\n\n# default RemoveOnFail to true unless explicitly provided\nif (-not $PSBoundParameters.ContainsKey('RemoveOnFail')) { $RemoveOnFail = $true }\n\nfunction Write-Tree {\n  param($Path, $Prefix = '', $Level = 0, $OutFile)\n  if ($Level -gt $Depth) { return }\n\n  $items = Get-ChildItem -LiteralPath $Path -Force -ErrorAction SilentlyContinue |\n    Where-Object { -not ($Exclude -contains $_.Name) } |\n    Sort-Object -Property @{ Expression = { -not $_.PSIsContainer } }, @{ Expression = { $_.Name } }\n\n  for ($i = 0; $i -lt $items.Count; $i++) {\n    $item = $items&#91;$i&#93;\n    $isLast = ($i -eq $items.Count - 1)\n    $branch = if ($isLast) { '\u2514\u2500 ' } else { '\u251c\u2500 ' }\n    $line = $Prefix + $branch + $item.Name\n    $line | Out-File -FilePath $OutFile -Append -Encoding utf8\n    if ($item.PSIsContainer) {\n      $newPrefix = if ($isLast) { $Prefix + '   ' } else { $Prefix + '\u2502  ' }\n      Write-Tree -Path $item.FullName -Prefix $newPrefix -Level ($Level + 1) -OutFile $OutFile\n    }\n  }\n}\n\n# footer template (contains markers)\n$footerTemplate = @\"\n&lt;!--FOOTER-START-->\n\n## WalkBooks Project Structure\n\nBelow is an auto-generated directory map (depth: $Depth).\nRegenerate by running GitHub Action Workflow \"Update Directory Tree\" or run locally:\n`pwsh .\\tools\\generate-tree.ps1 -Depth $Depth`\n\n&lt;!-- TREE-START -->\n&lt;!-- TREE-END -->\n\n&lt;!--FOOTER-END-->\n\"@.TrimEnd()\n\n# --- generate tree to tmp file ---\nRemove-Item -ErrorAction Ignore -Force $TmpFile\ntry {\n  '.' | Out-File -FilePath $TmpFile -Encoding utf8\n  Write-Tree -Path (Resolve-Path $Path).Path -Prefix '' -Level 0 -OutFile $TmpFile\n  $treeText = (Get-Content -Raw -LiteralPath $TmpFile) -replace \"^\\s+|\\s+$\",\"\"\n  $treeExists = ($treeText.Length -gt 0)\n} catch {\n  Write-Host \"Tree generation failed: $($_.Exception.Message)\"\n  $treeExists = $false\n}\n\nif (-not (Test-Path $ReadmeFile)) {\n  Write-Host \"README.md not found at path: $ReadmeFile\"\n  Remove-Item -ErrorAction Ignore -Force $TmpFile\n  exit 1\n}\n\n# --- load README and footer pattern ---\n$content = Get-Content -Raw -LiteralPath $ReadmeFile\n$footerStart = '&lt;!--FOOTER-START-->'\n$footerEnd   = '&lt;!--FOOTER-END-->'\n$footerPattern = &#91;regex&#93;::Escape($footerStart) + '.*?' + &#91;regex&#93;::Escape($footerEnd)\n\n# If footer missing, append canonical footer (so subsequent runs always find markers)\nif (-not ($content -match $footerPattern)) {\n  $content = $content.TrimEnd() + \"`n`n\" + $footerTemplate + \"`n\"\n  $content | Out-File -FilePath $ReadmeFile -Encoding utf8\n  Write-Host \"Appended canonical FOOTER block to README (markers added).\"\n}\n\n# reload content (in case we appended)\n$content = Get-Content -Raw -LiteralPath $ReadmeFile\n\n# Build fenced block (no markers inside)\nif ($treeExists) {\n  $treeText = Get-Content -Raw -LiteralPath $TmpFile\n  $fencedBlock = '```' + \"`n\" + $treeText.TrimEnd() + \"`n\" + '```'\n  # Replacement: header + instruction + fenced block (no markers)\n  $sectionHeader = \"## WalkBooks Project Structure`n`nBelow is an auto-generated directory map (depth: $Depth).`n`n\"\n  $instruction = \"Regenerate by running GitHub Action Workflow `\"Update Directory Tree`\" `nor run locally: ``pwsh .\\tools\\generate-tree.ps1 -Depth $Depth``.`n`n\"\n  $replacement = $sectionHeader + $instruction + $fencedBlock + \"`n\"\n  # Replace entire footer region with replacement (markers removed)\n  $newContent = &#91;regex&#93;::Replace($content, $footerPattern, &#91;System.Text.RegularExpressions.MatchEvaluator&#93;{ param($m) $replacement }, 'Singleline')\n  $newContent | Out-File -FilePath $ReadmeFile -Encoding utf8\n  Remove-Item -Force $TmpFile -ErrorAction SilentlyContinue\n  Write-Host \"Injected tree into README (footer replaced; markers removed).\"\n  exit 0\n}\n\n# --- tree did not generate ---\nif ($RemoveOnFail) {\n  # Remove the entire FOOTER block (including markers) if present\n  if ($content -match $footerPattern) {\n    $final = &#91;regex&#93;::Replace($content, $footerPattern, '', 'Singleline')\n    $final = $final.TrimEnd() + \"`n\"\n    $final | Out-File -FilePath $ReadmeFile -Encoding utf8\n    Write-Host \"Tree generation failed; removed FOOTER block from README.\"\n  } else {\n    Write-Host \"Tree generation failed; no FOOTER block found to remove.\"\n  }\n} else {\n  Write-Host \"Tree generation failed; leaving README unchanged.\"\n}\n\nRemove-Item -ErrorAction Ignore -Force $TmpFile\nexit 0\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6\"><\/path><\/svg><\/span><pre class=\"shiki dark-plus\" style=\"background-color: #1E1E1E\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #6A9955\">&lt;#<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">.<\/span><span style=\"color: #D4D4D4\">SYNOPSIS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">  Generate an ASCII directory tree and inject it into README.md inside a FOOTER block.<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">.<\/span><span style=\"color: #D4D4D4\">DESCRIPTION<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">  - Generates a compact ASCII tree (\u251c\u2500, \u2514\u2500, \u2502) to a temporary file.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">  - Replaces the marker region between &lt;!-- TREE-START --&gt; and &lt;!-- TREE-END --&gt; with a fenced code block (no markers left).<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">  - If README lacks the FOOTER block, the script appends a canonical FOOTER (with markers) first.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">  - On success: replace the entire FOOTER block with the Project Structure header + fenced tree (no markers left).<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">  - On failure: remove the entire FOOTER block (including markers) when RemoveOnFail is true.<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">.<\/span><span style=\"color: #D4D4D4\">EXAMPLE<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">  - Place in path: tools\/generate-tree.ps1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">  - Temp file: The generated tree is written to a temporary file before being injected into the README.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">  - Excludes: .git, bin, obj, node_modules, tmp, publish, .vs<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">  - Usage: pwsh .\\tools\\generate-tree.ps1 -Depth 2<\/span><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\">#&gt;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">param<\/span><span style=\"color: #D4D4D4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  &#91;<\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">&#93;<\/span><span style=\"color: #9CDCFE\">$Path<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&#39;.&#39;<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  &#91;<\/span><span style=\"color: #569CD6\">int<\/span><span style=\"color: #D4D4D4\">&#93;<\/span><span style=\"color: #9CDCFE\">$Depth<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #B5CEA8\">2<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  [<\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">[]]<\/span><span style=\"color: #9CDCFE\">$Exclude<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">@<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&#39;.git&#39;<\/span><span style=\"color: #D4D4D4\">,<\/span><span style=\"color: #CE9178\">&#39;bin&#39;<\/span><span style=\"color: #D4D4D4\">,<\/span><span style=\"color: #CE9178\">&#39;obj&#39;<\/span><span style=\"color: #D4D4D4\">,<\/span><span style=\"color: #CE9178\">&#39;node_modules&#39;<\/span><span style=\"color: #D4D4D4\">,<\/span><span style=\"color: #CE9178\">&#39;tmp&#39;<\/span><span style=\"color: #D4D4D4\">,<\/span><span style=\"color: #CE9178\">&#39;publish&#39;<\/span><span style=\"color: #D4D4D4\">,<\/span><span style=\"color: #CE9178\">&#39;.vs&#39;<\/span><span style=\"color: #D4D4D4\">),<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  &#91;<\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">&#93;<\/span><span style=\"color: #9CDCFE\">$TmpFile<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&#39;tree.tmp&#39;<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  &#91;<\/span><span style=\"color: #569CD6\">string<\/span><span style=\"color: #D4D4D4\">&#93;<\/span><span style=\"color: #9CDCFE\">$ReadmeFile<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&#39;README.md&#39;<\/span><span style=\"color: #D4D4D4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  &#91;<\/span><span style=\"color: #569CD6\">switch<\/span><span style=\"color: #D4D4D4\">&#93;<\/span><span style=\"color: #9CDCFE\">$RemoveOnFail<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\"># default RemoveOnFail to true unless explicitly provided<\/span><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (-not <\/span><span style=\"color: #9CDCFE\">$PSBoundParameters<\/span><span style=\"color: #DCDCAA\">.ContainsKey<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #CE9178\">&#39;RemoveOnFail&#39;<\/span><span style=\"color: #D4D4D4\">)) { <\/span><span style=\"color: #9CDCFE\">$RemoveOnFail<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">$true<\/span><span style=\"color: #D4D4D4\"> }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #569CD6\">function<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #DCDCAA\">Write-Tree<\/span><span style=\"color: #D4D4D4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #C586C0\">param<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">$Path<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">$Prefix<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&#39;&#39;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">$Level<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">$OutFile<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #9CDCFE\">$Level<\/span><span style=\"color: #D4D4D4\"> -gt <\/span><span style=\"color: #9CDCFE\">$Depth<\/span><span style=\"color: #D4D4D4\">) { <\/span><span style=\"color: #C586C0\">return<\/span><span style=\"color: #D4D4D4\"> }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">$items<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #DCDCAA\">Get-ChildItem<\/span><span style=\"color: #D4D4D4\"> -LiteralPath <\/span><span style=\"color: #9CDCFE\">$Path<\/span><span style=\"color: #D4D4D4\"> -Force -ErrorAction SilentlyContinue |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #DCDCAA\">Where-Object<\/span><span style=\"color: #D4D4D4\"> { -not (<\/span><span style=\"color: #9CDCFE\">$Exclude<\/span><span style=\"color: #D4D4D4\"> -contains <\/span><span style=\"color: #9CDCFE\">$_<\/span><span style=\"color: #DCDCAA\">.Name<\/span><span style=\"color: #D4D4D4\">) } |<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #DCDCAA\">Sort-Object<\/span><span style=\"color: #D4D4D4\"> -Property <\/span><span style=\"color: #569CD6\">@<\/span><span style=\"color: #D4D4D4\">{ <\/span><span style=\"color: #9CDCFE\">Expression<\/span><span style=\"color: #D4D4D4\"> = { -not <\/span><span style=\"color: #9CDCFE\">$_<\/span><span style=\"color: #DCDCAA\">.PSIsContainer<\/span><span style=\"color: #D4D4D4\"> } }, <\/span><span style=\"color: #569CD6\">@<\/span><span style=\"color: #D4D4D4\">{ <\/span><span style=\"color: #9CDCFE\">Expression<\/span><span style=\"color: #D4D4D4\"> = { <\/span><span style=\"color: #9CDCFE\">$_<\/span><span style=\"color: #DCDCAA\">.Name<\/span><span style=\"color: #D4D4D4\"> } }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #C586C0\">for<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #9CDCFE\">$i<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">; <\/span><span style=\"color: #9CDCFE\">$i<\/span><span style=\"color: #D4D4D4\"> -lt <\/span><span style=\"color: #9CDCFE\">$items<\/span><span style=\"color: #DCDCAA\">.Count<\/span><span style=\"color: #D4D4D4\">; <\/span><span style=\"color: #9CDCFE\">$i<\/span><span style=\"color: #D4D4D4\">++) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">$item<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">$items<\/span><span style=\"color: #D4D4D4\">&#91;<\/span><span style=\"color: #9CDCFE\">$i<\/span><span style=\"color: #D4D4D4\">&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">$isLast<\/span><span style=\"color: #D4D4D4\"> = (<\/span><span style=\"color: #9CDCFE\">$i<\/span><span style=\"color: #D4D4D4\"> -eq <\/span><span style=\"color: #9CDCFE\">$items<\/span><span style=\"color: #DCDCAA\">.Count<\/span><span style=\"color: #D4D4D4\"> - <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">$branch<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #9CDCFE\">$isLast<\/span><span style=\"color: #D4D4D4\">) { <\/span><span style=\"color: #CE9178\">&#39;\u2514\u2500 &#39;<\/span><span style=\"color: #D4D4D4\"> } <\/span><span style=\"color: #C586C0\">else<\/span><span style=\"color: #D4D4D4\"> { <\/span><span style=\"color: #CE9178\">&#39;\u251c\u2500 &#39;<\/span><span style=\"color: #D4D4D4\"> }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">$line<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">$Prefix<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #9CDCFE\">$branch<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #9CDCFE\">$item<\/span><span style=\"color: #DCDCAA\">.Name<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">$line<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">Out-File<\/span><span style=\"color: #D4D4D4\"> -FilePath <\/span><span style=\"color: #9CDCFE\">$OutFile<\/span><span style=\"color: #D4D4D4\"> -Append -Encoding utf8<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #9CDCFE\">$item<\/span><span style=\"color: #DCDCAA\">.PSIsContainer<\/span><span style=\"color: #D4D4D4\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #9CDCFE\">$newPrefix<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #9CDCFE\">$isLast<\/span><span style=\"color: #D4D4D4\">) { <\/span><span style=\"color: #9CDCFE\">$Prefix<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #CE9178\">&#39;   &#39;<\/span><span style=\"color: #D4D4D4\"> } <\/span><span style=\"color: #C586C0\">else<\/span><span style=\"color: #D4D4D4\"> { <\/span><span style=\"color: #9CDCFE\">$Prefix<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #CE9178\">&#39;\u2502  &#39;<\/span><span style=\"color: #D4D4D4\"> }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">      <\/span><span style=\"color: #DCDCAA\">Write-Tree<\/span><span style=\"color: #D4D4D4\"> -Path <\/span><span style=\"color: #9CDCFE\">$item<\/span><span style=\"color: #DCDCAA\">.FullName<\/span><span style=\"color: #D4D4D4\"> -Prefix <\/span><span style=\"color: #9CDCFE\">$newPrefix<\/span><span style=\"color: #D4D4D4\"> -Level (<\/span><span style=\"color: #9CDCFE\">$Level<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #B5CEA8\">1<\/span><span style=\"color: #D4D4D4\">) -OutFile <\/span><span style=\"color: #9CDCFE\">$OutFile<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\"># footer template (contains markers)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">$footerTemplate<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">@&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">&lt;!--FOOTER-START--&gt;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">## WalkBooks Project Structure<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">Below is an auto-generated directory map (depth: <\/span><span style=\"color: #9CDCFE\">$Depth<\/span><span style=\"color: #CE9178\">).<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">Regenerate by running GitHub Action Workflow &quot;Update Directory Tree&quot; or run locally:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">`pwsh .\\tools\\generate-tree.ps1 -Depth <\/span><span style=\"color: #9CDCFE\">$Depth<\/span><span style=\"color: #CE9178\">`<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">&lt;!-- TREE-START --&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">&lt;!-- TREE-END --&gt;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">&lt;!--FOOTER-END--&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #CE9178\">&quot;@<\/span><span style=\"color: #D4D4D4\">.TrimEnd()<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\"># --- generate tree to tmp file ---<\/span><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">Remove-Item<\/span><span style=\"color: #D4D4D4\"> -ErrorAction Ignore -Force <\/span><span style=\"color: #9CDCFE\">$TmpFile<\/span><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">try<\/span><span style=\"color: #D4D4D4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #CE9178\">&#39;.&#39;<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">Out-File<\/span><span style=\"color: #D4D4D4\"> -FilePath <\/span><span style=\"color: #9CDCFE\">$TmpFile<\/span><span style=\"color: #D4D4D4\"> -Encoding utf8<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #DCDCAA\">Write-Tree<\/span><span style=\"color: #D4D4D4\"> -Path (<\/span><span style=\"color: #DCDCAA\">Resolve-Path<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">$Path<\/span><span style=\"color: #D4D4D4\">).Path -Prefix <\/span><span style=\"color: #CE9178\">&#39;&#39;<\/span><span style=\"color: #D4D4D4\"> -Level <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\"> -OutFile <\/span><span style=\"color: #9CDCFE\">$TmpFile<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">$treeText<\/span><span style=\"color: #D4D4D4\"> = (<\/span><span style=\"color: #DCDCAA\">Get-Content<\/span><span style=\"color: #D4D4D4\"> -Raw -LiteralPath <\/span><span style=\"color: #9CDCFE\">$TmpFile<\/span><span style=\"color: #D4D4D4\">) -replace <\/span><span style=\"color: #CE9178\">&quot;^\\s+|\\s+$&quot;<\/span><span style=\"color: #D4D4D4\">,<\/span><span style=\"color: #CE9178\">&quot;&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">$treeExists<\/span><span style=\"color: #D4D4D4\"> = (<\/span><span style=\"color: #9CDCFE\">$treeText<\/span><span style=\"color: #DCDCAA\">.Length<\/span><span style=\"color: #D4D4D4\"> -gt <\/span><span style=\"color: #B5CEA8\">0<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">} <\/span><span style=\"color: #C586C0\">catch<\/span><span style=\"color: #D4D4D4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #DCDCAA\">Write-Host<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">&quot;Tree generation failed: <\/span><span style=\"color: #569CD6\">$(<\/span><span style=\"color: #9CDCFE\">$_<\/span><span style=\"color: #DCDCAA\">.Exception.Message<\/span><span style=\"color: #569CD6\">)<\/span><span style=\"color: #CE9178\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">$treeExists<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #569CD6\">$false<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (-not (<\/span><span style=\"color: #DCDCAA\">Test-Path<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #9CDCFE\">$ReadmeFile<\/span><span style=\"color: #D4D4D4\">)) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #DCDCAA\">Write-Host<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">&quot;README.md not found at path: <\/span><span style=\"color: #9CDCFE\">$ReadmeFile<\/span><span style=\"color: #CE9178\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #DCDCAA\">Remove-Item<\/span><span style=\"color: #D4D4D4\"> -ErrorAction Ignore -Force <\/span><span style=\"color: #9CDCFE\">$TmpFile<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #C586C0\">exit<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #B5CEA8\">1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\"># --- load README and footer pattern ---<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">$content<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #DCDCAA\">Get-Content<\/span><span style=\"color: #D4D4D4\"> -Raw -LiteralPath <\/span><span style=\"color: #9CDCFE\">$ReadmeFile<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">$footerStart<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&#39;&lt;!--FOOTER-START--&gt;&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">$footerEnd<\/span><span style=\"color: #D4D4D4\">   = <\/span><span style=\"color: #CE9178\">&#39;&lt;!--FOOTER-END--&gt;&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">$footerPattern<\/span><span style=\"color: #D4D4D4\"> = &#91;<\/span><span style=\"color: #569CD6\">regex<\/span><span style=\"color: #D4D4D4\">&#93;::Escape(<\/span><span style=\"color: #9CDCFE\">$footerStart<\/span><span style=\"color: #D4D4D4\">) + <\/span><span style=\"color: #CE9178\">&#39;.*?&#39;<\/span><span style=\"color: #D4D4D4\"> + &#91;<\/span><span style=\"color: #569CD6\">regex<\/span><span style=\"color: #D4D4D4\">&#93;::Escape(<\/span><span style=\"color: #9CDCFE\">$footerEnd<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\"># If footer missing, append canonical footer (so subsequent runs always find markers)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (-not (<\/span><span style=\"color: #9CDCFE\">$content<\/span><span style=\"color: #D4D4D4\"> -match <\/span><span style=\"color: #9CDCFE\">$footerPattern<\/span><span style=\"color: #D4D4D4\">)) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">$content<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">$content<\/span><span style=\"color: #DCDCAA\">.TrimEnd<\/span><span style=\"color: #D4D4D4\">() + <\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D7BA7D\">`n`n<\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #9CDCFE\">$footerTemplate<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D7BA7D\">`n<\/span><span style=\"color: #CE9178\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">$content<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">Out-File<\/span><span style=\"color: #D4D4D4\"> -FilePath <\/span><span style=\"color: #9CDCFE\">$ReadmeFile<\/span><span style=\"color: #D4D4D4\"> -Encoding utf8<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #DCDCAA\">Write-Host<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">&quot;Appended canonical FOOTER block to README (markers added).&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\"># reload content (in case we appended)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #9CDCFE\">$content<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #DCDCAA\">Get-Content<\/span><span style=\"color: #D4D4D4\"> -Raw -LiteralPath <\/span><span style=\"color: #9CDCFE\">$ReadmeFile<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\"># Build fenced block (no markers inside)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #9CDCFE\">$treeExists<\/span><span style=\"color: #D4D4D4\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">$treeText<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #DCDCAA\">Get-Content<\/span><span style=\"color: #D4D4D4\"> -Raw -LiteralPath <\/span><span style=\"color: #9CDCFE\">$TmpFile<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">$fencedBlock<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&#39;```&#39;<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D7BA7D\">`n<\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #9CDCFE\">$treeText<\/span><span style=\"color: #DCDCAA\">.TrimEnd<\/span><span style=\"color: #D4D4D4\">() + <\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D7BA7D\">`n<\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #CE9178\">&#39;```&#39;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #6A9955\"># Replacement: header + instruction + fenced block (no markers)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">$sectionHeader<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;## WalkBooks Project Structure<\/span><span style=\"color: #D7BA7D\">`n`n<\/span><span style=\"color: #CE9178\">Below is an auto-generated directory map (depth: <\/span><span style=\"color: #9CDCFE\">$Depth<\/span><span style=\"color: #CE9178\">).<\/span><span style=\"color: #D7BA7D\">`n`n<\/span><span style=\"color: #CE9178\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">$instruction<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #CE9178\">&quot;Regenerate by running GitHub Action Workflow <\/span><span style=\"color: #D7BA7D\">`&quot;<\/span><span style=\"color: #CE9178\">Update Directory Tree<\/span><span style=\"color: #D7BA7D\">`&quot;<\/span><span style=\"color: #CE9178\"> <\/span><span style=\"color: #D7BA7D\">`n<\/span><span style=\"color: #CE9178\">or run locally: <\/span><span style=\"color: #D7BA7D\">``<\/span><span style=\"color: #CE9178\">pwsh .\\tools\\generate-tree.ps1 -Depth <\/span><span style=\"color: #9CDCFE\">$Depth<\/span><span style=\"color: #D7BA7D\">``<\/span><span style=\"color: #CE9178\">.<\/span><span style=\"color: #D7BA7D\">`n`n<\/span><span style=\"color: #CE9178\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">$replacement<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">$sectionHeader<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #9CDCFE\">$instruction<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #9CDCFE\">$fencedBlock<\/span><span style=\"color: #D4D4D4\"> + <\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D7BA7D\">`n<\/span><span style=\"color: #CE9178\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #6A9955\"># Replace entire footer region with replacement (markers removed)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">$newContent<\/span><span style=\"color: #D4D4D4\"> = &#91;<\/span><span style=\"color: #569CD6\">regex<\/span><span style=\"color: #D4D4D4\">&#93;::Replace(<\/span><span style=\"color: #9CDCFE\">$content<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">$footerPattern<\/span><span style=\"color: #D4D4D4\">, &#91;<\/span><span style=\"color: #569CD6\">System.Text.RegularExpressions.MatchEvaluator<\/span><span style=\"color: #D4D4D4\">&#93;{ <\/span><span style=\"color: #C586C0\">param<\/span><span style=\"color: #D4D4D4\">(<\/span><span style=\"color: #9CDCFE\">$m<\/span><span style=\"color: #D4D4D4\">) <\/span><span style=\"color: #9CDCFE\">$replacement<\/span><span style=\"color: #D4D4D4\"> }, <\/span><span style=\"color: #CE9178\">&#39;Singleline&#39;<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #9CDCFE\">$newContent<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">Out-File<\/span><span style=\"color: #D4D4D4\"> -FilePath <\/span><span style=\"color: #9CDCFE\">$ReadmeFile<\/span><span style=\"color: #D4D4D4\"> -Encoding utf8<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #DCDCAA\">Remove-Item<\/span><span style=\"color: #D4D4D4\"> -Force <\/span><span style=\"color: #9CDCFE\">$TmpFile<\/span><span style=\"color: #D4D4D4\"> -ErrorAction SilentlyContinue<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #DCDCAA\">Write-Host<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">&quot;Injected tree into README (footer replaced; markers removed).&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #C586C0\">exit<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #B5CEA8\">0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #6A9955\"># --- tree did not generate ---<\/span><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #9CDCFE\">$RemoveOnFail<\/span><span style=\"color: #D4D4D4\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #6A9955\"># Remove the entire FOOTER block (including markers) if present<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #C586C0\">if<\/span><span style=\"color: #D4D4D4\"> (<\/span><span style=\"color: #9CDCFE\">$content<\/span><span style=\"color: #D4D4D4\"> -match <\/span><span style=\"color: #9CDCFE\">$footerPattern<\/span><span style=\"color: #D4D4D4\">) {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">$final<\/span><span style=\"color: #D4D4D4\"> = &#91;<\/span><span style=\"color: #569CD6\">regex<\/span><span style=\"color: #D4D4D4\">&#93;::Replace(<\/span><span style=\"color: #9CDCFE\">$content<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #9CDCFE\">$footerPattern<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #CE9178\">&#39;&#39;<\/span><span style=\"color: #D4D4D4\">, <\/span><span style=\"color: #CE9178\">&#39;Singleline&#39;<\/span><span style=\"color: #D4D4D4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">$final<\/span><span style=\"color: #D4D4D4\"> = <\/span><span style=\"color: #9CDCFE\">$final<\/span><span style=\"color: #DCDCAA\">.TrimEnd<\/span><span style=\"color: #D4D4D4\">() + <\/span><span style=\"color: #CE9178\">&quot;<\/span><span style=\"color: #D7BA7D\">`n<\/span><span style=\"color: #CE9178\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #9CDCFE\">$final<\/span><span style=\"color: #D4D4D4\"> | <\/span><span style=\"color: #DCDCAA\">Out-File<\/span><span style=\"color: #D4D4D4\"> -FilePath <\/span><span style=\"color: #9CDCFE\">$ReadmeFile<\/span><span style=\"color: #D4D4D4\"> -Encoding utf8<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #DCDCAA\">Write-Host<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">&quot;Tree generation failed; removed FOOTER block from README.&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  } <\/span><span style=\"color: #C586C0\">else<\/span><span style=\"color: #D4D4D4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">    <\/span><span style=\"color: #DCDCAA\">Write-Host<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">&quot;Tree generation failed; no FOOTER block found to remove.&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">} <\/span><span style=\"color: #C586C0\">else<\/span><span style=\"color: #D4D4D4\"> {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">  <\/span><span style=\"color: #DCDCAA\">Write-Host<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #CE9178\">&quot;Tree generation failed; leaving README unchanged.&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D4D4D4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #DCDCAA\">Remove-Item<\/span><span style=\"color: #D4D4D4\"> -ErrorAction Ignore -Force <\/span><span style=\"color: #9CDCFE\">$TmpFile<\/span><\/span>\n<span class=\"line\"><span style=\"color: #C586C0\">exit<\/span><span style=\"color: #D4D4D4\"> <\/span><span style=\"color: #B5CEA8\">0<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Implementation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">As mentioned, I want a directory tree in my repo&#8217;s main README, but don&#8217;t want it to become outdated. This problem has likely been solved many times, and here&#8217;s how I did it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First: I needed to get this workflow running successfully (initial job run was a &#8216;teachable moment&#8217;).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1200\" height=\"390\" loading=\"lazy\" src=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_1_works_now-1200x390.webp\" alt=\"GitHub Actions workflow - ASCII Directory Tree Art job is working now!\" class=\"wp-image-9855\" srcset=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_1_works_now-1200x390.webp 1200w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_1_works_now-600x195.webp 600w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_1_works_now-150x49.webp 150w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_1_works_now-1536x499.webp 1536w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_1_works_now-2048x666.webp 2048w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><figcaption class=\"wp-element-caption\">GitHub Actions workflow &#8211; ASCII Directory Tree Art job is working now!<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">When I got the workflow running, I checked the README to see how the dir tree looked. &#8230;Not good.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1200\" height=\"913\" loading=\"lazy\" src=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_2_dont_look_so_good-1200x913.webp\" alt=\"GitHub README's Directory Tree ASCII Art doesn't look so good\" class=\"wp-image-9857\" srcset=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_2_dont_look_so_good-1200x913.webp 1200w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_2_dont_look_so_good-600x457.webp 600w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_2_dont_look_so_good-150x114.webp 150w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_2_dont_look_so_good-1536x1169.webp 1536w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_2_dont_look_so_good-2048x1559.webp 2048w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><figcaption class=\"wp-element-caption\">yikes!<\/figcaption><\/figure>\n\n\n\n<div data-wp-context=\"{ &quot;autoclose&quot;: false, &quot;accordionItems&quot;: [] }\" data-wp-interactive=\"core\/accordion\" role=\"group\" class=\"wp-block-accordion is-layout-flow wp-block-accordion-is-layout-flow\"><\/div>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-8f761849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column has-custom-css has-kb-palette-3-background-color has-background is-layout-flow wp-block-column-is-layout-flow wp-custom-css-e5e8d3ad\">\n<h2 class=\"wp-block-heading\">Resilience<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The saga of figuring out how to make the README look nice for both happy and sad paths is in this colored section. It&#8217;s important to me that whether this workflow works or not, it doesn&#8217;t leave the repo README &#8216;messy&#8217;. <strong>Skip if that sounds boring.<\/strong> <\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">OK. Back to the drawing board on the PowerShell script. I developed and ran it locally, checking (then resetting) the README.md file to review progress. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Note: I think the ability to run the tree gen process locally is simplified when I can call a script file via CLI. That is why I didn&#8217;t try to put all this functionality into the YAML file, even if 1 file sounds more maintainable than 2. <\/em><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Tree gen clean-up issues (success or fail)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The green arrow in the below image shows the command to run the script. Parameters can be passed in. i.e.; &#8216;Depth 2&#8217; means the directory tree will represent directory contents 2 levels deep. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The red lines highlight artifacts in both terminal and the shell script of a &#8220;clean-up&#8221; solution I devised. At first, both successful and failed runs of the tree gen script would leave behind unwanted README content.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Success Scenario: <\/strong>If the tree generation worked and inserted into the README, my script was still leaving behind placeholder tags (i.e.; <code>&lt;!--TREE-START--&gt;<\/code>).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Failure Scenario: <\/strong>If the tree gen failed, it was correctly removing those placeholder markers, but leaving the &#8216;about&#8217; text falsely explaining &#8216;below is a dir tree&#8217;. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The solution to both success and failure scenarios was: <\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\">Always remove both FOOTER and TREE tags when tree gen succeeds. <\/li>\n\n\n\n<li class=\"\">Put everything inside FOOTER tags, if tree gen fails remove all content inside and including those markers. <\/li>\n\n\n\n<li class=\"\">First task of the script: parse README for the tags. If not found, append them to the README so the rest of the script can work.\n<ul class=\"wp-block-list\">\n<li class=\"\">(Solves the issue of a tree gen failure that results in removing the entire footer, which looks clean, but then tags and &#8216;about&#8217; content will still be needed to run the job again).<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1200\" height=\"797\" loading=\"lazy\" src=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_4_local_readme_run_script-1200x797.webp\" alt=\"Calling the shell script locally, coming up with a way to succeed OR fail gracefully.\" class=\"wp-image-9859\" srcset=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_4_local_readme_run_script-1200x797.webp 1200w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_4_local_readme_run_script-600x398.webp 600w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_4_local_readme_run_script-150x100.webp 150w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_4_local_readme_run_script-1536x1020.webp 1536w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_4_local_readme_run_script.webp 1607w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><figcaption class=\"wp-element-caption\">Calling the shell script locally, coming up with a way to succeed OR fail gracefully.<\/figcaption><\/figure>\n<\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Looking better<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Eventually I got the local README looking as it should, tree gen success or no. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s the reset README with all content deleted after the &#8216;Architecture Overview&#8217; section. This is also how it would look if the ASCII tree generation failed (no longer leaving behind cryptic tags or text about a tree that doesn&#8217;t exist).<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" width=\"1919\" height=\"987\" loading=\"lazy\" src=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_3_local_readme_before.png\" alt=\"GitHub README before inserting directory tree footer\" class=\"wp-image-9863\" srcset=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_3_local_readme_before.png 1919w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_3_local_readme_before-600x309.png 600w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_3_local_readme_before-1200x617.png 1200w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_3_local_readme_before-150x77.png 150w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_3_local_readme_before-1536x790.png 1536w\" sizes=\"auto, (max-width: 1919px) 100vw, 1919px\" \/><figcaption class=\"wp-element-caption\">GitHub README before inserting directory tree footer<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s the local README with the footer section appended by the shell script. Note the footer&#8217;s ASCII directory tree is visible now. On GitHub, this will automatically update as the repo&#8217;s directory changes.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1200\" height=\"1100\" loading=\"lazy\" src=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_5_local_readme_after-1200x1100.png\" alt=\"GitHub README after inserting directory tree footer\" class=\"wp-image-9865\" srcset=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_5_local_readme_after-1200x1100.png 1200w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_5_local_readme_after-600x550.png 600w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_5_local_readme_after-150x137.png 150w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_5_local_readme_after-1536x1408.png 1536w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_5_local_readme_after.png 1883w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><figcaption class=\"wp-element-caption\">GitHub README after inserting directory tree footer<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">I liked the way the local README looked now. Next step: commit and push the changes, which triggers my <abbr title=\"Continuous Integration and Continuous Deployment\">CI\/CD<\/abbr> workflow job and then the &#8220;Update Directory Tree&#8221; job.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" width=\"2200\" height=\"875\" loading=\"lazy\" src=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_6_GitHub_Actions_Workflow_works.png\" alt=\"GitHub Actions workflows - CI\/CD and Update Directory Tree jobs run on commit push\" class=\"wp-image-9867\" srcset=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_6_GitHub_Actions_Workflow_works.png 2200w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_6_GitHub_Actions_Workflow_works-600x239.png 600w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_6_GitHub_Actions_Workflow_works-1200x477.png 1200w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_6_GitHub_Actions_Workflow_works-150x60.png 150w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_6_GitHub_Actions_Workflow_works-1536x611.png 1536w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_6_GitHub_Actions_Workflow_works-2048x815.png 2048w\" sizes=\"auto, (max-width: 2200px) 100vw, 2200px\" \/><figcaption class=\"wp-element-caption\">Update Directory Tree job runs on commit push<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">So, how does the GitHub README look now? <\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" width=\"1570\" height=\"1972\" loading=\"lazy\" src=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_7_GitHub_README_looking_better.png\" alt=\"README file has auto-updating ASCII Directory Tree inserted via GitHub Actions workflow job.\" class=\"wp-image-9869\" srcset=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_7_GitHub_README_looking_better.png 1570w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_7_GitHub_README_looking_better-600x754.png 600w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_7_GitHub_README_looking_better-1200x1507.png 1200w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_7_GitHub_README_looking_better-119x150.png 119w, https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GitHub_Actions_update-tree_job_7_GitHub_README_looking_better-1223x1536.png 1223w\" sizes=\"auto, (max-width: 1570px) 100vw, 1570px\" \/><figcaption class=\"wp-element-caption\">README file has updated ASCII Directory Tree inserted via GitHub Actions workflow job.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Good enough. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>How many GitHub repos have outdated directory tree art? If it&#8217;s important enough to feature on your main README, why not automate tree updates? Do it with a GitHub Actions Workflow.<\/p>\n","protected":false},"author":1,"featured_media":9873,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"nf_dc_page":"","_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","om_disable_all_campaigns":false,"inline_featured_image":false,"kt_blocks_editor_width":"","_kad_blocks_custom_css":"","_kad_blocks_head_custom_js":"","_kad_blocks_body_custom_js":"","_kad_blocks_footer_custom_js":"","_monsterinsights_skip_tracking":false,"footnotes":""},"categories":[4],"tags":[953,954,36,772],"class_list":["post-9844","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-designer","tag-devops","tag-github","tag-tutorial","tag-web-development"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"#How many GitHub repos have outdated directory tree art? If it&#039;s important enough to feature on your main README, why not automate tree updates? Do it with a GitHub Actions Workflow.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Kris Bunda\"\/>\n\t<meta name=\"google-site-verification\" content=\"nClePLp4QbLXXv7FON_y2QT65NP7LibU8DWgB3TaPyY\" \/>\n\t<meta name=\"msvalidate.01\" content=\"D9A2CA8C318137423BE8EC5CD4C5DBBE\" \/>\n\t<meta name=\"p:domain_verify\" content=\"6096666fee42f7673dae5027429ae8fd\" \/>\n\t<meta name=\"keywords\" content=\"devops,github,tutorial,web development\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Kris Bunda Design\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Auto-Update Your README\u2019s ASCII Directory Tree with GitHub Actions | Kris Bunda Design\" \/>\n\t\t<meta property=\"og:description\" content=\"#How many GitHub repos have outdated directory tree art? If it&#039;s important enough to feature on your main README, why not automate tree updates? Do it with a GitHub Actions Workflow.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE.webp\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE.webp\" \/>\n\t\t<meta property=\"og:image:width\" content=\"1924\" \/>\n\t\t<meta property=\"og:image:height\" content=\"1084\" \/>\n\t\t<meta property=\"article:tag\" content=\"devops\" \/>\n\t\t<meta property=\"article:tag\" content=\"github\" \/>\n\t\t<meta property=\"article:tag\" content=\"tutorial\" \/>\n\t\t<meta property=\"article:tag\" content=\"web development\" \/>\n\t\t<meta property=\"article:tag\" content=\"designer\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-09-03T17:05:55+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-09-03T17:17:06+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:site\" content=\"@KrisBunda3D\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Auto-Update Your README\u2019s ASCII Directory Tree with GitHub Actions | Kris Bunda Design\" \/>\n\t\t<meta name=\"twitter:description\" content=\"#How many GitHub repos have outdated directory tree art? If it&#039;s important enough to feature on your main README, why not automate tree updates? Do it with a GitHub Actions Workflow.\" \/>\n\t\t<meta name=\"twitter:creator\" content=\"@KrisBunda3D\" \/>\n\t\t<meta name=\"twitter:image\" content=\"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE.webp\" \/>\n\t\t<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t\t<meta name=\"twitter:data1\" content=\"Kris Bunda\" \/>\n\t\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/2026\\\/09\\\/03\\\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\\\/#article\",\"name\":\"Auto-Update Your README\\u2019s ASCII Directory Tree with GitHub Actions | Kris Bunda Design\",\"headline\":\"Auto-Update Your README&#8217;s ASCII Directory Tree with GitHub Actions\",\"author\":{\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/author\\\/kris-bunda\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/#person\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE.webp\",\"width\":1924,\"height\":1084,\"caption\":\"Use GitHub Actions workflow to auto-update your repo's ASCII art Directory Tree\"},\"datePublished\":\"2026-09-03T12:05:55-05:00\",\"dateModified\":\"2026-09-03T12:17:06-05:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/2026\\\/09\\\/03\\\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/2026\\\/09\\\/03\\\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\\\/#webpage\"},\"articleSection\":\"Designer, DevOps, GitHub, tutorial, web development\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/2026\\\/09\\\/03\\\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/krisbunda.com\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/category\\\/designer\\\/#listItem\",\"name\":\"Designer\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/category\\\/designer\\\/#listItem\",\"position\":2,\"name\":\"Designer\",\"item\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/category\\\/designer\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/2026\\\/09\\\/03\\\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\\\/#listItem\",\"name\":\"Auto-Update Your README&#8217;s ASCII Directory Tree with GitHub Actions\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/2026\\\/09\\\/03\\\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\\\/#listItem\",\"position\":3,\"name\":\"Auto-Update Your README&#8217;s ASCII Directory Tree with GitHub Actions\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/category\\\/designer\\\/#listItem\",\"name\":\"Designer\"}}]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/#person\",\"name\":\"Kris Bunda\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/2026\\\/09\\\/03\\\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\\\/#personImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/0dea2176fcad4070f74ce4ee1a63460edd0cc3017656f3a98fa152e791f87b55?s=96&d=mm&r=r\",\"width\":96,\"height\":96,\"caption\":\"Kris Bunda\"},\"sameAs\":[\"@KrisBunda3D\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/author\\\/kris-bunda\\\/#author\",\"url\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/author\\\/kris-bunda\\\/\",\"name\":\"Kris Bunda\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/2026\\\/09\\\/03\\\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/0dea2176fcad4070f74ce4ee1a63460edd0cc3017656f3a98fa152e791f87b55?s=96&d=mm&r=r\",\"width\":96,\"height\":96,\"caption\":\"Kris Bunda\"},\"sameAs\":[\"@KrisBunda3D\"]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/2026\\\/09\\\/03\\\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\\\/#webpage\",\"url\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/2026\\\/09\\\/03\\\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\\\/\",\"name\":\"Auto-Update Your README\\u2019s ASCII Directory Tree with GitHub Actions | Kris Bunda Design\",\"description\":\"#How many GitHub repos have outdated directory tree art? If it's important enough to feature on your main README, why not automate tree updates? Do it with a GitHub Actions Workflow.\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/2026\\\/09\\\/03\\\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/author\\\/kris-bunda\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/author\\\/kris-bunda\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE.webp\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/2026\\\/09\\\/03\\\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\\\/#mainImage\",\"width\":1924,\"height\":1084,\"caption\":\"Use GitHub Actions workflow to auto-update your repo's ASCII art Directory Tree\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/2026\\\/09\\\/03\\\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\\\/#mainImage\"},\"datePublished\":\"2026-09-03T12:05:55-05:00\",\"dateModified\":\"2026-09-03T12:17:06-05:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/\",\"name\":\"Kris Bunda Design\",\"description\":\"Creative Development & Technical How-Tos\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/krisbunda.com\\\/blog\\\/#person\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Auto-Update Your README\u2019s ASCII Directory Tree with GitHub Actions | Kris Bunda Design","description":"#How many GitHub repos have outdated directory tree art? If it's important enough to feature on your main README, why not automate tree updates? Do it with a GitHub Actions Workflow.","canonical_url":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/","robots":"max-image-preview:large","keywords":"devops,github,tutorial,web development","webmasterTools":{"google-site-verification":"nClePLp4QbLXXv7FON_y2QT65NP7LibU8DWgB3TaPyY","msvalidate.01":"D9A2CA8C318137423BE8EC5CD4C5DBBE","p:domain_verify":"6096666fee42f7673dae5027429ae8fd","miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/#article","name":"Auto-Update Your README\u2019s ASCII Directory Tree with GitHub Actions | Kris Bunda Design","headline":"Auto-Update Your README&#8217;s ASCII Directory Tree with GitHub Actions","author":{"@id":"https:\/\/krisbunda.com\/blog\/author\/kris-bunda\/#author"},"publisher":{"@id":"https:\/\/krisbunda.com\/blog\/#person"},"image":{"@type":"ImageObject","url":"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE.webp","width":1924,"height":1084,"caption":"Use GitHub Actions workflow to auto-update your repo's ASCII art Directory Tree"},"datePublished":"2026-09-03T12:05:55-05:00","dateModified":"2026-09-03T12:17:06-05:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/#webpage"},"isPartOf":{"@id":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/#webpage"},"articleSection":"Designer, DevOps, GitHub, tutorial, web development"},{"@type":"BreadcrumbList","@id":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/krisbunda.com\/blog#listItem","position":1,"name":"Home","item":"https:\/\/krisbunda.com\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/krisbunda.com\/blog\/category\/designer\/#listItem","name":"Designer"}},{"@type":"ListItem","@id":"https:\/\/krisbunda.com\/blog\/category\/designer\/#listItem","position":2,"name":"Designer","item":"https:\/\/krisbunda.com\/blog\/category\/designer\/","nextItem":{"@type":"ListItem","@id":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/#listItem","name":"Auto-Update Your README&#8217;s ASCII Directory Tree with GitHub Actions"},"previousItem":{"@type":"ListItem","@id":"https:\/\/krisbunda.com\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/#listItem","position":3,"name":"Auto-Update Your README&#8217;s ASCII Directory Tree with GitHub Actions","previousItem":{"@type":"ListItem","@id":"https:\/\/krisbunda.com\/blog\/category\/designer\/#listItem","name":"Designer"}}]},{"@type":"Person","@id":"https:\/\/krisbunda.com\/blog\/#person","name":"Kris Bunda","image":{"@type":"ImageObject","@id":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/#personImage","url":"https:\/\/secure.gravatar.com\/avatar\/0dea2176fcad4070f74ce4ee1a63460edd0cc3017656f3a98fa152e791f87b55?s=96&d=mm&r=r","width":96,"height":96,"caption":"Kris Bunda"},"sameAs":["@KrisBunda3D"]},{"@type":"Person","@id":"https:\/\/krisbunda.com\/blog\/author\/kris-bunda\/#author","url":"https:\/\/krisbunda.com\/blog\/author\/kris-bunda\/","name":"Kris Bunda","image":{"@type":"ImageObject","@id":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/0dea2176fcad4070f74ce4ee1a63460edd0cc3017656f3a98fa152e791f87b55?s=96&d=mm&r=r","width":96,"height":96,"caption":"Kris Bunda"},"sameAs":["@KrisBunda3D"]},{"@type":"WebPage","@id":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/#webpage","url":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/","name":"Auto-Update Your README\u2019s ASCII Directory Tree with GitHub Actions | Kris Bunda Design","description":"#How many GitHub repos have outdated directory tree art? If it's important enough to feature on your main README, why not automate tree updates? Do it with a GitHub Actions Workflow.","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/krisbunda.com\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/#breadcrumblist"},"author":{"@id":"https:\/\/krisbunda.com\/blog\/author\/kris-bunda\/#author"},"creator":{"@id":"https:\/\/krisbunda.com\/blog\/author\/kris-bunda\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE.webp","@id":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/#mainImage","width":1924,"height":1084,"caption":"Use GitHub Actions workflow to auto-update your repo's ASCII art Directory Tree"},"primaryImageOfPage":{"@id":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/#mainImage"},"datePublished":"2026-09-03T12:05:55-05:00","dateModified":"2026-09-03T12:17:06-05:00"},{"@type":"WebSite","@id":"https:\/\/krisbunda.com\/blog\/#website","url":"https:\/\/krisbunda.com\/blog\/","name":"Kris Bunda Design","description":"Creative Development & Technical How-Tos","inLanguage":"en-US","publisher":{"@id":"https:\/\/krisbunda.com\/blog\/#person"}}]},"og:locale":"en_US","og:site_name":"Kris Bunda Design","og:type":"article","og:title":"Auto-Update Your README\u2019s ASCII Directory Tree with GitHub Actions | Kris Bunda Design","og:description":"#How many GitHub repos have outdated directory tree art? If it's important enough to feature on your main README, why not automate tree updates? Do it with a GitHub Actions Workflow.","og:url":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/","og:image":"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE.webp","og:image:secure_url":"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE.webp","og:image:width":"1924","og:image:height":"1084","article:tag":["devops","github","tutorial","web development","designer"],"article:published_time":"2026-09-03T17:05:55+00:00","article:modified_time":"2026-09-03T17:17:06+00:00","twitter:card":"summary_large_image","twitter:site":"@KrisBunda3D","twitter:title":"Auto-Update Your README\u2019s ASCII Directory Tree with GitHub Actions | Kris Bunda Design","twitter:description":"#How many GitHub repos have outdated directory tree art? If it's important enough to feature on your main README, why not automate tree updates? Do it with a GitHub Actions Workflow.","twitter:creator":"@KrisBunda3D","twitter:image":"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE.webp","twitter:label1":"Written by","twitter:data1":"Kris Bunda","twitter:label2":"Est. reading time","twitter:data2":"14 minutes"},"aioseo_meta_data":{"post_id":"9844","title":null,"description":"##post_excerpt","keywords":null,"keyphrases":{"focus":{"keyphrase":"","score":0,"analysis":{"keyphraseInTitle":{"score":0,"maxScore":9,"error":1}}},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"featured","og_image_url":"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE.webp","og_image_width":"1924","og_image_height":"1084","og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"featured","twitter_image_url":"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE.webp","twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"created":"2026-09-02 01:23:43","updated":"2026-09-03 17:17:51","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/krisbunda.com\/blog\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/krisbunda.com\/blog\/category\/designer\/\" title=\"Designer\">Designer<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tAuto-Update Your README\u2019s ASCII Directory Tree with GitHub Actions\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/krisbunda.com\/blog"},{"label":"Designer","link":"https:\/\/krisbunda.com\/blog\/category\/designer\/"},{"label":"Auto-Update Your README&#8217;s ASCII Directory Tree with GitHub Actions","link":"https:\/\/krisbunda.com\/blog\/2026\/09\/03\/auto-update-your-readmes-ascii-directory-tree-with-github-actions\/"}],"taxonomy_info":{"category":[{"value":4,"label":"Designer"}],"post_tag":[{"value":953,"label":"DevOps"},{"value":954,"label":"GitHub"},{"value":36,"label":"tutorial"},{"value":772,"label":"web development"}]},"featured_image_src_large":["https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE-1200x676.webp",1200,676,true],"author_info":{"display_name":"Kris Bunda","author_link":"https:\/\/krisbunda.com\/blog\/author\/kris-bunda\/"},"comment_info":0,"category_info":[{"term_id":4,"name":"Designer","slug":"designer","term_group":0,"term_taxonomy_id":4,"taxonomy":"category","description":"Posts focusing on web, graphic, CAD, and other design.","parent":0,"count":92,"filter":"raw","cat_ID":4,"category_count":92,"category_description":"Posts focusing on web, graphic, CAD, and other design.","cat_name":"Designer","category_nicename":"designer","category_parent":0}],"tag_info":[{"term_id":953,"name":"DevOps","slug":"devops","term_group":0,"term_taxonomy_id":957,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":954,"name":"GitHub","slug":"github","term_group":0,"term_taxonomy_id":958,"taxonomy":"post_tag","description":"","parent":0,"count":1,"filter":"raw"},{"term_id":36,"name":"tutorial","slug":"tutorial","term_group":0,"term_taxonomy_id":36,"taxonomy":"post_tag","description":"","parent":0,"count":34,"filter":"raw"},{"term_id":772,"name":"web development","slug":"web-development","term_group":0,"term_taxonomy_id":776,"taxonomy":"post_tag","description":"","parent":0,"count":6,"filter":"raw"}],"amp_enabled":true,"jetpack_featured_media_url":"https:\/\/krisbunda.com\/blog\/wp-content\/uploads\/2026\/09\/GITHUB_ACTIONS_REPO_DIRECTORY_TREE.webp","_links":{"self":[{"href":"https:\/\/krisbunda.com\/blog\/wp-json\/wp\/v2\/posts\/9844","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/krisbunda.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/krisbunda.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/krisbunda.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/krisbunda.com\/blog\/wp-json\/wp\/v2\/comments?post=9844"}],"version-history":[{"count":21,"href":"https:\/\/krisbunda.com\/blog\/wp-json\/wp\/v2\/posts\/9844\/revisions"}],"predecessor-version":[{"id":9885,"href":"https:\/\/krisbunda.com\/blog\/wp-json\/wp\/v2\/posts\/9844\/revisions\/9885"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/krisbunda.com\/blog\/wp-json\/wp\/v2\/media\/9873"}],"wp:attachment":[{"href":"https:\/\/krisbunda.com\/blog\/wp-json\/wp\/v2\/media?parent=9844"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/krisbunda.com\/blog\/wp-json\/wp\/v2\/categories?post=9844"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/krisbunda.com\/blog\/wp-json\/wp\/v2\/tags?post=9844"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}