analyticscli-ts-sdk — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited analyticscli-ts-sdk (Agent Skill) and scored it 92/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 2 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 2 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
@analyticscli/sdk lineanalyticscli1.6.8@analyticscli/sdk>=0.1.0-preview.6 <0.2.0analyticscli-ts-sdk-v1See Versioning Notes.
init({ ... }) with explicit identity mode (identityTrackingMode: 'consent_gated').init('<YOUR_APP_KEY>') shortform is acceptable for quick demos/tests or low-level client-only integrations.apiKey is enough for ingest.ANALYTICSCLI_PUBLISHABLE_API_KEY).WRITE_KEY env names in generated host-app snippets (ANALYTICSCLI_WRITE_KEY, EXPO_PUBLIC_ANALYTICSCLI_WRITE_KEY, etc.).runtimeEnv is auto-attached. Do not pass a mode string.debug is only a boolean for SDK console logging.endpoint and do not add endpoint env vars in app templates. Use the SDK default collector endpoint.platform, do not use framework labels (react-native, expo).web, ios, android, mac, windows) or omit the field.Platform.OS directly; the SDK normalizes values like macos -> mac and win32 -> windows.platform as runtime family only (web/ios/android/mac/windows), not as OS version/name.osName as operating-system label (for example iOS, Android, Windows, macOS, Web). Prefer always setting/populating osName; keep platform optional.init(...)/new AnalyticsClient(...) auto-emits one session_start event per client instance on SDK mount (source: sdk_mount), so host apps do not need manual startup wiring.session_start unless you intentionally also track a separate custom launch event (for example app_launch).appVersion from expo-application (nativeApplicationVersion); nullable values can be passed directly.dedupeOnboardingStepViewsPerSession in generated host-app code by default; SDK default is true. Only set it explicitly when the user requests a different behavior or asks for explicit config.dedupeScreenViewsPerSession in generated host-app code by default; SDK default is true. Only set it explicitly when the user requests a different behavior or asks for explicit config.screenViewDedupeWindowMs only when needed for a non-standard navigation stack; otherwise rely on SDK default (1200 ms).identityTrackingMode explicitly in generated host-app bootstrap code; use 'consent_gated' as the default.identityTrackingMode: 'consent_gated' (or strict) unless legal counsel approves a different setup.identify / setUser only work when full tracking is enabled (always_on, or after full-tracking consent in consent_gated).Promise singletons in app utility files.analytics.ts (not provider-specific names such as aptabase.ts).PAYWALL_EVENTS / PURCHASE_EVENTS from host app utility files. Import SDK constants directly when needed, or use createPaywallTracker(...).createPaywallTracker(...), create one tracker per stable paywall context and reuse it across shown/skip/purchase calls. Recreate only when defaults change.offering in tracker defaults.RevenueCat: offering identifier; Adapty: paywall/placement identifier; Superwall: placement/paywall identifier.
track(...) / trackEvent(...) for paywall or purchase milestones.Use one memoized createPaywallTracker(...) per screen/context and route lifecycle callbacks to tracker methods: shown (visible), purchaseStarted, one terminal event (purchaseSuccess/purchaseFailed/purchaseCancel), and skip (dismiss/close/back).
source, paywallId, optional offering) so events are not mixed across screens.setUser, identify, clearUser) directly instead of wrapping identify logic in host-app boilerplate.your app, your workspace) and avoid provider-centric phrasing such as our SaaS.@analyticscli/sdk is upgraded to the newest preview in that repo.track(...)/trackEvent(...):createOnboardingTracker(...), trackOnboardingEvent(...), trackOnboardingSurveyResponse(...), plus step helpers (step(...).view(), step(...).complete(), step(...).surveyResponse(...)).
onboarding:step_view as the default step progression signal. Treat onboarding:step_complete as optional and only emit it when a step has a meaningful completion boundary (for example explicit submit/continue confirmation or async success).onboarding:step_view + onboarding:survey_response; avoid unconditional onboarding:step_complete unless completion semantics are explicit.trackOnboardingSurveyResponse(...) (or tracker survey helpers) so SDK sanitization/normalization is preserved.step(...).surveyResponse(...) with only survey-specific fields at call sites.useFocusEffect(...) and analytics.screen(...).Purchases.logIn(...) (setUser on sign-in/session restore, clearUser on sign-out).When this skill writes host-app code, optimize for low boilerplate by default.
mapEventToCanonical(...) with many switch branches.identify/setUser unless required by an existing app contract.try/catch wrappers around every analytics helper unless the user asked for that policy.trackPaywallEvent, tracker helpers, screen, track) instead of generic proxy helpers.analytics.ts is needed, keep it focused to bootstrap + a few shared helpers. Avoid becoming an event-translation layer.Do not generate these patterns:
switch/if trees that translate event namesmapEventToCanonical(...) spanning many event casesPromise<AnalyticsClient | null> bootstrap patternscreatePaywallTracker(...) instance inside each paywall callback/event helpertrackPaywallTrackerEvent(...))screen(...) / trackScreenView(...) but never emit paywall:showntrack(...) / trackEvent(...) although stable paywall context is availabletrack(...) / trackEvent(...) although dedicated onboarding APIs are availableview_paywall, purchase_completed)apiKey fallback chains using *WRITE_KEY* env variables in host-app codeIf such a pattern already exists in the target codebase:
Before finishing, verify the generated integration code meets all checks:
init({ ... }) (no initFromEnv(...))endpoint env var in host app templatesidentify/setUser/clearUser) without extra wrappersplatform is web/ios/android/mac/windows or omitted (never framework labels)identityTrackingMode explicitly (default 'consent_gated')*WRITE_KEY* fallback)createPaywallTracker(...) defaults include offeringpaywall:shown via tracker when shown becomes visible (not only screen-view events)purchaseStarted + exactly one terminal outcome)skip(...)trackOnboardingEvent(...)) instead of generic track(...)trackOnboardingSurveyResponse(...) (or tracker survey helpers), not ad-hoc generic track(...) payloadsuseFocusEffect(...) + analytics.screen(...) with one owner per route transitiononboarding:step_complete on every step; default to onboarding:step_view and add step_complete only where completion semantics are explicitBefore SDK bootstrap, collect the required values from your dashboard:
readonly_token in the same API Keys area.analyticscli projects select (arrow-key picker), or pass --project <project_id> per command.import { init } from '@analyticscli/sdk';
const analytics = init({
apiKey: process.env.NEXT_PUBLIC_ANALYTICSCLI_PUBLISHABLE_API_KEY ?? '',
platform: 'web',
identityTrackingMode: 'consent_gated', // default
});init(...) is preferred for host apps. Resolve env values in app code and pass apiKey explicitly.
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as Application from 'expo-application';
import { Platform } from 'react-native';
import { init } from '@analyticscli/sdk';
const analytics = init({
apiKey: process.env.EXPO_PUBLIC_ANALYTICSCLI_PUBLISHABLE_API_KEY,
debug: __DEV__,
platform: Platform.OS,
appVersion: Application.nativeApplicationVersion,
identityTrackingMode: 'consent_gated', // default
storage: AsyncStorage, // optional for RN if you want persistent IDs after consent
});Consent gate for full tracking:
// user accepts full tracking
analytics.setFullTrackingConsent(true);
// user declines full tracking (strict analytics can continue)
analytics.setFullTrackingConsent(false);There is no "do not start yet" init flag. Tracking starts on init(...); ready() (or initAsync(...)) is only for explicitly blocking first-flow logic until async storage hydration is done.
Use useFocusEffect(...) for non-onboarding screens so screen views fire on route focus and not only on mount:
import { useFocusEffect } from '@react-navigation/native';
import { useCallback } from 'react';
import { analytics } from '@/utils/analytics';
export function SettingsScreen() {
useFocusEffect(
useCallback(() => {
analytics.screen('settings', {
screen_class: 'SettingsScreen',
source: 'tabs',
});
}, []),
);
return null;
}Notes:
The integration should cover more than SDK bootstrap:
appVersion, platform, source, flow identifiers)You can include trial/purchase/cancel lifecycle data inside user flows, but "perfectly synced in real time" is not realistic because app callbacks, store billing events, retries, and webhook delivery are eventually consistent.
Use this pattern for near-lossless correlation:
appUserID and AnalyticsCLI analytics.setUser(...) (or setUser(...) on raw client).paywall:shown, purchase:started, purchase:success/failed/cancel).userId, offering, paywallId, packageId, entitlementKey.rcEventId, rcEventType, original transaction/subscription ids, environment/store).unknown).Recommended custom lifecycle events (in addition to canonical purchase:* journey events):
billing:trial_startedbilling:trial_cancelledbilling:trial_convertedbilling:subscription_renewedbilling:subscription_cancelledbilling:subscription_expiredbilling:billing_issueThis split lets funnels answer both questions:
createOnboardingTracker(...) for onboarding flows.createOnboardingTracker(...).step(...).view()/complete() over generic track(...).view() as baseline and call complete() only on steps with explicit completion semantics.trackOnboardingSurveyResponse(...) or tracker survey helpers over generic track(...).view() + surveyResponse(...); emit complete() only when the host flow has a real completion boundary.useFocusEffect(...) to call analytics.screen(...) on focus.createPaywallTracker(...) when paywall context is stable in a flow (source, paywallId, experiment variant).createPaywallTracker(...) instance lifetime aligned to one stable paywall context (for example one screen flow); do not create a new tracker for every paywall event.offering in paywall tracker defaults when available from provider metadata (RevenueCat/Adapty/Superwall).trackPaywallEvent(...) for one-off paywall and purchase milestones.paywallTracker.shown(...)paywallTracker.purchaseStarted(...)paywallTracker.purchaseSuccess(...)paywallTracker.purchaseCancel(...)paywallTracker.purchaseFailed(...)paywallTracker.skip(...)ONBOARDING_EVENTS, PAYWALL_EVENTS, and PURCHASE_EVENTS.onboardingFlowId, onboardingFlowVersion, paywallId, source, and appVersion stable.onboarding:step_view (dedupeOnboardingStepViewsPerSession: true, default), immediate duplicate screen(...) calls (dedupeScreenViewsPerSession: true, default; window screenViewDedupeWindowMs, default 1200 ms), and immediate overlap between onboarding screen:* and onboarding:step_view for the same step (dedupeOnboardingScreenStepViewOverlapsPerSession: true, default).paywall:shown, purchase:started, and one terminal event: purchase:cancel or purchase:failed or purchase:success).For pre-production integrations, do not preserve legacy compatibility by default:
createOnboardingTracker(...), trackOnboardingSurveyResponse(...), createPaywallTracker(...)) over ad-hoc generic tracking wrappers.After integration or upgrade, verify ingestion with stable CLI checks:
analyticscli schema events
analyticscli goal-completion --start onboarding:start --complete onboarding:complete --last 30d
analyticscli get onboarding-journey --last 30d --format text~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.