diff --git a/.github/workflows/sync-java-api-and-pitfalls.yml b/.github/workflows/sync-java-api-and-pitfalls.yml
new file mode 100644
index 00000000..c864105c
--- /dev/null
+++ b/.github/workflows/sync-java-api-and-pitfalls.yml
@@ -0,0 +1,59 @@
+name: Sync java-api-and-pitfalls.md
+
+on:
+ schedule:
+ - cron: "0 8 * * 1"
+ workflow_dispatch:
+
+jobs:
+ sync:
+ runs-on: ubuntu-latest
+ if: github.repository_owner == 'restatedev'
+ permissions:
+ contents: write
+ pull-requests: write
+
+ env:
+ SOURCE_URL: https://raw.githubusercontent.com/restatedev/docs-restate/main/restate-plugin/skills/building-restate-services/references/java/api-and-pitfalls.md
+ DEST_FILENAME: java-api-and-pitfalls.md
+ TEMPLATES: |
+ java-gradle
+ java-maven
+ java-maven-quarkus
+ java-maven-spring-boot
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Download source file
+ run: curl -fsSL "$SOURCE_URL" -o /tmp/api-and-pitfalls.md
+
+ - name: Copy into each Java template with frontmatter
+ run: |
+ {
+ echo '---'
+ echo 'apply: by model decision'
+ echo 'instructions: Java SDK API reference and common pitfalls for Restate durable services'
+ echo '---'
+ echo
+ cat /tmp/api-and-pitfalls.md
+ } > /tmp/java-api-and-pitfalls.md
+
+ while IFS= read -r template; do
+ [ -z "$template" ] && continue
+ dest_dir="java/templates/$template/.aiassistant/rules"
+ mkdir -p "$dest_dir"
+ cp /tmp/java-api-and-pitfalls.md "$dest_dir/$DEST_FILENAME"
+ done <<< "$TEMPLATES"
+
+ - name: Create pull request if changed
+ uses: peter-evans/create-pull-request@v6
+ with:
+ commit-message: "chore: sync java-api-and-pitfalls.md from docs-restate"
+ title: "chore: sync java-api-and-pitfalls.md from docs-restate"
+ body: |
+ Automated weekly sync of `java-api-and-pitfalls.md` from
+ [restatedev/docs-restate](https://github.com/restatedev/docs-restate/blob/main/restate-plugin/skills/building-restate-services/references/java/api-and-pitfalls.md)
+ into each Java template under `java/templates/*/.aiassistant/rules/`.
+ branch: chore/sync-java-api-and-pitfalls
+ delete-branch: true
diff --git a/go/templates/go/.agents/plugins/marketplace.json b/go/templates/go/.agents/plugins/marketplace.json
new file mode 100644
index 00000000..3f69a183
--- /dev/null
+++ b/go/templates/go/.agents/plugins/marketplace.json
@@ -0,0 +1,22 @@
+{
+ "name": "restatedev-plugin",
+ "interface": {
+ "displayName": "Restate"
+ },
+ "plugins": [
+ {
+ "name": "restatedev",
+ "source": {
+ "source": "git-subdir",
+ "url": "https://github.com/restatedev/skills.git",
+ "path": "./plugins/restatedev",
+ "ref": "main"
+ },
+ "policy": {
+ "installation": "AVAILABLE",
+ "authentication": "NONE"
+ },
+ "category": "Coding"
+ }
+ ]
+}
diff --git a/go/templates/go/.claude/CLAUDE.md b/go/templates/go/.claude/CLAUDE.md
deleted file mode 100644
index 3801be90..00000000
--- a/go/templates/go/.claude/CLAUDE.md
+++ /dev/null
@@ -1,399 +0,0 @@
-# Restate Go SDK Rules
-
-## Core Concepts
-
-* Restate provides durable execution: code automatically stores completed steps and resumes from where it left off on failures
-* All handlers receive a `Context`/`ObjectContext`/`WorkflowContext`/`ObjectSharedContext`/`WorkflowSharedContext` object as the first argument
-* Handlers can take typed inputs and return typed outputs using Go structs and JSON serialization
-
-## Service Types
-
-### Basic Services
-
-```go {"CODE_LOAD::go/develop/myservice/main.go"} theme={null}
-package main
-
-import (
- "context"
- "fmt"
- "log"
-
- restate "github.com/restatedev/sdk-go"
- server "github.com/restatedev/sdk-go/server"
-)
-
-type MyService struct{}
-
-func (MyService) MyHandler(ctx restate.Context, greeting string) (string, error) {
- return fmt.Sprintf("%s!", greeting), nil
-}
-
-func main() {
- if err := server.NewRestate().
- Bind(restate.Reflect(MyService{})).
- Start(context.Background(), "0.0.0.0:9080"); err != nil {
- log.Fatal(err)
- }
-}
-```
-
-### Virtual Objects (Stateful, Key-Addressable)
-
-```go {"CODE_LOAD::go/develop/myvirtualobject/main.go"} theme={null}
-package main
-
-import (
- "context"
- "fmt"
- "log"
-
- restate "github.com/restatedev/sdk-go"
- server "github.com/restatedev/sdk-go/server"
-)
-
-type MyObject struct{}
-
-func (MyObject) MyHandler(ctx restate.ObjectContext, greeting string) (string, error) {
- return fmt.Sprintf("%s %s!", greeting, restate.Key(ctx)), nil
-}
-
-func (MyObject) MyConcurrentHandler(ctx restate.ObjectSharedContext, greeting string) (string, error) {
- return fmt.Sprintf("%s %s!", greeting, restate.Key(ctx)), nil
-}
-
-func main() {
- if err := server.NewRestate().
- Bind(restate.Reflect(MyObject{})).
- Start(context.Background(), "0.0.0.0:9080"); err != nil {
- log.Fatal(err)
- }
-}
-```
-
-### Workflows
-
-```go {"CODE_LOAD::go/develop/myworkflow/main.go"} theme={null}
-package myworkflow
-
-import (
- "context"
- restate "github.com/restatedev/sdk-go"
- "github.com/restatedev/sdk-go/server"
- "log/slog"
- "os"
-)
-
-type MyWorkflow struct{}
-
-func (MyWorkflow) Run(ctx restate.WorkflowContext, req string) (string, error) {
- // implement the workflow logic here
- return "success", nil
-}
-
-func (MyWorkflow) InteractWithWorkflow(ctx restate.WorkflowSharedContext) error {
- // implement interaction logic here
- // e.g. resolve a promise that the workflow is waiting on
- return nil
-}
-
-func main() {
- server := server.NewRestate().
- Bind(restate.Reflect(MyWorkflow{}))
-
- if err := server.Start(context.Background(), ":9080"); err != nil {
- slog.Error("application exited unexpectedly", "err", err.Error())
- os.Exit(1)
- }
-}
-```
-
-## Context Operations
-
-### State Management (Virtual Objects & Workflows only)
-
-❌ Never use global variables - not durable, lost across replicas.
-✅ Use `restate.Get()` and `restate.Set()` - durable and scoped to the object's key.
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#state"} theme={null}
-// Get state keys
-stateKeys, err := restate.Keys(ctx)
-if err != nil {
- return err
-}
-_ = stateKeys
-
-// Get state
-myString := "my-default"
-if s, err := restate.Get[*string](ctx, "my-string-key"); err != nil {
- return err
-} else if s != nil {
- myString = *s
-}
-
-count, err := restate.Get[int](ctx, "count")
-if err != nil {
- return err
-}
-
-// Set state
-restate.Set(ctx, "my-key", "my-new-value")
-restate.Set(ctx, "count", count+1)
-
-// Clear state
-restate.Clear(ctx, "my-key")
-restate.ClearAll(ctx)
-```
-
-### Service Communication
-
-#### Request-Response
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#service_calls"} theme={null}
-// Call a Service
-svcResponse, err := restate.Service[string](ctx, "MyService", "MyHandler").
- Request(request)
-if err != nil {
- return err
-}
-
-// Call a Virtual Object
-objResponse, err := restate.Object[string](ctx, "MyObject", objectKey, "MyHandler").
- Request(request)
-if err != nil {
- return err
-}
-
-// Call a Workflow
-wfResponse, err := restate.Workflow[string](ctx, "MyWorkflow", workflowId, "Run").
- Request(request)
-if err != nil {
- return err
-}
-```
-
-#### One-Way Messages
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#sending_messages"} theme={null}
-// Send to service
-restate.ServiceSend(ctx, "MyService", "MyHandler").Send(request)
-
-// Send to virtual object
-restate.ObjectSend(ctx, "MyObject", objectKey, "MyHandler").Send(request)
-
-// Send to workflow
-restate.WorkflowSend(ctx, "MyWorkflow", workflowId, "Run").Send(request)
-```
-
-#### Delayed Messages
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#delayed_messages"} theme={null}
-restate.ServiceSend(ctx, "MyService", "MyHandler").Send(request, restate.WithDelay(5*time.Hour))
-```
-
-### Run Actions or Side Effects (Non-Deterministic Operations)
-
-❌ Never call external APIs/DBs directly - will re-execute during replay, causing duplicates.
-✅ Wrap in `restate.Run()` - Restate journals the result; runs only once.
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#durable_steps"} theme={null}
-// Wrap non-deterministic code in restate.Run
-result, err := restate.Run(ctx, func(ctx restate.RunContext) (string, error) {
- return callExternalAPI(), nil
-})
-if err != nil {
- return err
-}
-```
-
-### Deterministic randoms and time
-
-❌ Never use `rand.Float64()` - non-deterministic and breaks replay logic.
-✅ Use `restate.Rand()` or `restate.UUID()` - Restate journals the result for deterministic replay.
-
-```go {"CODE_LOAD::go/develop/journalingresults.go#uuid"} theme={null}
-uuid := restate.UUID(ctx)
-```
-
-```go {"CODE_LOAD::go/develop/journalingresults.go#random_nb"} theme={null}
-randomInt := restate.Rand(ctx).Uint64()
-randomFloat := restate.Rand(ctx).Float64()
-mathRandV2 := rand.New(restate.RandSource(ctx))
-```
-
-❌ Never use `time.Now()` - returns different values during replay.
-✅ Wrap `time.Now()` in `restate.Run` to let Restate record the timestamp.
-
-### Durable Timers and Sleep
-
-❌ Never use `time.Sleep()` or timers - not durable, lost on restarts.
-✅ Use `restate.Sleep()` - durable timer that survives failures.
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#durable_timers"} theme={null}
-// Sleep
-err := restate.Sleep(ctx, 30*time.Second)
-if err != nil {
- return err
-}
-
-// Schedule delayed call (different from sleep + send)
-restate.ServiceSend(ctx, "MyService", "MyHandler").
- Send("Hi", restate.WithDelay(5*time.Hour))
-```
-
-### Awakeables (External Events)
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#awakeables"} theme={null}
-// Create awakeable
-awakeable := restate.Awakeable[string](ctx)
-awakeableId := awakeable.Id()
-
-// Send ID to external system
-if _, err := restate.Run(ctx, func(ctx restate.RunContext) (string, error) {
- return requestHumanReview(name, awakeableId), nil
-}); err != nil {
- return err
-}
-
-// Wait for result
-review, err := awakeable.Result()
-if err != nil {
- return err
-}
-
-// Resolve from another handler
-restate.ResolveAwakeable(ctx, awakeableId, "Looks good!")
-
-// Reject from another handler
-restate.RejectAwakeable(ctx, awakeableId, fmt.Errorf("Cannot be reviewed"))
-```
-
-### Durable Promises (Workflows only)
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#workflow_promises"} theme={null}
-// Wait for promise
-promise := restate.Promise[string](ctx, "review")
-review, err := promise.Result()
-if err != nil {
- return err
-}
-
-// Resolve promise from another handler
-err = restate.Promise[string](ctx, "review").Resolve(review)
-if err != nil {
- return err
-}
-```
-
-## Concurrency
-
-Always use Restate `Wait*` functions instead of Go's native goroutines and channels - they journal execution order for deterministic replay.
-
-### Select the first successful completion
-
-```go {"CODE_LOAD::go/develop/journalingresults.go#race"} theme={null}
-sleepFuture := restate.After(ctx, 30*time.Second)
-callFuture := restate.Service[string](ctx, "MyService", "MyHandler").RequestFuture("hi")
-
-fut, err := restate.WaitFirst(ctx, sleepFuture, callFuture)
-if err != nil {
- return "", err
-}
-switch fut {
-case sleepFuture:
- if err := sleepFuture.Done(); err != nil {
- return "", err
- }
- return "sleep won", nil
-case callFuture:
- result, err := callFuture.Response()
- if err != nil {
- return "", err
- }
- return fmt.Sprintf("call won with result: %s", result), nil
-}
-```
-
-### Wait for all tasks to complete
-
-```go {"CODE_LOAD::go/develop/journalingresults.go#all"} theme={null}
-callFuture1 := restate.Service[string](ctx, "MyService", "MyHandler").RequestFuture("hi")
-callFuture2 := restate.Service[string](ctx, "MyService", "MyHandler").RequestFuture("hi again")
-
-// Collect all results
-var subResults []string
-for fut, err := range restate.Wait(ctx, callFuture1, callFuture2) {
- if err != nil {
- return "", err
- }
- response, err := fut.(restate.ResponseFuture[string]).Response()
- if err != nil {
- return "", err
- }
- subResults = append(subResults, response)
-}
-```
-
-### Invocation Management
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#cancel"} theme={null}
-invocationId := restate.
- ServiceSend(ctx, "MyService", "MyHandler").
- // Optional: send attaching idempotency key
- Send("Hi", restate.WithIdempotencyKey("my-idempotency-key")).
- GetInvocationId()
-
-// Later re-attach to the request
-response, err := restate.AttachInvocation[string](ctx, invocationId).Response()
-if err != nil {
- return err
-}
-
-// I don't need this invocation anymore, let me just cancel it
-restate.CancelInvocation(ctx, invocationId)
-```
-
-## Error Handling
-
-Restate retries failures indefinitely by default. For permanent business-logic failures (invalid input, declined payment), use TerminalError to stop retries immediately.
-
-### Terminal Errors (No Retry)
-
-```go {"CODE_LOAD::go/develop/errorhandling.go#here"} theme={null}
-return restate.TerminalError(fmt.Errorf("Something went wrong."), 500)
-```
-
-### Retryable Errors
-
-```go theme={null}
-// Any other error will be retried
-return fmt.Errorf("Temporary failure - will retry")
-```
-
-## SDK Clients (External Invocations)
-
-```go {"CODE_LOAD::go/develop/agentsmd/clients.go#here"} theme={null}
-restateClient := restateingress.NewClient("http://localhost:8080")
-
-// Request-response
-result, err := restateingress.Service[string, string](
- restateClient, "MyService", "MyHandler").
- Request(context.Background(), "Hi")
-if err != nil {
- // handle error
-}
-
-// One-way
-restateingress.ServiceSend[string](
- restateClient, "MyService", "MyHandler").
- Send(context.Background(), "Hi")
-
-// Delayed
-restateingress.ServiceSend[string](
- restateClient, "MyService", "MyHandler").
- Send(context.Background(), "Hi", restate.WithDelay(1*time.Hour))
-```
-
-
----
-
-> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://docs.restate.dev/llms.txt
\ No newline at end of file
diff --git a/go/templates/go/.claude/settings.json b/go/templates/go/.claude/settings.json
new file mode 100644
index 00000000..1c69c4f5
--- /dev/null
+++ b/go/templates/go/.claude/settings.json
@@ -0,0 +1,13 @@
+{
+ "extraKnownMarketplaces": {
+ "restatedev-plugin": {
+ "source": {
+ "source": "github",
+ "repo": "restatedev/skills"
+ }
+ }
+ },
+ "enabledPlugins": {
+ "restatedev@restatedev-plugin": true
+ }
+}
diff --git a/go/templates/go/.cursor/mcp.json b/go/templates/go/.cursor/mcp.json
deleted file mode 100644
index 78a21c5a..00000000
--- a/go/templates/go/.cursor/mcp.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "mcpServers": {
- "restate-docs": {
- "url": "https://docs.restate.dev/mcp"
- }
- }
-}
\ No newline at end of file
diff --git a/go/templates/go/.cursor/rules/AGENTS.md b/go/templates/go/.cursor/rules/AGENTS.md
deleted file mode 100644
index 3801be90..00000000
--- a/go/templates/go/.cursor/rules/AGENTS.md
+++ /dev/null
@@ -1,399 +0,0 @@
-# Restate Go SDK Rules
-
-## Core Concepts
-
-* Restate provides durable execution: code automatically stores completed steps and resumes from where it left off on failures
-* All handlers receive a `Context`/`ObjectContext`/`WorkflowContext`/`ObjectSharedContext`/`WorkflowSharedContext` object as the first argument
-* Handlers can take typed inputs and return typed outputs using Go structs and JSON serialization
-
-## Service Types
-
-### Basic Services
-
-```go {"CODE_LOAD::go/develop/myservice/main.go"} theme={null}
-package main
-
-import (
- "context"
- "fmt"
- "log"
-
- restate "github.com/restatedev/sdk-go"
- server "github.com/restatedev/sdk-go/server"
-)
-
-type MyService struct{}
-
-func (MyService) MyHandler(ctx restate.Context, greeting string) (string, error) {
- return fmt.Sprintf("%s!", greeting), nil
-}
-
-func main() {
- if err := server.NewRestate().
- Bind(restate.Reflect(MyService{})).
- Start(context.Background(), "0.0.0.0:9080"); err != nil {
- log.Fatal(err)
- }
-}
-```
-
-### Virtual Objects (Stateful, Key-Addressable)
-
-```go {"CODE_LOAD::go/develop/myvirtualobject/main.go"} theme={null}
-package main
-
-import (
- "context"
- "fmt"
- "log"
-
- restate "github.com/restatedev/sdk-go"
- server "github.com/restatedev/sdk-go/server"
-)
-
-type MyObject struct{}
-
-func (MyObject) MyHandler(ctx restate.ObjectContext, greeting string) (string, error) {
- return fmt.Sprintf("%s %s!", greeting, restate.Key(ctx)), nil
-}
-
-func (MyObject) MyConcurrentHandler(ctx restate.ObjectSharedContext, greeting string) (string, error) {
- return fmt.Sprintf("%s %s!", greeting, restate.Key(ctx)), nil
-}
-
-func main() {
- if err := server.NewRestate().
- Bind(restate.Reflect(MyObject{})).
- Start(context.Background(), "0.0.0.0:9080"); err != nil {
- log.Fatal(err)
- }
-}
-```
-
-### Workflows
-
-```go {"CODE_LOAD::go/develop/myworkflow/main.go"} theme={null}
-package myworkflow
-
-import (
- "context"
- restate "github.com/restatedev/sdk-go"
- "github.com/restatedev/sdk-go/server"
- "log/slog"
- "os"
-)
-
-type MyWorkflow struct{}
-
-func (MyWorkflow) Run(ctx restate.WorkflowContext, req string) (string, error) {
- // implement the workflow logic here
- return "success", nil
-}
-
-func (MyWorkflow) InteractWithWorkflow(ctx restate.WorkflowSharedContext) error {
- // implement interaction logic here
- // e.g. resolve a promise that the workflow is waiting on
- return nil
-}
-
-func main() {
- server := server.NewRestate().
- Bind(restate.Reflect(MyWorkflow{}))
-
- if err := server.Start(context.Background(), ":9080"); err != nil {
- slog.Error("application exited unexpectedly", "err", err.Error())
- os.Exit(1)
- }
-}
-```
-
-## Context Operations
-
-### State Management (Virtual Objects & Workflows only)
-
-❌ Never use global variables - not durable, lost across replicas.
-✅ Use `restate.Get()` and `restate.Set()` - durable and scoped to the object's key.
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#state"} theme={null}
-// Get state keys
-stateKeys, err := restate.Keys(ctx)
-if err != nil {
- return err
-}
-_ = stateKeys
-
-// Get state
-myString := "my-default"
-if s, err := restate.Get[*string](ctx, "my-string-key"); err != nil {
- return err
-} else if s != nil {
- myString = *s
-}
-
-count, err := restate.Get[int](ctx, "count")
-if err != nil {
- return err
-}
-
-// Set state
-restate.Set(ctx, "my-key", "my-new-value")
-restate.Set(ctx, "count", count+1)
-
-// Clear state
-restate.Clear(ctx, "my-key")
-restate.ClearAll(ctx)
-```
-
-### Service Communication
-
-#### Request-Response
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#service_calls"} theme={null}
-// Call a Service
-svcResponse, err := restate.Service[string](ctx, "MyService", "MyHandler").
- Request(request)
-if err != nil {
- return err
-}
-
-// Call a Virtual Object
-objResponse, err := restate.Object[string](ctx, "MyObject", objectKey, "MyHandler").
- Request(request)
-if err != nil {
- return err
-}
-
-// Call a Workflow
-wfResponse, err := restate.Workflow[string](ctx, "MyWorkflow", workflowId, "Run").
- Request(request)
-if err != nil {
- return err
-}
-```
-
-#### One-Way Messages
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#sending_messages"} theme={null}
-// Send to service
-restate.ServiceSend(ctx, "MyService", "MyHandler").Send(request)
-
-// Send to virtual object
-restate.ObjectSend(ctx, "MyObject", objectKey, "MyHandler").Send(request)
-
-// Send to workflow
-restate.WorkflowSend(ctx, "MyWorkflow", workflowId, "Run").Send(request)
-```
-
-#### Delayed Messages
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#delayed_messages"} theme={null}
-restate.ServiceSend(ctx, "MyService", "MyHandler").Send(request, restate.WithDelay(5*time.Hour))
-```
-
-### Run Actions or Side Effects (Non-Deterministic Operations)
-
-❌ Never call external APIs/DBs directly - will re-execute during replay, causing duplicates.
-✅ Wrap in `restate.Run()` - Restate journals the result; runs only once.
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#durable_steps"} theme={null}
-// Wrap non-deterministic code in restate.Run
-result, err := restate.Run(ctx, func(ctx restate.RunContext) (string, error) {
- return callExternalAPI(), nil
-})
-if err != nil {
- return err
-}
-```
-
-### Deterministic randoms and time
-
-❌ Never use `rand.Float64()` - non-deterministic and breaks replay logic.
-✅ Use `restate.Rand()` or `restate.UUID()` - Restate journals the result for deterministic replay.
-
-```go {"CODE_LOAD::go/develop/journalingresults.go#uuid"} theme={null}
-uuid := restate.UUID(ctx)
-```
-
-```go {"CODE_LOAD::go/develop/journalingresults.go#random_nb"} theme={null}
-randomInt := restate.Rand(ctx).Uint64()
-randomFloat := restate.Rand(ctx).Float64()
-mathRandV2 := rand.New(restate.RandSource(ctx))
-```
-
-❌ Never use `time.Now()` - returns different values during replay.
-✅ Wrap `time.Now()` in `restate.Run` to let Restate record the timestamp.
-
-### Durable Timers and Sleep
-
-❌ Never use `time.Sleep()` or timers - not durable, lost on restarts.
-✅ Use `restate.Sleep()` - durable timer that survives failures.
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#durable_timers"} theme={null}
-// Sleep
-err := restate.Sleep(ctx, 30*time.Second)
-if err != nil {
- return err
-}
-
-// Schedule delayed call (different from sleep + send)
-restate.ServiceSend(ctx, "MyService", "MyHandler").
- Send("Hi", restate.WithDelay(5*time.Hour))
-```
-
-### Awakeables (External Events)
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#awakeables"} theme={null}
-// Create awakeable
-awakeable := restate.Awakeable[string](ctx)
-awakeableId := awakeable.Id()
-
-// Send ID to external system
-if _, err := restate.Run(ctx, func(ctx restate.RunContext) (string, error) {
- return requestHumanReview(name, awakeableId), nil
-}); err != nil {
- return err
-}
-
-// Wait for result
-review, err := awakeable.Result()
-if err != nil {
- return err
-}
-
-// Resolve from another handler
-restate.ResolveAwakeable(ctx, awakeableId, "Looks good!")
-
-// Reject from another handler
-restate.RejectAwakeable(ctx, awakeableId, fmt.Errorf("Cannot be reviewed"))
-```
-
-### Durable Promises (Workflows only)
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#workflow_promises"} theme={null}
-// Wait for promise
-promise := restate.Promise[string](ctx, "review")
-review, err := promise.Result()
-if err != nil {
- return err
-}
-
-// Resolve promise from another handler
-err = restate.Promise[string](ctx, "review").Resolve(review)
-if err != nil {
- return err
-}
-```
-
-## Concurrency
-
-Always use Restate `Wait*` functions instead of Go's native goroutines and channels - they journal execution order for deterministic replay.
-
-### Select the first successful completion
-
-```go {"CODE_LOAD::go/develop/journalingresults.go#race"} theme={null}
-sleepFuture := restate.After(ctx, 30*time.Second)
-callFuture := restate.Service[string](ctx, "MyService", "MyHandler").RequestFuture("hi")
-
-fut, err := restate.WaitFirst(ctx, sleepFuture, callFuture)
-if err != nil {
- return "", err
-}
-switch fut {
-case sleepFuture:
- if err := sleepFuture.Done(); err != nil {
- return "", err
- }
- return "sleep won", nil
-case callFuture:
- result, err := callFuture.Response()
- if err != nil {
- return "", err
- }
- return fmt.Sprintf("call won with result: %s", result), nil
-}
-```
-
-### Wait for all tasks to complete
-
-```go {"CODE_LOAD::go/develop/journalingresults.go#all"} theme={null}
-callFuture1 := restate.Service[string](ctx, "MyService", "MyHandler").RequestFuture("hi")
-callFuture2 := restate.Service[string](ctx, "MyService", "MyHandler").RequestFuture("hi again")
-
-// Collect all results
-var subResults []string
-for fut, err := range restate.Wait(ctx, callFuture1, callFuture2) {
- if err != nil {
- return "", err
- }
- response, err := fut.(restate.ResponseFuture[string]).Response()
- if err != nil {
- return "", err
- }
- subResults = append(subResults, response)
-}
-```
-
-### Invocation Management
-
-```go {"CODE_LOAD::go/develop/agentsmd/actions.go#cancel"} theme={null}
-invocationId := restate.
- ServiceSend(ctx, "MyService", "MyHandler").
- // Optional: send attaching idempotency key
- Send("Hi", restate.WithIdempotencyKey("my-idempotency-key")).
- GetInvocationId()
-
-// Later re-attach to the request
-response, err := restate.AttachInvocation[string](ctx, invocationId).Response()
-if err != nil {
- return err
-}
-
-// I don't need this invocation anymore, let me just cancel it
-restate.CancelInvocation(ctx, invocationId)
-```
-
-## Error Handling
-
-Restate retries failures indefinitely by default. For permanent business-logic failures (invalid input, declined payment), use TerminalError to stop retries immediately.
-
-### Terminal Errors (No Retry)
-
-```go {"CODE_LOAD::go/develop/errorhandling.go#here"} theme={null}
-return restate.TerminalError(fmt.Errorf("Something went wrong."), 500)
-```
-
-### Retryable Errors
-
-```go theme={null}
-// Any other error will be retried
-return fmt.Errorf("Temporary failure - will retry")
-```
-
-## SDK Clients (External Invocations)
-
-```go {"CODE_LOAD::go/develop/agentsmd/clients.go#here"} theme={null}
-restateClient := restateingress.NewClient("http://localhost:8080")
-
-// Request-response
-result, err := restateingress.Service[string, string](
- restateClient, "MyService", "MyHandler").
- Request(context.Background(), "Hi")
-if err != nil {
- // handle error
-}
-
-// One-way
-restateingress.ServiceSend[string](
- restateClient, "MyService", "MyHandler").
- Send(context.Background(), "Hi")
-
-// Delayed
-restateingress.ServiceSend[string](
- restateClient, "MyService", "MyHandler").
- Send(context.Background(), "Hi", restate.WithDelay(1*time.Hour))
-```
-
-
----
-
-> To find navigation and other pages in this documentation, fetch the llms.txt file at: https://docs.restate.dev/llms.txt
\ No newline at end of file
diff --git a/go/templates/go/.mcp.json b/go/templates/go/.mcp.json
deleted file mode 100644
index 1c3a90b0..00000000
--- a/go/templates/go/.mcp.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "mcpServers": {
- "restate-docs": {
- "type": "http",
- "url": "https://docs.restate.dev/mcp"
- }
- }
-}
\ No newline at end of file
diff --git a/go/templates/go/README.md b/go/templates/go/README.md
index 466cbfb1..46d26489 100644
--- a/go/templates/go/README.md
+++ b/go/templates/go/README.md
@@ -7,3 +7,9 @@ You can run locally with `go run .` and register to Restate with
You can build a docker image using [ko](https://github.com/ko-build/ko):
`ko build --platform=all`
+
+## Using AI coding tools
+
+If you use Claude Code or Codex, then the Restate plugin will automatically be installed. For Cursor, you need to use `/add-plugin`.
+
+Plugin repo: https://github.com/restatedev/skills/tree/main
diff --git a/java/templates/java-gradle/.agents/plugins/marketplace.json b/java/templates/java-gradle/.agents/plugins/marketplace.json
new file mode 100644
index 00000000..3f69a183
--- /dev/null
+++ b/java/templates/java-gradle/.agents/plugins/marketplace.json
@@ -0,0 +1,22 @@
+{
+ "name": "restatedev-plugin",
+ "interface": {
+ "displayName": "Restate"
+ },
+ "plugins": [
+ {
+ "name": "restatedev",
+ "source": {
+ "source": "git-subdir",
+ "url": "https://github.com/restatedev/skills.git",
+ "path": "./plugins/restatedev",
+ "ref": "main"
+ },
+ "policy": {
+ "installation": "AVAILABLE",
+ "authentication": "NONE"
+ },
+ "category": "Coding"
+ }
+ ]
+}
diff --git a/java/templates/java-gradle/.aiassistant/rules/restate.md b/java/templates/java-gradle/.aiassistant/rules/restate.md
deleted file mode 120000
index b7e6491d..00000000
--- a/java/templates/java-gradle/.aiassistant/rules/restate.md
+++ /dev/null
@@ -1 +0,0 @@
-../../AGENTS.md
\ No newline at end of file
diff --git a/java/templates/java-gradle/.aiassistant/rules/restate.md b/java/templates/java-gradle/.aiassistant/rules/restate.md
new file mode 100644
index 00000000..599b1e28
--- /dev/null
+++ b/java/templates/java-gradle/.aiassistant/rules/restate.md
@@ -0,0 +1,32 @@
+---
+apply: by model decision
+instructions: Guidelines for working with Restate durable services in this project
+---
+
+# Restate Java project
+
+This project uses [Restate](https://restate.dev) — a durable execution runtime for resilient services, workflows, and AI agents. Restate captures every completed step so handlers can resume exactly where they left off after crashes, restarts, or retries.
+
+## Core concepts
+
+- **Services** — stateless handlers that run deterministically; retries resume from the last durable step.
+- **Virtual Objects** — stateful, key-addressed handlers. State lives in Restate; Restate serializes per-key access.
+- **Workflows** — long-running, multi-step flows with a single lifecycle per key.
+- **Contexts** — every handler receives `Context`, `ObjectContext`, `WorkflowContext` (or their `Shared` variants). All non-deterministic work (I/O, random, time, RPC) must go through the context so it is journaled.
+
+## Rules for this codebase
+
+- Never perform side effects directly. Wrap I/O, randomness, timers, and RPCs in `ctx.run(...)`, `ctx.sleep(...)`, etc., so they are durable.
+- Keep handler code deterministic between journal entries: same inputs must produce the same sequence of context calls.
+- Prefer the typed client APIs (`XxxClient.fromContext(ctx, key)`) for service-to-service calls instead of raw HTTP.
+- Serializable inputs/outputs only — use Jackson-compatible POJOs or records.
+
+## Getting more detail
+
+For API reference, lifecycle semantics, debugging, migration guidance, Kafka/idempotency patterns, and framework integrations (Quarkus, Spring Boot), query the **`restate-docs` MCP server** configured for this project (`.ai/mcp/mcp.json`). It's bound to `https://docs.restate.dev/mcp` and can search the full documentation on demand.
+
+Useful entry points the MCP server can resolve:
+- SDK API and pitfalls
+- Designing services and picking a service type
+- Invocation lifecycle, cancellation, idempotency, sends, Kafka
+- Debugging stuck invocations and journal mismatches
diff --git a/java/templates/java-gradle/.claude/CLAUDE.md b/java/templates/java-gradle/.claude/CLAUDE.md
deleted file mode 120000
index be77ac83..00000000
--- a/java/templates/java-gradle/.claude/CLAUDE.md
+++ /dev/null
@@ -1 +0,0 @@
-../AGENTS.md
\ No newline at end of file
diff --git a/java/templates/java-gradle/.claude/settings.json b/java/templates/java-gradle/.claude/settings.json
new file mode 100644
index 00000000..1c69c4f5
--- /dev/null
+++ b/java/templates/java-gradle/.claude/settings.json
@@ -0,0 +1,13 @@
+{
+ "extraKnownMarketplaces": {
+ "restatedev-plugin": {
+ "source": {
+ "source": "github",
+ "repo": "restatedev/skills"
+ }
+ }
+ },
+ "enabledPlugins": {
+ "restatedev@restatedev-plugin": true
+ }
+}
diff --git a/java/templates/java-gradle/.cursor/mcp.json b/java/templates/java-gradle/.cursor/mcp.json
deleted file mode 120000
index 3aa24b56..00000000
--- a/java/templates/java-gradle/.cursor/mcp.json
+++ /dev/null
@@ -1 +0,0 @@
-../.ai/mcp/mcp.json
\ No newline at end of file
diff --git a/java/templates/java-gradle/.idea/compiler.xml b/java/templates/java-gradle/.idea/compiler.xml
deleted file mode 100644
index ba35960a..00000000
--- a/java/templates/java-gradle/.idea/compiler.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/java/templates/java-gradle/.junie/guidelines.md b/java/templates/java-gradle/.junie/guidelines.md
deleted file mode 120000
index be77ac83..00000000
--- a/java/templates/java-gradle/.junie/guidelines.md
+++ /dev/null
@@ -1 +0,0 @@
-../AGENTS.md
\ No newline at end of file
diff --git a/java/templates/java-gradle/.junie/mcp/mcp.json b/java/templates/java-gradle/.junie/mcp/mcp.json
deleted file mode 120000
index f454b32d..00000000
--- a/java/templates/java-gradle/.junie/mcp/mcp.json
+++ /dev/null
@@ -1 +0,0 @@
-../../.ai/mcp/mcp.json
\ No newline at end of file
diff --git a/java/templates/java-gradle/.mcp.json b/java/templates/java-gradle/.mcp.json
deleted file mode 100644
index 1c3a90b0..00000000
--- a/java/templates/java-gradle/.mcp.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "mcpServers": {
- "restate-docs": {
- "type": "http",
- "url": "https://docs.restate.dev/mcp"
- }
- }
-}
\ No newline at end of file
diff --git a/java/templates/java-gradle/AGENTS.md b/java/templates/java-gradle/AGENTS.md
deleted file mode 100644
index 69af5b7d..00000000
--- a/java/templates/java-gradle/AGENTS.md
+++ /dev/null
@@ -1,407 +0,0 @@
-> ## Documentation Index
-> Fetch the complete documentation index at: https://docs.restate.dev/llms.txt
-> Use this file to discover all available pages before exploring further.
-
-# Restate Java SDK Rules
-
-## Core Concepts
-
-* Restate provides durable execution: code automatically stores completed steps and resumes from where it left off on failures
-* All handlers receive a `Context`/`ObjectContext`/`WorkflowContext`/`SharedObjectContext`/`SharedWorkflowContext` object as the first argument
-* Handlers can take typed inputs and return typed outputs using Java classes and Jackson serialization
-
-## Service Types
-
-### Basic Services
-
-```java Java {"CODE_LOAD::java/src/main/java/develop/MyService.java#here"} theme={null}
-import dev.restate.sdk.Context;
-import dev.restate.sdk.annotation.Handler;
-import dev.restate.sdk.annotation.Service;
-import dev.restate.sdk.endpoint.Endpoint;
-import dev.restate.sdk.http.vertx.RestateHttpServer;
-
-@Service
-public class MyService {
- @Handler
- public String myHandler(Context ctx, String greeting) {
- return greeting + "!";
- }
-
- public static void main(String[] args) {
- RestateHttpServer.listen(Endpoint.bind(new MyService()));
- }
-}
-```
-
-### Virtual Objects (Stateful, Key-Addressable)
-
-```java Java {"CODE_LOAD::java/src/main/java/develop/MyObject.java#here"} theme={null}
-import dev.restate.sdk.ObjectContext;
-import dev.restate.sdk.SharedObjectContext;
-import dev.restate.sdk.annotation.Handler;
-import dev.restate.sdk.annotation.Shared;
-import dev.restate.sdk.annotation.VirtualObject;
-import dev.restate.sdk.endpoint.Endpoint;
-import dev.restate.sdk.http.vertx.RestateHttpServer;
-
-@VirtualObject
-public class MyObject {
-
- @Handler
- public String myHandler(ObjectContext ctx, String greeting) {
- String objectId = ctx.key();
-
- return greeting + " " + objectId + "!";
- }
-
- @Shared
- public String myConcurrentHandler(SharedObjectContext ctx, String input) {
- return "my-output";
- }
-
- public static void main(String[] args) {
- RestateHttpServer.listen(Endpoint.bind(new MyObject()));
- }
-}
-```
-
-### Workflows
-
-```java Java {"CODE_LOAD::java/src/main/java/develop/MyWorkflow.java#here"} theme={null}
-import dev.restate.sdk.SharedWorkflowContext;
-import dev.restate.sdk.WorkflowContext;
-import dev.restate.sdk.annotation.Shared;
-import dev.restate.sdk.annotation.Workflow;
-import dev.restate.sdk.endpoint.Endpoint;
-import dev.restate.sdk.http.vertx.RestateHttpServer;
-
-@Workflow
-public class MyWorkflow {
-
- @Workflow
- public String run(WorkflowContext ctx, String input) {
-
- // implement workflow logic here
-
- return "success";
- }
-
- @Shared
- public String interactWithWorkflow(SharedWorkflowContext ctx, String input) {
- // implement interaction logic here
- return "my result";
- }
-
- public static void main(String[] args) {
- RestateHttpServer.listen(Endpoint.bind(new MyWorkflow()));
- }
-}
-```
-
-## Context Operations
-
-### State Management (Virtual Objects & Workflows only)
-
-❌ Never use static variables - not durable, lost across replicas.
-✅ Use `ctx.get()` and `ctx.set()` - durable and scoped to the object's key.
-
-```java {"CODE_LOAD::java/src/main/java/develop/agentsmd/Actions.java#state"} theme={null}
-// Get state keys
-Collection keys = ctx.stateKeys();
-
-// Get state
-StateKey STRING_STATE_KEY = StateKey.of("my-key", String.class);
-String stringState = ctx.get(STRING_STATE_KEY).orElse("my-default");
-
-StateKey INT_STATE_KEY = StateKey.of("count", Integer.class);
-int count = ctx.get(INT_STATE_KEY).orElse(0);
-
-// Set state
-ctx.set(STRING_STATE_KEY, "my-new-value");
-ctx.set(INT_STATE_KEY, count + 1);
-
-// Clear state
-ctx.clear(STRING_STATE_KEY);
-ctx.clearAll();
-```
-
-### Service Communication
-
-#### Request-Response
-
-```java {"CODE_LOAD::java/src/main/java/develop/agentsmd/Actions.java#service_calls"} theme={null}
-// Call a Service
-String svcResponse = MyServiceClient.fromContext(ctx).myHandler(request).await();
-
-// Call a Virtual Object
-String objResponse = MyObjectClient.fromContext(ctx, objectKey).myHandler(request).await();
-
-// Call a Workflow
-String wfResponse = MyWorkflowClient.fromContext(ctx, workflowId).run(request).await();
-```
-
-#### Generic Calls
-
-Call a service without using the generated client, but just String names.
-
-```java {"CODE_LOAD::java/src/main/java/develop/agentsmd/Actions.java#generic_calls"} theme={null}
-// Generic service call
-Target target = Target.service("MyService", "myHandler");
-String response =
- ctx.call(Request.of(target, TypeTag.of(String.class), TypeTag.of(String.class), request))
- .await();
-
-// Generic object call
-Target objectTarget = Target.virtualObject("MyObject", "object-key", "myHandler");
-String objResponse =
- ctx.call(
- Request.of(
- objectTarget, TypeTag.of(String.class), TypeTag.of(String.class), request))
- .await();
-
-// Generic workflow call
-Target workflowTarget = Target.workflow("MyWorkflow", "wf-id", "run");
-String wfResponse =
- ctx.call(
- Request.of(
- workflowTarget, TypeTag.of(String.class), TypeTag.of(String.class), request))
- .await();
-```
-
-#### One-Way Messages
-
-```java {"CODE_LOAD::java/src/main/java/develop/agentsmd/Actions.java#sending_messages"} theme={null}
-// Call a Service
-MyServiceClient.fromContext(ctx).send().myHandler(request);
-
-// Call a Virtual Object
-MyObjectClient.fromContext(ctx, objectKey).send().myHandler(request);
-
-// Call a Workflow
-MyWorkflowClient.fromContext(ctx, workflowId).send().run(request);
-```
-
-#### Delayed Messages
-
-```java {"CODE_LOAD::java/src/main/java/develop/agentsmd/Actions.java#delayed_messages"} theme={null}
-MyServiceClient.fromContext(ctx).send().myHandler(request, Duration.ofDays(5));
-```
-
-#### With Idempotency Key
-
-```java theme={null}
-Client restateClient = Client.connect("http://localhost:8080");
-MyServiceClient.fromClient(restateClient)
- .send()
- .myHandler("Hi", opt -> opt.idempotencyKey("my-key"));
-```
-
-### Run Actions or Side Effects (Non-Deterministic Operations)
-
-❌ Never call external APIs/DBs directly - will re-execute during replay, causing duplicates.
-✅ Wrap in `ctx.run()` - Restate journals the result; runs only once.
-
-```java {"CODE_LOAD::java/src/main/java/develop/agentsmd/Actions.java#durable_steps"} theme={null}
-// Wrap non-deterministic code in ctx.run
-String result = ctx.run("call external API", String.class, () -> callExternalAPI());
-
-// Wrap with name for better tracing
-String namedResult = ctx.run("my-side-effect", String.class, () -> callExternalAPI());
-```
-
-### Deterministic randoms and time
-
-❌ Never use `Math.random()` - non-deterministic and breaks replay logic.
-✅ Use `ctx.random()` or `ctx.uuid()` - Restate journals the result for deterministic replay.
-
-❌ Never use `System.currentTimeMillis()`, `new Date()` - returns different values during replay.
-✅ Use `ctx.timer()` - Restate records and replays the same timestamp.
-
-### Durable Timers and Sleep
-
-❌ Never use `Thread.sleep()` or `CompletableFuture.delayedExecutor()` - not durable, lost on restarts.
-✅ Use `ctx.sleep()` - durable timer that survives failures.
-
-```java {"CODE_LOAD::java/src/main/java/develop/agentsmd/Actions.java#durable_timers"} theme={null}
-// Sleep
-ctx.sleep(Duration.ofSeconds(30));
-
-// Schedule delayed call (different from sleep + send)
-Target target = Target.service("MyService", "myHandler");
-ctx.send(
- Request.of(target, TypeTag.of(String.class), TypeTag.of(String.class), "Hi"),
- Duration.ofHours(5));
-```
-
-### Awakeables (External Events)
-
-```java {"CODE_LOAD::java/src/main/java/develop/agentsmd/Actions.java#awakeables"} theme={null}
-// Create awakeable
-Awakeable awakeable = ctx.awakeable(String.class);
-String awakeableId = awakeable.id();
-
-// Send ID to external system
-ctx.run(() -> requestHumanReview(name, awakeableId));
-
-// Wait for result
-String review = awakeable.await();
-
-// Resolve from another handler
-ctx.awakeableHandle(awakeableId).resolve(String.class, "Looks good!");
-
-// Reject from another handler
-ctx.awakeableHandle(awakeableId).reject("Cannot be reviewed");
-```
-
-### Durable Promises (Workflows only)
-
-```java {"CODE_LOAD::java/src/main/java/develop/agentsmd/Actions.java#workflow_promises"} theme={null}
-DurablePromiseKey REVIEW_PROMISE = DurablePromiseKey.of("review", String.class);
-// Wait for promise
-String review = ctx.promise(REVIEW_PROMISE).future().await();
-
-// Resolve promise from another handler
-ctx.promiseHandle(REVIEW_PROMISE).resolve(review);
-```
-
-## Concurrency
-
-Always use Restate combinators (`DurableFuture.all`, `DurableFuture.any`) instead of Java's native `CompletableFuture` methods - they journal execution order for deterministic replay.
-
-### `DurableFuture.all()` - Wait for All
-
-Returns when all futures complete. Use to wait for multiple operations to finish.
-
-```java {"CODE_LOAD::java/src/main/java/develop/agentsmd/Actions.java#combine_all"} theme={null}
-// Wait for all to complete
-DurableFuture call1 = MyServiceClient.fromContext(ctx).myHandler("request1");
-DurableFuture call2 = MyServiceClient.fromContext(ctx).myHandler("request2");
-
-DurableFuture.all(call1, call2).await();
-```
-
-### `DurableFuture.any()` - First Successful Result
-
-Returns the first successful result, ignoring rejections until all fail.
-
-```java {"CODE_LOAD::java/src/main/java/develop/agentsmd/Actions.java#combine_any"} theme={null}
-// Wait for any to complete
-int indexCompleted = DurableFuture.any(call1, call2).await();
-```
-
-### Invocation Management
-
-```java {"CODE_LOAD::java/src/main/java/develop/agentsmd/Actions.java#cancel"} theme={null}
-var handle =
- MyServiceClient.fromContext(ctx)
- .send()
- .myHandler(request, req -> req.idempotencyKey("abc123"));
-var response = handle.attach().await();
-// Cancel invocation
-handle.cancel();
-```
-
-## Serialization
-
-### Default (Jackson JSON)
-
-By default, Java SDK uses Jackson for JSON serialization with POJOs.
-
-```java Java {"CODE_LOAD::java/src/main/java/develop/SerializationExample.java#state_keys"} theme={null}
-// Primitive types
-var myString = StateKey.of("myString", String.class);
-// Generic types need TypeRef (similar to Jackson's TypeReference)
-var myMap = StateKey.of("myMap", TypeTag.of(new TypeRef