diff --git a/action.yml b/action.yml
new file mode 100644
index 0000000..6521197
--- /dev/null
+++ b/action.yml
@@ -0,0 +1,73 @@
+name: "Terraform/OpenTofu PR Commenter"
+description: "Posts Terraform/OpenTofu `fmt`, `init`, `plan`, or `validate` results as PR comments on GitHub or Gitea."
+author: "Charish Patel"
+
+# Define the inputs
+inputs:
+ command:
+ description: "The command type: fmt | init | plan | validate"
+ required: true
+ commenter_input:
+ description: "Stdout or file list from previous Terraform/OpenTofu step"
+ required: true
+ commenter_exitcode:
+ description: "Exit code from previous Terraform/OpenTofu step"
+ required: true
+ pr_number:
+ description: "PR number"
+ required: false
+ pr_comments_url:
+ description: "URL for PR comments API"
+ required: false
+ pr_comment_uri:
+ description: "URI for individual PR comment API"
+ required: false
+ github_token:
+ description: "GitHub token (if running in GitHub Actions)"
+ required: false
+ gitea_token:
+ description: "Gitea token (if running in Gitea Actions)"
+ required: false
+ gitea_action:
+ description: "Set to true if running in Gitea Actions"
+ required: false
+ default: "false"
+ gitea_api_url:
+ description: "Gitea API URL"
+ required: false
+ gitea_repository:
+ description: "Gitea repository in format owner/repo"
+ required: false
+ highlight_changes:
+ description: "Set to false to disable diff highlighting"
+ required: false
+ default: "true"
+ tf_workspace:
+ description: "Terraform/OpenTofu workspace (default: 'default')"
+ required: false
+ default: default
+
+
+# Define the action type
+runs:
+ using: "composite"
+ steps:
+ - name: Run TF/Tofu PR Commenter
+ shell: bash
+ run: |
+ chmod +x ${{ github.action_path }}/tf-pr-comment.sh
+ ${{ github.action_path }}/tf-pr-comment.sh "${{ inputs.command }}" "${{ inputs.commenter_input }}" "${{ inputs.commenter_exitcode }}"
+ env:
+ # PR Environment Variables
+ PR_NUMBER: ${{ inputs.pr_number }}
+ PR_COMMENTS_URL: ${{ inputs.pr_comments_url }}
+ PR_COMMENT_URI: ${{ inputs.pr_comment_uri }}
+ GITHUB_TOKEN: ${{ inputs.github_token }}
+ GITEA_TOKEN: ${{ inputs.gitea_token }}
+ GITEA_ACTION: ${{ inputs.gitea_action }}
+ GITEA_API_URL: ${{ inputs.gitea_api_url }}
+ GITEA_REPOSITORY: ${{ inputs.gitea_repository }}
+ # Optional highlighting
+ HIGHLIGHT_CHANGES: ${{ inputs.highlight_changes }}
+ TF_WORKSPACE: ${{ inputs.tf_workspace }}
+
diff --git a/tf-pr-comment.sh b/tf-pr-comment.sh
new file mode 100644
index 0000000..75fe08f
--- /dev/null
+++ b/tf-pr-comment.sh
@@ -0,0 +1,148 @@
+#!/usr/bin/env bash
+set -e
+
+##################
+# Inputs
+##################
+COMMAND="$1" # fmt | init | plan | validate
+COMMENTER_INPUT="$2" # stdout/stderr output OR newline-separated file list (for fmt)
+COMMENTER_EXITCODE="$3" # exit code from previous step
+WORKSPACE="${TF_WORKSPACE:-default}"
+
+##################
+# CI Detection
+##################
+if [[ -n "$GITHUB_ACTIONS" ]]; then
+ CI_PLATFORM="github"
+elif [[ -n "$GITEA_ACTION" ]]; then
+ CI_PLATFORM="gitea"
+else
+ CI_PLATFORM="local"
+fi
+echo "Detected CI platform: $CI_PLATFORM"
+
+##################
+# PR Variables
+##################
+if [[ "$CI_PLATFORM" == "github" ]]; then
+ : "${PR_NUMBER:?PR_NUMBER not set}"
+ : "${PR_COMMENTS_URL:?PR_COMMENTS_URL not set}"
+ : "${PR_COMMENT_URI:?PR_COMMENT_URI not set}"
+ AUTH_HEADER="Authorization: token $GITHUB_TOKEN"
+elif [[ "$CI_PLATFORM" == "gitea" ]]; then
+ : "${PR_NUMBER:?PR_NUMBER not set}"
+ : "${PR_COMMENTS_URL:?PR_COMMENTS_URL not set}"
+ : "${PR_COMMENT_URI:?PR_COMMENT_URI not set}"
+ : "${GITEA_TOKEN:?GITEA_TOKEN not set}"
+ AUTH_HEADER="Authorization: token $GITEA_TOKEN"
+else
+ echo "Local mode: PR variables must be set manually."
+ exit 1
+fi
+
+ACCEPT_HEADER="Accept: application/vnd.github.v3+json"
+CONTENT_HEADER="Content-Type: application/json"
+
+##################
+# Shared Settings
+##################
+DETAILS_STATE=" open"
+COLOURISE=${HIGHLIGHT_CHANGES:-true}
+
+##################
+# Helper Functions
+##################
+handle_pr_comment() {
+ local type="$1"
+ local body="$2"
+
+ PR_COMMENT_ID=$(curl -sS -H "$AUTH_HEADER" -H "$ACCEPT_HEADER" -L "$PR_COMMENTS_URL" \
+ | jq ".[] | select(.body|test(\"### tofu \\\`$type\\\`\")) | .id")
+
+ if [[ "$PR_COMMENT_ID" ]]; then
+ echo "Updating existing $type PR comment: $PR_COMMENT_ID"
+ PR_COMMENT_URL="$PR_COMMENT_URI/$PR_COMMENT_ID"
+ PR_PAYLOAD=$(jq -n --arg body "$body" '{body: $body}')
+ curl -sS -X PATCH -H "$AUTH_HEADER" -H "$ACCEPT_HEADER" -H "$CONTENT_HEADER" \
+ -d "$PR_PAYLOAD" -L "$PR_COMMENT_URL" > /dev/null
+ else
+ echo "Creating new $type PR comment"
+ PR_PAYLOAD=$(jq -n --arg body "$body" '{body: $body}')
+ curl -sS -X POST -H "$AUTH_HEADER" -H "$ACCEPT_HEADER" -H "$CONTENT_HEADER" \
+ -d "$PR_PAYLOAD" -L "$PR_COMMENTS_URL" > /dev/null
+ fi
+}
+
+format_diff_block() {
+ local content="$1"
+ local codeblock=""
+ if [[ $CI_PLATFORM == "github" && $COLOURISE == "true" ]]; then
+ content=$(echo "$content" | sed -r 's/^~/!/g')
+ codeblock="diff"
+ fi
+ echo "```$codeblock
+$content
+```"
+}
+
+##################
+# Command Handlers
+##################
+case "$COMMAND" in
+
+fmt)
+ STATUS="Succeeded"
+ [[ "$COMMENTER_EXITCODE" -ne 0 ]] && STATUS="Failed"
+
+ ALL_DIFFS=""
+ # COMMENTER_INPUT is expected to be a newline-separated file list
+ while IFS= read -r file; do
+ if [[ -n "$file" && -f "$file" ]]; then
+ FILE_DIFF=$(tofu fmt -no-color -write=false -diff "$file" 2>/dev/null || true)
+ DIFF_BLOCK=$(format_diff_block "$FILE_DIFF")
+ ALL_DIFFS="$ALL_DIFFS
+$file
+
+$DIFF_BLOCK
+ "
+ fi
+ done <<< "$COMMENTER_INPUT"
+
+ # Fallback if no valid files found
+ [[ -z "$ALL_DIFFS" ]] && ALL_DIFFS=$(format_diff_block "$COMMENTER_INPUT")
+
+ PR_COMMENT="### tofu \`fmt\` $STATUS for Workspace: \`$WORKSPACE\`
+$ALL_DIFFS"
+ handle_pr_comment "$COMMAND" "$PR_COMMENT"
+ ;;
+
+plan)
+ STATUS="Succeeded"
+ [[ "$COMMENTER_EXITCODE" -ne 0 ]] && STATUS="Failed"
+ PR_OUTPUT=$(format_diff_block "$COMMENTER_INPUT")
+ PR_COMMENT="### tofu \`plan\` $STATUS for Workspace: \`$WORKSPACE\`
+Show Output
+
+$PR_OUTPUT
+ "
+ handle_pr_comment "$COMMAND" "$PR_COMMENT"
+ ;;
+
+init|validate)
+ STATUS="Succeeded"
+ [[ "$COMMENTER_EXITCODE" -ne 0 ]] && STATUS="Failed"
+ PR_COMMENT="### tofu \`$COMMAND\` $STATUS for Workspace: \`$WORKSPACE\`
+Show Output
+
+\`\`\`
+$COMMENTER_INPUT
+\`\`\`
+ "
+ handle_pr_comment "$COMMAND" "$PR_COMMENT"
+ ;;
+
+*)
+ echo "Unsupported command: $COMMAND"
+ exit 1
+ ;;
+esac