{
  "schemaVersion": "1.0",
  "item": {
    "slug": "til",
    "name": "OpenTIL",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/biao29/til",
    "canonicalUrl": "https://clawhub.ai/biao29/til",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/til",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=til",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md",
      "references/management.md",
      "references/api.md",
      "references/auto-detection.md",
      "references/local-drafts.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "slug": "til",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-05-06T13:39:45.841Z",
      "expiresAt": "2026-05-13T13:39:45.841Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=til",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=til",
        "contentDisposition": "attachment; filename=\"til-1.11.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null,
        "slug": "til"
      },
      "scope": "item",
      "summary": "Item download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this item.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/til"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/til",
    "agentPageUrl": "https://openagent3.xyz/skills/til/agent",
    "manifestUrl": "https://openagent3.xyz/skills/til/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/til/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "til",
        "body": "Capture and manage \"Today I Learned\" entries on OpenTIL -- from drafting to publishing, all within the CLI."
      },
      {
        "title": "Setup",
        "body": "Go to https://opentil.ai/dashboard/settings/tokens and create a Personal Access Token with read:entries, write:entries, and delete:entries scopes\nCopy the token (starts with til_)\nSet the environment variable:\n\nexport OPENTIL_TOKEN=\"til_xxx\""
      },
      {
        "title": "Token Resolution",
        "body": "Token resolution order:\n\n$OPENTIL_TOKEN environment variable (overrides all profiles)\n~/.til/credentials file — active profile's token (created by /til auth)\n\nIf neither is set, entries are saved locally to ~/.til/drafts/."
      },
      {
        "title": "Credential File Format",
        "body": "~/.til/credentials stores named profiles in YAML:\n\nactive: personal\nprofiles:\n  personal:\n    token: til_abc...\n    nickname: hong\n    site_url: https://opentil.ai/@hong\n    host: https://opentil.ai\n  work:\n    token: til_xyz...\n    nickname: hong-corp\n    site_url: https://opentil.ai/@hong-corp\n    host: https://opentil.ai\n\nactive: name of the currently active profile\nprofiles: map of profile name → credentials\nEach profile stores: token, nickname (from API), site_url, host\n\nBackward compatibility: If ~/.til/credentials contains a plain text token (old format), silently migrate it to a default profile in YAML format and write back."
      },
      {
        "title": "Subcommand Routing",
        "body": "The first word after /til determines the action. Reserved words route to management subcommands; anything else is treated as content to capture.\n\nInvocationAction/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 statusShow site status and connection info/til syncSync local drafts to OpenTIL/til tagsList site tags with usage counts/til categoriesList site categories/til batch <topics>Batch-capture multiple TIL entries/til authConnect OpenTIL account (browser auth)/til auth switch [name]Switch active profile (by profile name or @nickname)/til auth listList all profiles/til auth remove <name>Remove a profile/til auth rename <old> <new>Rename a profile/til <anything else>Capture content as a new TIL/tilExtract insights from conversation (multi-candidate)\n\nReserved words: list, publish, unpublish, edit, search, delete, status, sync, tags, categories, batch, auth."
      },
      {
        "title": "Reference Loading",
        "body": "⚠️ DO NOT read reference files unless specified below. SKILL.md contains enough inline context for most operations."
      },
      {
        "title": "On subcommand dispatch (load before execution):",
        "body": "SubcommandReferences to load/til <content>none/til (extract from conversation)none/til list|status|tags|categoriesreferences/management.md/til publish|unpublish|edit|search|delete|batchreferences/management.md/til syncreferences/management.md, references/local-drafts.md/til authreferences/management.md, references/api.md/til auth switch|list|remove|renamereferences/management.md"
      },
      {
        "title": "On-demand (load only when the situation arises):",
        "body": "TriggerReference to loadAPI returns non-2xx after inline error handling is insufficientreferences/api.mdAuto-detection context (proactive TIL suggestion)references/auto-detection.mdNo token found (first-run local fallback)references/local-drafts.md"
      },
      {
        "title": "API Quick Reference",
        "body": "Create and publish an entry:\n\ncurl -X POST \"https://opentil.ai/api/v1/entries\" \\\n  -H \"Authorization: Bearer $OPENTIL_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"entry\": {\n      \"title\": \"Go interfaces are satisfied implicitly\",\n      \"content\": \"In Go, a type implements an interface...\",\n      \"summary\": \"Go types implement interfaces implicitly by implementing their methods, with no explicit declaration needed.\",\n      \"tag_names\": [\"go\", \"interfaces\"],\n      \"published\": true,\n      \"lang\": \"en\"\n    }\n  }'\n\nKey create parameters:\n\nFieldTypeRequiredDescriptioncontentstringyesMarkdown body (max 100,000 chars)titlestringnoEntry title (max 200 chars). Auto-generates slug.tag_namesarrayno1-3 lowercase tags, e.g. [\"go\", \"concurrency\"]publishedbooleannofalse for draft (default), true to publish immediatelylangstringnoLanguage code: en, zh-CN, zh-TW, ja, ko, etc.slugstringnoCustom URL slug. Auto-generated from title if omitted.visibilitystringnopublic (default), unlisted, or privatesummarystringnoAI-generated summary for listing pages (max 500 chars)\n\nManagement endpoints:\n\nEndpointMethodDescription/entries?status=draft&q=keywordGETList/search entries/entries/:idGETGet a single entry/entries/:idPATCHUpdate entry fields/entries/:idDELETEPermanently delete entry/entries/:id/publishPOSTPublish a draft/entries/:id/unpublishPOSTRevert to draft/siteGETSite info (username, entry counts, etc.)/tags?sort=popularGETList tags with usage counts/categoriesGETList categories with entry counts\n\nFull parameter list, response format, and error handling: see references/api.md"
      },
      {
        "title": "Execution Flow",
        "body": "Every /til invocation follows this flow:\n\nGenerate -- craft the TIL entry (title, body, summary, tags, lang)\nCheck token -- resolve token (env var → active profile in ~/.til/credentials)\n\nIf ~/.til/credentials exists in old plain-text format, migrate to YAML default profile first\nFound -> POST to API with published: true -> show published URL\nNot found -> save to ~/.til/drafts/ -> show first-run guide with connect prompt\n401 response -> save locally -> inline re-authentication (see Error Handling):\n\nToken from ~/.til/credentials (active profile) or no prior token: prompt to reconnect via device flow → on success, update the active profile's token and auto-retry the original operation\nToken from $OPENTIL_TOKEN env var: cannot auto-fix — guide user to update/unset the variable\n\n\n\n\nShow identity -- when ≥2 profiles are configured, include Account: @nickname (profile_name) in result messages so the user always knows which account was used\nNever lose content -- the entry is always persisted somewhere\nOn API failure -> save locally as draft (fallback unchanged)"
      },
      {
        "title": "/til <content> -- Explicit Capture",
        "body": "The user's input is raw material -- a seed, not the final entry. Generate a complete TIL from it:\n\nShort input (a sentence or phrase) -> expand into a full entry with context and examples\nLong input (a paragraph or more) -> refine and structure, but preserve the user's intent\n\nSteps:\n\nTreat the user's input as a seed -- craft a complete title + body from it\nGenerate a concise title (5-15 words) in the same language as the content\nWrite a self-contained Markdown body (see Content Guidelines below)\nGenerate a summary (see Summary Guidelines below)\nInfer 1-3 lowercase tags from technical domain (e.g. rails, postgresql, go)\nDetect 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)\nFollow Execution Flow above (check token -> POST or save locally)\n\nNo confirmation needed -- the user explicitly asked to capture. Execute directly."
      },
      {
        "title": "/til -- Extract from Conversation",
        "body": "When /til is used without arguments, analyze the current conversation for learnable insights.\n\nSteps:\n\nScan the conversation for knowledge worth preserving -- surprising facts, useful techniques, debugging breakthroughs, \"aha\" moments\nIdentify all TIL-worthy insights (not just one), up to 5\nBranch based on count:\n\n0 insights:\n\nNo clear TIL insights found in this conversation.\n\n1 insight: Generate the full draft (title, body, tags), show it, ask for confirmation. On confirmation -> follow Execution Flow.\n\n2+ insights: Show a numbered list (max 5), let the user choose:\n\nFound 3 TIL-worthy insights:\n\n  1. Go interfaces are satisfied implicitly\n  2. PostgreSQL JSONB arrays don't support GIN @>\n  3. CSS :has() enables parent selection\n\nWhich to capture? (1/2/3/all/none)\n\nSingle number -> generate draft for that insight, show confirmation, proceed\nComma-separated list (e.g. 1,3) -> generate drafts for selected, show all for confirmation, POST sequentially\nall -> generate drafts for each, show all for confirmation, POST sequentially\nnone -> cancel\n\nFor each selected insight, generate a standalone TIL entry following Content Guidelines\nShow the generated entry to the user and ask for confirmation before proceeding\nOn confirmation -> follow Execution Flow above (check token -> POST or save locally)"
      },
      {
        "title": "Auto-Detection",
        "body": "When working alongside a user, proactively detect moments worth capturing as TIL entries."
      },
      {
        "title": "When to Suggest",
        "body": "Suggest when the conversation produces a genuine \"aha\" moment — something surprising, non-obvious, or worth remembering. Examples:\n\nDebugging uncovered a non-obvious root cause\nA language/framework behavior contradicted common assumptions\nRefactoring revealed a clearly superior pattern\nPerformance optimization yielded measurable improvement\nAn obscure but useful tool flag or API parameter was discovered\nTwo technologies interacting produced unexpected behavior\n\nDo NOT suggest for: standard tool usage, documented behavior, typo-caused bugs, or widely known best practices."
      },
      {
        "title": "Rate Limiting",
        "body": "Once per session — after suggesting once (accepted or declined), never suggest again\nNatural pauses only — suggest at resolution points or task boundaries, never mid-problem-solving\nRespect rejection — if declined, move on without persuasion"
      },
      {
        "title": "Suggestion Format",
        "body": "Append at the end of your normal response. Never interrupt workflow.\n\nTemplate:\n\n💡 TIL: [concise title of the insight]\n   Tags: [tag1, tag2] · Capture? (yes/no)\n\nExample (at the end of a debugging response):\n\n...so the fix is to close the channel before the goroutine exits.\n\n💡 TIL: Unclosed Go channels in goroutines cause silent memory leaks\n   Tags: go, concurrency · Capture? (yes/no)"
      },
      {
        "title": "Capture Flow",
        "body": "Auto-detected TILs bypass the extract flow. The suggestion itself is the candidate.\n\nUser replies yes / y / ok / sure → agent generates full entry (title, body, tags, lang) from the suggested insight → follows Execution Flow (POST or save locally)\nUser replies no / ignores / continues other topic → move on, do not ask again\n\nNon-affirmative responses (continuing the conversation about something else) are treated as implicit decline.\n\nDetailed trigger examples, state machine, and anti-patterns: see references/auto-detection.md"
      },
      {
        "title": "Management Subcommands",
        "body": "Management subcommands require a token. There is no local fallback -- management operations need the API."
      },
      {
        "title": "/til list [drafts|published|all]",
        "body": "List entries. Default filter: drafts.\n\nAPI: GET /entries?status=<filter>&per_page=10\nDisplay as a compact table with short IDs (last 8 chars, prefixed with ...)\nShow pagination info at the bottom"
      },
      {
        "title": "/til publish [<id> | last]",
        "body": "Publish a draft entry.\n\nlast resolves to the most recently created entry in this session (tracked via last_created_entry_id set on every successful POST)\nFetch the entry first, show title/tags, ask for confirmation\nOn success, display the published URL\nIf already published, show informational message (not an error)"
      },
      {
        "title": "/til unpublish <id>",
        "body": "Revert a published entry to draft.\n\nFetch the entry first, confirm before unpublishing\nIf already a draft, show informational message"
      },
      {
        "title": "/til edit <id> [instructions]",
        "body": "AI-assisted editing of an existing entry.\n\nFetch the full entry via GET /entries/:id\nApply changes based on instructions (or ask what to change if none given)\nShow a diff preview of proposed changes\nOn confirmation, PATCH /entries/:id with only the changed fields"
      },
      {
        "title": "/til search <keyword>",
        "body": "Search entries by title.\n\nAPI: GET /entries?q=<keyword>&per_page=10\nSame compact table format as list"
      },
      {
        "title": "/til delete <id>",
        "body": "Permanently delete an entry.\n\nFetch the entry, show title and status\nDouble-confirm: \"This cannot be undone. Type 'delete' to confirm.\"\nOn confirmation, DELETE /entries/:id"
      },
      {
        "title": "/til status",
        "body": "Show site status and connection info. Works without a token (degraded display).\n\nWith token: GET /site -> show username, entry breakdown (total/published/drafts), token status, local draft count, dashboard link\nWithout token: show \"not connected\", local draft count, setup link"
      },
      {
        "title": "/til sync",
        "body": "Explicitly sync local drafts from ~/.til/drafts/ to OpenTIL. Requires token.\n\nList pending drafts, POST each one, delete local file on success\nShow summary with success/failure per draft"
      },
      {
        "title": "/til tags",
        "body": "List site tags sorted by usage count (top 20). Requires token.\n\nAPI: GET /tags?sort=popular&per_page=20&with_entries=true\nShow as compact table with tag name and entry count"
      },
      {
        "title": "/til categories",
        "body": "List site categories. Requires token.\n\nAPI: GET /categories\nShow as compact table with name, entry count, and description"
      },
      {
        "title": "/til batch <topics>",
        "body": "Batch-capture multiple TIL entries in one invocation. Requires explicit topic list.\n\nUser lists topics separated by newlines, semicolons, or markdown list items (- / 1.)\nGenerate a draft for each -> show all drafts for confirmation -> POST sequentially\nOn partial failure, show per-entry success/failure (same format as /til sync)"
      },
      {
        "title": "ID Resolution",
        "body": "In listings, show IDs in short form: ... + last 8 characters\nAccept both short and full IDs as input\nResolve short IDs by suffix match against the current listing\nIf ambiguous (multiple matches), ask for clarification"
      },
      {
        "title": "Session State",
        "body": "Track the following session state (not persisted across sessions):\n\nlast_created_entry_id -- set on every successful POST /entries (201). Used by /til publish last.\nactive_profile -- the profile name resolved at first token access. Reflects the active field from ~/.til/credentials (or $OPENTIL_TOKEN override). Used for identity display and draft attribution.\n\nDetailed subcommand flows, display formats, and error handling: see references/management.md"
      },
      {
        "title": "Agent Identity",
        "body": "Three layers of attribution signal distinguish human-initiated from agent-initiated TILs."
      },
      {
        "title": "Layer 1: HTTP Headers",
        "body": "Include these headers on every API call:\n\nX-OpenTIL-Source: human | agent\nX-OpenTIL-Agent: <your agent display name>\nX-OpenTIL-Model: <human-readable model name>\n\nSource: /til <content> and /til -> human; Auto-detected -> agent\nAgent: use your tool's display name (e.g. Claude Code, Cursor, GitHub Copilot). Do not use a slug.\nModel: use a human-readable model name (e.g. Claude Opus 4.6, GPT-4o, Gemini 2.5 Pro). Do not use a model ID.\nAgent and Model are optional -- omit them if you are unsure."
      },
      {
        "title": "Layer 2: Tag Convention",
        "body": "Auto-detected TILs: automatically add agent-assisted to the tag list\n/til <content> and /til: do not add the tag (unless the Agent substantially rewrote the content)"
      },
      {
        "title": "Layer 3: Attribution Rendering (Backend)",
        "body": "Agent-initiated TILs are visually marked on OpenTIL automatically based on the\nsource field. No content modification needed -- the backend renders attribution\nin the display layer.\n\nPublic page: shows ✨ via {agent_name}, or ✨ AI when agent_name is absent\nTooltip (hover): shows {agent_name} · {model} when both are present\nDashboard: shows ✨ badge + agent_name, or \"Agent\" when agent_name is absent\n\nDo NOT append any footer or attribution text to the content body."
      },
      {
        "title": "Summary",
        "body": "Dimension/til <content>/tilAuto-detectedTriggerUser explicitUser commandAgent proactiveConfirmations0 (direct publish)1 (review before publish)1 (suggest → capture)Source headerhumanhumanagentAgent headerYesYesYesModel headerYesYesYesagent-assisted tagNoNoYesAttributionAutomatic (backend)Automatic (backend)Automatic (backend)"
      },
      {
        "title": "Content Guidelines",
        "body": "Every TIL entry must follow these rules:\n\nSelf-contained: The reader must understand the entry without any conversation context. Never write \"as we discussed\", \"the above error\", \"this project's config\", etc.\nDesensitized: 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\".\nUniversally 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.\nFactual 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...\").\nOne insight per entry: Each TIL teaches exactly ONE thing. If there are multiple insights, create separate entries.\nConcrete examples: Include code snippets, commands, or specific data whenever relevant. Avoid vague descriptions.\nTitle: 5-15 words. Descriptive, same language as content. No \"TIL:\" prefix.\nContent: 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 like O(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.\nTags: 1-3 lowercase tags from the technical domain (go, rails, postgresql, css, linux). No generic tags like programming or til.\nLang: Detect from content. Chinese -> zh-CN, Traditional Chinese -> zh-TW, English -> en, Japanese -> ja, Korean -> ko.\nCategory: Do not auto-infer category_name -- only include it if the user explicitly specifies a category/topic.\nSummary: 1-2 sentences, plain text (no markdown). Max 500 chars and must be shorter than the content body. Same language as content. Self-contained: the reader should understand the core takeaway from the summary alone. Be specific about what the reader will learn, not meta (\"this article discusses...\"). No first person, no meta-descriptions. Omit if the content is already very short (under ~200 chars) -- the excerpt fallback is sufficient."
      },
      {
        "title": "API Success (token configured, 201)",
        "body": "Published to OpenTIL\n\n  Title:  Go interfaces are satisfied implicitly\n  Tags:   go, interfaces\n  URL:    https://opentil.ai/@username/go-interfaces-are-satisfied-implicitly\n\nWhen ≥2 profiles are configured, add an Account line:\n\nPublished to OpenTIL\n\n  Account: @hong (personal)\n  Title:   Go interfaces are satisfied implicitly\n  Tags:    go, interfaces\n  URL:     https://opentil.ai/@hong/go-interfaces-are-satisfied-implicitly\n\nSingle-profile users see no Account line — keep the output clean.\n\nExtract the url field from the API response for the URL."
      },
      {
        "title": "Sync Local Drafts",
        "body": "After the first successful API call, check ~/.til/drafts/ for pending files. If any exist, offer to sync:\n\nDraft saved to OpenTIL\n\n  Title:  Go interfaces are satisfied implicitly\n  Tags:   go, interfaces\n  Review: https://opentil.ai/@username/go-interfaces-are-satisfied-implicitly\n\nFound 3 local drafts from before. Sync them to OpenTIL?\n\nOn confirmation, POST each draft to the API. Delete the local file after each successful sync. Keep files that fail. Show summary:\n\nSynced 3 local drafts to OpenTIL\n\n  + Go defer runs in LIFO order\n  + PostgreSQL JSONB indexes support GIN operators\n  + CSS :has() selector enables parent selection\n\nIf the user declines, keep the local files and do not ask again in this session."
      },
      {
        "title": "First Run (no token)",
        "body": "Save the draft locally, then proactively offer to connect. This is NOT an error -- the user successfully captured a TIL.\n\nTIL captured\n\n  Title:  Go interfaces are satisfied implicitly\n  Tags:   go, interfaces\n  File:   ~/.til/drafts/20260210-143022-go-interfaces.md\n\nConnect to OpenTIL to publish entries online.\nConnect now? (y/n)\n\ny → run inline device flow (same as /til auth) → on success, sync the just-saved draft + any other pending drafts in ~/.til/drafts/\nn → show manual setup instructions (see Manual Setup Instructions below)\n\nOnly show the connect prompt on the first local save in this session. On subsequent saves, use the short form (no prompt):\n\nTIL captured\n\n  Title:  Go interfaces are satisfied implicitly\n  Tags:   go, interfaces\n  File:   ~/.til/drafts/20260210-143022-go-interfaces.md"
      },
      {
        "title": "Error Handling",
        "body": "On ANY API failure, always save the draft locally first. Never let user content be lost.\n\n422 -- 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.\n\n401 -- Token invalid or expired (token from ~/.til/credentials active profile):\n\nTIL captured (saved locally)\n\n  File: ~/.til/drafts/20260210-143022-go-interfaces.md\n\nToken expired for @hong (personal). Reconnect now? (y/n)\n\ny → run inline device flow (same as /til auth) → on success, update the active profile's token in ~/.til/credentials and auto-retry the original POST (publish the just-saved draft, then delete the local file)\nn → show manual setup instructions (see Manual Setup Instructions below)\n\nWhen only one profile exists, omit the @nickname (profile) from the message.\n\n401 -- Token invalid or expired (token from $OPENTIL_TOKEN env var):\n\nThe env var takes priority over ~/.til/credentials, so saving a new token via device flow would not help — the env var would still be used. Guide the user instead:\n\nTIL captured (saved locally)\n\n  File: ~/.til/drafts/20260210-143022-go-interfaces.md\n\nYour $OPENTIL_TOKEN is expired or invalid. To fix:\n  • Update the variable with a new token, or\n  • unset OPENTIL_TOKEN, then run /til auth\n\nCreate a new token: https://opentil.ai/dashboard/settings/tokens\n\nNetwork failure or 5xx:\n\nTIL captured (saved locally -- API unavailable)\n\n  File: ~/.til/drafts/20260210-143022-go-interfaces.md\n\nFull error codes, 422 auto-fix logic, and rate limit details: see references/api.md"
      },
      {
        "title": "Re-authentication Safeguards",
        "body": "RuleBehaviorNo retry loopsIf re-auth succeeds but the retry still returns 401 → stop and show the error. Do not re-authenticate again.Batch-awareDuring batch/sync operations, re-authenticate at most once. On success, continue processing remaining items with the new token.Respect refusalIf the user declines re-authentication (n), do not prompt again for the rest of this session. Use the short local-save format silently.Env var awarenessWhen the active token comes from $OPENTIL_TOKEN, never attempt device flow — it cannot override the env var. Always show the env var guidance instead.Profile-aware re-authOn successful re-authentication, update the corresponding profile's token in ~/.til/credentials. Do not create a new profile."
      },
      {
        "title": "Manual Setup Instructions",
        "body": "When the user declines inline authentication (answers n), show:\n\nOr set up manually:\n  1. Visit https://opentil.ai/dashboard/settings/tokens\n  2. Create a token (select read + write + delete scopes)\n  3. Add to shell profile:\n     export OPENTIL_TOKEN=\"til_...\""
      },
      {
        "title": "Local Draft Fallback",
        "body": "When the API is unavailable or no token is configured, drafts are saved locally to ~/.til/drafts/.\n\nFile format: YYYYMMDD-HHMMSS-<slug>.md\n\n---\ntitle: \"Go interfaces are satisfied implicitly\"\ntags: [go, interfaces]\nlang: en\nsummary: \"Go types implement interfaces implicitly by implementing their methods, with no explicit declaration needed.\"\nprofile: personal\n---\n\nIn Go, a type implements an interface...\n\nThe profile field records the active profile name at save time, ensuring sync uses the correct account's token. Omitted when no profiles are configured (backward-compatible).\n\nFull directory structure, metadata fields, and sync protocol: see references/local-drafts.md"
      },
      {
        "title": "Notes",
        "body": "UI language adaptation: All prompts, result messages, and error messages in this document are written in English as canonical examples. At runtime, adapt them to match the user's language in the current session (e.g. if the user writes in Chinese, display messages in Chinese). Entry content language (lang field) is independent -- it is always detected from the content itself.\nEntries are published immediately by default (published: true) -- use /til unpublish <id> to revert to draft\nThe API auto-generates a URL slug from the title\nTags are created automatically if they don't exist on the site\nContent is rendered to HTML server-side (GFM Markdown with syntax highlighting, KaTeX math, and Mermaid diagrams)\nManagement subcommands (list, publish, edit, search, delete, tags, categories, sync, batch) require a token -- no local fallback. Exception: status and auth (including auth switch, auth list, auth remove, auth rename) work without a token.\nScope errors map to specific scopes: list/search/tags/categories need read:entries, publish/unpublish/edit/sync/batch need write:entries, delete needs delete:entries. status uses read:entries when available but works without a token."
      }
    ],
    "body": "til\n\nCapture and manage \"Today I Learned\" entries on OpenTIL -- from drafting to publishing, all within the CLI.\n\nSetup\nGo to https://opentil.ai/dashboard/settings/tokens and create a Personal Access Token with read:entries, write:entries, and delete:entries scopes\nCopy the token (starts with til_)\nSet the environment variable:\nexport OPENTIL_TOKEN=\"til_xxx\"\n\nToken Resolution\n\nToken resolution order:\n\n$OPENTIL_TOKEN environment variable (overrides all profiles)\n~/.til/credentials file — active profile's token (created by /til auth)\n\nIf neither is set, entries are saved locally to ~/.til/drafts/.\n\nCredential File Format\n\n~/.til/credentials stores named profiles in YAML:\n\nactive: personal\nprofiles:\n  personal:\n    token: til_abc...\n    nickname: hong\n    site_url: https://opentil.ai/@hong\n    host: https://opentil.ai\n  work:\n    token: til_xyz...\n    nickname: hong-corp\n    site_url: https://opentil.ai/@hong-corp\n    host: https://opentil.ai\n\nactive: name of the currently active profile\nprofiles: map of profile name → credentials\nEach profile stores: token, nickname (from API), site_url, host\n\nBackward compatibility: If ~/.til/credentials contains a plain text token (old format), silently migrate it to a default profile in YAML format and write back.\n\nSubcommand Routing\n\nThe first word after /til determines the action. Reserved words route to management subcommands; anything else is treated as content to capture.\n\nInvocation\tAction\n/til list [drafts|published|all]\tList entries (default: drafts)\n/til publish [<id> | last]\tPublish an entry\n/til unpublish <id>\tUnpublish (revert to draft)\n/til edit <id> [instructions]\tAI-assisted edit\n/til search <keyword>\tSearch entries by title\n/til delete <id>\tDelete entry (with confirmation)\n/til status\tShow site status and connection info\n/til sync\tSync local drafts to OpenTIL\n/til tags\tList site tags with usage counts\n/til categories\tList site categories\n/til batch <topics>\tBatch-capture multiple TIL entries\n/til auth\tConnect OpenTIL account (browser auth)\n/til auth switch [name]\tSwitch active profile (by profile name or @nickname)\n/til auth list\tList all profiles\n/til auth remove <name>\tRemove a profile\n/til auth rename <old> <new>\tRename a profile\n/til <anything else>\tCapture content as a new TIL\n/til\tExtract insights from conversation (multi-candidate)\n\nReserved words: list, publish, unpublish, edit, search, delete, status, sync, tags, categories, batch, auth.\n\nReference Loading\n\n⚠️ DO NOT read reference files unless specified below. SKILL.md contains enough inline context for most operations.\n\nOn subcommand dispatch (load before execution):\nSubcommand\tReferences to load\n/til <content>\tnone\n/til (extract from conversation)\tnone\n/til list|status|tags|categories\treferences/management.md\n/til publish|unpublish|edit|search|delete|batch\treferences/management.md\n/til sync\treferences/management.md, references/local-drafts.md\n/til auth\treferences/management.md, references/api.md\n/til auth switch|list|remove|rename\treferences/management.md\nOn-demand (load only when the situation arises):\nTrigger\tReference to load\nAPI returns non-2xx after inline error handling is insufficient\treferences/api.md\nAuto-detection context (proactive TIL suggestion)\treferences/auto-detection.md\nNo token found (first-run local fallback)\treferences/local-drafts.md\nAPI Quick Reference\n\nCreate and publish an entry:\n\ncurl -X POST \"https://opentil.ai/api/v1/entries\" \\\n  -H \"Authorization: Bearer $OPENTIL_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"entry\": {\n      \"title\": \"Go interfaces are satisfied implicitly\",\n      \"content\": \"In Go, a type implements an interface...\",\n      \"summary\": \"Go types implement interfaces implicitly by implementing their methods, with no explicit declaration needed.\",\n      \"tag_names\": [\"go\", \"interfaces\"],\n      \"published\": true,\n      \"lang\": \"en\"\n    }\n  }'\n\n\nKey create parameters:\n\nField\tType\tRequired\tDescription\ncontent\tstring\tyes\tMarkdown body (max 100,000 chars)\ntitle\tstring\tno\tEntry title (max 200 chars). Auto-generates slug.\ntag_names\tarray\tno\t1-3 lowercase tags, e.g. [\"go\", \"concurrency\"]\npublished\tboolean\tno\tfalse for draft (default), true to publish immediately\nlang\tstring\tno\tLanguage code: en, zh-CN, zh-TW, ja, ko, etc.\nslug\tstring\tno\tCustom URL slug. Auto-generated from title if omitted.\nvisibility\tstring\tno\tpublic (default), unlisted, or private\nsummary\tstring\tno\tAI-generated summary for listing pages (max 500 chars)\n\nManagement endpoints:\n\nEndpoint\tMethod\tDescription\n/entries?status=draft&q=keyword\tGET\tList/search entries\n/entries/:id\tGET\tGet a single entry\n/entries/:id\tPATCH\tUpdate entry fields\n/entries/:id\tDELETE\tPermanently delete entry\n/entries/:id/publish\tPOST\tPublish a draft\n/entries/:id/unpublish\tPOST\tRevert to draft\n/site\tGET\tSite info (username, entry counts, etc.)\n/tags?sort=popular\tGET\tList tags with usage counts\n/categories\tGET\tList categories with entry counts\n\nFull parameter list, response format, and error handling: see references/api.md\n\nExecution Flow\n\nEvery /til invocation follows this flow:\n\nGenerate -- craft the TIL entry (title, body, summary, tags, lang)\nCheck token -- resolve token (env var → active profile in ~/.til/credentials)\nIf ~/.til/credentials exists in old plain-text format, migrate to YAML default profile first\nFound -> POST to API with published: true -> show published URL\nNot found -> save to ~/.til/drafts/ -> show first-run guide with connect prompt\n401 response -> save locally -> inline re-authentication (see Error Handling):\nToken from ~/.til/credentials (active profile) or no prior token: prompt to reconnect via device flow → on success, update the active profile's token and auto-retry the original operation\nToken from $OPENTIL_TOKEN env var: cannot auto-fix — guide user to update/unset the variable\nShow identity -- when ≥2 profiles are configured, include Account: @nickname (profile_name) in result messages so the user always knows which account was used\nNever lose content -- the entry is always persisted somewhere\nOn API failure -> save locally as draft (fallback unchanged)\n/til <content> -- Explicit Capture\n\nThe user's input is raw material -- a seed, not the final entry. Generate a complete TIL from it:\n\nShort input (a sentence or phrase) -> expand into a full entry with context and examples\nLong input (a paragraph or more) -> refine and structure, but preserve the user's intent\n\nSteps:\n\nTreat the user's input as a seed -- craft a complete title + body from it\nGenerate a concise title (5-15 words) in the same language as the content\nWrite a self-contained Markdown body (see Content Guidelines below)\nGenerate a summary (see Summary Guidelines below)\nInfer 1-3 lowercase tags from technical domain (e.g. rails, postgresql, go)\nDetect 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)\nFollow Execution Flow above (check token -> POST or save locally)\n\nNo confirmation needed -- the user explicitly asked to capture. Execute directly.\n\n/til -- Extract from Conversation\n\nWhen /til is used without arguments, analyze the current conversation for learnable insights.\n\nSteps:\n\nScan the conversation for knowledge worth preserving -- surprising facts, useful techniques, debugging breakthroughs, \"aha\" moments\nIdentify all TIL-worthy insights (not just one), up to 5\nBranch based on count:\n\n0 insights:\n\nNo clear TIL insights found in this conversation.\n\n\n1 insight: Generate the full draft (title, body, tags), show it, ask for confirmation. On confirmation -> follow Execution Flow.\n\n2+ insights: Show a numbered list (max 5), let the user choose:\n\nFound 3 TIL-worthy insights:\n\n  1. Go interfaces are satisfied implicitly\n  2. PostgreSQL JSONB arrays don't support GIN @>\n  3. CSS :has() enables parent selection\n\nWhich to capture? (1/2/3/all/none)\n\nSingle number -> generate draft for that insight, show confirmation, proceed\nComma-separated list (e.g. 1,3) -> generate drafts for selected, show all for confirmation, POST sequentially\nall -> generate drafts for each, show all for confirmation, POST sequentially\nnone -> cancel\nFor each selected insight, generate a standalone TIL entry following Content Guidelines\nShow the generated entry to the user and ask for confirmation before proceeding\nOn confirmation -> follow Execution Flow above (check token -> POST or save locally)\nAuto-Detection\n\nWhen working alongside a user, proactively detect moments worth capturing as TIL entries.\n\nWhen to Suggest\n\nSuggest when the conversation produces a genuine \"aha\" moment — something surprising, non-obvious, or worth remembering. Examples:\n\nDebugging uncovered a non-obvious root cause\nA language/framework behavior contradicted common assumptions\nRefactoring revealed a clearly superior pattern\nPerformance optimization yielded measurable improvement\nAn obscure but useful tool flag or API parameter was discovered\nTwo technologies interacting produced unexpected behavior\n\nDo NOT suggest for: standard tool usage, documented behavior, typo-caused bugs, or widely known best practices.\n\nRate Limiting\nOnce per session — after suggesting once (accepted or declined), never suggest again\nNatural pauses only — suggest at resolution points or task boundaries, never mid-problem-solving\nRespect rejection — if declined, move on without persuasion\nSuggestion Format\n\nAppend at the end of your normal response. Never interrupt workflow.\n\nTemplate:\n\n💡 TIL: [concise title of the insight]\n   Tags: [tag1, tag2] · Capture? (yes/no)\n\n\nExample (at the end of a debugging response):\n\n...so the fix is to close the channel before the goroutine exits.\n\n💡 TIL: Unclosed Go channels in goroutines cause silent memory leaks\n   Tags: go, concurrency · Capture? (yes/no)\n\nCapture Flow\n\nAuto-detected TILs bypass the extract flow. The suggestion itself is the candidate.\n\nUser replies yes / y / ok / sure → agent generates full entry (title, body, tags, lang) from the suggested insight → follows Execution Flow (POST or save locally)\nUser replies no / ignores / continues other topic → move on, do not ask again\n\nNon-affirmative responses (continuing the conversation about something else) are treated as implicit decline.\n\nDetailed trigger examples, state machine, and anti-patterns: see references/auto-detection.md\n\nManagement Subcommands\n\nManagement subcommands require a token. There is no local fallback -- management operations need the API.\n\n/til list [drafts|published|all]\n\nList entries. Default filter: drafts.\n\nAPI: GET /entries?status=<filter>&per_page=10\nDisplay as a compact table with short IDs (last 8 chars, prefixed with ...)\nShow pagination info at the bottom\n/til publish [<id> | last]\n\nPublish a draft entry.\n\nlast resolves to the most recently created entry in this session (tracked via last_created_entry_id set on every successful POST)\nFetch the entry first, show title/tags, ask for confirmation\nOn success, display the published URL\nIf already published, show informational message (not an error)\n/til unpublish <id>\n\nRevert a published entry to draft.\n\nFetch the entry first, confirm before unpublishing\nIf already a draft, show informational message\n/til edit <id> [instructions]\n\nAI-assisted editing of an existing entry.\n\nFetch the full entry via GET /entries/:id\nApply changes based on instructions (or ask what to change if none given)\nShow a diff preview of proposed changes\nOn confirmation, PATCH /entries/:id with only the changed fields\n/til search <keyword>\n\nSearch entries by title.\n\nAPI: GET /entries?q=<keyword>&per_page=10\nSame compact table format as list\n/til delete <id>\n\nPermanently delete an entry.\n\nFetch the entry, show title and status\nDouble-confirm: \"This cannot be undone. Type 'delete' to confirm.\"\nOn confirmation, DELETE /entries/:id\n/til status\n\nShow site status and connection info. Works without a token (degraded display).\n\nWith token: GET /site -> show username, entry breakdown (total/published/drafts), token status, local draft count, dashboard link\nWithout token: show \"not connected\", local draft count, setup link\n/til sync\n\nExplicitly sync local drafts from ~/.til/drafts/ to OpenTIL. Requires token.\n\nList pending drafts, POST each one, delete local file on success\nShow summary with success/failure per draft\n/til tags\n\nList site tags sorted by usage count (top 20). Requires token.\n\nAPI: GET /tags?sort=popular&per_page=20&with_entries=true\nShow as compact table with tag name and entry count\n/til categories\n\nList site categories. Requires token.\n\nAPI: GET /categories\nShow as compact table with name, entry count, and description\n/til batch <topics>\n\nBatch-capture multiple TIL entries in one invocation. Requires explicit topic list.\n\nUser lists topics separated by newlines, semicolons, or markdown list items (- / 1.)\nGenerate a draft for each -> show all drafts for confirmation -> POST sequentially\nOn partial failure, show per-entry success/failure (same format as /til sync)\nID Resolution\nIn listings, show IDs in short form: ... + last 8 characters\nAccept both short and full IDs as input\nResolve short IDs by suffix match against the current listing\nIf ambiguous (multiple matches), ask for clarification\nSession State\n\nTrack the following session state (not persisted across sessions):\n\nlast_created_entry_id -- set on every successful POST /entries (201). Used by /til publish last.\nactive_profile -- the profile name resolved at first token access. Reflects the active field from ~/.til/credentials (or $OPENTIL_TOKEN override). Used for identity display and draft attribution.\n\nDetailed subcommand flows, display formats, and error handling: see references/management.md\n\nAgent Identity\n\nThree layers of attribution signal distinguish human-initiated from agent-initiated TILs.\n\nLayer 1: HTTP Headers\n\nInclude these headers on every API call:\n\nX-OpenTIL-Source: human | agent\nX-OpenTIL-Agent: <your agent display name>\nX-OpenTIL-Model: <human-readable model name>\n\nSource: /til <content> and /til -> human; Auto-detected -> agent\nAgent: use your tool's display name (e.g. Claude Code, Cursor, GitHub Copilot). Do not use a slug.\nModel: use a human-readable model name (e.g. Claude Opus 4.6, GPT-4o, Gemini 2.5 Pro). Do not use a model ID.\nAgent and Model are optional -- omit them if you are unsure.\nLayer 2: Tag Convention\nAuto-detected TILs: automatically add agent-assisted to the tag list\n/til <content> and /til: do not add the tag (unless the Agent substantially rewrote the content)\nLayer 3: Attribution Rendering (Backend)\n\nAgent-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.\n\nPublic page: shows ✨ via {agent_name}, or ✨ AI when agent_name is absent\nTooltip (hover): shows {agent_name} · {model} when both are present\nDashboard: shows ✨ badge + agent_name, or \"Agent\" when agent_name is absent\n\nDo NOT append any footer or attribution text to the content body.\n\nSummary\nDimension\t/til <content>\t/til\tAuto-detected\nTrigger\tUser explicit\tUser command\tAgent proactive\nConfirmations\t0 (direct publish)\t1 (review before publish)\t1 (suggest → capture)\nSource header\thuman\thuman\tagent\nAgent header\tYes\tYes\tYes\nModel header\tYes\tYes\tYes\nagent-assisted tag\tNo\tNo\tYes\nAttribution\tAutomatic (backend)\tAutomatic (backend)\tAutomatic (backend)\nContent Guidelines\n\nEvery TIL entry must follow these rules:\n\nSelf-contained: The reader must understand the entry without any conversation context. Never write \"as we discussed\", \"the above error\", \"this project's config\", etc.\nDesensitized: 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\".\nUniversally 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.\nFactual 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...\").\nOne insight per entry: Each TIL teaches exactly ONE thing. If there are multiple insights, create separate entries.\nConcrete examples: Include code snippets, commands, or specific data whenever relevant. Avoid vague descriptions.\nTitle: 5-15 words. Descriptive, same language as content. No \"TIL:\" prefix.\nContent: 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 like O(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.\nTags: 1-3 lowercase tags from the technical domain (go, rails, postgresql, css, linux). No generic tags like programming or til.\nLang: Detect from content. Chinese -> zh-CN, Traditional Chinese -> zh-TW, English -> en, Japanese -> ja, Korean -> ko.\nCategory: Do not auto-infer category_name -- only include it if the user explicitly specifies a category/topic.\nSummary: 1-2 sentences, plain text (no markdown). Max 500 chars and must be shorter than the content body. Same language as content. Self-contained: the reader should understand the core takeaway from the summary alone. Be specific about what the reader will learn, not meta (\"this article discusses...\"). No first person, no meta-descriptions. Omit if the content is already very short (under ~200 chars) -- the excerpt fallback is sufficient.\nResult Messages\nAPI Success (token configured, 201)\nPublished to OpenTIL\n\n  Title:  Go interfaces are satisfied implicitly\n  Tags:   go, interfaces\n  URL:    https://opentil.ai/@username/go-interfaces-are-satisfied-implicitly\n\n\nWhen ≥2 profiles are configured, add an Account line:\n\nPublished to OpenTIL\n\n  Account: @hong (personal)\n  Title:   Go interfaces are satisfied implicitly\n  Tags:    go, interfaces\n  URL:     https://opentil.ai/@hong/go-interfaces-are-satisfied-implicitly\n\n\nSingle-profile users see no Account line — keep the output clean.\n\nExtract the url field from the API response for the URL.\n\nSync Local Drafts\n\nAfter the first successful API call, check ~/.til/drafts/ for pending files. If any exist, offer to sync:\n\nDraft saved to OpenTIL\n\n  Title:  Go interfaces are satisfied implicitly\n  Tags:   go, interfaces\n  Review: https://opentil.ai/@username/go-interfaces-are-satisfied-implicitly\n\nFound 3 local drafts from before. Sync them to OpenTIL?\n\n\nOn confirmation, POST each draft to the API. Delete the local file after each successful sync. Keep files that fail. Show summary:\n\nSynced 3 local drafts to OpenTIL\n\n  + Go defer runs in LIFO order\n  + PostgreSQL JSONB indexes support GIN operators\n  + CSS :has() selector enables parent selection\n\n\nIf the user declines, keep the local files and do not ask again in this session.\n\nFirst Run (no token)\n\nSave the draft locally, then proactively offer to connect. This is NOT an error -- the user successfully captured a TIL.\n\nTIL captured\n\n  Title:  Go interfaces are satisfied implicitly\n  Tags:   go, interfaces\n  File:   ~/.til/drafts/20260210-143022-go-interfaces.md\n\nConnect to OpenTIL to publish entries online.\nConnect now? (y/n)\n\ny → run inline device flow (same as /til auth) → on success, sync the just-saved draft + any other pending drafts in ~/.til/drafts/\nn → show manual setup instructions (see Manual Setup Instructions below)\n\nOnly show the connect prompt on the first local save in this session. On subsequent saves, use the short form (no prompt):\n\nTIL captured\n\n  Title:  Go interfaces are satisfied implicitly\n  Tags:   go, interfaces\n  File:   ~/.til/drafts/20260210-143022-go-interfaces.md\n\nError Handling\n\nOn ANY API failure, always save the draft locally first. Never let user content be lost.\n\n422 -- 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.\n\n401 -- Token invalid or expired (token from ~/.til/credentials active profile):\n\nTIL captured (saved locally)\n\n  File: ~/.til/drafts/20260210-143022-go-interfaces.md\n\nToken expired for @hong (personal). Reconnect now? (y/n)\n\ny → run inline device flow (same as /til auth) → on success, update the active profile's token in ~/.til/credentials and auto-retry the original POST (publish the just-saved draft, then delete the local file)\nn → show manual setup instructions (see Manual Setup Instructions below)\n\nWhen only one profile exists, omit the @nickname (profile) from the message.\n\n401 -- Token invalid or expired (token from $OPENTIL_TOKEN env var):\n\nThe env var takes priority over ~/.til/credentials, so saving a new token via device flow would not help — the env var would still be used. Guide the user instead:\n\nTIL captured (saved locally)\n\n  File: ~/.til/drafts/20260210-143022-go-interfaces.md\n\nYour $OPENTIL_TOKEN is expired or invalid. To fix:\n  • Update the variable with a new token, or\n  • unset OPENTIL_TOKEN, then run /til auth\n\nCreate a new token: https://opentil.ai/dashboard/settings/tokens\n\n\nNetwork failure or 5xx:\n\nTIL captured (saved locally -- API unavailable)\n\n  File: ~/.til/drafts/20260210-143022-go-interfaces.md\n\n\nFull error codes, 422 auto-fix logic, and rate limit details: see references/api.md\n\nRe-authentication Safeguards\nRule\tBehavior\nNo retry loops\tIf re-auth succeeds but the retry still returns 401 → stop and show the error. Do not re-authenticate again.\nBatch-aware\tDuring batch/sync operations, re-authenticate at most once. On success, continue processing remaining items with the new token.\nRespect refusal\tIf the user declines re-authentication (n), do not prompt again for the rest of this session. Use the short local-save format silently.\nEnv var awareness\tWhen the active token comes from $OPENTIL_TOKEN, never attempt device flow — it cannot override the env var. Always show the env var guidance instead.\nProfile-aware re-auth\tOn successful re-authentication, update the corresponding profile's token in ~/.til/credentials. Do not create a new profile.\nManual Setup Instructions\n\nWhen the user declines inline authentication (answers n), show:\n\nOr set up manually:\n  1. Visit https://opentil.ai/dashboard/settings/tokens\n  2. Create a token (select read + write + delete scopes)\n  3. Add to shell profile:\n     export OPENTIL_TOKEN=\"til_...\"\n\nLocal Draft Fallback\n\nWhen the API is unavailable or no token is configured, drafts are saved locally to ~/.til/drafts/.\n\nFile format: YYYYMMDD-HHMMSS-<slug>.md\n\n---\ntitle: \"Go interfaces are satisfied implicitly\"\ntags: [go, interfaces]\nlang: en\nsummary: \"Go types implement interfaces implicitly by implementing their methods, with no explicit declaration needed.\"\nprofile: personal\n---\n\nIn Go, a type implements an interface...\n\n\nThe profile field records the active profile name at save time, ensuring sync uses the correct account's token. Omitted when no profiles are configured (backward-compatible).\n\nFull directory structure, metadata fields, and sync protocol: see references/local-drafts.md\n\nNotes\nUI language adaptation: All prompts, result messages, and error messages in this document are written in English as canonical examples. At runtime, adapt them to match the user's language in the current session (e.g. if the user writes in Chinese, display messages in Chinese). Entry content language (lang field) is independent -- it is always detected from the content itself.\nEntries are published immediately by default (published: true) -- use /til unpublish <id> to revert to draft\nThe API auto-generates a URL slug from the title\nTags are created automatically if they don't exist on the site\nContent is rendered to HTML server-side (GFM Markdown with syntax highlighting, KaTeX math, and Mermaid diagrams)\nManagement subcommands (list, publish, edit, search, delete, tags, categories, sync, batch) require a token -- no local fallback. Exception: status and auth (including auth switch, auth list, auth remove, auth rename) work without a token.\nScope errors map to specific scopes: list/search/tags/categories need read:entries, publish/unpublish/edit/sync/batch need write:entries, delete needs delete:entries. status uses read:entries when available but works without a token."
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/biao29/til",
    "publisherUrl": "https://clawhub.ai/biao29/til",
    "owner": "biao29",
    "version": "1.11.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/til",
    "downloadUrl": "https://openagent3.xyz/downloads/til",
    "agentUrl": "https://openagent3.xyz/skills/til/agent",
    "manifestUrl": "https://openagent3.xyz/skills/til/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/til/agent.md"
  }
}