Integrate Black formatter with Bazel for local development (#244)

fix: Integrate black formatter with Bazel for local development

Adds two bazel run targets for Python code formatting:
- //:black_fix - Reformats Python files in-place
- //:black_check - Checks formatting without modifying files

Resolves Issue #226
diff --git a/BUILD b/BUILD
index 4642551..0e627ad 100644
--- a/BUILD
+++ b/BUILD
@@ -12,7 +12,33 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+load("@pip//:requirements.bzl", "requirement")
+load("@rules_python//python:py_binary.bzl", "py_binary")
+load("@rules_shell//shell:sh_binary.bzl", "sh_binary")
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
+
 exports_files([
     "build_defs.bzl",
     "LICENSE",
 ])
+
+# Black formatter binary
+py_binary(
+    name = "black_runner",
+    srcs = ["scripts/black_runner.py"],
+    deps = [requirement("black")],
+)
+
+# Fix formatting: bazel run //:black_fix -- .
+sh_binary(
+    name = "black_fix",
+    srcs = ["scripts/black_fix.sh"],
+    data = [":black_runner"],
+)
+
+# Check formatting: bazel run //:black_check
+sh_binary(
+    name = "black_check",
+    srcs = ["scripts/black_check.sh"],
+    data = [":black_runner"],
+)
diff --git a/MODULE.bazel b/MODULE.bazel
index 2201b56..42ba8e4 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -16,3 +16,12 @@
 )
 bazel_dep(name = "rules_python", version = "1.0.0")
 bazel_dep(name = "rules_cc", version = "0.1.0")
+bazel_dep(name = "rules_shell", version = "0.3.0")
+
+pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
+pip.parse(
+    hub_name = "pip",
+    python_version = "3.11",
+    requirements_lock = "//:requirements.txt",
+)
+use_repo(pip, "pip")
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..aaa2d91
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,10 @@
+# Python dependencies for Emboss development tools
+# Managed by Bazel via rules_python pip.parse extension
+
+# Black formatter and its dependencies
+black==24.8.0
+click>=8.0.0
+mypy_extensions>=0.4.3
+packaging>=22.0
+pathspec>=0.9.0
+platformdirs>=2
diff --git a/scripts/black_check.sh b/scripts/black_check.sh
new file mode 100755
index 0000000..dcd804a
--- /dev/null
+++ b/scripts/black_check.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# Copyright 2026 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Checks Python formatting without modifying files.
+# Usage: bazel run //:black_check
+
+# Change to workspace directory
+cd "${BUILD_WORKSPACE_DIRECTORY}" || exit 1
+
+# Find the black_runner binary in runfiles
+RUNFILES_DIR="${BASH_SOURCE[0]}.runfiles"
+BLACK_RUNNER="${RUNFILES_DIR}/_main/black_runner"
+
+exec "${BLACK_RUNNER}" --check --diff .
diff --git a/scripts/black_fix.sh b/scripts/black_fix.sh
new file mode 100755
index 0000000..c7c02fa
--- /dev/null
+++ b/scripts/black_fix.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# Copyright 2026 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Runs black to fix Python formatting in-place.
+# Usage: bazel run //:black_fix -- .
+
+# Change to workspace directory
+cd "${BUILD_WORKSPACE_DIRECTORY}" || exit 1
+
+# Find the black_runner binary in runfiles
+RUNFILES_DIR="${BASH_SOURCE[0]}.runfiles"
+BLACK_RUNNER="${RUNFILES_DIR}/_main/black_runner"
+
+exec "${BLACK_RUNNER}" "$@"
diff --git a/scripts/black_runner.py b/scripts/black_runner.py
new file mode 100644
index 0000000..646b706
--- /dev/null
+++ b/scripts/black_runner.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+
+# Copyright 2026 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Entry point for running black formatter via Bazel."""
+
+import sys
+
+import black
+
+if __name__ == "__main__":
+    sys.exit(black.main())