Windmill Trigger Type Checklist
windmill-labs/windmill
Checklist of every backend, frontend, CLI and capture change needed to add a new TriggerCrud-based trigger type, such as Azure, GCP or Kafka, to Windmill.
Gram's declarative GCP Pub/Sub system — topics and subscriptions are declared as protobuf message options, generated into infra/gen/kcc.yaml, and used at runtime via a type-safe publisher/subscriber…
$ npx skills add speakeasy-api/gram --skill gram-pubsub -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install speakeasy-api/gram gram-pubsub --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/speakeasy-api/gram.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/gram-pubsub .claude/skills/gram-pubsub && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "gram-pubsub" agent skill from https://github.com/speakeasy-api/gram/tree/main/.agents/skills/gram-pubsub into .claude/skills/gram-pubsub/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "gram-pubsub", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/speakeasy-api/gram/tree/main/.agents/skills/gram-pubsubType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add speakeasy-api/gram --skill gram-pubsub -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install speakeasy-api/gram gram-pubsub --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/speakeasy-api/gram.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/gram-pubsub .agents/skills/gram-pubsub && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "gram-pubsub" agent skill from https://github.com/speakeasy-api/gram/tree/main/.agents/skills/gram-pubsub into .agents/skills/gram-pubsub/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "gram-pubsub", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add speakeasy-api/gram --skill gram-pubsub -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install speakeasy-api/gram gram-pubsub --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/speakeasy-api/gram.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/gram-pubsub .cursor/skills/gram-pubsub && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "gram-pubsub" agent skill from https://github.com/speakeasy-api/gram/tree/main/.agents/skills/gram-pubsub into .cursor/skills/gram-pubsub/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "gram-pubsub", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/speakeasy-api/gram.git --path .agents/skills/gram-pubsub--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add speakeasy-api/gram --skill gram-pubsub -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install speakeasy-api/gram gram-pubsub --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/speakeasy-api/gram.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/gram-pubsub .gemini/skills/gram-pubsub && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "gram-pubsub" agent skill from https://github.com/speakeasy-api/gram/tree/main/.agents/skills/gram-pubsub into .gemini/skills/gram-pubsub/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "gram-pubsub", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install speakeasy-api/gram gram-pubsubInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add speakeasy-api/gram --skill gram-pubsub -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/speakeasy-api/gram.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/gram-pubsub .github/skills/gram-pubsub && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "gram-pubsub" agent skill from https://github.com/speakeasy-api/gram/tree/main/.agents/skills/gram-pubsub into .github/skills/gram-pubsub/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "gram-pubsub", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add speakeasy-api/gram --skill gram-pubsub -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install speakeasy-api/gram gram-pubsub --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/speakeasy-api/gram.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/gram-pubsub .opencode/skills/gram-pubsub && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "gram-pubsub" agent skill from https://github.com/speakeasy-api/gram/tree/main/.agents/skills/gram-pubsub into .opencode/skills/gram-pubsub/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "gram-pubsub", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
gram-pubsubGram's declarative GCP Pub/Sub system — topics and subscriptions are declared as protobuf message options, generated into infra/gen/kcc.yaml, and used at runtime via a type-safe publisher/subscriber…
Gram Pubsub is an agent skill from speakeasy-api/gram. Gram's declarative GCP Pub/Sub system — topics and subscriptions are declared as protobuf message options, generated into infra/gen/kcc.yaml, and used at runtime via a type-safe publisher/subscriber library and the gram streams process. Activate for any Pub/Sub work in Gram: adding or changing a topic/subscription, declaring (gcp.pubsub.v1.topic)/(gcp.pubsub.v1.subscription) options, publishing or consuming messages, implementing a stream handler, dead-letter queues, or the local emulator — including phrasings…
Its SKILL.md is about 4.5k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Backend & APIs, covering Event-driven systems. It works with Google Cloud and gRPC. The repository describes itself as: Securely scale AI usage across your organization. A single stack to Connect, Secure, Observe and Distribute agents, MCPs, and Skills within your company. The licence is AGPL-3.0.
2 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 4d32da1. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
misegoFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Gram Pubsub loads about 4.5k tokens when it runs. Until then it costs about 164 tokens; SKILL.md has 1,891 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from speakeasy-api/gram at commit 4d32da1, republished under its AGPL-3.0 licence (© speakeasy-api). 1,891 words, ~4,494 tokens.
.claude/skills/gram-pubsub/SKILL.md (or your agent's skills folder).Gram declares its GCP Pub/Sub topology as data on protobuf messages, not as
hand-written Terraform or Config Connector YAML. You annotate a "marker" message
with a topic or subscription option; a generator walks the compiled descriptors
and emits a Config Connector Helm values document (infra/gen/kcc.yaml); the
deployment tooling consumes that document to provision real topics and
subscriptions. The same proto options drive a type-safe Go
publisher/subscriber library, so the infrastructure and the application code can
never disagree about a topic's name or a subscription's wiring — both read from
one source of truth.
The whole point of the design is that an engineer adding a topic touches only a
.proto file under infra/proto/. Everything downstream — the Config Connector
specs, the per-environment rollout, and the runtime handle — is derived from it.
Everything in this skill lives in this repo (infra/); the deployment side is
referred to only abstractly as "the deployment tooling."
A topic or a subscription is declared by attaching a message option to a protobuf message. The message itself can be the event schema (for a topic) or an empty placeholder (for a subscription) — what matters is the option, not the fields. One message must not carry both a topic and a subscription option; declare them separately. This keeps a topic's payload schema and a consumer's config as distinct, independently evolvable things.
The option definitions live in infra/proto/gcp/pubsub/v1/options.proto:
(gcp.pubsub.v1.topic) — TopicOptions: optional name, retention_hint,
labels.(gcp.pubsub.v1.subscription) — SubscriptionOptions: optional name,
required topic (the proto full name of the topic-declaring message, e.g.
"gram.outbox.v1.Event"), plus retention, ack_deadline, retry_policy,
filter, dead_letter, expiration_ttl, retain_acked_messages, labels.Add the topic option to the message that is the event payload, so the schema
and the topic travel together. See infra/proto/gram/outbox/v1/event.proto:
message Event {
string id = 1;
string type = 2;
google.protobuf.Timestamp created_at = 3;
bytes payload = 4;
option (gcp.pubsub.v1.topic) = {
retention_hint: { seconds: 604800 /* 7 days */ }
};
}With no explicit name, the topic ID is the kebab-cased proto full name:
gram.outbox.v1.Event → gram-outbox-v1-event. Set name only when you need
to diverge from that.
Declare a subscription on its own marker message (no payload fields needed) and
point topic at the topic message's full name. See
infra/proto/gram/outbox/v1/processor.proto:
message Processor {
option (gcp.pubsub.v1.subscription) = {
topic: "gram.outbox.v1.Event"
ack_deadline: { seconds: 30 }
retry_policy: {
minimum_backoff: { seconds: 10 }
maximum_backoff: { seconds: 600 }
}
dead_letter: { max_delivery_attempts: 5 }
};
}With no explicit name, the subscription ID is the kebab-cased proto full name
(same rule as topics): gram.outbox.v1.Processor → gram-outbox-v1-processor.
Set name only when you need to diverge from that. The
topic reference is validated against the discovered topic set during
generation, so a typo fails the build rather than producing a dangling
subscription.
When a subscription sets dead_letter, the generator auto-creates a DLQ
topic — you do not declare it. The default name is <subscription>-dlq
(override with dead_letter.name). The DLQ topic carries the same message
schema as the source and is labeled dlq_for: <subscription>. This is why
subscription IDs are length-capped below the topic limit: room must be left for
the -dlq suffix.
Always run the generator after editing any .proto under infra/proto/ and
commit the result — infra/gen/kcc.yaml is the committed artifact the
deployment tooling consumes:
mise run gen:infraThis task (.mise-tasks/gen/infra.sh) does three things: buf generate (proto
→ Go in infra/gen/), buf build (compiled FileDescriptorSet →
infra/cmd/infra/descriptors.pb), then go run ./infra/main.go gen-cc to write
infra/gen/kcc.yaml. The generated Go and the descriptor blob are gitignored
(**/descriptors.pb) and excluded from formatting; infra/gen/kcc.yaml is
committed.
infra/internal/gcp/)| File | Responsibility |
|---|---|
pubsub_discover.go | Walks the descriptor set, extracts options, resolves names, dedupes, validates, synthesizes DLQ topics. The DesiredTopic / DesiredSubscription structs are the in-memory topology. |
cc_pubsub.go | Projects the topology into sorted Config Connector specs (buildPubSubValues). |
values.go | The Helm values document types (pubSubValuesDocument). Specs embed the real Config Connector PubSubTopicSpec / PubSubSubscriptionSpec types so field names match the CRD exactly. |
cc.go | ConfigConnectorPubSub.Provision orchestrates discover → build → write, emitting the # Code generated … DO NOT EDIT. YAML. |
Key behaviors worth knowing:
validateTopicID / validateSubscriptionID): GCP naming
rules — must start with a letter, 3–255 chars, no goog prefix, no full
resource paths. Duplicate topic or subscription names, an unknown topic
reference, or a DLQ name colliding with a declared topic all fail generation.managed_by: proto_pubsub_orchestrator plus a proto_message label carrying the source
message's full name; subscriptions also carry topic_proto_message. These
make resources traceable back to their declaration.604800s) as Config Connector expects.pubsub key (names, labels, specs). Per-resource deployment metadata
(project, namespace, deletion/prune policy) is applied downstream by the
deployment tooling, never here — so don't expect this generator to emit it.infra/pkg/gcp/)Application code never hard-codes a topic or subscription name. It hands a proto message to a broker and gets back a generic, type-safe handle. The broker resolves names from the same proto options used for generation.
Two brokers implement both PublisherBroker and SubscriberBroker:
PubSubBroker (pubsub_gcp.go) — talks to real GCP; assumes topics/subs
already exist (Config Connector created them).EmulatedPubSubBroker (pubsub_local.go) — for local dev against the
emulator; reconciles topics and subscriptions on demand since the emulator
has no Config Connector.Both take the embedded descriptors blob. Usage (condensed from
infra/cmd/infra/demo.go, the runnable reference):
broker := gcppub.NewEmulatedPubSub(logger, projectID, client, descriptors)
// Publisher for the topic declared by *outboxv1.Event.
pub, _ := gcppub.PubSubPublisherForMessage(ctx, broker, &outboxv1.Event{})
// Subscriber for the *outboxv1.Processor subscription, receiving *outboxv1.Event.
// Read as: "a handle on the Processor subscription delivering Event messages."
sub, _ := gcppub.PubSubSubscriberForMessage(ctx, broker, &outboxv1.Event{}, &outboxv1.Processor{})
pub.Publish(ctx, &msg).Get(ctx) // proto-marshaled, with content-type + schema attributes
// The callback receives the unmarshaled message plus delivery metadata.
// Return nil to ack; return a non-nil error to nack (and trigger redelivery /
// dead-lettering if enabled for the topic and subscription).
sub.Receive(ctx, func(ctx context.Context, msg *outboxv1.Event, meta gcppub.MessageMetadata) error {
_ = msg // already unmarshaled to *outboxv1.Event
_ = meta.ID // broker-assigned message ID
_ = meta.Attributes
_ = meta.DeliveryAttempt // set when dead-lettering is enabled, else nil
return nil
})publisher.go / subscriber.go define the generic Publisher[M] and
Subscriber[M] over cloud.google.com/go/pubsub/v2. Messages are
proto-marshaled and tagged with content-type: application/x-protobuf and a
schema attribute (the message full name); the subscriber unmarshals back into
a fresh M and hands it to your callback along with a MessageMetadata. The
callback's return value drives ack/nack — nil acks, non-nil nacks — so you no
longer call Ack/Nack yourself. Tune behavior with
WithPubSubPublishSettings / WithPubSubReceiveSettings.
This is the low-level library. Inside the Gram server you almost never call
sub.Receive directly — you write a streams.Handler and register it in the
gram streams process, which wraps the receive loop with tracing, panic
recovery, and ack/nack plumbing for you. See the next section.
Subscribers run in a dedicated long-running process, gram streams
(server/cmd/gram/streams.go). It is its own service — separate from the API
server and the Temporal worker — so message consumers scale and fail
independently. Locally it runs as the streams process under pitchfork; start
it with pitchfork start streams. Adding a consumer is two steps: write a
handler, then register it.
A consumer is a streams.Handler[M] (server/internal/streams/handlers.go),
where M is the proto message the topic carries:
type Handler[M any] interface {
Handle(context.Context, M, gcp.MessageMetadata) error
}Implement it in the domain package that owns the behavior (not in streams/ and
not in cmd/gram/). server/internal/ping/handler.go is the canonical
reference — a struct holding its dependencies, a NewHandler constructor, and a
Handle method:
type Handler struct {
logger *slog.Logger
}
func NewHandler(logger *slog.Logger) *Handler {
return &Handler{logger: logger}
}
func (h *Handler) Handle(ctx context.Context, m *pingv2.Message, _ gcp.MessageMetadata) error {
// ... do the work ...
return nil
}The handler's job is narrow: process one message and return. The return value
is the ack/nack signal — nil acks the message; a non-nil error nacks it,
triggering redelivery and eventual dead-lettering if the subscription declares a
dead_letter policy (see "Authoring a subscription"). So return an error only
when you genuinely want the message retried; for a poison message you can never
process, log it and return nil to drop it. Do not call Ack/Nack, start
your own receive goroutine, or open a span for the receive loop — the runner
does all of that (next step).
HandlerFunc[M] adapts a bare function to the interface when a struct is
overkill.
In streams.go, the Action builds the shared dependencies (db, redis, the
gcp.NewPubSubBroker) and a receiverGroup. Register each handler inside the
marked block with mustReceive:
// Start subscription receivers in this block
{
mustReceive(rg, &pingv2.Message{}, &pingv2.Processor{}, ping.NewHandler(logger))
}The three positional arguments mirror the proto declarations:
&pingv2.Message{} — the topic message; its type fixes M, the payload
your handler receives.&pingv2.Processor{} — the subscription marker message (the one carrying
(gcp.pubsub.v1.subscription)).ping.NewHandler(...) — your handler, with its dependencies injected from the
ones the Action already constructed. If a handler needs the db or redis,
pass them here; add new shared dependencies to the Action body.Trailing gcp.SubscriberOptions (e.g. WithPubSubReceiveSettings) can follow.
Use mustReceive (it panics on a misconfigured subscription, which is a
programmer error caught at startup); receive returns the error if you need to
handle it.
receive/mustReceive exist so handlers stay tiny. For each registration the
runner: resolves the subscription via gcp.PubSubSubscriberForMessage, launches
the receive loop in the shared errgroup (so any subscriber's fatal error tears
the whole process down for a clean restart), starts a stream.handleMessage
span per message, stamps pub/sub subscriber context values for telemetry,
recovers panics in your handler (converting them to a nacking error instead
of crashing the goroutine), and treats context.Canceled as a clean shutdown
rather than a failure. Because all of this is centralized, you get consistent
observability and failure semantics across every consumer without writing any of
it.
ping also ships a StartPublisher (server/internal/ping/publisher.go)
launched with group.Go alongside the receivers — a heartbeat that publishes a
message every few seconds so you can confirm the publish→subscribe loop is alive
in logs. It is a sanity-check harness, not a pattern to copy for real
publishers: production code publishes from wherever the event originates (an API
handler, a workflow activity) using gcp.PubSubPublisherForMessage, not from a
loop in the streams process.
mise.toml sets PUBSUB_EMULATOR_HOST and compose.yml runs a
pubsub-emulator service (the google/cloud-sdk emulators image on port 8085).
With the emulator host set, EmulatedPubSubBroker creates topics/subscriptions
lazily on first publish/subscribe, so you don't need Config Connector locally.
The infra demo command (go run ./infra/main.go demo) is an end-to-end
publish/subscribe loop you can run to sanity-check the framework.
This is the part that most often confuses people, so it's worth being precise about what this repo does and does not do.
infra/gen/kcc.yaml does not create
anything in GCP. The proto is the source; infra/gen/kcc.yaml is the
committed artifact. Provisioning happens later, in separate deployment
tooling, not as a side effect of merging.main directly.
A topology change therefore reaches one environment at a time as that
environment is rolled forward — so a freshly declared topic legitimately may
not exist yet in a given environment even though it is on main.So when something seems missing in a real environment, the productive question is
not "is the proto correct?" but "did infra/gen/kcc.yaml get regenerated and
committed, and has the environment in question been rolled forward to a revision
that includes it?" The rollout mechanics themselves live with the deployment
tooling, outside this repo.
mise run gen:infra and commit infra/gen/kcc.yaml after any proto
change. A stale kcc.yaml is what actually ships — the proto is only the
source. Regenerating without committing the result is the most common reason a
declaration silently does nothing downstream.topic references use the proto full name, not the resolved topic ID
(gram.outbox.v1.Event, not gram-outbox-v1-event).dead_letter.© speakeasy-api, AGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .agents/skills/gram-pubsub of speakeasy-api/gram.
Open the folder on GitHubat commit 4d32da1
Gram Pubsub next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Gram Pubsub this skillspeakeasy-api/gram | 272 | — | ~4.5k | Automated safety check: Pass | AGPL-3.0 | |
| Windmill Trigger Type Checklistwindmill-labs/windmill | 18k | — | ~4.7k | Automated safety check: Pass | Custom licence | |
| Polylith Base CreationDavidVujic/python-polylith | 553 | — | ~757 | Automated safety check: Pass | MIT | |
| GCP Cloud Rundavila7/claude-code-templates | 32k | 7 repos | ~1.7k | Automated safety check: Pass | MIT | |
| AWS Lambda Microvmsawslabs/agent-plugins | 912 | 1 repos | ~4.1k | Automated safety check: Pass | Apache-2.0 | |
| Gcloudgoogle/skills | 21k | — | ~3.4k | Automated safety check: Pass | Apache-2.0 |
windmill-labs/windmill
Checklist of every backend, frontend, CLI and capture change needed to add a new TriggerCrud-based trigger type, such as Azure, GCP or Kafka, to Windmill.
DavidVujic/python-polylith
Create a Polylith base with poly create base — the entry point of a deployable application (HTTP API, CLI, message-queue consumer, AWS Lambda handler, GCP Cloud Function, scheduled job).
davila7/claude-code-templates
Specialized skill for building production-ready serverless applications on GCP.
awslabs/agent-plugins
Build, run, debug, and operate applications on AWS Lambda MicroVMs — Firecracker-isolated, snapshot-resumable serverless compute environments that run inside a container with up to 8-hour lifetimes.
google/skills
Provides safety-critical validation, guardrails, and data reduction for gcloud CLI operations across Google Cloud Platform (GCP) services and infrastructure.
sundial-org/awesome-openclaw-skills
Control Nest smart home devices (thermostat, cameras, doorbell) via the Device Access API.
speakeasy-api/gram
A skill your agent uses when automating the Gram dashboard in a browser, capturing screenshots, inspecting pages.
speakeasy-api/gram
A skill your agent uses when adding, changing, restyling, reviewing, validating, or previewing a Gram/Speakeasy transactional email, in Go or in LMX/MJML — a template<name.go, a TemplateKey…
speakeasy-api/gram
A skill your agent uses when adding, changing, or styling UI in client/admin (the Gram admin dashboard) that touches shadcn/ui — a button, dialog, table, sidebar, badge, select, tabs, tooltip, card…
speakeasy-api/gram
A skill your agent uses when adding, editing, reviewing, testing, or locating a reviewed skill distributed with the Platform MCP plugin; triggers include "Platform MCP skill", "platformmcpskills"…
speakeasy-api/gram
A skill your agent uses when changing or reviewing Gram ClickHouse schemas, migrations, queries, inserts, access principals, bootstrap SQL, Cloud compatibility, partial migration failures, or…
speakeasy-api/gram
A skill your agent uses when gating a feature behind a flag, dogfooding or gradually rolling out a change, choosing between productfeatures and PostHog feature flags, adding or checking a product…
Works with
Categories
Gram's declarative GCP Pub/Sub system — topics and subscriptions are declared as protobuf message options, generated into infra/gen/kcc.yaml, and used at runtime via a type-safe publisher/subscriber…. Gram Pubsub is an agent skill from speakeasy-api/gram.yaml, and used at runtime via a type-safe publisher/subscriber library and the gram streams process.
Gram Pubsub fits situations like: tasks that involve Event-driven systems.
Run `npx skills add speakeasy-api/gram --skill gram-pubsub -a claude-code`. Or copy the skill folder (.agents/skills/gram-pubsub in speakeasy-api/gram) into .claude/skills/gram-pubsub in your project. Claude Code loads it when a task matches its description.
Run `npx skills add speakeasy-api/gram --skill gram-pubsub -a codex`. Or copy the skill folder (.agents/skills/gram-pubsub in speakeasy-api/gram) into .agents/skills/gram-pubsub in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add speakeasy-api/gram --skill gram-pubsub -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/gram-pubsub, .gemini/skills/gram-pubsub, .github/skills/gram-pubsub and .opencode/skills/gram-pubsub in your project.
Going by SKILL.md and its folder, Gram Pubsub needs the command-line tools its instructions call (mise and go).
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Gram Pubsub is published under the AGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.5k tokens (SKILL.md is roughly 18k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Gram Pubsub: Windmill Trigger Type Checklist (windmill-labs/windmill, 18k stars), Polylith Base Creation (DavidVujic/python-polylith, 553 stars), GCP Cloud Run (davila7/claude-code-templates, 32k stars) and AWS Lambda Microvms (awslabs/agent-plugins, 912 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
speakeasy-api (a GitHub organization) maintains it in speakeasy-api/gram, which has 272 GitHub stars. The repository holds 39 skills in this directory. The repository was last updated on October 7, 2026.
Source: speakeasy-api/gram on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.