til

GitHub 作者 opentil

Capture and manage TIL (Today I Learned) entries on OpenTIL. Use /til <content> to capture, /til to extract insights from conversation, or /til list|publish|edit|search|delete|status|sync|tags|categories|batch to manage entries -- all without leaving the CLI.

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install github:LeoYeAI~openclaw-master-skills~til
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/github%3ALeoYeAI~openclaw-master-skills~til/file -o til.md
# til

Capture and manage "Today I Learned" entries on OpenTIL -- from drafting to publishing, all within the CLI.

## Setup

1. Go to https://opentil.ai/dashboard/settings/tokens and create a Personal Access Token with `read:entries`, `write:entries`, and `delete:entries` scopes
2. Copy the token (starts with `til_`)
3. Set the environment variable:

```bash
export OPENTIL_TOKEN="til_xxx"
```

### Token Resolution

Token resolution order:
1. `$OPENTIL_TOKEN` environment variable (overrides all profiles)
2. `~/.til/credentials` file — active profile's token (created by `/til auth`)

If neither is set, entries are saved locally to `~/.til/drafts/`.

### Credential File Format

`~/.til/credentials` stores named profiles in YAML:

```yaml
active: personal
profiles:
  personal:
    token: til_abc...
    nickname: hong
    site_url: https://opentil.ai/@hong
    host: https://opentil.ai
  work:
    token: til_xyz...
    nickname: hong-corp
    site_url: https://opentil.ai/@hong-corp
    host: https://opentil.ai
```

- `active`: name of the currently active profile
- `profiles`: map of profile name → credentials
- Each profile stores: `token`, `nickname` (from API), `site_url`, `host`

**Backward compatibility**: If `~/.til/credentials` contains a plain text token (old format), silently migrate it to a `default` profile in YAML format and write back.

## 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 auth switch [name]` | Switch active profile (by profile name or @nickname) |
| `/til auth list` | List 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 |
| `/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](references/management.md) |
| `/til publish\|unpublish\|edit\|search\|delete\|batch` | [references/management.md](references/management.md) |
| `/til sync` | [references/management.md](references/management.md), [references/local-drafts.md](references/local-drafts.md) |
| `/til auth` | [references/management.md](references/management.md), [references/api.md](references/api.md) |
| `/til auth switch\|list\|remove\|rename` | [references/management.md](references/management.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](references/api.md) |
| Auto-detection context (proactive TIL suggestion) | [references/auto-detection.md](references/auto-detection.md) |
| No token found (first-run local fallback) | [references/local-drafts.md](references/local-drafts.md) |

## API Quick Reference

**Create and publish an entry:**

```bash
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...",
      "summary": "Go types implement interfaces implicitly by implementing their methods, with no explicit declaration needed.",
      "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` |
| `summary` | string | no | AI-generated summary for listing pages (max 500 chars) |

**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:

1. **Generate** -- craft the TIL entry (title, body, summary, tags, lang)
2. **Check token** -- resolve token (env var → active profile in `~/.til/credentials`)
   - If `~/.til/credentials` exists in old plain-text format, migrate to YAML `default` profile first
   - **Found** -> POST to API with `published: true` -> show published URL
   - **Not found** -> save to `~/.til/drafts/` -> show first-run guide with connect prompt
   - **401 response** -> save locally -> inline re-authentication (see Error Handling):
     - Token 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
     - Token from `$OPENTIL_TOKEN` env var: cannot auto-fix — guide user to update/unset the variable
3. **Show identity** -- when ≥2 profiles are configured, include `Account: @nickname (profile_name)` in result messages so the user always knows which account was used
4. **Never lose content** -- the entry is always persisted somewhere
5. **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:**

1. Treat the user's input as a seed -- craft a complete title + body from it
2. Generate a concise title (5-15 words) in the same language as the content
3. Write a self-contained Markdown body (see Content Guidelines below)
4. Generate a summary (see Summary Guidelines below)
5. Infer 1-3 lowercase tags from technical domain (e.g. `rails`, `postgresql`, `go`)
6. Detect la