Agent skill

Gram Pubsub

by speakeasy-api in 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…

AGPL-3.0Auto-check passedBackend & APIs

Install Gram Pubsub

skills CLI
$ npx skills add speakeasy-api/gram --skill gram-pubsub -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install speakeasy-api/gram gram-pubsub --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ 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-src

Use ~/.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/

Facts

Skill name
gram-pubsub
GitHub stars
272
Token cost
~4.5k tokens
SKILL.md length
1,891 words
Files
1
Skills in repo
39
Repo updated
First seen
Licence
AGPL-3.0

At a glance

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…

  • Works in 2 steps: write a handler → register it in the streams runner
  • Tasks that involve Event-driven systems
  • SKILL.md covers Mental model: marker messages, Authoring a topic, Authoring a subscription and Regenerating after a proto…, plus 6 more sections
  • Calls mise and go

What it does

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.

When your agent uses it

  • Tasks that involve Event-driven systems

Example prompts

  • “add an outbox topic”
  • “wire up a consumer”
  • “why isn”
  • “/gram-pubsub”

Workflow steps

2 steps, taken from the step headings in SKILL.md.

  1. write a handler
  2. register it in the streams runner

What it can do on your machine

Read from SKILL.md and the folder at commit 4d32da1. It shows what the files ask for, not the result of running them.

  • Tool permissions

    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.

  • Runs code

    Shell commands in SKILL.md call:

    • mise
    • go

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

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.

Always · name and description, kept in context so the agent knows when to use it
~164
When it runs · the whole SKILL.md, loaded when a task matches
~4.5k

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.

Safety

Auto-check passed

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.

SKILL.md

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.

