| name | deeplink-discovery |
| description | 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. |
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
- Run the extractor for the target app:
python3 scripts/extract_deeplinks.py "<App Name>" --format json
- If the app exists and declares custom schemes, run the optional bundle string scan:
python3 scripts/extract_deeplinks.py "<App Name>" --scan-bundle --scan-unregistered
- Inspect source or bundle internals for route handlers and examples. Start with focused searches:
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"
-
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.
-
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.
-
Search the web for documentation and examples before finalizing important app sections. Prefer official docs, then open-source repos/issues, then community posts.
-
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.
-
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:
"<scheme>://" "<App Name>" deeplink
"<scheme>://" "<App Name>" "URL scheme"
site:<official-domain> "<scheme>://"
site:github.com "<scheme>://" "<App Name>"
Current high-value official sources:
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 switchonURL.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:
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:
### 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:
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.