ai:skills scope, and that scope carries a justification the owner reads at
install.
The manifest entry
tappify.extension.json instead — the
$schema line gives your editor completion and validation, and
tappify extension doctor checks the result.
Writing a skill
skills/growth-review.md is a procedure for the assistant, at most 4,000 words. Write the steps,
not the sales copy: which of your tools to call first, what your numbers mean, and when your data
does not answer the question.
bundle.skill_size, because the assistant reads all of it the
moment it picks the skill. Reference material belongs in a knowledge file instead, which is
retrieved a paragraph at a time.
name is at most 60 characters and description 10 to 300. Together they are all the assistant
sees until it picks your skill: Tappify lists your skills by name and description on every turn
and loads the body only when the assistant selects one, so length costs you nothing until it is
used.
tools lists your own tool ids. A skill may only rely on the tools its own extension declares;
naming another extension’s tool fails the publish with skills.tools.
Tappify registers your skills when the install is created, when the owner grants ai:skills, and
when an install is resumed, and re-reads them from the new manifest whenever one of your releases
goes live. A paused, retired or uninstalled install keeps none. At most 5 skills.
Writing a prompt
template is the question, with placeholders Tappify fills in. {{project.*}} is resolved on
the server before the chip reaches the browser; {{context.*}} and {{input.*}} are resolved in
the owner’s browser when they click.
Those three are the whole of
{{project.*}}. A chip in the chat’s suggestion row or under a
result card carries no context of its own, so a {{context.*}} there resolves to nothing — and
so does any other key Tappify does not recognise. The sentence around it is tidied up rather than
left with a gap, which is why a template reads better when the placeholder is a whole clause. A
template that leans on the surface — Why did {{project.name}} lose users at {{context.stage}}? —
belongs on a prompt declared for widget or page, which are the surfaces that carry one.
surfaces says where the chip is offered:
after names your own tool ids, and the manifest refuses any other extension’s. A result_card
chip is offered only under a card one of the tools in after produced.
title is at most 60 characters, template at most 2,000, and the resolved question a chip
carries is cut to 500. At most 20 prompts.
Asking for input first
What the owner sees
A chip with your tile and yourtitle. Clicking it puts the finished question in the owner’s
composer and stops there: they read it, edit it if they want, and press send themselves. Nothing
you write is ever sent on their behalf. Tappify offers at most two of your chips in any one row,
after its own.
When the owner does send, the turn records a prompt_sent against the chip it came from. A chip
naming a prompt the live release no longer declares is refused with PROMPT_NOT_DECLARED.
Opening the chat from your own surface
Your widget and your page can reach the composer directly, without a declared prompt.From <your extension name>:, which the owner can read and edit before they send. That context
is capped at 2 KB and a cut is marked with an ellipsis.
insights:write, that puts your card into the transcript as a card the host draws, and
opens the chat drawer. Without the scope it falls back to a sentence Tappify writes about the
card, in the composer, for the owner to send.