gtme
Health Uyari
- License — License: Apache-2.0
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 6 GitHub stars
Code Gecti
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Gecti
- Permissions — No dangerous permissions requested
Bu listing icin henuz AI raporu yok.
Run outbound campaigns from Claude Code: YAML pipelines, an append-only ledger, nothing sent twice.
/gtme: run outbound campaigns from Claude Code
/gtme is a CLI and a Claude Code plugin. You describe a campaign in
your own words, Claude writes it as a small YAML pipeline, and /gtme runs
it against your vendors. An append-only ledger remembers every fact, so a
re-run pays only for what's new and nobody gets the same message twice.
▶ Watch the 42-second demo.
One prompt runs a pipeline, the re-run pays nothing, and the send waits for
your yes. More at gtme.run.
Get started in three steps
Install the binary.
brew install gtme-run/tap/gtme # macOS and Linux, arm64 and amd64Add the plugin in Claude Code.
/plugin marketplace add gtme-run/gtme /plugin install gtme@gtme-runType
/gtme. The console opens beside the chat. Ask for a campaign,
or start with "See it run" in START.md, which needs no
keys and spends nothing.
Other ways to install, and building from a checkout, are under
Install.
From a sentence to a sent campaign
Four prompts in Claude Code, and what each one returns. Every command is
the one Claude runs for you, and every output comes from a real run.
1. You ask for the campaign.
Find VPs and heads of marketing at 50 to 200 person SaaS companies in
Apollo. Keep the ones likely to own outbound tooling, write a first line
and a PS from their recent posts, and add them to my Q3 VP Marketing
campaign in Instantly. Five people to start.
Claude writes apollo-to-instantly.yaml:
name: apollo-to-instantly
version: 1
source:
use: apollo/search
with:
query: "saas"
titles: ["vp marketing", "head of marketing"]
employee_ranges: ["50,200"]
limit: 5
steps:
- id: icp-filter
use: ai/filter
uses: [first_name, title, company_name]
with:
template: >
Keep only contacts likely to own outbound tooling decisions.
batch_size: 25
...
- id: personalize
use: ai/compose
when: icp-filter.passed
uses: [recent_posts, role_history]
with:
template: >
Write first_line and ps_line using recent_posts and role_history.
first_line references something specific and recent; ps_line is one
short, low-pressure sentence. No flattery, no exclamation marks.
batch_size: 25
- id: send
use: instantly/add-to-campaign
with:
campaign: 0198a0b1-2c3d-4e5f-8a9b-0c1d2e3f4a5b # the campaign id
variables:
first_line: first_line
ps_line: ps_line
idempotency: email
2. You ask for a rehearsal.
Dry-run it against my real accounts.
Claude runs gtme run apollo-to-instantly.yaml --dry-run:
run 01M3D1ZTKBQBS57C9WPK8NQM60 — done (dry run — nothing sent)
step adapter in out empty cached filtered failed cost avoided
source apollo/search 0 5 - 0 - - $0 -
icp-filter ai/filter 5 5 - 0 - - $0.0105 -
reveal apollo/enrich 5 5 - 0 - - $0.0500 -
linkedin harvest/profile 5 5 - 0 - - $0.0600 -
personalize ai/compose 5 5 - 0 - - $0.0232 -
send instantly/add-to-campaign 5 0 - 0 - - $0 -
send: preflight ok — campaign "Q3 VP Marketing" (0198a0b1-2c3d-4e5f-8a9b-0c1d2e3f4a5b) — 3 check(s) (✓ campaign active, ✓ variable first_line referenced, ✓ variable ps_line referenced)
total: $0.1437 (estimated) spent
Five people found, judged, enriched, and drafted for fourteen cents. The
send step checked the campaign and held all five.
3. You say send.
Looks good. Send it.
Claude asks before the armed run, and you confirm. Then it runsgtme run apollo-to-instantly.yaml, which adds the five to the campaign.
4. You run it again.
Run it again.
Claude runs gtme run apollo-to-instantly.yaml again:
step adapter in out empty cached filtered failed cost avoided
source apollo/search 0 5 - 0 - - $0 -
icp-filter ai/filter 5 0 - 5 - - $0 ?
reveal apollo/enrich 5 0 - 5 - - $0 $0.0500
linkedin harvest/profile 5 0 - 5 - - $0 $0.0600
personalize ai/compose 5 0 - 5 - - $0 ?
send instantly/add-to-campaign 5 0 - 0 - - $0 -
send: 5 already delivered
total: $0 (estimated) spent, $0.1100+? avoided via cache (20 records skipped)
Every paid step comes back from the ledger at $0, and idempotency: email
keeps the send from adding anyone twice.
The console
Claude Code folds a command's output to one line, so the plugin carries a
console: a pane beside the chat that /gtme opens, and that a run the
agent starts opens by itself. It shows the latest run's steps as they
move, with the receipt's counts and spend. When a human/* step is
waiting on you, each record gets its controls there (approve or reject, a
review label, a draft to edit), and every answer is recorded in the
ledger. The console never runs anything that spends or sends; that stays
with you. plugin/README.md has the details.
The four skills behind it are /gtme:create-pipeline,/gtme:run-pipeline, /gtme:create-adapter, and /gtme:analyze. Claude
picks one from what you ask, or you can call one by name.
Under the hood
Outbound tooling assumes you want a UI, credits, and someone else's opinion
of your workflow. If you'd rather have a CLI, a ledger you can query, YAML
you can diff, and receipts for every dollar — this is that. Campaigns are
pipelines. Adapters are data. Judgment is versioned. Everything replays.
What it is
- A single static binary (
gtme) that executes campaign pipelines
defined in YAML. No daemon, no hosted anything, no login. - An append-only SQLite ledger as the data bus. Steps never pass
records to each other — they read a projection of what's known and write
facts back, with provenance, confidence, and freshness on every value.
What your pipelines learn accumulates; what a run did is receipted. - Contracts everywhere. Every step declares
needsandprovidesas
JSON Schema against a canonical field registry, sogtme planproves a
pipeline coherent — end to end — before a single record moves or a cent
is spent. AI steps live behind the same contract as everything else. - Adapters as data. Most vendor APIs are CRUD over HTTP, so most
adapters here are bindings: declarative YAML interpreted by one
generic engine — auth, pagination, extraction, error mapping,
idempotency, cost, all frozen at authoring time. The moment an
integration needs real logic, it graduates to a process adapter speaking
NDJSON over stdin/stdout, in any language.
Use cases
- Outbound campaigns — source → enrich → AI-judge → AI-compose →
deliver, with the send gated behind a human-reviewable receipt. - Enrichment that stops re-billing you — freshness-windowed caching
means top-ups and re-runs pay only for what the ledger doesn't already
know; the receipt prints what the cache saved. - Qualify/send decomposition — a cheap qualify pipeline fills a
group (a durable, hand-editable set); a separate send pipeline
consumes it, records touches, and suppresses re-contacts by window.
AI verdicts become recorded decisions: an identity is judged once per
scope, not re-rolled on every run. - Research enrichment —
http/enrichfetches a page to markdown as a
ledger fact (with mandatory freshness — web content rots), and AI steps
judge the stored content. Fetch once, judge many. - Deterministic transforms in SQL —
sql/transformandsql/filterrun
declared, read-only queries over the ledger: derive fields, bucket
titles, gate on "3+ known contacts at this company" — no AI spend for
computable questions. - The universal floor — CSV in, groups in; any-URL POST out, CSV
out. Anything with an export or an import button is wireable
today, even before a proper adapter exists. - Portable campaigns —
gtme freeze --bundlesnapshots a run into a
self-contained folder (pipeline + bindings + fixtures + hash manifest)
that runs, simulates, and diffs anywhere.
Architecture
pipeline.yaml (the only authoring surface)
│
gtme plan ──── contracts, credentials, cost — $0
│
┌────────────────────┼─────────────────────────────┐
│ runner ▼ │
│ ┌─────────┐ ┌────────┐ ┌─────────┐ ┌─────────┐
│ │ source │→ │ enrich │→ │ ai/sql │→ │ deliver │ steps see only
│ └────┬────┘ └───┬────┘ └────┬────┘ └────┬────┘ projections,
│ │ ▲ │ ▲ │ ▲ │ ▲ never the ledger
├────────┼──┼────────┼──┼─────────┼──┼─────────┼──┼──┐
│ ▼ │ ▼ │ ▼ │ ▼ │ │
│ ledger (append-only SQLite) │
│ identities · field_values (facts + provenance) │
│ runs · step_events · costs · deliveries │
│ groups · group_events (decisions about sets) │
│ payloads (raw responses — cache, purgeable) │
└────────────────────────────────────────────────────┘
The load-bearing choices:
- The ledger is the bus. Cache-aware waterfalls, resume after a crash,
cost attribution, and "what do we know about this person" as a SQL query
all fall out of one structural decision: facts live in the ledger, not
in a stream between steps. - Two adapter tiers. Bindings (YAML, interpreted, cannot execute code)
for anything that sells an API; NDJSON processes for anything that must
be fought for. Both speak the same wire protocol and pass the same
conformance kit: golden vendor payloads in → canonical records out. - A closed grammar. The pipeline surface is a small, enumerable set of
keys — no expression language, no DSL to hallucinate. Expressivity comes
from composing a small vocabulary against a canonical field registry. - The gate ladder:
simulate → plan → dry-run → armed. Simulation
executes the whole pipeline from conformance fixtures — no network, no
keys, deterministic. Plan proves contracts. Dry-run does everything but
deliver, rendering exactly what would send. Arming is the same command
without the flag. - Agent-operable by design.
gtme help --agentemits the full CLI and
adapter surface as one machine-readable document, andgtme help --bindingsthe contract for authoring an adapter it does not ship;
every error names its fix; every question about state has a deterministic answer path. An
agent can author a pipeline and validate its structure and behavior
before a human reviews one artifact and arms it.
Why engineers who do GTM
Speed to deploy. A new vendor integration is a YAML file — the Apollo
source adapter shipped here is ~150 lines of declarative binding
(see it), and the authoring
loop is generate → conformance-test → done, which is exactly the loop a
coding agent closes well. APIs that publish OpenAPI are bind-time targets,
not build projects. The universal floor means day one is never blocked on
an adapter existing.
Rapid iteration. Simulation makes behavior checkable offline before
credentials exist. The cache makes iteration cheap: change a prompt and
re-run — enrichments skip, only judgment re-spends. Filter verdicts and
their reasons land in the ledger, so tuning a prompt is a SQL query over
what the model actually decided, not vibes. Freeze any run back into the
exact YAML that produced it.
Safety. Delivery is the one destructive edge and it is gated three
ways: the dry-run receipt is a per-record approval artifact a human reads
before arming; idempotency keys make re-runs structurally unable to
double-send; suppression windows enforce contact policy across pipelines.
Everything upstream is replayable because the ledger is append-only.
Blank merge fields never send. Groups freeze AI judgment into recorded,
inspectable decisions instead of per-run dice rolls.
Cost. Every step's spend lands in the receipt, attributed per record
and per model. The cache prints what it saved you, not just what you
spent. Fetch-once economics: N AI steps across M runs reuse one paid
enrichment while it's fresh. Raw vendor responses are retained (bounded,
TTL'd, evictable) so improving an extraction back-fills from payloads
you already paid for — at zero vendor spend.
Install
brew install gtme-run/tap/gtme # macOS and Linux, arm64 and amd64
The tap installs the
prebuilt binary from the releases
page, verified against
the checksums.txt published beside it; without Homebrew, do the same by
hand — untar, put gtme on your PATH, gtme init. With Go 1.24+, go install github.com/gtme-run/gtme/cmd/gtme@latest builds the tagged
release straight from the module. Building from a checkout is git clone
./install.sh, which also installs the repo's example external
adapters so the README quickstart works offline.
Then add the Claude Code plugin with the two/pluginlines in
Get started. Whichever way,
START.md is the next page: four ways to start,
each one pipeline file, each ending in a receipt.
The adapters
What ships today, and the vendor adapters the registry serves — see
ADAPTERS.md for each one's config and behavior, orgtme help --agent for the machine-generated, always-current surface of
what the binary carries. Adapters marked (registry) install withgtme adapters add <id>:
| In | csv/source · group-as-source · apollo/search (registry) |
| Enrich | apollo/enrich (registry) · harvest/profile (registry) · harvest/recent-posts (registry) · http/enrich (any URL → markdown or JSON) · sql/transform |
| Judge | ai/filter · sql/filter · ai/review |
| Write | ai/compose · text/compose (a template, no model) |
| Ask | human/filter · human/compose · human/review · agent/* (the same three, answered by an agent) |
| Out | instantly/add-to-campaign (registry) · attio/assert (registry) · http/deliver (any URL) · csv/deliver · group/deliver (the next stage) |
Adding your own doesn't require touching this repo: drop a binding.yaml
into ~/.gtme/adapters/<name>/ and the id resolves immediately.
Steps a person answers
A human/* step puts judgment in the pipeline without pretending a model
made it. At a terminal gtme run walks the records and asks; anywhere
else — cron, CI, a pipe — every record waits in the ledger, and gtme answer records the verdict later. Either way the answer lands as a fact
with the person's name in its provenance, and the run collects it exactly
as it collects a deferred batch.
gtme run review.yaml # ends "pending — 12 awaiting human/review"
gtme show --run last --pending # read what is waiting
gtme answer last [email protected] --set grade=B --note "too generic"
gtme run review.yaml # collects the answers, continues
agent/* is the same three steps for an agent that answers with its own
judgment rather than a person's; it never prompts, and --as puts its
name in the ledger.
Five things worth knowing before you put one in a pipeline:
- Under cron, a pipeline with a participant step waits. A pending run
is resumed, not re-sourced, so nothing new is picked up until someone
answers. The pattern that avoids it is two pipelines: the reviewing one
a person runs, ending in agroup:, and the scheduled one sourcing from
that group.gtme planprints a note when a deliver step follows a
person. - Ctrl-C mid-walk is safe. The records you answered are settled, the
rest stay pending, and the run ends pending rather than failed. - The latest answer before collection wins. Answers are ledger state,
idempotent per record; answering again replaces the earlier one. - An unchanged value is not asked twice. A person's judgment is cached
like a model's: rewrite the draft and it comes back, leave it alone and
it does not.cache: 0dorrespend: trueask again on purpose. - A review labels; it never gates.
when: <review>.passedfails plan.
Use a filter when the answer should stop a record.
A campaign is a folder
Everything that defines a campaign is text, so a campaign works the way
the rest of your engineering does: a directory under version control.
q3-outbound/
qualify.yaml # source → enrich → ai/filter ⇒ group (cheap, run often)
send.yaml # group → compose → deliver (deliberate, gated)
contacts.csv # input data, when the source is a file
Pipelines diff in code review. Prompt changes are commits. A colleague's
campaign is a git pull. And when a run is worth keeping,gtme freeze --bundle snapshots it into a self-contained artifact:
bundle/
manifest.json # format version, source run id, sha256 per file
pipeline.yaml # the exact config that ran
adapters/ # every referenced binding, at its frozen version,
apollo-search/ # with its fixtures — so the bundle simulates offline
registry/ # the field vocabulary the contracts speak
gtme run <bundle> accepts the folder anywhere a pipeline path goes,
verifies every hash first, and resolves the bundle's own bindings ahead of
whatever the machine has installed. Contracts travel; your ledger,
credentials, and input data stay yours. The natural unit for a playbook, a
handoff, or a per-client repo.
The Claude Code plugin in plugin/ carries four skills — create a pipeline from a goal, run one up the ladder, add a
vendor, analyze the ledger — each a procedure that reads its facts fromgtme help --agent at run time. Five such playbooks ship in bundles/: qualify → group →
send, an email waterfall, the account shape, events via CSV + cron, and
posts to engagers via a traverse. Every one simulates offline from the
checkout (cd bundles/<pattern> && gtme run . --simulate) and CI proves
it; each README says what its receipt shows and which rung comes next.
Build from a checkout
git clone https://github.com/gtme-run/gtme && cd gtme
./install.sh # builds gtme, installs it to ~/.local/bin, runs gtme init
install.sh is deliberately boring: it compiles from the checkout and
copies one static binary into place — no curl-pipe-to-shell, no network
fetch. It warns you if ~/.local/bin isn't on your PATH (add it to your
shell profile, or run PREFIX=/usr/local ./install.sh instead). Prefer
not to install? make build gives you ./bin/gtme and every command
below works with that path.
The zero-key demo — run a whole campaign, offline, right now:
gtme adapters add apollo/search apollo/enrich instantly/add-to-campaign # keyless: verified, then pinned
gtme run examples/demo.yaml --simulate
The Apollo source serves its conformance fixtures, the AI steps answer
synthetically (and are marked synthetic in provenance), the reveal step
prints its estimated charge, and delivery is held with its variables
resolved into the receipt — Instantly's campaign check is skipped and
says so, because a simulated run reads no target. One sourced record has no email — Apollo's
locked-email placeholder is treated as absent, never as an identity key —
so it is keyed by name hash; both held records then read "Jane Doe",
because the reveal fixture answers every lookup with the same sanitized
person. Fixtures are canned responses, and the receipt says SIMULATED
so nobody mistakes them for data. Run it twice: the receipts are
identical, because a simulated run persists nothing.
The zero-key top-up — the second receipt, on a ledger that persists:
gtme run examples/hello.yaml # three people scored at $0.01 each, one kept, out.csv written
gtme run examples/hello.yaml # again: 3 cached, $0.0300 avoided, nothing delivered twice
demo/enrich is the binary's own synthetic enrichment: no vendor, no key,
deterministic scores labelled synthetic in the value itself, at a stated
pretend price — so the receipt's cached and avoided columns fill in
for real, and every dollar it prints is labelled demo/enrich wherever it
appears. The SQL filter and the CSV delivery are the real adapters. What
this proves is the mechanism, not the savings: the counter is real, the
$0.03 is not. The same column with a vendor's real price is the ladder
below, and a second run of apollo-to-instantly.yaml at campaign size
is where it turns into money.
Then with real keys, the same file climbs the ladder:
gtme secret set APOLLO_API_KEY # prompts, no echo
gtme secret set ANTHROPIC_API_KEY
gtme secret set INSTANTLY_API_KEY # the campaign whose id is in the file must exist
gtme plan examples/demo.yaml # contracts + cost, still $0
gtme run examples/demo.yaml --dry-run # everything but delivery
gtme run examples/demo.yaml # armed
gtme run examples/demo.yaml # again: cache skips, zero re-delivery
Then interrogate what happened — no log spelunking:
gtme show [email protected] --provenance # every fact, who wrote it, when
gtme runs last # the receipt, reconstructed
gtme query "SELECT field, value FROM current_fields ..."
gtme groups # decisions about sets, with tallies
Further exploration
- SPEC.md — the canon. Every observable behavior, DECIDED
sections, acceptance criteria. The binary is built from this document;
code that diverges from it is a bug even when it works. - DECISIONS.md — the why: the ADR log, including the
autopsy of why Singer died twice and how the field registry and binding
tier answer both causes. - VALIDATION.md — the receipts: real campaigns, real
API drift absorbed, real dollar amounts, findings logged honestly. - ADAPTERS.md — every shipped adapter, what it does,
and the config that matters. - spec/bindings/ — vendor adapters as reviewable,
diffable YAML, with the conformance fixtures that double as simulation. - PROCESS.md — how design conversations become spec, and
why nothing is decided until it's in the repo. - ROADMAP.md — named, deliberately deferred: the expand
role, payload re-extraction, an OpenAPI→binding codegen skill, and more. - CONTRIBUTING.md — the contribution surface is
adapters, and most adapters are one reviewable YAML file; here's the
shape and the checklist. gtme help --agent— the whole surface, machine-readable, for your
agent;gtme help --bindings— the binding contract (schema, discovery
path, a reference binding) when it needs an adapter that is not here.
License
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi
