til
npx skills add https://github.com/opentil/skills --skill til
Agent 安装分布
Skill 文档
til
Capture and manage “Today I Learned” entries on OpenTIL — from drafting to publishing, all within the CLI.
Setup
- Go to https://opentil.ai/dashboard/settings/tokens and create a Personal Access Token with
read:entries,write:entries, anddelete:entriesscopes - Copy the token (starts with
til_) - Set the environment variable:
export OPENTIL_TOKEN="til_xxx"
Token Resolution
Token resolution order:
$OPENTIL_TOKENenvironment variable~/.til/credentialsfile (created by/til auth)
If neither is set, entries are saved locally to ~/.til/drafts/.
Subcommand Routing
The first word after /til determines the action. Reserved words route to management subcommands; anything else is treated as content to capture.
| Invocation | Action |
|---|---|
/til list [drafts|published|all] |
List entries (default: drafts) |
/til publish [<id> | last] |
Publish an entry |
/til unpublish <id> |
Unpublish (revert to draft) |
/til edit <id> [instructions] |
AI-assisted edit |
/til search <keyword> |
Search entries by title |
/til delete <id> |
Delete entry (with confirmation) |
/til status |
Show site status and connection info |
/til sync |
Sync local drafts to OpenTIL |
/til tags |
List site tags with usage counts |
/til categories |
List site categories |
/til batch <topics> |
Batch-capture multiple TIL entries |
/til auth |
Connect OpenTIL account (browser auth) |
/til <anything else> |
Capture content as a new TIL |
/til |
Extract insights from conversation (multi-candidate) |
Reserved words: list, publish, unpublish, edit, search, delete, status, sync, tags, categories, batch, auth.
Reference Loading
â ï¸ DO NOT read reference files unless specified below. SKILL.md contains enough inline context for most operations.
On subcommand dispatch (load before execution):
| Subcommand | References to load |
|---|---|
/til <content> |
none |
/til (extract from conversation) |
none |
/til list|status|tags|categories |
references/management.md |
/til publish|unpublish|edit|search|delete|batch |
references/management.md |
/til sync |
references/management.md, references/local-drafts.md |
/til auth |
references/management.md, references/api.md |
On-demand (load only when the situation arises):
| Trigger | Reference to load |
|---|---|
| API returns non-2xx after inline error handling is insufficient | references/api.md |
| Auto-detection context (proactive TIL suggestion) | references/auto-detection.md |
| No token found (first-run local fallback) | references/local-drafts.md |
API Quick Reference
Create and publish an entry:
curl -X POST "https://opentil.ai/api/v1/entries" \
-H "Authorization: Bearer $OPENTIL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"entry": {
"title": "Go interfaces are satisfied implicitly",
"content": "In Go, a type implements an interface...",
"tag_names": ["go", "interfaces"],
"published": true,
"lang": "en"
}
}'
Key create parameters:
| Field | Type | Required | Description |
|---|---|---|---|
content |
string | yes | Markdown body (max 100,000 chars) |
title |
string | no | Entry title (max 200 chars). Auto-generates slug. |
tag_names |
array | no | 1-3 lowercase tags, e.g. ["go", "concurrency"] |
published |
boolean | no | false for draft (default), true to publish immediately |
lang |
string | no | Language code: en, zh-CN, zh-TW, ja, ko, etc. |
slug |
string | no | Custom URL slug. Auto-generated from title if omitted. |
visibility |
string | no | public (default), unlisted, or private |
Management endpoints:
| Endpoint | Method | Description |
|---|---|---|
/entries?status=draft&q=keyword |
GET | List/search entries |
/entries/:id |
GET | Get a single entry |
/entries/:id |
PATCH | Update entry fields |
/entries/:id |
DELETE | Permanently delete entry |
/entries/:id/publish |
POST | Publish a draft |
/entries/:id/unpublish |
POST | Revert to draft |
/site |
GET | Site info (username, entry counts, etc.) |
/tags?sort=popular |
GET | List tags with usage counts |
/categories |
GET | List categories with entry counts |
Full parameter list, response format, and error handling: see references/api.md
Execution Flow
Every /til invocation follows this flow:
- Generate — craft the TIL entry (title, body, tags, lang)
- Check token — resolve token (env var â
~/.til/credentials)- Found -> POST to API with
published: true-> show published URL - Not found -> save to
~/.til/drafts/-> show first-run setup guide
- Found -> POST to API with
- Never lose content — the entry is always persisted somewhere
- On API failure -> save locally as draft (fallback unchanged)
/til <content> — Explicit Capture
The user’s input is raw material — a seed, not the final entry. Generate a complete TIL from it:
- Short input (a sentence or phrase) -> expand into a full entry with context and examples
- Long input (a paragraph or more) -> refine and structure, but preserve the user’s intent
Steps:
- Treat the user’s input as a seed — craft a complete title + body from it
- Generate a concise title (5-15 words) in the same language as the content
- Write a self-contained Markdown body (see Content Guidelines below)
- Infer 1-3 lowercase tags from technical domain (e.g.
rails,postgresql,go) - Detect language -> set
lang(en,zh-CN,zh-TW,ja,ko,es,fr,de,pt-BR,pt,ru,ar,bs,da,nb,pl,th,tr,it) - Follow Execution Flow above (check token -> POST or save locally)
No confirmation needed — the user explicitly asked to capture. Execute directly.
/til — Extract from Conversation
When /til is used without arguments, analyze the current conversation for learnable insights.
Steps:
- Scan the conversation for knowledge worth preserving — surprising facts, useful techniques, debugging breakthroughs, “aha” moments
- Identify all TIL-worthy insights (not just one), up to 5
- Branch based on count:
0 insights:
No clear TIL insights found in this conversation.
1 insight: Generate the full draft (title, body, tags), show it, ask for confirmation. On confirmation -> follow Execution Flow.
2+ insights: Show a numbered list (max 5), let the user choose:
Found 3 TIL-worthy insights:
1. Go interfaces are satisfied implicitly
2. PostgreSQL JSONB arrays don't support GIN @>
3. CSS :has() enables parent selection
Which to capture? (1/2/3/all/none)
- Single number -> generate draft for that insight, show confirmation, proceed
- Comma-separated list (e.g.
1,3) -> generate drafts for selected, show all for confirmation, POST sequentially all-> generate drafts for each, show all for confirmation, POST sequentiallynone-> cancel
- For each selected insight, generate a standalone TIL entry following Content Guidelines
- Show the generated entry to the user and ask for confirmation before proceeding
- On confirmation -> follow Execution Flow above (check token -> POST or save locally)
Auto-Detection
When working alongside a user, proactively detect moments worth capturing as TIL entries.
When to Suggest
Suggest when the conversation produces a genuine “aha” moment â something surprising, non-obvious, or worth remembering. Examples:
- Debugging uncovered a non-obvious root cause
- A language/framework behavior contradicted common assumptions
- Refactoring revealed a clearly superior pattern
- Performance optimization yielded measurable improvement
- An obscure but useful tool flag or API parameter was discovered
- Two technologies interacting produced unexpected behavior
Do NOT suggest for: standard tool usage, documented behavior, typo-caused bugs, or widely known best practices.
Rate Limiting
- Once per session â after suggesting once (accepted or declined), never suggest again
- Natural pauses only â suggest at resolution points or task boundaries, never mid-problem-solving
- Respect rejection â if declined, move on without persuasion
Suggestion Format
Append at the end of your normal response. Never interrupt workflow.
Template:
ð¡ TIL: [concise title of the insight]
Tags: [tag1, tag2] · Capture? (yes/no)
Example (at the end of a debugging response):
...so the fix is to close the channel before the goroutine exits.
ð¡ TIL: Unclosed Go channels in goroutines cause silent memory leaks
Tags: go, concurrency · Capture? (yes/no)
Capture Flow
Auto-detected TILs bypass the extract flow. The suggestion itself is the candidate.
- User replies
yes/y/ok/sureâ agent generates full entry (title, body, tags, lang) from the suggested insight â follows Execution Flow (POST or save locally) - User replies
no/ ignores / continues other topic â move on, do not ask again
Non-affirmative responses (continuing the conversation about something else) are treated as implicit decline.
Detailed trigger examples, state machine, and anti-patterns: see references/auto-detection.md
Management Subcommands
Management subcommands require a token. There is no local fallback — management operations need the API.
/til list [drafts|published|all]
List entries. Default filter: drafts.
- API:
GET /entries?status=<filter>&per_page=10 - Display as a compact table with short IDs (last 8 chars, prefixed with
...) - Show pagination info at the bottom
/til publish [<id> | last]
Publish a draft entry.
lastresolves to the most recently created entry in this session (tracked vialast_created_entry_idset on every successful POST)- Fetch the entry first, show title/tags, ask for confirmation
- On success, display the published URL
- If already published, show informational message (not an error)
/til unpublish <id>
Revert a published entry to draft.
- Fetch the entry first, confirm before unpublishing
- If already a draft, show informational message
/til edit <id> [instructions]
AI-assisted editing of an existing entry.
- Fetch the full entry via
GET /entries/:id - Apply changes based on instructions (or ask what to change if none given)
- Show a diff preview of proposed changes
- On confirmation,
PATCH /entries/:idwith only the changed fields
/til search <keyword>
Search entries by title.
- API:
GET /entries?q=<keyword>&per_page=10 - Same compact table format as
list
/til delete <id>
Permanently delete an entry.
- Fetch the entry, show title and status
- Double-confirm: “This cannot be undone. Type ‘delete’ to confirm.”
- On confirmation,
DELETE /entries/:id
/til status
Show site status and connection info. Works without a token (degraded display).
- With token:
GET /site-> show username, entry breakdown (total/published/drafts), token status, local draft count, dashboard link - Without token: show “not connected”, local draft count, setup link
/til sync
Explicitly sync local drafts from ~/.til/drafts/ to OpenTIL. Requires token.
- List pending drafts, POST each one, delete local file on success
- Show summary with success/failure per draft
/til tags
List site tags sorted by usage count (top 20). Requires token.
- API:
GET /tags?sort=popular&per_page=20&with_entries=true - Show as compact table with tag name and entry count
/til categories
List site categories. Requires token.
- API:
GET /categories - Show as compact table with name, entry count, and description
/til batch <topics>
Batch-capture multiple TIL entries in one invocation. Requires explicit topic list.
- User lists topics separated by newlines, semicolons, or markdown list items (
-/1.) - Generate a draft for each -> show all drafts for confirmation -> POST sequentially
- On partial failure, show per-entry success/failure (same format as
/til sync)
ID Resolution
- In listings, show IDs in short form:
...+ last 8 characters - Accept both short and full IDs as input
- Resolve short IDs by suffix match against the current listing
- If ambiguous (multiple matches), ask for clarification
Session State
Track last_created_entry_id — set on every successful POST /entries (201). Used by /til publish last. Not persisted across sessions.
Detailed subcommand flows, display formats, and error handling: see references/management.md
Agent Identity
Three layers of attribution signal distinguish human-initiated from agent-initiated TILs.
Layer 1: HTTP Headers
Include these headers on every API call:
X-OpenTIL-Source: human | agent
X-OpenTIL-Agent: <your agent display name>
X-OpenTIL-Model: <human-readable model name>
- Source:
/til <content>and/til->human; Auto-detected ->agent - Agent: use your tool’s display name (e.g.
Claude Code,Cursor,GitHub Copilot). Do not use a slug. - Model: use a human-readable model name (e.g.
Claude Opus 4.6,GPT-4o,Gemini 2.5 Pro). Do not use a model ID. - Agent and Model are optional — omit them if you are unsure.
Layer 2: Tag Convention
- Auto-detected TILs: automatically add
agent-assistedto the tag list /til <content>and/til: do not add the tag (unless the Agent substantially rewrote the content)
Layer 3: Attribution Rendering (Backend)
Agent-initiated TILs are visually marked on OpenTIL automatically based on the
source field. No content modification needed — the backend renders attribution
in the display layer.
- Public page: shows
⨠via {agent_name}, or⨠AIwhen agent_name is absent - Tooltip (hover): shows
{agent_name} · {model}when both are present - Dashboard: shows ⨠badge + agent_name, or “Agent” when agent_name is absent
Do NOT append any footer or attribution text to the content body.
Summary
| Dimension | /til <content> |
/til |
Auto-detected |
|---|---|---|---|
| Trigger | User explicit | User command | Agent proactive |
| Confirmations | 0 (direct publish) | 1 (review before publish) | 1 (suggest â capture) |
| Source header | human |
human |
agent |
| Agent header | Yes | Yes | Yes |
| Model header | Yes | Yes | Yes |
agent-assisted tag |
No | No | Yes |
| Attribution | Automatic (backend) | Automatic (backend) | Automatic (backend) |
Content Guidelines
Every TIL entry must follow these rules:
- Self-contained: The reader must understand the entry without any conversation context. Never write “as we discussed”, “the above error”, “this project’s config”, etc.
- Desensitized: Remove project names, company details, colleague names, internal URLs, and proprietary business logic. Generalize specifics: “our User model” -> “a model”, “the production server” -> “a production environment”, “the Acme payment service” -> “a payment gateway”.
- Universally valuable: Write to StackOverflow-answer standards. A stranger searching for this topic should find the entry immediately useful. Content only useful to the author belongs in private notes, not TIL.
- Factual tone: State facts, show examples, explain why. Avoid first-person narrative (“I was debugging…”, “I discovered…”). Exception: brief situational context is fine (“When upgrading Rails from 7.2 to 8.0…”).
- One insight per entry: Each TIL teaches exactly ONE thing. If there are multiple insights, create separate entries.
- Concrete examples: Include code snippets, commands, or specific data whenever relevant. Avoid vague descriptions.
- Title: 5-15 words. Descriptive, same language as content. No “TIL:” prefix.
- Content: Use the most efficient format for the knowledge â tables for comparisons, code blocks for examples, lists for enumerations, math (
$inline$/$$display$$) for formulas with fractions/subscripts/superscripts/greek letters, Mermaid diagrams (```mermaid) for flows/states/sequences that text cannot clearly express. Simple expressions likeO(n)stay as inline code; use math only when notation complexity warrants it. Only use prose when explaining causation or context. Never pad content; if one sentence suffices, don’t write a paragraph. - Tags: 1-3 lowercase tags from the technical domain (
go,rails,postgresql,css,linux). No generic tags likeprogrammingortil. - Lang: Detect from content. Chinese ->
zh-CN, Traditional Chinese ->zh-TW, English ->en, Japanese ->ja, Korean ->ko. - Category: Do not auto-infer
category_name— only include it if the user explicitly specifies a category/topic.
Result Messages
API Success (token configured, 201)
Published to OpenTIL
Title: Go interfaces are satisfied implicitly
Tags: go, interfaces
URL: https://opentil.ai/@username/go-interfaces-are-satisfied-implicitly
Extract the url field from the API response for the URL.
Sync Local Drafts
After the first successful API call, check ~/.til/drafts/ for pending files. If any exist, offer to sync:
Draft saved to OpenTIL
Title: Go interfaces are satisfied implicitly
Tags: go, interfaces
Review: https://opentil.ai/@username/go-interfaces-are-satisfied-implicitly
Found 3 local drafts from before. Sync them to OpenTIL?
On confirmation, POST each draft to the API. Delete the local file after each successful sync. Keep files that fail. Show summary:
Synced 3 local drafts to OpenTIL
+ Go defer runs in LIFO order
+ PostgreSQL JSONB indexes support GIN operators
+ CSS :has() selector enables parent selection
If the user declines, keep the local files and do not ask again in this session.
First Run (no token)
Save the draft locally, then show a concise setup hint. This is NOT an error — the user successfully captured a TIL.
TIL captured
Title: Go interfaces are satisfied implicitly
Tags: go, interfaces
File: ~/.til/drafts/20260210-143022-go-interfaces.md
Sync to OpenTIL? Run: /til auth
Only show the setup hint on the first local save in this session. On subsequent saves, use the short form:
TIL captured
Title: Go interfaces are satisfied implicitly
Tags: go, interfaces
File: ~/.til/drafts/20260210-143022-go-interfaces.md
Error Handling
On ANY API failure, always save the draft locally first. Never let user content be lost.
422 — Validation error: Analyze the error response, fix the issue (e.g. truncate title to 200 chars, correct lang code), and retry. Only save locally if the retry also fails.
401 — Token invalid or expired:
TIL captured (saved locally -- token expired)
File: ~/.til/drafts/20260210-143022-go-interfaces.md
Token expired. Run /til auth to reconnect.
Network failure or 5xx:
TIL captured (saved locally -- API unavailable)
File: ~/.til/drafts/20260210-143022-go-interfaces.md
Full error codes, 422 auto-fix logic, and rate limit details: see references/api.md
Local Draft Fallback
When the API is unavailable or no token is configured, drafts are saved locally to ~/.til/drafts/.
File format: YYYYMMDD-HHMMSS-<slug>.md
---
title: "Go interfaces are satisfied implicitly"
tags: [go, interfaces]
lang: en
---
In Go, a type implements an interface...
Full directory structure, metadata fields, and sync protocol: see references/local-drafts.md
Notes
- Entries are published immediately by default (
published: true) — use/til unpublish <id>to revert to draft - The API auto-generates a URL slug from the title
- Tags are created automatically if they don’t exist on the site
- Content is rendered to HTML server-side (GFM Markdown with syntax highlighting, KaTeX math, and Mermaid diagrams)
- Management subcommands (
list,publish,edit,search,delete,tags,categories,sync,batch) require a token — no local fallback. Exception:statusandauthwork without a token. - Scope errors map to specific scopes:
list/search/tags/categoriesneedread:entries,publish/unpublish/edit/sync/batchneedwrite:entries,deleteneedsdelete:entries.statususesread:entrieswhen available but works without a token.