# Deeplink Discovery

> Discover and verify app deeplinks for the awesome-deeplinks repo by combining scripts/extract_deeplinks.py Info.plist extraction, bundle/source-code inspection, and web documentation research. Use when Codex needs to research app URL schemes, inspect internal handlers, find non-web custom deeplink routes, enhance routes with official docs or web evidence, update README deeplink tables, or avoid noisy ordinary web URL rows.

- Skill: `f/deeplink-discovery` (Agent Skill)
- Install (CLI): `npx skillmds@latest add f/deeplink-discovery`
- Raw SKILL.md: https://api.skillmd.com/api/skills/f/deeplink-discovery/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: f (https://skillmd.com/u/f)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/f/deeplink-discovery

---


# Deeplink Discovery

Use this skill from the repository root. The Python extractor is the deterministic first pass; agent-led source inspection is the second pass; web research is the enhancement pass for documented examples, parameters, and version drift.

## Workflow

1. Run the extractor for the target app:

```sh
python3 scripts/extract_deeplinks.py "<App Name>" --format json
```

2. If the app exists and declares custom schemes, run the optional bundle string scan:

```sh
python3 scripts/extract_deeplinks.py "<App Name>" --scan-bundle --scan-unregistered
```

3. Inspect source or bundle internals for route handlers and examples. Start with focused searches:

```sh
rg -n "deeplink|deep link|URLScheme|CFBundleURLSchemes|registerUriHandler|handleUri|openExternal|protocol|scheme|route" "<App.app>/Contents"
rg -n "[A-Za-z][A-Za-z0-9+.-]{1,63}://" "<App.app>/Contents"
```

4. For Electron apps, inspect packed archives too. `rg -a` can find literal strings in `app.asar`, but parsing the ASAR header gives cleaner evidence when output is noisy. Search both full schemes and route-parser branches such as `switch`, `case`, `kind`, `host`, `pathname`, and accepted query parameter names.

5. Follow promising hits into nearby source. For minified JavaScript, search for route fragments such as `/open`, `/settings`, `/callback`, `command`, `workspace`, `install`, `auth`, `join`, `file`, `threads/new`, `plugins/install`, `oauth_callback`, `mcp-auth-callback`, `magic-link`, and `shared-artifact`.

6. Search the web for documentation and examples before finalizing important app sections. Prefer official docs, then open-source repos/issues, then community posts.

7. Promote only useful non-web custom deeplinks into `README.md`. Skip `http://` and `https://` schemes as catalog rows, but allow them inside query parameter values when the custom scheme requires that.

8. Categorize each app by app type and platform. Use `macOS (installed)` for local bundle evidence, and add `iOS`, `Android`, `Windows`, or `Linux` only when official docs, source, or credible app evidence support it.

## Web Enhancement Pass

Use web search to enrich local findings with documented parameters, examples, aliases, and caveats:

```sh
"<scheme>://" "<App Name>" deeplink
"<scheme>://" "<App Name>" "URL scheme"
site:<official-domain> "<scheme>://"
site:github.com "<scheme>://" "<App Name>"
```

Current high-value official sources:

- Claude Desktop: <https://support.claude.com/en/articles/14729294-open-claude-desktop-with-a-link>
- Codex app: <https://developers.openai.com/codex/app/commands>
- Cursor: <https://cursor.com/docs/reference/deeplinks>
- Slack: <https://docs.slack.dev/interactivity/deep-linking/>
- Raycast: <https://developers.raycast.com/information/lifecycle/deeplinks>
- Apple Shortcuts open/create: <https://support.apple.com/guide/shortcuts/open-create-and-run-a-shortcut-apda283236d7/ios>
- Apple Shortcuts run: <https://support.apple.com/guide/shortcuts/run-a-shortcut-from-a-url-apd624386f42/ios>
- Apple FaceTime: <https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/FacetimeLinks/FacetimeLinks.html>
- Apple SMS: <https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/SMSLinks/SMSLinks.html>
- Apple Phone: <https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/PhoneLinks/PhoneLinks.html>
- Apple Mail: <https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/MailLinks/MailLinks.html>
- Spotify URI docs: <https://developer.spotify.com/documentation/web-api/concepts/spotify-uris-ids>
- Chrome for iOS: <https://chromium.googlesource.com/chromium/src/+/main/docs/ios/opening_links.md>

Web evidence rules:

- Official docs can promote a route from `candidate` to `documented` even when local minified code is hard to read.
- If local bundle evidence and official docs disagree, note version skew and prefer the installed app when cataloging this machine; prefer official docs when cataloging current public support.
- Treat GitHub issues, snippets, blog posts, screenshots, and forum posts as leads unless confirmed by bundle/source or official docs.
- Replace real tokens, auth codes, session IDs, file paths, and account identifiers with placeholders.
- Do not add plain `http://` or `https://` rows. HTTPS may appear inside a parameter value, such as `imageUrl=<https-image-url>`, when the custom scheme requires it.
- Remember that valid app links are not always `scheme://...`: Spotify uses `spotify:<type>:<id>`, SMS uses `sms:<phone-number>`, Phone uses `tel:<phone-number>`, Mail uses `mailto:<email>`, and System Settings uses `x-apple.systempreferences:<pane-id>`.

## Codebase Learnings

