feat: include Go codegen

See https://github.com/bazel-contrib/rules_go/issues/3658
diff --git a/examples/proto/BUILD.bazel b/examples/proto/BUILD.bazel
index 588f1ea..74446b7 100644
--- a/examples/proto/BUILD.bazel
+++ b/examples/proto/BUILD.bazel
@@ -1,8 +1,8 @@
 load("@aspect_rules_ts//ts:proto.bzl", "ts_proto_library")
-load("@rules_go//proto:def.bzl", "go_proto_library")
 load("@rules_proto//proto:defs.bzl", "proto_library")
 load("@rules_rust_prost//:defs.bzl", "rust_prost_library")
 load("@protobuf//bazel:py_proto_library.bzl", "py_proto_library")
+load("//tools:go_proto_library.bzl", "go_proto_library")
 
 package(default_visibility = ["//visibility:public"])
 
diff --git a/examples/proto/greeter.pb.go b/examples/proto/greeter.pb.go
new file mode 100644
index 0000000..af1da7a
--- /dev/null
+++ b/examples/proto/greeter.pb.go
@@ -0,0 +1,195 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.36.3
+// 	protoc        v5.28.0
+// source: proto/greeter.proto
+
+package greeter_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	anypb "google.golang.org/protobuf/types/known/anypb"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type HelloRequest struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Name          string                 `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	Details       []*anypb.Any           `protobuf:"bytes,2,rep,name=details,proto3" json:"details,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *HelloRequest) Reset() {
+	*x = HelloRequest{}
+	mi := &file_proto_greeter_proto_msgTypes[0]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *HelloRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*HelloRequest) ProtoMessage() {}
+
+func (x *HelloRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_greeter_proto_msgTypes[0]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.
+func (*HelloRequest) Descriptor() ([]byte, []int) {
+	return file_proto_greeter_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *HelloRequest) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *HelloRequest) GetDetails() []*anypb.Any {
+	if x != nil {
+		return x.Details
+	}
+	return nil
+}
+
+type HelloReply struct {
+	state         protoimpl.MessageState `protogen:"open.v1"`
+	Message       string                 `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *HelloReply) Reset() {
+	*x = HelloReply{}
+	mi := &file_proto_greeter_proto_msgTypes[1]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *HelloReply) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*HelloReply) ProtoMessage() {}
+
+func (x *HelloReply) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_greeter_proto_msgTypes[1]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use HelloReply.ProtoReflect.Descriptor instead.
+func (*HelloReply) Descriptor() ([]byte, []int) {
+	return file_proto_greeter_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *HelloReply) GetMessage() string {
+	if x != nil {
+		return x.Message
+	}
+	return ""
+}
+
+var File_proto_greeter_proto protoreflect.FileDescriptor
+
+var file_proto_greeter_proto_rawDesc = []byte{
+	0x0a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f,
+	0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e,
+	0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x52, 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x64,
+	0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67,
+	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41,
+	0x6e, 0x79, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x26, 0x0a, 0x0a, 0x48,
+	0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73,
+	0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73,
+	0x61, 0x67, 0x65, 0x32, 0x3f, 0x0a, 0x07, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x34,
+	0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+	0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70,
+	0x6c, 0x79, 0x22, 0x00, 0x42, 0x22, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x19, 0x65,
+	0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74,
+	0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_proto_greeter_proto_rawDescOnce sync.Once
+	file_proto_greeter_proto_rawDescData = file_proto_greeter_proto_rawDesc
+)
+
+func file_proto_greeter_proto_rawDescGZIP() []byte {
+	file_proto_greeter_proto_rawDescOnce.Do(func() {
+		file_proto_greeter_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_greeter_proto_rawDescData)
+	})
+	return file_proto_greeter_proto_rawDescData
+}
+
+var file_proto_greeter_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_proto_greeter_proto_goTypes = []any{
+	(*HelloRequest)(nil), // 0: proto.HelloRequest
+	(*HelloReply)(nil),   // 1: proto.HelloReply
+	(*anypb.Any)(nil),    // 2: google.protobuf.Any
+}
+var file_proto_greeter_proto_depIdxs = []int32{
+	2, // 0: proto.HelloRequest.details:type_name -> google.protobuf.Any
+	0, // 1: proto.Greeter.SayHello:input_type -> proto.HelloRequest
+	1, // 2: proto.Greeter.SayHello:output_type -> proto.HelloReply
+	2, // [2:3] is the sub-list for method output_type
+	1, // [1:2] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_proto_greeter_proto_init() }
+func file_proto_greeter_proto_init() {
+	if File_proto_greeter_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_proto_greeter_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_proto_greeter_proto_goTypes,
+		DependencyIndexes: file_proto_greeter_proto_depIdxs,
+		MessageInfos:      file_proto_greeter_proto_msgTypes,
+	}.Build()
+	File_proto_greeter_proto = out.File
+	file_proto_greeter_proto_rawDesc = nil
+	file_proto_greeter_proto_goTypes = nil
+	file_proto_greeter_proto_depIdxs = nil
+}
diff --git a/examples/tools/go_proto_library.bzl b/examples/tools/go_proto_library.bzl
new file mode 100644
index 0000000..3bbaa63
--- /dev/null
+++ b/examples/tools/go_proto_library.bzl
@@ -0,0 +1,44 @@
+"go_proto_library wrapper macro to check in the resulting stub files for the editor"
+
+load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files")
+load("@aspect_bazel_lib//lib:output_files.bzl", "make_output_files")
+load("@bazel_skylib//lib:paths.bzl", "paths")
+load("@rules_go//proto:def.bzl", _go_proto_library = "go_proto_library")
+
+def go_proto_library(name, importpath, proto_srcs = [], **kwargs):
+    """Wrap go_proto_library with write_source_files.
+
+    This causes the resulting .pb.go files to be checked into the source tree.
+    Args:
+        name: name of the go_proto_library rule produced
+        importpath: passed to go_proto_library#importpath
+        proto_srcs: the srcs of the proto_library target passed to go_proto_library#proto
+            If unset, a glob() of all ".proto" files in the package is used.
+        **kwargs: remaining arguments to go_proto_library
+    """
+
+    # Based on our knowledge of the rule implementation,
+    # predict the output paths it writes.
+    proto_out_path = "{0}/{1}_/{2}/%s.pb.go".format(
+        native.package_name(),
+        name,
+        importpath,
+    )
+
+    if len(proto_srcs) < 1:
+        proto_srcs = native.glob(["*.proto"])
+
+    _go_proto_library(
+        name = name,
+        importpath = importpath,
+        **kwargs
+    )
+
+    write_source_files(
+        name = name + ".update_go_pb",
+        files = {
+            base + ".pb.go": make_output_files(base + "_pb_go", name, [proto_out_path % base], output_group = "go_generated_srcs")
+            for base in [paths.replace_extension(p, "") for p in proto_srcs]
+        },
+        visibility = ["//write_source_files:__pkg__"],
+    )