17 KiB
Skills Demo Page — Design Document
Purpose
A standalone web page that demonstrates Didactyl skills running in a browser context. The page presents a minimal word-processing interface where users can load text, discover skills from Nostr relays, execute them against the text via an LLM, and edit/republish skill definitions.
This document is the complete specification for building the page. The front-end project consuming this document has its own LLM calling capability and Nostr connectivity.
Concepts
What is a Skill?
A skill is a Nostr event (kind 31123 for public, 31124 for private) that defines a self-contained LLM execution unit. Each skill specifies:
- A template with system and user prompt sections
- An LLM specification (model preferences with fallback chain)
- Parameters like temperature and max_tokens
- A description for human display
Skills are replaceable events keyed by their d tag (the d_tag). Publishing a new event with the same d tag replaces the previous version.
Skill Event Structure
{
"kind": 31123,
"pubkey": "<author hex pubkey>",
"created_at": 1709750000,
"content": "{\"description\":\"Check spelling and grammar\",\"context_mode\":\"full\",\"llm\":\"openai/gpt-4o-mini, cheap\",\"tools\":false,\"max_tokens\":2000,\"temperature\":0.1,\"template\":\"system:\\nYou are a spelling and grammar checker.\\n\\nRules:\\n- Fix spelling errors\\n- Fix grammar errors\\n- Preserve original formatting\\n- Return ONLY the corrected text, no explanations\\n\\nuser:\\n{{message}}\"}",
"tags": [
["d", "spellcheck"],
["app", "didactyl"],
["scope", "public"],
["description", "Spelling and grammar checker"],
["t", "text-processing"],
["t", "skills-demo"]
]
}
The content field is a JSON string containing these fields:
| Field | Type | Required | Description |
|---|---|---|---|
description |
string | yes | Human-readable description |
context_mode |
string | no | full for demo skills (skill provides its own template) |
llm |
string | no | LLM model preference with fallback chain |
tools |
bool | no | false for text-processing skills |
max_tokens |
int | no | Max tokens for LLM response |
temperature |
float | no | Sampling temperature |
template |
string | yes | The prompt template with system: and user: sections |
Template Format
Templates use a simple section-based format:
system:
You are a spelling and grammar checker.
Rules:
- Fix spelling errors
- Fix grammar errors
user:
{{message}}
The {{message}} placeholder is replaced with the user's text at execution time.
Skill Address Format
Skills are referenced by their NIP-33 address: <kind>:<author_pubkey_hex>:<d_tag>
Example: 31123:52a3e82f7b3743852fbe804cfcbf4db3448115887895247c001f2b50e790acb8:spellcheck
Demo Skills
Four skills are published by the Didactyl agent (pubkey 52a3e82f7b3743852fbe804cfcbf4db3448115887895247c001f2b50e790acb8). All are tagged with t:skills-demo for easy discovery.
1. Spellcheck — d:spellcheck
system:
You are a spelling and grammar checker.
Rules:
- Fix spelling errors
- Fix grammar errors
- Preserve original formatting and paragraph structure
- Preserve line breaks exactly as they appear
- Ignore technical terms: API, JSON, HTTP, nostr, pubkey, npub, nsec, NIP
- Canadian English preferred
- Return ONLY the corrected text, no explanations or commentary
user:
{{message}}
- temperature:
0.1 - max_tokens:
4000
2. Condense — d:condense-5
system:
You are a text condensing editor.
Rules:
- Reduce the text length by approximately 5%
- Preserve the original meaning and tone
- Preserve paragraph structure
- Remove redundant words and phrases
- Tighten sentences without changing voice
- Do not add new information
- Return ONLY the condensed text, no explanations or commentary
user:
{{message}}
- temperature:
0.3 - max_tokens:
4000
3. Translate to Japanese — d:translate-ja
system:
You are a professional translator specializing in English to Japanese translation.
Rules:
- Translate the entire text from English to Japanese
- Use natural, fluent Japanese appropriate for the content register
- Preserve paragraph structure and formatting
- Preserve any technical terms in their commonly accepted Japanese form
- If a term has no standard Japanese equivalent, keep the English term in katakana
- Return ONLY the Japanese translation, no explanations or commentary
user:
{{message}}
- temperature:
0.3 - max_tokens:
4000
4. Convert to Poem — d:convert-to-poem
system:
You are a creative poet.
Rules:
- Convert the given text into a poem
- Capture the key themes and meaning of the original text
- Use vivid imagery and poetic devices
- Aim for 12-20 lines
- Free verse is acceptable but light rhyme is welcome
- Return ONLY the poem, no explanations or commentary
user:
{{message}}
- temperature:
0.9 - max_tokens:
2000
Discovering Skills from Nostr
Query Filter
To find the demo skills, query relays with this filter:
{
"kinds": [31123],
"#t": ["skills-demo"],
"limit": 20
}
To find skills by a specific author:
{
"kinds": [31123],
"authors": ["52a3e82f7b3743852fbe804cfcbf4db3448115887895247c001f2b50e790acb8"],
"#t": ["skills-demo"],
"limit": 20
}
Recommended Relays
wss://relay.damus.iowss://nos.lolwss://relay.primal.net
Parsing a Skill Event
- Receive the event from the relay
- Parse
event.contentas JSON to get the skill definition object - Extract
description,template,temperature,max_tokens - Extract the
dtag value fromevent.tagsas the skill d_tag - Extract the
descriptiontag fromevent.tagsas a display label - The skill is ready to display and execute
Page Layout
+------------------------------------------------------------------+
| SKILLS DEMO [dark/light]|
+------------------------------------------------------------------+
| | |
| +------------------------------+ | +---------------------------+|
| | TEXT EDITOR | | | SKILLS PANEL ||
| | | | | ||
| | [large editable textarea ]| | | Loading skills... ||
| | [filled with default text ]| | | ||
| | [ ]| | | [x] spellcheck ||
| | [ ]| | | Spelling and grammar ||
| | [ ]| | | checker ||
| | [ ]| | | ||
| | [ ]| | | [ ] condense-5 ||
| | [ ]| | | Reduce text by 5% ||
| | [ ]| | | ||
| | [ ]| | | [ ] translate-ja ||
| | [ ]| | | Translate to Japanese ||
| | [ ]| | | ||
| | [ ]| | | [ ] convert-to-poem ||
| | | | | Convert to poem ||
| +------------------------------+ | | | ||
| | | [Run Selected Skill] ||
| +------------------------------+ | | [Edit Skill] ||
| | STATUS BAR | | | ||
| | Ready / Running... / Done | | | ─── Skill Details ─── ||
| +------------------------------+ | | template: ... ||
| | | temperature: 0.1 ||
| | | max_tokens: 4000 ||
| | +---------------------------+|
+------------------------------------------------------------------+
Left Column — Text Editor (65% width)
- A large
<textarea>orcontenteditablediv, minimum 500px tall - Pre-filled with default sample text (see Default Text section below)
- Fully editable by the user
- When a skill runs, the text content is sent as
{{message}} - After the LLM responds, the textarea content is replaced with the result
- An undo button restores the previous text (keep a history stack)
Right Column — Skills Panel (35% width)
- Skills list: Radio buttons or selectable cards, one skill selected at a time
- Each skill card shows:
- Skill d_tag (the
dtag) - Description (from the
descriptiontag or content field) - Author pubkey (truncated, e.g.,
52a3e8...0acb8)
- Skill d_tag (the
- Run Selected Skill button: executes the selected skill against the current text
- Edit Skill button: opens the skill editor (see Skill Editor section)
- Skill Details section: shows the full template, temperature, max_tokens for the selected skill
- Refresh Skills button: re-queries relays for updated skill events
Status Bar
- Shows current state:
Ready,Running skill: spellcheck...,Done (1.2s), orError: ... - Displays token usage if available from the LLM response
Skill Execution Flow
sequenceDiagram
participant User
participant Page as Demo Page
participant LLM as LLM Provider
User->>Page: Select skill + click Run
Page->>Page: Parse skill template
Page->>Page: Split template into system/user sections
Page->>Page: Replace message placeholder with textarea content
Page->>LLM: Send system prompt + user message
Note over Page,LLM: Use skill temperature and max_tokens
LLM-->>Page: Response text
Page->>Page: Push current text to undo stack
Page->>Page: Replace textarea with response
Page->>User: Show completion status
Template Parsing
The template field uses a simple format with system: and user: section markers:
system:
<system prompt content>
user:
<user prompt content with {{message}} placeholder>
Parsing algorithm:
- Split the template string on the line
user:(case-sensitive, must be at start of line) - Everything before
user:is the system section; strip the leadingsystem:prefix - Everything after
user:is the user section - In the user section, replace
{{message}}with the textarea content - Trim whitespace from both sections
Pseudocode:
function parseSkillTemplate(template, userText) {
const userMarkerIndex = template.indexOf('\nuser:\n');
if (userMarkerIndex === -1) {
// Fallback: entire template is system, user text is the message
return {
system: template.replace(/^system:\n/, '').trim(),
user: userText
};
}
let systemPart = template.substring(0, userMarkerIndex);
systemPart = systemPart.replace(/^system:\n/, '').trim();
let userPart = template.substring(userMarkerIndex + '\nuser:\n'.length);
userPart = userPart.replace('{{message}}', userText).trim();
return { system: systemPart, user: userPart };
}
LLM Call
With the parsed system and user prompts, call the LLM:
const response = await callLLM({
messages: [
{ role: 'system', content: parsed.system },
{ role: 'user', content: parsed.user }
],
temperature: skill.temperature || 0.7,
max_tokens: skill.max_tokens || 2000
});
The page's own LLM calling capability handles the actual API request. The skill just provides the prompts and parameters.
Skill Editor
When the user clicks Edit Skill, a modal or inline editor opens for the selected skill.
Editor Fields
| Field | Input Type | Description |
|---|---|---|
| Slug | text input (readonly for existing, editable for new) | The d tag identifier |
| Description | text input | One-line human description |
| Template | large textarea (monospace) | The full system:/user: template |
| Temperature | number input (0.0 - 2.0, step 0.1) | LLM sampling temperature |
| Max Tokens | number input (100 - 8000) | Maximum response tokens |
Editor Actions
- Save & Publish: Builds a new kind
31123event and publishes to relays - Cancel: Closes editor without changes
- New Skill: Opens editor with blank fields for creating a new skill
Publishing a Skill
sequenceDiagram
participant Editor as Skill Editor
participant Signer as Nostr Signer
participant Relay as Nostr Relays
Editor->>Editor: Build skill content JSON
Editor->>Editor: Build event with kind 31123 + tags
Editor->>Signer: Sign event
Signer-->>Editor: Signed event
Editor->>Relay: Publish to connected relays
Relay-->>Editor: OK confirmation
Editor->>Editor: Update local skill list
Building the event:
function buildSkillEvent(d_tag, description, template, temperature, maxTokens) {
const content = JSON.stringify({
description: description,
context_mode: 'full',
llm: 'default',
tools: false,
max_tokens: maxTokens,
temperature: temperature,
template: template
});
return {
kind: 31123,
content: content,
tags: [
['d', d_tag],
['app', 'didactyl'],
['scope', 'public'],
['description', description],
['t', 'text-processing'],
['t', 'skills-demo']
],
created_at: Math.floor(Date.now() / 1000)
};
}
The event must be signed with the user's Nostr private key (via NIP-07 browser extension or the app's own signer) before publishing to relays.
Default Text
Pre-fill the textarea with this sample text that exercises the demo skills well:
The Sovereign Agent
In the not-too-distant future, software agents will roam freely across decentralized networks, communicating through cryptographic protocols that no central authority can censor or control. These agents will learn new capabilities not from app stores or corporate APIs, but from each other — sharing skills as freely as humans share ideas.
Imagine an agent that wakes up on any computer in the world. You enter twelve words, and there it is — your agent, with all its memories, skills, and personality intact. It doesn't live on a server you rent. It doesn't depend on a company staying in business. It exists wherever you need it, sovereign and portable.
The key insight is that skills are the new apps. Instead of installing software, agents adopt skills — small, self-contained instructions that teach them how to perform specific tasks. A spelling checker. A translator. A poet. Each skill is just a Nostr event, discoverable by anyone, adoptable by any agent.
This is not science fiction. The protocols exist today. Nostr provides the communication layer. Bitcoin provides the economic layer. Cryptography provides the trust layer. What remains is to build the agents themselves — and to set them free.
There are some erors in this text that a good spellchecker should catch. The writting could also be more concise, and it would be intresting to see it translated or converted into verse.
Note: The last paragraph intentionally contains spelling errors (erors, writting, intresting) to demonstrate the spellcheck skill.
Undo/Redo
Maintain a text history stack:
const textHistory = [];
let historyIndex = -1;
function pushHistory(text) {
// Truncate any forward history
textHistory.splice(historyIndex + 1);
textHistory.push(text);
historyIndex = textHistory.length - 1;
}
function undo() {
if (historyIndex > 0) {
historyIndex--;
return textHistory[historyIndex];
}
return null;
}
function redo() {
if (historyIndex < textHistory.length - 1) {
historyIndex++;
return textHistory[historyIndex];
}
return null;
}
- Push the current text to history before replacing it with the LLM response
- Push the initial default text as the first history entry
- Show Undo/Redo buttons below the textarea
Responsive Behavior
- On screens wider than 900px: two-column layout as shown
- On screens narrower than 900px: stack vertically — text editor on top, skills panel below
- The textarea should have a minimum height of 400px and resize vertically
Error Handling
| Scenario | Behavior |
|---|---|
| No relays connected | Show "No relay connection" in skills panel with retry button |
| No skills found | Show "No skills found. Try different relays or create a new skill." |
| Skill content parse error | Show "Invalid skill format" badge on the skill card, disable Run |
| LLM call fails | Show error in status bar, do not replace textarea content |
| Publish fails | Show error in editor, keep editor open for retry |
Summary of Nostr Operations
| Operation | Kind | Direction | When |
|---|---|---|---|
| Discover skills | 31123 |
Read from relays | Page load + refresh |
| Execute skill | — | N/A (local LLM call) | User clicks Run |
| Publish skill | 31123 |
Write to relays | User saves in editor |
The page does NOT need to interact with the Didactyl agent's HTTP API. All skill discovery and publishing happens directly via Nostr relays. Skill execution happens locally using the page's own LLM capability.