Help us improve
Share bugs, ideas, or general feedback.
From jaan-to
Generates GTM tracking code with enforced naming conventions, supporting HTML attributes and dataLayer pushes. Use when adding analytics tracking to UI interactions.
npx claudepluginhub parhumm/jaan-to --plugin jaan-toHow this skill is triggered — by the user, by Claude, or both
Slash command
/jaan-to:data-gtm-datalayerThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Generate production-ready GTM tracking code with enforced naming conventions.
Guides Google Tag Manager setup, tags, triggers, variables, data layer implementation, debugging, custom templates, and API automation for web, apps, and servers.
Guides setup, improvement, and auditing of analytics tracking (GA4, Mixpanel, Segment, GTM). Helps define tracking plans, choose event schemas, and debug firing issues.
Helps set up, audit, and improve analytics tracking (GA4, Mixpanel, Segment, GTM) with tracking plans, event naming conventions, and data quality checks.
Share bugs, ideas, or general feedback.
Generate production-ready GTM tracking code with enforced naming conventions.
$JAAN_LEARN_DIR/jaan-to-data-gtm-datalayer.learn.md - Past lessons (loaded in Pre-Execution)$JAAN_TEMPLATES_DIR/jaan-to-data-gtm-datalayer.template.md - Output template${CLAUDE_PLUGIN_ROOT}/docs/extending/language-protocol.md - Language resolution protocolTracking Request: $ARGUMENTS
MANDATORY — Read and execute ALL steps in: ${CLAUDE_PLUGIN_ROOT}/docs/extending/pre-execution-protocol.md
Skill name: data-gtm-datalayer
Execute: Step 0 (Init Guard) → A (Load Lessons) → B (Resolve Template) → C (Offer Template Seeding)
Read and apply language protocol: ${CLAUDE_PLUGIN_ROOT}/docs/extending/language-protocol.md
Override field for this skill: language_data-gtm-datalayer
Check $ARGUMENTS:
Mode A - PRD Input:
If path to $JAAN_OUTPUTS_DIR/ or PRD text provided:
Mode B - Description Input: If text description of what to track:
Mode C - Interactive Wizard: If no arguments, ask questions in order:
"What type of tracking do you need?"
- click-html - HTML attributes (data-al-*) for simple clicks
- click-datalayer - dataLayer.push for flow-based clicks
- impression - dataLayer.push for visibility/exposure events
"What is the feature name? (e.g., player, checkout, onboarding)"
Apply naming rules:
"What is the item name? (e.g., play, pause, submit, modal-purchase)"
Apply same naming rules as feature.
"What is the action? (default: Click)"
If user provides custom action, apply naming rules. If empty/skipped, use "Click".
"Any additional params? Enter as key=value, one per line. (or 'skip')"
Example:
source=modal
count=3
active=true
Parse into object with ES5 type detection:
true / false → bool (no quotes): true3, 42) → int (no quotes): 3"modal"If none provided, omit params entirely from output.
Show full dataLayer preview before generating:
For click-html:
TRACKING SUMMARY
────────────────────────────────────────
<button data-al-feature="{feature}" data-al-item="{item}">...</button>
────────────────────────────────────────
For click-datalayer (without params):
TRACKING SUMMARY
────────────────────────────────────────
dataLayer.push({
event: "al_tracker_custom",
al: {
feature: "{feature}",
item: "{item}",
action: "{action}"
},
_clear: true
});
────────────────────────────────────────
For click-datalayer (with params):
TRACKING SUMMARY
────────────────────────────────────────
dataLayer.push({
event: "al_tracker_custom",
al: {
feature: "{feature}",
item: "{item}",
action: "{action}",
params: {
source: "modal",
count: 3,
active: true,
}
},
_clear: true
});
────────────────────────────────────────
Values are typed: strings in "quotes", ints/bools without quotes.
For impression (without params):
TRACKING SUMMARY
────────────────────────────────────────
dataLayer.push({
event: "al_tracker_impression",
al: {
feature: "{feature}",
item: "{item}"
},
_clear: true
});
────────────────────────────────────────
For impression (with params):
TRACKING SUMMARY
────────────────────────────────────────
dataLayer.push({
event: "al_tracker_impression",
al: {
feature: "{feature}",
item: "{item}",
params: {
variant: "A",
position: 1,
visible: true,
}
},
_clear: true
});
────────────────────────────────────────
"Generate tracking code with these values? [y/edit]"
Show the full dataLayer preview above (not just field summary).
"Proceed with code generation? [y/n/edit]"
Do NOT proceed to Phase 2 without explicit approval.
Based on tracking type, generate the appropriate code:
<element data-al-feature="{feature}" data-al-item="{item}">{element content}</element>
Example output:
<button data-al-feature="player" data-al-item="pause">Pause</button>
Without params:
dataLayer.push({
event: "al_tracker_custom",
al: {
feature: "{feature}",
item: "{item}",
action: "{action}"
},
_clear: true
});
With params (ES5 typed values):
dataLayer.push({
event: "al_tracker_custom",
al: {
feature: "{feature}",
item: "{item}",
action: "{action}",
params: {
{key1}: {value1}, // string: "value", int: 3, bool: true
{key2}: {value2},
}
},
_clear: true
});
Without params:
dataLayer.push({
event: "al_tracker_impression",
al: {
feature: "{feature}",
item: "{item}"
},
_clear: true
});
With params (ES5 typed values):
dataLayer.push({
event: "al_tracker_impression",
al: {
feature: "{feature}",
item: "{item}",
params: {
{key1}: {value1}, // string: "value", int: 3, bool: true
{key2}: {value2},
}
},
_clear: true
});
Before preview, verify:
event key present in all dataLayer pushes_clear: true included in all dataLayer pushesparams: {} (omit entirely if no params)"quotes", int/bool without)If any check fails, fix before preview.
Display the generated code in conversation:
GENERATED TRACKING CODE
───────────────────────
{code block}
EXAMPLE WITH VALUES
───────────────────
{example showing real values based on user input}
"Save tracking code to output? [y/n]"
If approved, set up the output structure:
source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/id-generator.sh"
# Define subdomain directory
SUBDOMAIN_DIR="$JAAN_OUTPUTS_DIR/data/gtm"
mkdir -p "$SUBDOMAIN_DIR"
# Generate next ID
NEXT_ID=$(generate_next_id "$SUBDOMAIN_DIR")
# Create folder and file paths (slug from feature-item, e.g., "player-pause")
slug="{lowercase-hyphenated-feature-item}"
OUTPUT_FOLDER="${SUBDOMAIN_DIR}/${NEXT_ID}-${slug}"
MAIN_FILE="${OUTPUT_FOLDER}/${NEXT_ID}-gtm-${slug}.md"
Output Configuration
- ID: {NEXT_ID}
- Folder: $JAAN_OUTPUTS_DIR/data/gtm/{NEXT_ID}-{slug}/
- Main file: {NEXT_ID}-gtm-{slug}.md
mkdir -p "$OUTPUT_FOLDER"
# Use template from $JAAN_TEMPLATES_DIR/jaan-to-data-gtm-datalayer.template.md
cat > "$MAIN_FILE" <<'EOF'
{generated tracking documentation with Executive Summary}
EOF
source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/index-updater.sh"
add_to_index \
"$SUBDOMAIN_DIR/README.md" \
"$NEXT_ID" \
"${NEXT_ID}-${slug}" \
"{Tracking Title}" \
"{1-2 sentence summary: feature-item tracking with event type}"
✓ Tracking code written to: $JAAN_OUTPUTS_DIR/data/gtm/{NEXT_ID}-{slug}/{NEXT_ID}-gtm-{slug}.md ✓ Index updated: $JAAN_OUTPUTS_DIR/data/gtm/README.md
"Any feedback? [y/n]"
If yes:
"[1] Fix now [2] Learn for future [3] Both"
/jaan-to:learn-add data-gtm-datalayer "{feedback}"$JAAN_OUTPUTS_DIR path$JAAN_OUTPUTS_DIR/data/gtm/{slug}/