Skip to content
Back to skills

Postey Voice

ASecurity

Learn how the user actually writes — from the posts they published, and from what they approved, edited or rejected — and keep a brand profile that gets sharper with use.

  • 6 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 25, 2026
ai-agentsrustgobashexpressapi

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Pro scans all 9 files and shows the line behind each finding

Scanned November 11, 2026

npx -y skills add posteyai/skills --skill postey-voice --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Postey Voice?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Postey Voice
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/posteyai-postey-voice/badge)](https://www.skillsdirectory.com/skills/posteyai-postey-voice)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: postey-voice
version: 1.2.1
description: >
  Learn how the user actually writes — from the posts they published, and from
  what they approved, edited or rejected — and keep a brand profile that gets
  sharper with use.
when_to_use: >
  Use when the user says "learn my voice", "write like me", "that doesn't sound
  like me", gives a handle or website to learn from, or when a draft is edited
  before publishing and the correction should be remembered.

# Requires the `postey` skill. It ships brand-profile-template.md (the schema this
# pack fills in) and the craft layer every flow cites, plus the read paths below.
requires:
  - postey

# Local-machine work only: reading the user's own files and doing deterministic
# arithmetic over them. The CLI calls no Postey API and creates nothing.
allowed-tools:
  - Bash(${CLAUDE_SKILL_DIR}/scripts/voice.js:*)

# This skill OWNS no server capability. Voice is judgment, and
# docs/skills-mcp-contract.md assigns judgment to the skill; every write still
# goes through the hub. It only reads, and it stores what it learns client-side.
capabilities:
  owns:
  reads:
    - post.list
    - post.read
    - comment.internal.list
    - analytics.top_posts
    - post.analytics
  prompts:
    - improve-post

mcp-tools:
  resources:
    - postey://accounts
    - postey://posts/{post_id}/content/{platform}
    - postey://posts/{post_id}/comments/{platform}
    - postey://posts/{post_id}/analytics
    - postey://accounts/{account_id}/analytics/posts
  tools:
    # GENERATED from capabilities: by scripts/gen-mcp-tools.js — do not hand-edit.
    - get_posts
    # Fallbacks only: each is superseded by a postey:// resource this skill
    # declares. Use them when the client cannot read MCP resources.
    - get_internal_comments
    - get_post_content
  prompts:
    - improve-post

routing:
  read-only-state: mcp-resource > mcp-tool
  analytics:       mcp-resource
  fallback:        mcp-tool
---

# Postey Voice

Two ways to learn a voice, and they compose:

| | Cold start | Warm loop |
|---|---|---|
| **What it reads** | Posts already published | What happens to each draft — edits, approvals, deletions |
| **When it runs** | Once, on first use | Every time a draft is acted on |
| **Needs history?** | Yes — nothing to read on a new account | No, it builds its own |
| **Produces** | The initial profile | Rules appended to the ledger |
| **Recoverable later?** | Yes, published posts persist | **No** — capture live or lose it |

## One profile per account — ask first

**Read `postey://accounts` and, if more than one is connected, ask which account this profile is
for. Never derive a voice without naming the account.**

A creator with one account, a brand with three, and an agency with twelve are the same code path
and different failure costs. For the agency, applying one profile to the wrong account puts a
client's voice in another client's post.

| Situation | What to do |
|---|---|
| One account connected | Use it, and say which one you used |
| Several connected, user named one | Use that one |
| Several connected, user named none | **Ask.** Do not infer from the most recent post or the first in the list |
| Deriving from local files, no account in play | Allowed, but the profile is unscoped — say so |

Every profile carries `profile_for`. A profile whose `profile_for` is `null` was derived without
an account and **must not be applied to a named account** — re-derive with the account set.
`corpus.accounts` is a separate field: the accounts the evidence was read *from*.

The CLI mirrors this. `--account <id>` records the scope and names the output
`voice-profile-<id>.json`, so two accounts cannot overwrite each other's profile. Omitting it
writes `profile_for: null` and warns.

## What this skill cannot know

**It knows nothing before it is installed.** Say so rather than implying otherwise.

The server has no `REJECTED` state — `PostStatusEnum` is `DRAFT | SCHEDULED | PUBLISHING |
PUBLISHED`. Rejection is expressed by deleting the draft, which destroys the evidence. So:

| Signal | Available retroactively? |
|---|---|
| Published posts, and which performed best | **yes** — this is the cold start |
| Reviewer notes (internal comments) | **yes** |
| What was edited between draft and publish | **no** — must be captured live |
| What was rejected, and why | **no** — must be captured live |

A rejection made in the Postey web UI is invisible to this skill, permanently. Only drafts this
agent created in-session can be tracked through to their verdict.

## Bulk ingest — content the user already has

Fastest path to a first profile, and it needs no account access at all:

```bash
${CLAUDE_SKILL_DIR}/scripts/voice.js ingest ./their-writing --account 317
```

Pass `--account` whenever the profile is for a connected account — it names the output
`voice-profile-317.json` and records the scope. Omit it only when the corpus genuinely belongs to
no account yet; the result is then explicitly unscoped.

Accepts a directory, a single file, or a JSON export (`.md`, `.txt`, `.json`; a bare array,
`{posts:[…]}` or `{data:[…]}`). A JSON export becomes one document per row, keeping `post_id`,
`platform` and `published_at`; a text file becomes one document dated by its mtime.

Useful flags: `--account <id>` to scope the profile, `--scope LINKEDIN` to attribute everything to one platform, `--since <ISO>` to ignore
old work, `--out <file>` to write the ledger.

It emits countable features and rule observations — never an opinion. Judgements like "warm but
authoritative" are yours to make from reading the corpus; the CLI only measures what cannot be
argued with, so that every rule can name its evidence.

Then compile:

```bash
${CLAUDE_SKILL_DIR}/scripts/voice.js compile ./voice-ledger.json
```

Full flag list: [command-reference.md](command-reference.md).

## Cold start — the published corpus

1. `get_posts(status=PUBLISHED)` per account.
2. Read each one's text: `postey://posts/{post_id}/content/{platform}`.
3. Weight by `postey://accounts/{account_id}/analytics/posts` — profile what **worked**, not
   merely what shipped. **Check the numbers are real first.** On the account checked during
   development, every LinkedIn post reported zero impressions while the same content on X reported
   between 481 and 2,935. Weighting by a metric that is uniformly zero silently weights by nothing;
   say so and fall back to unweighted rather than implying the ranking meant something.
### Corpus hygiene — read this before trusting a pass

A Postey account accumulates **QA and pipeline-test posts**, and they are `PUBLISHED` like anything
else. A real account checked during development had 88 published LinkedIn posts, of which a large
share were artifacts: `AUDIT-PUBLISH-PROBE`, `publish-fix validation test`, `testf`,
`sdsd sdssdaaaa`, `qwetgvdbmFgh`. Ingest those and you will learn that the user writes gibberish.

Exclude a post from the corpus when any of these hold:

- the title or text reads as a probe — `test`, `probe`, `validation`, `[dev]`, `please ignore`
- the text is not language — no sentence structure, repeated character runs
- it is a duplicate of another post with a near-identical opening

Then say how many you dropped and why. A silent filter is as misleading as no filter.

4. Derive observable features only: sentence length distribution, how posts open and close,
   punctuation habits, emoji and hashtag rate, whether there is a CTA and what shape it takes.

Write findings into the profile with the post IDs they came from. A claim about the user's voice
with no citation is a guess, and it will be treated as evidence later.

## Warm loop — the verdict ledger

**The edit delta is the core of this skill.** It is free, it is specific, and it states exactly
what was wrong in the user's own correction. Analytics is the tiebreaker, not the primary signal.

Two gates. Both are mandatory; skip either and the loop silently never runs:

- **Before drafting anything** — read the profile and the active rules.
- **After every outcome** — append a verdict to the ledger.

Append verdicts to the same ledger file, then re-run `voice.js compile`. Thresholds are applied by
the script, not by eye — counting evidence across sessions by hand is exactly what drifts.

Format and thresholds: [rules-ledger.md](rules-ledger.md).
Profile shape: [voice-profile-schema.md](voice-profile-schema.md).

## Where the profile lives

Client-side — memory, project knowledge, or a file the user keeps. The MCP server has no write
path for skill state, and the hub's `brand-profile-template.md` already establishes this pattern.

**It is user content.** Never commit it, never log it, never send it anywhere.

## Hard limits

A learned rule never overrides a platform limit. Character counts, media rules and platform
mechanics come from `postey://platform-limits` and the hub, which owns platform truth. If a rule
and a limit disagree, the limit wins and the rule is noted as inapplicable there.

## The interview flow

When there is no corpus and no profile — a new account, or a user who wants to state their voice
rather than have it inferred — run the interview: [references/brand-voice.md](references/brand-voice.md).
Prefer evidence over self-report where both exist: what someone publishes is a better record of
how they write than what they say about how they write.

Files in this skill

  • SKILL.md9.2 KB
  • command-reference.md4.3 KB
  • pack.json664 B
  • references/brand-voice.md3.2 KB
  • rules-ledger.md5.7 KB
  • scripts/voice.js9.6 KB
  • scripts/voiceFeatures.js4.4 KB
  • scripts/voiceRules.js3.1 KB
  • voice-profile-schema.md3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in ai-agents →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.