From repost-with-agent
User-configured allow/skip rules for Repost-with-agent candidates. Apply before dedupe/publish so Ethan can block categories such as X video/livestream promos without polluting publish ledgers.
How this skill is triggered — by the user, by Claude, or both
Slash command
/repost-with-agent:repost-custom-rulesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Custom rules are a user preference filter that runs AFTER source scrape and
Custom rules are a user preference filter that runs AFTER source scrape and BEFORE dedupe / URL expansion / publish. They answer: “even if this is new, should we refuse to repost it?”
They are NOT publish proof and NOT duplicate proof. A rule skip must never be recorded as a successful post.
Rules live in ~/.repost-with-agent/pairs.json:
customRules: apply globally across pairs.pair.customRules: apply only to that pair.Append-only considered/skipped state lives at:
~/.repost-with-agent/considered.jsonl
This file records candidates the agent has already considered and rejected for a user-rule reason. It prevents every scheduled tick/backfill from re-reasoning the same not-post-worthy item, without touching append-only publish ledgers.
{
"id": "skip-x-ai-slop-machine-videos",
"enabled": true,
"action": "skip",
"scope": {
"sourcePlatform": "x"
},
"match": {
"anyOf": [
{ "sourceItemIds": ["2051336931150123230"] },
{
"semanticSimilarToAny": [
"vibe coding an ai slop machine #ai #programming #developer",
"video or livestream promo about vibe-coding an AI slop machine"
],
"mediaTypesAny": ["video", "livestream", "live"],
"treatUnknownMediaAsMatch": true
}
]
},
"reason": "Ethan does not want X video/livestream promo posts about vibe-coding an AI slop machine reposted.",
"examples": [
{
"sourcePlatform": "x",
"sourceItemId": "2051336931150123230",
"canonicalSourceUrl": "https://x.com/REEEthan_YT/status/2051336931150123230",
"text": "vibe coding an ai slop machine #ai #programming #developer"
}
],
"createdAt": "<ISO-8601>",
"updatedAt": "<ISO-8601>"
}
A rule matches a candidate only when the rule is enabled, action is skip, the
scope matches, and the match object matches.
Scope fields:
pairIds: optional list. If present, current pair.id must be listed.sourcePlatform: optional exact platform string.destinationPlatforms: optional list. If present, current
pair.destination.platform must be listed.Match fields:
sourceItemIds: exact source item IDs.canonicalSourceUrls: exact canonical source URLs.textIncludesAll: all listed substrings must appear in normalized text.textIncludesAny: at least one listed substring must appear in normalized text.textRegex: regex string the candidate text must match.sourceKindsAny: at least one candidate source kind must match. Expected
values include original, repost, reshare, quote, and unknown.
Use this for platform-native repost/reshare blocks that should not be treated
as the user's original wording.semanticSimilarToAny: agent judgment. Match if the candidate has the same
communicative content/topic as any listed example; lean conservative when the
user explicitly wanted that content blocked.mediaTypesAny: at least one candidate media type must match. Expected values
include image, video, livestream, live, gif, link-card, none,
and unknown.treatUnknownMediaAsMatch: when true, a candidate with unknown media status
passes mediaTypesAny. Use sparingly for user-block rules where missing a
bad post is worse than skipping a possibly-good one.Composition:
anyOf is an OR of match objects.allOf is an AND of match objects.allOf / anyOf with ordinary fields.Source scrape should collect the normal repost fields plus media hints when the source platform exposes them:
{
"sourceItemId": "<platform item id>",
"canonicalUrl": "<source URL>",
"text": "<visible post body>",
"publishedAt": "<ISO-8601>",
"sourceKind": "original",
"isRepost": false,
"repostEvidence": "<optional exact UI/DOM reason, e.g. Reposted by Ethan Sarif-Kattan>",
"mediaTypes": ["video"],
"mediaEvidence": "visible video player / Live badge / aria-label text"
}
If media cannot be determined, set mediaTypes: ["unknown"] rather than
pretending it is text-only.
customRules and pair.customRules from
~/.repost-with-agent/pairs.json. Missing arrays mean no custom rules.~/.repost-with-agent/considered.jsonl if it exists.sourcePlatform and sourceItemId (or canonical URL) match, andstatus === "skipped-rule", andpairId/destination fields match this run,
then drop the candidate before dedupe. Do not append another duplicate
considered line just because a later scheduled tick saw it again.candidate.custom_rule.skipped line to
~/.repost-with-agent/considered.jsonl if that exact
(ruleId, sourcePlatform, sourceItemId/canonicalUrl, pair/destination scope)
is not already present.pair.custom_rule.skipped for this run.{
"ts": "<ISO-8601>",
"event": "candidate.custom_rule.skipped",
"ruleId": "skip-x-ai-slop-machine-videos",
"pairId": "<optional pair id when destination-specific>",
"sourcePlatform": "x",
"sourceItemId": "2051336931150123230",
"canonicalSourceUrl": "https://x.com/REEEthan_YT/status/2051336931150123230",
"destinationPlatform": "<optional destination platform>",
"destinationAccountHint": "<optional configured destination account hint>",
"candidateExcerpt": "<first 200 chars>",
"sourceKind": "original | repost | reshare | quote | unknown",
"mediaTypes": ["video"],
"status": "skipped-rule",
"reason": "<human reason from rule>",
"note": "<optional migration/context note>"
}
Invariants:
posted.jsonl or global-posted.jsonl for a pure custom-rule
skip. Those ledgers mean destination state was proven; a preference skip does
not prove destination state.Skipped <pairId>: <sourceItemId> <canonicalSourceUrl> matched custom rule <ruleId>.
Do not send one Telegram message per skip unless Ethan explicitly asks.Ethan wants the X video/livestream item below — and future matching X video/livestream promos — blocked from reposting:
https://x.com/REEEthan_YT/status/2051336931150123230vibe coding an ai slop machine #ai #programming #developerskip-x-ai-slop-machine-videosThis item had already been posted to Bluesky, Threads, and Facebook before the rule existed. Do not rewrite or remove those append-only publish/global ledger records. Treat the seeded considered entry only as “from now on, this kind of candidate is not post-worthy.”
Ethan wants LinkedIn reposts/reshares skipped entirely. The LinkedIn activity feed mixes original posts with reposts of other people's posts, and reposts must not be treated as Ethan-authored source text.
skip-linkedin-repostssourcePlatform: "linkedin"sourceKind of repost or reshare"Reposted by <user>" header or an embedded
feed-shared-update-v2__reshare blockThis is a preference skip, not proof of a destination post. Append to
considered.jsonl and per-pair audit only.
skills/repost-run/SKILL.md — applies this immediately after source scrape.skills/repost-backfill/SKILL.md — applies this before backfill dedupe and
inside the publish loop when refreshing candidates.docs/state-files.md — formal schema for customRules and
considered.jsonl.Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub ethansk/repost-with-agent --plugin repost-with-agent