| name | katok |
| description | Search local KakaoTalk keyword, BM25, and EmbeddingGemma vector indexes through the katok CLI, list rooms, export chunks or transcripts, extract attachments, and prepare guarded message sends only after explicit user confirmation. |
katok
Use the katok CLI as the only execution surface. This skill stays thin: it checks readiness, calls CLI search commands, retrieves explicit chunks when needed, and summarizes results.
Privacy Rules
- Do not inspect local database internals from the skill.
- Do not handle auth caches or decryption material.
- Do not read KakaoTalk DB files directly. Use
katok sync --source macos --json.
- Search commands return minimal snippets and chunk ids by default.
- Full chunk content is shown only when the user explicitly asks for an exact result, asks to open a result, or provides a chunk id.
Commands
export PATH="$HOME/.cargo/bin:$PATH"
katok doctor --json
katok permissions macos
katok permissions macos --accessibility
katok doctor --macos-probe --json
katok sync --source macos --json
katok sync --json
katok index --json
katok index --full --json
katok wipe-index --yes
katok source chats --source macos --json
katok search keyword "๊ฒ์์ด" --json
katok search bm25 "๊ฒ์์ด" --json
katok search semantic "์ง๋ ํ์ ๋ณด๊ณ ์" --json
katok chunk get <chunk-id> --json
katok chunk get <chunk-id> --redact --json
katok chunk context <chunk-id> --json
katok chunk parent <chunk-id> --json
katok chunks --chat <chat-id> --json
katok transcript --chat <chat-id> --json
katok transcript --chat <chat-id> --since 2026-07-20 --json
katok transcript --chat <chat-id> --out <dir> --json
katok media get --chat <chat-id> --out <dir> --json
katok media get --chat <chat-id> --kind file --json
katok media get --chat <chat-id> --out <dir> --no-cdn --json
katok media backfill --dry-run --json
katok media backfill --json
katok send --chat <chat-id> --dry-run --json
For synthetic QA only:
katok sync --source fixture tests/fixtures/kakao/replies.jsonl --json
KATOK_EMBEDDER=local-test katok index --json
KATOK_EMBEDDER=mock katok index --json
Synthetic runs must be isolated with the --data-dir <tmp> flag. There is no KATOK_DATA_DIR
environment variable; setting one is silently ignored and the run writes into the real archive.
Operating Pattern
- If
katok is not found after install, run export PATH="$HOME/.cargo/bin:$PATH" and retry.
- If macOS permission setup is needed, run
katok permissions macos so the user can grant Full Disk Access in System Settings.
- Run
katok doctor --json before search to inspect freshness without triggering macOS app-data permission prompts.
- Inspect the
freshness section from doctor --json before search.
- Run
katok sync --source macos --json when freshness.recommendation.sync_before_search is true, when the user asks for recent messages, or when search freshness matters.
- Run
katok index --json before semantic search when freshness.recommendation.index_before_semantic_search is true or after a sync that should affect vector search.
- Use
katok search keyword ..., katok search bm25 ..., and katok search semantic ... for discovery.
- A search returns a global top-k across rooms (default 10). For a person or topic that may occur across many rooms, raise
--limit (for example 100) and group the returned hits by the stable chat_id; a room outside the first 10 is not an engine omission.
- Use
katok chunk get ... only for explicit retrieval.
- Run
katok doctor --macos-probe --json only for setup or permission diagnostics, because it may trigger a macOS "access data from other apps" prompt.
Message Sending Safety
Sending is an irreversible external side effect. Use the dedicated
katok-send skill rules whenever the user explicitly
asks to send or stage a message.
- Never infer send permission from a search, summary, drafting, or setup request.
- Never pass
--accept-use-policy until the user has explicitly confirmed the
exact room and final message or image in the current interaction.
- Run
katok send --chat <chat-id> --dry-run --json first.
- Prefer
--chat over a room name; names are not unique.
- Do not automate loops, schedules, recipient lists, bulk delivery, or retries.
- Do not assist spam, impersonation, account theft, post-refusal contact,
stalking, harassment, privacy violations, or protection-measure evasion.
- Read
ACCEPTABLE_USE_POLICY.md and DISCLAIMER.md; Accessibility permission
is not Kakao approval.
- Image paste UI failures (webp confirm miss โ PNG once, etc.): see
katok-send Troubleshooting.
--source macos reads the live macOS KakaoTalk SQLCipher database locally in Rust; the terminal must have Full Disk Access to ~/Library/Containers/com.kakao.KakaoTalkMac/.
Use katok chunk context <chunk-id> --json to inspect the immediate previous and next micro chunks in the same chat. Use katok chunk parent <chunk-id> --json to jump from a micro chunk to its larger 5-minute same-chat window parent chunk. Semantic search returns parent-window hits with child_chunk_ids; use these chunk commands to navigate from broad context back to exact messages.
katok index runs the local embeddinggemma-300m-q4 embedder in-process by default. Do not ask the user to start a Python, Jina, TEI, or local HTTP embedding server. Use KATOK_EMBEDDER=mock only for synthetic QA and KATOK_EMBEDDER=local-test only when you need deterministic local vector tests without downloading the model.
The index never follows the KakaoTalk database on its own, so a search only ever reflects the last sync. Run katok sync --source macos --json before the first query of a session and before any question about recent messages. After sync, doctor compares the archive revision with the committed semantic generation and requests indexing when they differ; semantic search also refuses stale or orphaned generations instead of silently searching them. Freshness also depends on KakaoTalk itself running (pgrep -x KakaoTalk), because the source database receives new messages only while the app is up.
Sync is cheap enough to run often because only chats whose messages changed have their chunk tails recomputed. Three runs still pay the full cost: the first sync on an empty archive, the first sync after chunk_gap_group_seconds or chunk_gap_direct_seconds changes, and the first sync after an upgrade that bumps the chunker version, which includes the first run against an archive written before the version was recorded. Each of these invalidates every stored chunk once, after which sync returns to the incremental path. The payload reports rebuilt_chats and a timings_ms breakdown (read_source, upsert_messages, rebuild_chunks), so a slow run can be attributed to a stage instead of guessed at.
Field Notes
- Most group rooms come back as a
chat-<id> placeholder rather than a real title, because the source database does not always carry the room name. Grepping room names will not find them.
- To locate a room whose title is a placeholder, search for a term used inside it and group the hits by
chat_name. The chat_id holding the most hits is that room. Each hit carries chunk_id, chat_name, sender_nickname, started_at, and snippet.
katok chunks --chat <chat-id> returns chunk metadata only, never text. To export a whole room, collect the chunk ids and run katok chunk get per chunk, then assemble in timestamp order.
katok chunk get --redact masks the entire text, not only the PII inside it. Use it for a line you must quote, not for a readable export.
- KakaoTalk system feed entries (invite, join, leave) arrive as JSON strings such as
{"inviter":...} or {"member":...,"feedType":N}. Filter them out of anything a person will read.
Deleted Messages
Sync only upserts, so a message removed upstream stays in the archive unless it
is reconciled away:
katok sync --prune-preview --json # what the source no longer has; deletes nothing
katok sync --prune-deleted --json # actually remove it
Only the time range the source still reports for a chat is considered. KakaoTalk
prunes its own database and outliving that is the reason this archive exists, so
messages older than the source's reach are kept, and a chat the source did not
mention at all is skipped. Preview before deleting โ katok cannot undo it.
Media Attachments
katok media get --chat <chat-id> --out <dir> resolves each media message through four tiers in order: the decrypted local cache, a GET of the attachment's own presigned CDN URL verified by SHA-1, the decrypted .thm thumbnail, and finally a metadata stub. That CDN GET is the only network access anywhere in the CLI, and --no-cdn disables it so resolution stays entirely local.
Photos (type 2), albums (type 27), videos (type 3), and generic file attachments (type 18) are all covered; --kind photo|video|file narrows a run and defaults to every kind. Photos and albums read the .img cache; videos read the .vid cache, whose filename stem uses a v key prefix instead of p. For those, the output extension is sniffed from the decoded body, so a video lands as .mp4.
A file attachment is one message type covering every extension โ zip, pdf, xlsx, hwp, pptx, csv, mp3 and the rest โ so nothing needs adding per format.
Two things make files behave unlike photos:
- They have no local cache at all. KakaoTalk writes only
.thm, .img, and .vid into the container; a file attachment never touches disk. The CDN is its only tier, --no-cdn returns nothing for it, and a stub for a file says unavailable rather than not-cached because there was never a cache that could have held it.
- The attachment
name is authoritative for the extension, not the body. A zip sniffs as .bin, so output is written as <logId>_<original name> with the name sanitised so it cannot escape the output directory.
Three practical notes for video:
- The local
.vid cache only holds videos that were actually played on this Mac. Anything else must come from the CDN tier, so --no-cdn will miss it.
- The presigned CDN URL carries an
expires epoch. Past that, the video is unrecoverable from the archive and only the sender can re-send it.
- KakaoTalk re-encodes video on send. The retrieved file is the compressed copy the room received, not the sender's original.
Preserving Attachments Before They Expire
A presigned URL lasts roughly 14 days. For a video that means falling back to whatever is cached; for a file, which has no cache, expiry is permanent loss. katok media backfill exists for that window:
katok media backfill --dry-run --json # what would be fetched, no requests at all
katok media backfill --json # every room, live links only, kind=file by default
It walks every room holding media, skips anything already saved without a network call โ so re-running is idempotent and resumes an interrupted run โ and reports per-tier totals. --dry-run distinguishes planned (would fetch) from stub/cdn-expired (already gone), which switching the CDN tier off cannot do.
Run it on a schedule if attachments matter. A backfill started after the window has closed cannot recover anything: report the expired count honestly rather than implying the files can be retrieved.
Related Skills
katok send is documented separately in skills/katok-send/SKILL.md. It is the
only subcommand that writes, and it does so by driving the running official
KakaoTalk app's UI rather than a Kakao remote private protocol or API.
Platform
Assume Apple Silicon macOS. Intel macOS is not a supported target for the packaged local EmbeddingGemma path.