sdtk-marketing-kit
v0.10.0
Published
Standalone local-first governed-marketing CLI: measured KPI digest, a blocking truthfulness/claims linter (check), an eval-first campaign engine (campaign/attribution/eval), a channel registry (gate status, owned vs rented), an asset truthfulness gate (ca
Downloads
1,682
Readme
sdtk-marketing-kit
Governed, honest, measured marketing operations — local-first, free, MIT.
The SDTK governance kernel applied to marketing: this toolkit enforces truthfulness (you cannot ship an overclaim or a faked "demo"), measures before it targets, and treats human approval as a code gate, not a setting. It is designed to be operated by an agent (Hermes) and gated by a human. Phase 1 ships the two most-proven pieces: a measured KPI digest and a blocking truthfulness linter.
Install
npm i -g sdtk-marketing-kit
sdtk-marketing helpRequires Node ≥ 18.
Commands (Phase 1)
sdtk-marketing check <file|dir>…
Lint marketing post copy for truthfulness (BR-03 / honest-scorecard). Exits 1 on any error.
- Blocking errors — claims that violate the "claims-NOT-made" discipline:
trusted by, unverified adoption/user counts, revenue figures (MRR/ARR), productivity multipliers ("10x faster"), zero-touch / unattended / live-bot claims (negated honest usage like "nothing here is zero-touch" is allowed). - Warnings (review, non-blocking) — framings that are only truthful next to a real asset:
unedited,real/raw demo,screen recording; and astated-count-mismatchheuristic (catches "Six palettes (a · b · c · d · e · f · g)" = 6-vs-7). - Flags:
--json(structured output for an agent),--strict(warnings also block),--stdin(lint copy piped in — the agent-gate path: pipe the exact publishable text so meta docs and asset notes don't cause false positives).
printf '%s' "$post_body" | sdtk-marketing check --stdin --json # exit 1 blocks the postPoint it at post copy, not meta docs. Governance/analysis docs that discuss the banned terms (proposals, audits) will trip the rules by design — lint the actual posts you're about to publish.
sdtk-marketing check governance/marketing/SDTK_FB_POST5_*.md --jsonsdtk-marketing digest
Measured weekly KPI digest: npm downloads · github stars/forks/issues · plausible visitors · lemonsqueezy orders · facebook followers.
- npm + github need no keys. The rest read keys from env; unset keys print
not configured— a valid state, not a failure (sections light up as you provision keys). - A configured section that fails prints
FAILED: <reason>and the run exits 1 (fail-loud). 0is a real value (e.g.lemonsqueezy: orders 7d 0is a PASS), never hidden.- Env:
PLAUSIBLE_API_KEY/PLAUSIBLE_SITE_ID,LEMONSQUEEZY_API_KEY,FACEBOOK_PAGE_ACCESS_TOKEN/FB_PAGE_ID; overridesMKT_NPM_PACKAGES,MKT_GITHUB_REPO. Flag:--json.
campaign · attribution · eval — the eval-first engine (P2.1)
Pre-register a distribution campaign's success criteria, record each channel's measured funnel, and
get KEEP / ITERATE / TRIM verdicts. Thresholds lock at campaign start so a result can't be
rationalised after the numbers land. The funnel is visitor → pack download → order.
sdtk-marketing campaign start distribution-r2 --goal "find dev/maker channels" \
--window-hours 72 --keep-visitors 30 --keep-downloads 3 --trim-visitors 10
sdtk-marketing attribution record distribution-r2 --channel devto --visitors 41 --downloads 5 --orders 0
sdtk-marketing eval distribution-r2 [--json]
# [KEEP ] devto: 41v → 5d → 0o (v→d 12.2%) ↳ ≥30v AND ≥3d — make it a repeatable slot- Decision: KEEP =
visitors ≥ keep-visitors AND downloads ≥ keep-downloads; TRIM =visitors < trim-visitors; ITERATE = the middle band, or visitors-ok-but-downloads-short. - Ledger:
.sdtk/marketing/campaigns/<id>/(campaign.json+attribution.json) — file-backed, resumable,--jsonfor an agent. Override the root withSDTK_MARKETING_HOME. - Attribution — auto or manual.
attribution pull <campaign>auto-fills each channel's visitors/downloads from the Plausible Stats API perutm_source(needsPLAUSIBLE_API_KEY+PLAUSIBLE_SITE_ID; pulls for channels in thechannelregistry that have autm). Or record by hand withattribution record. Per-channel orders stay manual — Lemon Squeezy checkout carries no UTM. The value is applying locked thresholds to measured numbers.
channel — the distribution registry (P2.2)
Tracks each channel's gate status and whether it's owned vs rented, so an agent proposes a postable channel instead of hitting a wall (the lesson from every quality channel gating posting — Reddit karma, IH earned privilege).
sdtk-marketing channel add devto --type rented --status open --gate none --utm devto --reach "dev/maker SEO, evergreen"
sdtk-marketing channel add indiehackers --type rented --status warming-up --gate "earned posting privilege"
sdtk-marketing channel status indiehackers ready # flip when the gate clears
sdtk-marketing channel list # splits POSTABLE-NOW vs earn-in- status:
open/ready= postable now;warming-up/blocked/trimmed/paused= not. - type:
owned(email/site — no gate, no algorithm) vsrented(borrowed reach, plays by a gate). - Ledger:
.sdtk/marketing/channels.json.--jsonfor an agent.
capture · asset verify — the asset truthfulness gate (P2.3)
The visual counterpart to check: an evidence_capture must be a real recording of the real
product — a generation tool (ComfyUI/LTX/WAN/Remotion) can never produce evidence; a
generated_creative may only be cosmetic and must declare a truthfulness boundary.
# produce provenance-stamped evidence from a LIVE url (pixel grab delegated to your browser tool)
export SDTK_MARKETING_CAPTURE_CMD='node scripts/shot.js {url} {out}' # operator-configured
sdtk-marketing capture https://sdtk.dev/pricing --out assets/pricing.png --id post5-pricing
# register an AI-generated cosmetic asset (must declare what it may/may not imply)
sdtk-marketing asset add hero-titlecard --role generated_creative --method remotion \
--truthfulness "cinematic title card — cosmetic only, never implies it is the product"
sdtk-marketing asset verify --all # exit 1 if any evidence asset is fake / any creative lacks a boundary- The gate:
evidence_captureneeds a real capture method (browser_capture/screen_record) + asource_url; an AI method in an evidence slot FAILS.generated_creativeneeds atruthfulness_boundary. Exit 1 on any error. captureis honest: it stampsbrowser_captureprovenance only after a real capture actually produced the file (viaSDTK_MARKETING_CAPTURE_CMD); the kit stays dependency-free and never fabricates provenance.- Ledger:
.sdtk/marketing/assets/<id>.json.
report — the campaign scorecard (P3)
Composes a campaign's locked thresholds + recorded funnel into a scorecard, and a measured-only
draft post in the honest-scorecard voice — the loop feeding itself. The draft is measured by
construction, so it passes check:
sdtk-marketing report distribution-r2 # operator scorecard (verdicts + totals)
sdtk-marketing report distribution-r2 --post \
| sdtk-marketing check --stdin # the draft post, self-verified truthfulvideo — reproducible video-build workflows (Phase 3a)
An agent builds channel videos by naming an exact, reproducible workflow — the kit holds the
definition + honesty rules, the box does the render. The four installed backends (ComfyUI, LTX-2.3,
WAN-2.1, Remotion) collapse to two delegate commands, mirroring capture so the kit stays
dependency-free:
sdtk-marketing video list # the registry (id · purpose · backend · honesty)
sdtk-marketing video show tutorial-feature-walkthrough # the exact recipe + which delegate env it uses
sdtk-marketing video run tutorial-feature-walkthrough \
--capture usage.cast --feature usage # render → record an honest asset → re-verify
sdtk-marketing video run intro-spectacle --dry-run # print the exact backend command, render nothingDelegated render: the box supplies
SDTK_MARKETING_VIDEO_CMD_REMOTION/SDTK_MARKETING_VIDEO_CMD_COMFYUI(templates with{graph} {capture} {input} {out}). Unset → fail-closed, nothing stamped.Output stays outside the kit: MP4 lands under
SDTK_MARKETING_ASSET_HOME(the ledger keeps definitions + provenance, not bytes).Honesty is enforced twice (D3): a
tutorial/reviewisevidence_captureand requires a real--capture(avhs/screen_recordrecording of the actual CLI) — the kit will not fabricate a tutorial from a generator; ComfyUI/LTX/WAN output isgenerated_creative, allowed for intro/b-roll only and must carry a truthfulness boundary. The produced asset is then re-checked by the sameasset verifygate.Quality gate: honesty isn't enough — a tiny/short stub render passes every honesty check and still wastes an upload. Each workflow declares minimum specs; the runner probes the real output via
SDTK_MARKETING_VIDEO_PROBE_CMD(a{file}template) and a sub-spec render fails closed instead of being recorded. If no probe is configured, the gate is skipped with a warning.Size and duration alone were not enough, twice: a 1920×1080 / 300 s render passed and turned out to be a slideshow (frozen 97.7 % of its runtime), and the motion gate added to fix that then passed a moving empty screen (27 % near-blank frames). So the probe may also report motion and content, and a workflow may gate on them:
| Probe field | Workflow threshold | Catches | |---|---|---| |
median_mafd|min_median_mafd| a render that barely moves | |frozen_ratio|max_frozen_ratio| a slideshow | |edge_density|min_edge_density| an empty screen | |luma|luma_range| too dark to read, or blown out |The probe accepts either the original
"<w> <h> <duration>"orkey=valuepairs:width=1920 height=1080 duration=75 median_mafd=0.544 frozen_ratio=0.485 edge_density=3.8 luma=182Existing probe commands keep working untouched — a threshold whose field the probe does not report is announced as NOT ENFORCED rather than silently passing.
video calibrate— derive your own thresholds. The shipped numbers were calibrated on light-mode product UI plus one dark-composite film, and each workflow carries acalibrated_againstnote saying so. They are not universal: a threshold picked by intuition once rejected a capture that measured better than the reference film's own signature frame. Point this at footage you consider good and paste the result into your workflow:sdtk-marketing video calibrate ref-a.mp4 ref-b.mp4 # measured spread + suggested fields sdtk-marketing video calibrate ref-a.mp4 --margin 0.2 --jsonNone of these gates certify that a video is good. They rule out defects that were actually hit. Watch it before you publish it.
publish youtube / publish facebook — attended, sha-gated video publish (Phase 3b/3c)
A video reaches YouTube (or a Facebook Page) only after the owner approves this exact payload. The upload is impossible without a matching approval sha, so an agent cannot auto-post even if told to:
# 1) prepare — verifies the asset, guarantees the UTM, check-gates the copy, prints a payload sha
sdtk-marketing publish youtube tutorial-usage \
--title "How I run sdtk usage in 60s" --description-file desc.md --tags "sdtk,cli,devtools"
# → prints the drafted metadata + `payload sha256: <hex>` and uploads NOTHING
# 2) approve — re-run the identical command with the sha; only a MATCH uploads
sdtk-marketing publish youtube tutorial-usage \
--title "How I run sdtk usage in 60s" --description-file desc.md --tags "sdtk,cli,devtools" \
--approve <hex>- The asset gate first: publish refuses an asset that fails
asset verify(no faked evidence ships) and refuses if the MP4 is missing on disk. - The copy gate: the title + description run through
check— an overclaim blocks the publish. - Attribution built in: the description is guaranteed to carry
sdtk.dev/heroes?utm_source=youtube&utm_medium=video&utm_campaign=<id>, and a successful publish upserts theyoutubechannel — soattribution pullmeasures it automatically. - Delegated upload, no secrets in the kit: the actual upload runs via the box's delegate
(
SDTK_MARKETING_PUBLISH_CMD_YOUTUBE/SDTK_MARKETING_PUBLISH_CMD_FACEBOOK_VIDEO, a{file} {title} {description-file} {tags} {privacy}template); the OAuth/Page token lives on the box. Unset → fail-closed, nothing uploaded. - Conservative by default:
--privacydefaults to the safest state per platform (youtubeunlisted, facebookunpublished) — the sha-gate approves the upload; going fully public stays a separate deliberate flip on the platform. - Drift = fail-closed: change any metadata after approval and the sha no longer matches — the upload is refused until the new payload is re-approved. The channel and payload are part of the hash, so a YouTube approval can never be replayed against Facebook.
What's coming (see the toolkit proposal)
An MCP / n8n surface exposing the whole governed spine (check · eval · video · publish) to an agent
runtime. The attended schedule / gate approve / publish loop is provided by HerSocial today;
this kit standardizes it.
Design principles
- Never auto-publish; never fabricate a metric or a claim; AI only for cosmetic assets.
- Not a spam / mass-posting tool — respects channel gates, eval-first.
- File-backed and resumable; every command emits
--jsonfor an agent operator.
Develop
npm test # node scripts/marketing-smoke.test.js — offline, no networkLicense: MIT.
