ae-migrate-tracking-code
Migrate existing third-party SDK tracking code (Firebase, Amplitude, Sensors Data, Mixpanel, GA4) to the AE SDK (数数 / ThinkingAI / Agentic Engine / AE SDK) — switch (replace) or add alongside existing tracking (dual-write). Trigger words: 埋点迁移、埋点切换、第三方埋点迁移、第三方埋点切换、迁移埋点到数数、把 xx SDK 切换到数数、把已有埋点用数数实现一遍、用数数替换 xx SDK、集成数数并参考已有 xx SDK 埋点、Firebase 迁移、Amplitude 迁移、Mixpanel 迁移、神策迁移、Sensors Data 迁移、GA4 迁移、gtag 迁移、双写、tracking migration、migrate Firebase/Amplitude/Mixpanel/Sensors Data/GA4 to AE、switch xx SDK to ThinkingAI、replace xx SDK with AE、replace logEvent with track、re-implement existing tracking with ThinkingAI、add AE tracking alongside existing analytics、dual-write. Provider-agnostic core with per-provider adapters; reuses ae-generate-tracking-plan and ae-generate-tracking-code.
How do I install this agent skill?
npx skills add https://github.com/thinkingaiagenticengine/ae-cli --skill ae-migrate-tracking-codeIs this agent skill safe to install?
- Gen Agent Trust Hubpass
This skill is a developer tool designed to facilitate the migration of analytics tracking code from various third-party SDKs to the ThinkingAI (AE) SDK. It uses a structured approach involving codebase scanning, intermediate representation, and automated code insertion. No malicious patterns or security vulnerabilities were identified.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
ae-migrate-tracking-code
Conversation language: This skill document is in English, but all output to the user MUST be in the user's input language. English input → English reply; Chinese input → Chinese reply; Japanese input → Japanese reply. If uncertain, default to English. This applies to all output: section titles, phase names, prompts, code comments, etc. ⚠️ CRITICAL: Many provider adapter docs contain Chinese glossary notes. When reading them to answer an English/Japanese user, translate headings and comments to the user's language. Do NOT copy Chinese text verbatim from this document into English/Japanese replies.
Terminology Glossary
| 中文 | English | Notes |
|---|---|---|
| 埋点迁移 | Tracking Migration | Convert existing third-party SDK tracking calls to AE SDK |
| 源 SDK | Source SDK / Provider | The third-party SDK already in the project (Firebase, Amplitude, ...) |
| 调用点 | Call Site | A source-SDK invocation (logEvent / setUserId / setUserProperty / Identify) |
| 归一化中间表示 | IR (Intermediate Representation) | Provider-neutral dump of extracted call sites |
| 映射 | Mapping | Source event/property → AE event/property translation |
| 切换 | Switch | Replace source calls with AE track() and remove source SDK |
| 双写 / 新增 | Add / Dual-write | Keep source SDK and add AE track() alongside |
| 依赖标识 | Dependency Identifier | Package/module name used to remove the source SDK in switch mode |
| 自动采集事件 | Auto-track Event | Events the source SDK collects automatically (e.g. session_start) |
| 公共属性 | Super Property | Property attached to every event automatically. ⚠️ The correct Chinese AE term is "公共属性" or "公共事件属性". Never translate "Super Property" as "超级属性" — that is NOT a valid AE term. |
| 用户属性 | User Property | Property set on the user profile |
| 用户体系 | User Identity System | distinct_id / account_id strategy in AE |
When to Trigger
Trigger when user says: "migrate Firebase tracking to AE / replace Amplitude with ThinkingAI / my project already has Firebase/Amplitude tracking, switch it to AE SDK / add AE tracking alongside existing analytics" etc. — e.g. 把当前工程里的 xx SDK 切换到数数、把已有埋点用数数实现一遍、用数数替换 xx SDK、集成数数并参考已有的 xx SDK 埋点.
The target AE SDK may be referred to by any of: 数数 / 数数 SDK / ThinkingAI / ThinkingAI SDK / Agentic Engine / Agentic Engine SDK / AE / AE SDK — they are one SDK. The trigger signal is "an existing third-party source SDK + 迁移/切换/替换/实现一遍", or "集成/接入数数 并参考已有的 xx SDK 埋点". A bare 接入数数 SDK / 加数数埋点 with no source SDK is greenfield — it belongs to ae-generate-tracking-code, not this skill.
Scope: this skill is codebase-only — it scans existing third-party tracking calls (Phase 0/1). If there is no source code with third-party tracking (only product docs / descriptions), do NOT use this skill; route to ae-generate-tracking-plan + ae-generate-tracking-code instead. Historical data import into AE is offline data-migration work, out of scope — this skill only switches the live client/server SDK, preserving user IDs and event/property names so new data joins the imported data.
This skill orchestrates the migration end-to-end but does not duplicate the two existing skills:
- Plan generation/upload → hand off to
ae-generate-tracking-plan(codebase path). - AE SDK code insertion → hand off to
ae-generate-tracking-code(insert mode), with insertion sites supplied from the migration mapping instead of grep-discovered business triggers.
Provider Registry (extensible)
The core is provider-agnostic. Each third-party platform is one adapter file. To support a new provider, add one adapter under references/providers/ and one row below — do not modify this SKILL.md.
| Provider | Adapter | Platforms covered | Detect pattern (summary) |
|---|---|---|---|
| Firebase Analytics | references/providers/firebase.md | Web v8/v9, Android, iOS, Flutter, React Native, Unity, C++ | firebase/analytics, firebase.analytics(), FirebaseAnalytics, FIRAnalytics, Analytics.logEvent (iOS Swift), FirebaseAnalytics.instance |
| Amplitude | references/providers/amplitude.md | Web legacy (amplitude-js), Browser 2.x, Node, Python, Go, Java, Android, iOS, React Native, Flutter, Unity | amplitude-js, @amplitude/analytics-browser, @amplitude/analytics-node, amplitude.init, amplitude.getInstance(), amplitude.logEvent, amplitude.track, Identify, server imports (from amplitude import, analytics-go, com.amplitude:java-sdk) |
| Sensors Data (神策) | references/providers/sensors-data.md | Web, Android, iOS, Harmony, macOS/tvOS, C++, React Native, Flutter, Unity, Unreal, Cocos2d-x, mini-programs (WeChat/other), QuickApp, APICloud, uni-app, Weex, Egret, LayaAir + server SDKs (Java/Python/Go/Node/PHP/Ruby/C/.NET/Lua) | sensorsdata.min.js, sensors.init, sensors.track, SensorsDataAPI, SensorsAnalyticsSDK, profileSet, setProfile (legacy), server sa-sdk-* (Python/Go/Node/PHP/Ruby/C/.NET/Lua) |
| Mixpanel | references/providers/mixpanel.md | JavaScript, Node, Python, Go, Java, Ruby, PHP, Android, iOS, Swift, React Native, Flutter, Unity | mixpanel-browser, mixpanel.init, mixpanel.track, mixpanel.identify, mixpanel.people, MixpanelAPI, mixpanel.NewApiClient (Go), Mixpanel::Tracker (Ruby), Mixpanel::getInstance (PHP), mp.track / people_set (Python) |
| Google Analytics 4 | references/providers/ga4.md | Web (gtag.js / dataLayer), Measurement Protocol (server) | gtag('event', gtag('config', gtag('set', dataLayer (GA4 only when gtag(/G- also present), G-XXXXXXX, Measurement Protocol mp/collect |
Adapter contract (required fields per adapter) is defined in references/providers/README.md.
Supported scope: only the providers/platforms listed above (and the per-platform tables in each adapter's Recognition section) are supported. Anything not listed is out of scope and not supported by this skill — do not fabricate a mapping for unlisted platforms.
Phase 0 — Detect source SDK and version
- Locate the project and its package/dependency manifest (
package.json,build.gradle,Podfile,pubspec.yaml,*.csproj, ...) plus source imports. - Match against the registry above. Read the matching adapter's Recognition section to confirm the provider and pin the version variant:
- Firebase: v8 namespaced vs v9 modular (web), plus Android/iOS/Flutter native forms.
- Amplitude: legacy (
getInstance().logEvent) vs SDK 2.x (amplitude.track/@amplitude/analytics-browser).
- If multiple providers are present, list them and ask which to migrate first (one provider per run keeps mapping clean). Exception — GA4 and Firebase Analytics are the same data stream: GA4 is Firebase's web layer (one measurement ID is both a GA4 property and a Firebase project). If
gtag(...)andlogEvent(...)coexist in one project, they are not two providers — merge them into a single migration run: one scan, onedraft.json/mapping.json, one dedup pass over the shared event / property / user-id names. Never produce two drafts for one Google data stream. - If no registry match but tracking code exists → show the found calls, ask the user for the provider name, and (per
references/providers/README.md) collect a minimal adapter from the user-provided docs/snippet before continuing.
Phase 1 — Extract call sites into IR
Scan the project for four call categories, using the adapter's Recognition patterns:
- Event calls:
logEvent(...)/track(...)and their params/properties. - Identity calls:
setUserId(...)/identify(...)/login(...). - User property calls:
setUserProperty(...)/setUserProperties(...)/Identifyoperations. - Super property calls (common event properties):
register(...)/registerSuperProperties(...)(Mixpanel, Sensors Data) /setDefaultEventParameters(...)(Firebase). Record them askind: super_propertywith the fullpropsmap — do not misread them as user properties (seemapping-framework.md§6). Amplitude and GA4 have no such API.
Record each site as file:line, call kind, event/param values (literals, or variable names to resolve by reading the surrounding code). Write the normalized dump to .ae-cli/migration/scan.json following references/ir.md.
Wrapper-layer resolution (reverse call-graph tracing) — real projects almost never call the source SDK directly at every business event; they wrap it in an analytics helper (trackEvent(name, props) → mixpanel.track(name, props)). A naive grep sees only the wrapper's single SDK call (event name = parameter → would be resolved:false) and misses every real business event. So when an SDK call's event name is a function parameter (a runtime variable flowing from an enclosing function's argument), do not immediately record resolved:false:
- Identify the enclosing function
F— it is an analytics wrapper only whenF's sole role is forwarding that parameter into the SDK call. If the event name comes from local logic/expressions rather than a parameter, it is NOT a wrapper — record the site asresolved:falseas usual. - Grep for every caller of
F. Resolve each caller with a static event name into a normal call site:kindis inferred from the SDK callFdelegates to (event / identity / user_property),event_namefrom the caller's literal or constant argument,source_call= the caller's invocation line,file= the caller's file, andvia_wrapper=F's name. - Callers with a dynamic event name stay
resolved:false. If no callers are found (an exported wrapper used externally, or dynamic dispatch), fall back to recording the wrapper's own SDK call as a singleresolved:falsesite. - Record
Fitself in scan.json's top-levelwrappersarray (seereferences/ir.md) — it is the pivot for tracing, not a migration target.
Auto-track note: source SDK auto-collected events (e.g. Firebase session_start, first_open, screen_view) are not call sites. Handle them per the adapter's Auto-track decision table in Phase 2, not here.
Phase 2 — Map to AE (write draft.json + mapping.json)
- Read
references/mapping-framework.md(generic algorithm) + the provider adapter (differences). - For each extracted event call: map to an AE event (snake_case name, or verbatim when forward compatibility is required —
mapping-framework.md§1;display_namein the user's language), map each param to an AE property with a type, and apply naming constraints from the adapter (reserved prefixes, special characters, length caps). - Apply dedup + promotion rules: same property on 3+ events → common event property (super property); preset
#properties never become super properties. Explicit super-property call sites (register/setDefaultEventParameters) also land indraft.json'scommon_event_propertiespool — seemapping-framework.md§6. - Map identity → AE
login(accountId)(account_id_sourceuser_account); plus visitor-id continuity (setDistinctId/identify, init-time) when forward compatibility with imported historical data is in scope — seemapping-framework.md§4. User property ops → AEuser_set/user_setOnce/user_add/user_append/user_unsetper the adapter's mapping table. Super property calls → AEsetSuperProperties({...})(static common event property), permapping-framework.md§6. - Decide each event's
platform(client / server) from where the call site lives (app code → client; backend → server). - Ask the user to confirm the migration mode (see Modes below) and whether historical source-platform data has been (or will be) imported into AE offline — drives forward-compatible naming per
mapping-framework.md§1 and visitor-id continuity per §4. The import itself is offline data-migration work, out of scope; this skill only switches the live SDK — then:- Write
.ae-cli/migration/mapping.json(source site → AE event + mode decision, schema inreferences/ir.md). - Write
.ae-cli/draft.jsonin the AE plan schema (same schemaae-generate-tracking-planproduces) so the plan skill can consume it directly.
- Write
Show the mapping summary table and get user ok before proceeding.
Phase 3 — Generate and upload the tracking plan
Hand off to ae-generate-tracking-plan using the codebase/draft already produced:
ae-cli tracking plan draft --in .ae-cli/draft.json --out .ae-cli/draft.xlsxae-cli tracking plan validate --in .ae-cli/draft.json --fix- Resolve AE host/login/projectId per the plan skill, then
ae-cli tracking plan upload ...(ask the user before uploading; append vs replace per that skill's conflict detection).
Phase 4 — Insert AE SDK code (switch or add)
Hand off to ae-generate-tracking-code (insert mode), but insertion sites come from .ae-cli/migration/mapping.json, not from grep business-trigger discovery:
- Client/server platform + language are already in
draft.jsonmeta; resolve APP_ID / SERVER_URL as that skill does. - For each mapped AE event, the insertion point is the original call site recorded in mapping.json.
Mode switch (replace) — high-risk, confirm before deleting:
- Before any deletion, explicitly list what will be removed (the source call sites and the source SDK dependency from the adapter's Dependency identifiers) and get the user's confirmation to delete.
- Replace the source
logEvent/trackcall with the AEtrack(...)call at the same site; remove now-unused source imports. - Inline event names that came from a module constant: emit the literal snake_case name in the AE
track(...)call (it must match the// @tracking <event_name>marker). Before deleting the constant definition, grep the whole project for its name — a module constant is often imported by multiple files, fed to a server-side report, or asserted in tests. Only remove it when the migrated call sites are its sole references (then it is dead code); otherwise keep it. Do not keep a constant that only feeds the migrated call. - Remove the source SDK dependency using the adapter's Dependency identifiers (e.g.
firebase/analytics,@amplitude/analytics-browser). - Remove dead wrappers: when every call site resolved through a wrapper is
replaced, the wrapper function is dead code — delete it (and its now-unused source import). If the wrapper's file holds nothing else, remove the file. (addmode keeps the wrapper — it still feeds the source SDK — and adds the AE call at each business call site.) - Convert identity/user-property calls to
login(...)/ user-property calls similarly; super-property calls →setSuperProperties({...})in place at the original call site (never hoisted — the source'sregisterposition encodes when the values become available, e.g. after login; seemapping-framework.md§6).
Mode add (dual-write):
- Keep the source call untouched; add the AE
track(...)call immediately adjacent (before or after, matching surrounding style). - Keep the two SDKs independent — in both directions. Dual-write means two independent writes, for every provider / every SDK (Firebase, Amplitude, Sensors Data, Mixpanel, GA4, …), not a source-guarded AE write:
- The AE
track()/login()/ user-property /setSuperProperties()call must NOT be nested inside the source SDK's availability guard, early-return, or error path — e.g. afterif (!analytics) return, behindisSupported(), or inside the source'stry/catch. If the source call sits behind such a guard, lift the AE call out so it runs unconditionally. - Give each SDK its own
try/catcharound its write: a source-SDK failure (unavailable,isSupported() === false, or a thrown error) must never suppress the AE write, and an AE failure must never suppress (or crash) the source write. - The guard must cover the source SDK's full failure surface, not a single exception type. A pre-existing narrow catch (e.g. Python
except ValueErrorfor 400 validation, Javacatch (IllegalArgumentException)) is not the independence guard — other failures (unconfigured API key →RuntimeError, network/timeout) still escape and crash before the AE write. Broaden the catch to all exceptions, run the AE write, then re-raise the source exception so the original status/error semantics (400/503) are unchanged. - Give each SDK's init its own
try/catchtoo, so that one SDK being unloaded or disabled at load time (init throws / module missing) does not take down the other SDK. Both writes must keep working no matter which SDK is unloaded or disabled. - This applies to every
add/add_after/add_beforeentry in mixed runs too.
- The AE
- Keep the source SDK dependency; only add the AE SDK dependency.
Async call sites — a source call that is awaited (React Native / Flutter await analytics().logEvent(...), some await mixpanel.track(...)) returns a Promise, but the AE client track() / login() / user-property / setSuperProperties() calls are synchronous:
switch: drop theawaiton the replaced call — awaiting a synchronous AE call is a lint warning and changes nothing. If the source call sat inside atry/catchwhosecatchhandled the source SDK's Promise rejection (network retry, offline fallback, error reporting), that rejection branch dies with the source SDK: remove the now-deadcatchlogic; if thetryblock then holds only the AE call, collapse thetry/catchinto the bare AE call (AE sync failures then follow the code skill's Code Style — loud inswitch). Never wrap the AE call in a made-upasync/awaitto keep the shapes matching.add: keep the source call'sawaitand itstry/catchexactly as-is, and place the AE call outside that block, un-awaited, in its owntry/catchper the independence rule above.
Both modes follow the code skill's insert rules: SDK init from the wiki main doc (never guess imports), git-status pre-check, batched Edit with language-style check, // @tracking <event_name> comment prefix.
User-property append / unset (ae_call = user_append / user_unset): these two are not in the plan's update_type enum (only user_set / user_setOnce / user_add are), so they do not enter draft.json — in the draft their update_type is written user_set (placeholder). At code-insertion time, emit the AE SDK call directly per the mapping's ae_call (user_append → userAppend, user_unset → userUnset), reading the real method signature from the wiki main doc (never guess).
⚠️ add mode overrides the code skill's "Do NOT add try/catch" Code Style. That rule is a single-SDK rule (greenfield insert / switch after cut-over): with one SDK a loud failure is the point, so init/track failures must surface rather than be swallowed. Dual-write is the opposite — the "each SDK its own try/catch" rule above applies to the generated AE code too:
- The AE init must be guarded the same way (its own
try/catch/ availability check), so an AE-SDK load failure never throws at load time and never takes down the source SDK. - Never emit a load-time SDK capture that becomes a permanently-
undefinedreference and throws on every call (e.g.var ta = window.TA;in a wrapper) — a missing AE SDK must be a per-call no-op, not a crash of business logic or the source write. switchmode does NOT get this override: after cut-over AE is the only SDK, so it follows the code skill's Code Style as-is (failures stay loud).
Phase 5 — Verify
Reuse the code skill's validation flow:
ae-cli auth status/ confirm active host; debug device add/select.- Trigger the migrated events and query
ae-cli tracking debug-data list. - In
addmode, verify AE data alongside the still-running source SDK; inswitchmode, confirm the source SDK is fully removed and AE data flows.
Modes
| Mode | Source SDK kept? | AE call placement | Dependency change |
|---|---|---|---|
switch | removed | replaces source call | remove source dep, add AE dep |
add | kept | added alongside | add AE dep only |
Ask the user once in Phase 2 which mode to use. Default to add (dual-write): present add as the recommended option in the confirmation prompt. Treat switch as the non-default, destructive choice — offer it only when the user has explicitly expressed wanting a full cut-over, and never mark it recommended.
Staged / mixed runs: mode is the run-level default, but each mapping entry carries its own action (replace vs add_after/add_before), and the per-entry action is authoritative. A switch run may still keep selected entries as add_after for a staged roll-out (e.g. cut over identity + sign-up now, keep purchase dual-writing while its data is validated). In that case: replaced files drop the source SDK, dual-write files keep it, and the source SDK dependency is removed only on a full cut-over (every entry replace). See references/ir.md §mapping.json and mapping-framework.md §8.
⚠️
switchis destructive (high-risk): it deletes source tracking calls, their imports, and the source SDK dependency. Never delete old code by default. Before deleting anything, explicitly tell the user what will be removed (call sites + dependencies) and get their confirmation; re-confirm again at Phase 4 immediately before deletion, even ifswitchwas already chosen in Phase 2.
Prohibitions
- Guessing AE SDK imports/package names — always read the wiki main doc via
ae-generate-tracking-code(references/sdk-index.md). - Modifying event/property names away from the AE plan (
snake_case, registered names). - Migrating source SDK auto-track events as manual
track()calls — map them to AE auto-track switches or drop them per the adapter. - Nesting the AE
track()/login()/ user-property calls inside the source SDK's availability guard, early-return, ortry/catchinaddmode, or leaving either SDK's init unguarded, or guarding the source write with a narrow exception-type catch that still lets a source failure crash before the AE write — for every provider, dual-write must stay two independent writes (each SDK its owntry/catch, init included, covering the full failure surface), so one SDK failing or being unloaded never takes down the other. - Skipping the mapping summary confirmation in Phase 2.
- Writing
user_append/user_unsetintodraft.json'supdate_type(the plan enum isuser_set/user_setOnce/user_addonly) — append/unset stay inmapping.jsonae_calland are emitted directly at Phase 4. - Translating "Super Property" as "超级属性" in any user-facing output.
- Writing source or AE code before the git-status pre-check (insert mode).
- Editing the provider adapters to add provider-specific logic into the core — new providers are new adapter files only.
- Deleting source tracking code or removing the source SDK dependency (
switchmode) without explicitly warning the user and getting their confirmation immediately before deletion.
Internal Reference
references/ir.md— normalized IR schema (scan.json/mapping.json).references/mapping-framework.md— provider-agnostic mapping algorithm.references/ae-preset-properties.md— authoritative AE#preset-property list + system fields (only these#names ingest).references/providers/README.md— adapter contract + how to add a provider.references/providers/firebase.md— Firebase adapter.references/providers/amplitude.md— Amplitude adapter.references/providers/sensors-data.md— Sensors Data (神策) adapter.references/providers/mixpanel.md— Mixpanel adapter.references/providers/ga4.md— Google Analytics 4 (gtag.js / Measurement Protocol) adapter.
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/thinkingaiagenticengine/ae-cli/ae-migrate-tracking-code">View ae-migrate-tracking-code on skillZs</a>