Download SKILL.mdSave it as .claude/skills/gram-pubsub/SKILL.md (or your agent's skills folder).
name
gram-pubsub
description
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 like "add an outbox topic", "wire up a consumer", or "why isn't my topic created in GCP" even when Pub/Sub isn't named.
metadata.relevant_files
infra/proto/**/*.proto, infra/internal/gcp/*.go, infra/pkg/gcp/*.go, infra/cmd/infra/*.go, server/cmd/gram/streams.go, server/internal/streams/*.go…

Gram Pub/Sub

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."

Mental model: marker messages

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.

Authoring a topic

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:

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.

Authoring a subscription

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:

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.

Dead-letter queues are synthesized

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.

Regenerating after a proto change

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:infra

This 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.

How generation works (infra/internal/gcp/)

FileResponsibility
pubsub_discover.goWalks the descriptor set, extracts options, resolves names, dedupes, validates, synthesizes DLQ topics. The DesiredTopic / DesiredSubscription structs are the in-memory topology.
cc_pubsub.goProjects the topology into sorted Config Connector specs (buildPubSubValues).
values.goThe Helm values document types (pubSubValuesDocument). Specs embed the real Config Connector PubSubTopicSpec / PubSubSubscriptionSpec types so field names match the CRD exactly.
cc.goConfigConnectorPubSub.Provision orchestrates discover → build → write, emitting the # Code generated … DO NOT EDIT. YAML.

Key behaviors worth knowing:

  • Validation (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.
  • Labels: every generated resource gets 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.
  • Stable output: topics and subscriptions are sorted by name so the generated file diffs cleanly across runs. Durations render as integer-second strings (e.g. 604800s) as Config Connector expects.
  • Separation of concerns: the generator emits only the topology under a 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.

Runtime: publishing and subscribing (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):

go
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.

Implementing a subscriber in the server

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.

Step 1 — write a handler

A consumer is a streams.Handler[M] (server/internal/streams/handlers.go), where M is the proto message the topic carries:

go
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:

go
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.

Show full SKILL.md (714 more words)Show less
Step 2 — register it in the streams runner

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:

go
// 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.

What the runner does for you

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.

Driving the flow end to end (optional publisher)

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.

Local development

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.

How a declaration reaches a real environment

This is the part that most often confuses people, so it's worth being precise about what this repo does and does not do.

  • Declaring a topic and committing 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.
  • Rollout is decoupled and version-pinned per environment. Each environment runs the topology from a specific committed revision, not from 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.
  • Local working ≠ deployed. The emulator reconciles topics/subscriptions on the fly, so "works locally but missing in GCP" is the expected signature when the rollout simply hasn't reached that environment yet.
  • PR previews never create real topics, by design.

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.

Gotchas and conventions

  • Run 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).
  • Don't declare DLQ topics — they're synthesized from dead_letter.
  • One option per message: a message carrying both a topic and a subscription option fails generation by design.
  • Retiring a topic is a deliberate, decoupled act. Removing a declaration stops the topology from managing it, but the deployment tooling is configured so existing GCP topics are not destroyed by a topology change. Treat removals as a separate, intentional step rather than assuming a deleted declaration tears down the resource.

© 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

Files

Just SKILL.md in .agents/skills/gram-pubsub of speakeasy-api/gram.

Open the folder on GitHubat commit 4d32da1

Compare with similar skills

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.

Gram Pubsub compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Gram Pubsub this skillspeakeasy-api/gram272—~4.5kAutomated safety check: PassAGPL-3.0
Windmill Trigger Type Checklistwindmill-labs/windmill18k—~4.7kAutomated safety check: PassCustom licence
Polylith Base CreationDavidVujic/python-polylith553—~757Automated safety check: PassMIT
GCP Cloud Rundavila7/claude-code-templates32k7 repos~1.7kAutomated safety check: PassMIT
AWS Lambda Microvmsawslabs/agent-plugins9121 repos~4.1kAutomated safety check: PassApache-2.0
Gcloudgoogle/skills21k—~3.4kAutomated safety check: PassApache-2.0

Similar skills

  • 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.

    18k GitHub stars~4.7k tokensUpdated yesterday
    Backend & APIsAuto-check passed
  • Polylith Base Creation

    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).

    553 GitHub stars~757 tokensUpdated 3 days ago
    Backend & APIsAuto-check passed
  • GCP Cloud Run

    davila7/claude-code-templates

    Specialized skill for building production-ready serverless applications on GCP.

    32k GitHub starsUsed in 7 repos~1.7k tokens
    Backend & APIsAuto-check passed
  • AWS Lambda Microvms

    awslabs/agent-plugins

    Official

    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.

    912 GitHub starsUsed in 1 repo~4.1k tokens
    Backend & APIsAuto-check passed
  • Gcloud

    google/skills

    Official

    Provides safety-critical validation, guardrails, and data reduction for gcloud CLI operations across Google Cloud Platform (GCP) services and infrastructure.

    21k GitHub stars~3.4k tokensUpdated yesterday
    Backend & APIsAuto-check passed
  • Nest Devices

    sundial-org/awesome-openclaw-skills

    Control Nest smart home devices (thermostat, cameras, doorbell) via the Device Access API.

    663 GitHub stars~2.3k tokensUpdated 7 mo ago
    Backend & APIsAuto-check: notes

More from speakeasy-api/gram

All 39 skills in this repo
  • Gram Playwright CLI

    speakeasy-api/gram

    A skill your agent uses when automating the Gram dashboard in a browser, capturing screenshots, inspecting pages.

    272 GitHub stars~3.4k tokensUpdated today
    Auto-check passed
  • Transactional Email

    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…

    272 GitHub stars~4.7k tokensUpdated today
    Auto-check passed
  • Admin Shadcn

    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…

    272 GitHub stars~1k tokensUpdated today
    Auto-check passed
  • 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"…

    272 GitHub stars~2.2k tokensUpdated today
    Auto-check passed
  • Clickhouse

    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…

    272 GitHub stars~3.2k tokensUpdated today
    Auto-check passed
  • Feature Flag

    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…

    272 GitHub stars~2.6k tokensUpdated today
    Auto-check passed

Categories

Questions about Gram Pubsub

What does Gram Pubsub do?

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.

When should I use Gram Pubsub?

Gram Pubsub fits situations like: tasks that involve Event-driven systems.

How do I install Gram Pubsub in Claude Code?

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.

How do I install Gram Pubsub in Codex?

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.

Can I use Gram Pubsub in Cursor, Gemini CLI or GitHub Copilot?

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.

What does Gram Pubsub need to run?

Going by SKILL.md and its folder, Gram Pubsub needs the command-line tools its instructions call (mise and go).

Does Gram Pubsub access the network?

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.

Is Gram Pubsub safe to install?

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.

What licence does Gram Pubsub use?

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.

How many tokens does Gram Pubsub use?

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.

What are the alternatives to Gram Pubsub?

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.

Who maintains Gram Pubsub?

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.