Info.plist usually proves only scheme ownership. Route templates usually live in minified JavaScript, Electron ASAR files, native binaries, or helper bundles.

Claude Desktop:

- Start in `/Applications/Claude.app/Contents/Resources/ion-dist`.
- Search `ion-dist/assets` and `ion-dist/i18n` for literal builders such as `claude://cowork/new`, `claude://code/new`, `claude://login/google-auth`, `claude://claude.ai/magic-link`, `claude://claude.ai/sso-callback`, and `claude://cowork/shared-artifact`.
- Look for generic route bridges that convert browser paths into `claude://claude.ai/<path>`. A bridge like `deepLinkUrl ?? claude://claude.ai/${route}${query}` implies families such as chat, project, settings, admin-settings, customize, create, task, space, local_sessions, and claude-code-desktop when those web routes exist.
- Search for query allowlists such as `["q","prompt","prompt_url","folder","file","repo"]` to learn accepted parameters.

Codex app:

- `rg -a` finds a few literal strings in `/Applications/Codex.app/Contents/Resources/app.asar` and `/Applications/Codex.app/Contents/Resources/codex`, but direct binary output is noisy.
- Parse `app.asar` and inspect virtual files containing `codex://`, `threads/new`, `oauth_callback`, `pluginInstall`, `petInstall`, `localConversation`, or `applyCodexAppConfig`.
- The useful route parser pattern includes constants like `TB=\`codex:\``, a `switch` on `URL.host`, and handlers for `plugins`, `pets`, `automations`, `codex-app`, `connector`, `new`, `settings`, `skills`, and `threads`.
- Accepted Codex query names found in the parser include `prompt`, `originUrl`, `path`, `marketplace`, `marketplacePath`, `mode`, `name`, `description`, `imageUrl`, `hostId`, `source`, and `returnTo`.
- Official docs currently document `codex://threads/new`, `codex://new?<query>`, `codex://threads/<thread-id>`, `codex://settings[/browser-use|/computer-use/google-chrome|/connections]`, `codex://skills`, `codex://automations`, plugin install/detail/local-plugin links, and pet install links.

## Electron ASAR Notes

When a route appears in `app.asar`, prefer extracting snippets from the virtual file that contains the parser instead of pasting binary `rg` output. The useful Codex pattern was:

```sh
python3 - <<'PY'
from pathlib import Path
import json, struct

asar = Path("/Applications/Codex.app/Contents/Resources/app.asar")
blob = asar.read_bytes()
_, _, _, json_len = struct.unpack("<IIII", blob[:16])
header = json.loads(blob[16:16 + json_len])
base = 16 + ((json_len + 3) // 4) * 4

def walk(node, prefix=""):
    for name, entry in node.get("files", {}).items():
        path = f"{prefix}/{name}" if prefix else name
        if "files" in entry:
            yield from walk(entry, path)
        elif "offset" in entry:
            yield path, int(entry["offset"]), int(entry["size"])

for path, offset, size in walk(header):
    chunk = blob[base + offset:base + offset + size]
    if b"codex://" in chunk or b"threads/new" in chunk or b"oauth_callback" in chunk:
        print(path)
PY
```

## Evidence Rules

- Prefer `CFBundleURLTypes` for scheme ownership.
- Treat bundle/source string matches as candidates until manually verified, documented, or clearly tied to a handler.
- Use description wording to reflect evidence quality when needed: `Opens settings.` for verified/documented routes, `Candidate session resume route.` for unconfirmed routes.
- Replace private values with placeholders such as `<workspace-id>`, `<channel-id>`, `<repo-owner>`, `<absolute-path>`, and `<token>`.
- Do not add auth callback URLs containing real `code`, `state`, token, session, or account values.
- Do not add destructive routes unless the description clearly warns that the link should not be opened casually.

## README Row Shape

Every app entry must keep this table shape:

```md
### App Name

- Category: Developer Tools
- Platforms: macOS (installed), iOS (documented)
- Bundle ID: `com.example.app`
- Source app: `/Applications/App Name.app`
- Evidence: local `Info.plist`; official docs

| Deeplink format | Example link | Description |
| --- | --- | --- |
| `app://<route>` | [`app://example`](https://f.github.io/awesome-deeplinks/open.html?url=app%3A%2F%2Fexample) | What this route does and how it was verified. |
| `app:<value>` | [`app:value`](https://f.github.io/awesome-deeplinks/open.html?url=app%3Avalue) | What this non-`//` route does and how it was verified. |
```

Example links should be clickable code links through the repo opener: ``[`app://example`](https://f.github.io/awesome-deeplinks/open.html?url=app%3A%2F%2Fexample)`` and ``[`app:value`](https://f.github.io/awesome-deeplinks/open.html?url=app%3Avalue)``.

Description wording:

- `Opens settings.`
- `Handles SSO callback routing.`
- `Creates a command.`

## Validation

Before finishing, run:

```sh
python3 -m py_compile scripts/extract_deeplinks.py
python3 scripts/extract_deeplinks.py "<App Name>" --format markdown
rg -n "[ \t]+$" README.md contributions.md CONTRIBUTING.md scripts skills
```

If `--scan-bundle --scan-unregistered` finds noisy candidates, keep them out of the README and mention them as unverified notes instead.

