diff --git a/.github/scripts/generate-prdoc.py b/.github/scripts/generate-prdoc.py
index ba7def20fcb99bb48830a0a75d789a1ca8c20397..a6a97008dca638f1083b13fdd7fd10672bba9835 100644
--- a/.github/scripts/generate-prdoc.py
+++ b/.github/scripts/generate-prdoc.py
@@ -48,9 +48,8 @@ def create_prdoc(pr, audience, title, description, patch, bump, force):
 	else:
 		print(f"No preexisting PrDoc for PR {pr}")
 
-	prdoc = { "doc": [{}], "crates": [] }
+	prdoc = { "title": title, "doc": [{}], "crates": [] }
 
-	prdoc["title"] = title
 	prdoc["doc"][0]["audience"] = audience
 	prdoc["doc"][0]["description"] = description
 
@@ -58,13 +57,19 @@ def create_prdoc(pr, audience, title, description, patch, bump, force):
 
 	modified_paths = []
 	for diff in whatthepatch.parse_patch(patch):
-		modified_paths.append(diff.header.new_path)
+		new_path = diff.header.new_path
+		# Sometimes this lib returns `/dev/null` as the new path...
+		if not new_path.startswith("/dev"):
+			modified_paths.append(new_path)
 
 	modified_crates = {}
 	for p in modified_paths:
 		# Go up until we find a Cargo.toml
 		p = os.path.join(workspace.path, p)
 		while not os.path.exists(os.path.join(p, "Cargo.toml")):
+			print(f"Could not find Cargo.toml in {p}")
+			if p == '/':
+				exit(1)
 			p = os.path.dirname(p)
 		
 		with open(os.path.join(p, "Cargo.toml")) as f:
@@ -95,9 +100,19 @@ def create_prdoc(pr, audience, title, description, patch, bump, force):
 
 	# write the parsed PR documentation back to the file
 	with open(path, "w") as f:
-		yaml.dump(prdoc, f)
+		yaml.dump(prdoc, f, sort_keys=False)
 		print(f"PrDoc for PR {pr} written to {path}")
 
+# Make the `description` a multiline string instead of escaping \r\n.
+def setup_yaml():
+	def yaml_multiline_string_presenter(dumper, data):
+		if len(data.splitlines()) > 1:
+			data = '\n'.join([line.rstrip() for line in data.strip().splitlines()])
+			return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
+		return dumper.represent_scalar('tag:yaml.org,2002:str', data)
+
+	yaml.add_representer(str, yaml_multiline_string_presenter)
+
 def parse_args():
 	parser = argparse.ArgumentParser()
 	parser.add_argument("--pr", type=int, required=True)
@@ -108,6 +123,7 @@ def parse_args():
 
 if __name__ == "__main__":
 	args = parse_args()
-	force = True if args.force.lower() == "true" else False
+	force = True if (args.force or "false").lower() == "true" else False
 	print(f"Args: {args}, force: {force}")
+	setup_yaml()
 	from_pr_number(args.pr, args.audience, args.bump, force)
diff --git a/.github/workflows/command-prdoc.yml b/.github/workflows/command-prdoc.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3a08b9a5fb286a7757715ac37006cf093b2b8b15
--- /dev/null
+++ b/.github/workflows/command-prdoc.yml
@@ -0,0 +1,90 @@
+name: Command PrDoc
+
+on:
+  workflow_dispatch:
+    inputs:
+      pr:
+        type: number
+        description: Number of the Pull Request
+        required: true
+      bump:
+        type: choice
+        description: Default bump level for all crates
+        default: "TODO"
+        required: true
+        options:
+          - "TODO"
+          - "no change"
+          - "patch"
+          - "minor"
+          - "major"
+      audience:
+        type: choice
+        description: Audience of the PrDoc
+        default: "TODO"
+        required: true
+        options:
+          - "TODO"
+          - "Runtime Dev"
+          - "Runtime User"
+          - "Node Dev"
+          - "Node User"
+      overwrite:
+        type: choice
+        description: Overwrite existing PrDoc
+        default: "true"
+        required: true
+        options:
+          - "true"
+          - "false"
+
+concurrency:
+  group: command-prdoc
+  cancel-in-progress: true
+
+jobs:
+  set-image:
+    runs-on: ubuntu-latest
+    outputs:
+      IMAGE: ${{ steps.set_image.outputs.IMAGE }}
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v4
+      - id: set_image
+        run: cat .github/env >> $GITHUB_OUTPUT
+  cmd-prdoc:
+    needs: [set-image]
+    runs-on: ubuntu-latest
+    timeout-minutes: 20
+    container:
+      image: ${{ needs.set-image.outputs.IMAGE }}
+    permissions:
+      contents: write
+      pull-requests: write
+    steps:
+      - name: Download repo
+        uses: actions/checkout@v4
+      - name: Install gh cli
+        id: gh
+        uses: ./.github/actions/set-up-gh
+        with:
+          pr-number: ${{ inputs.pr }}
+          GH_TOKEN: ${{ github.token }}
+      - name: Generate PrDoc
+        run: |
+          python3 -m pip install -q cargo-workspace PyGithub whatthepatch pyyaml toml
+
+          python3 .github/scripts/generate-prdoc.py --pr "${{ inputs.pr }}" --bump "${{ inputs.bump }}" --audience "${{ inputs.audience }}" --force "${{ inputs.overwrite }}"
+
+      - name: Report failure
+        if: ${{ failure() }}
+        run: gh pr comment ${{ inputs.pr }} --body "<h2>Command failed ❌</h2> Run by @${{ github.actor }} for <code>${{ github.workflow }}</code> failed. See logs <a href=\"$RUN\">here</a>."
+        env:
+          RUN: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+          GH_TOKEN: ${{ github.token }}
+      - name: Push Commit
+        uses: stefanzweifel/git-auto-commit-action@v5
+        with:
+          commit_message: Add PrDoc (auto generated)
+          branch: ${{ steps.gh.outputs.branch }}
+          file_pattern: 'prdoc/*.prdoc'