Skip to content
cccskills
FreePublished on GitHub

custom-blocks

Create or repair a sandboxed extension embedded as a Custom Block in an Analytics dashboard when native panels cannot represent a one-off visualization or interaction. The skill also covers embedding, cloning customer-specific dashboards, and focused data-loading fixes.

OriginalShow the original description

Create, embed, clone, or repair a Custom Block (extension panel). Use for a bespoke visualization native panels cannot draw, or to fix or copy an extension-backed dashboard.

BuilderIO/agent-native7,527 stars(GitHub 7,527 + cccskills 0)

Run score Not tested

See how to install

Good for

  • Adding a one-off visualization to a dashboard
  • Embedding an existing extension in a dashboard
  • Cloning a dashboard for another customer
  • Repairing an extension's data loading

Files included (1)

  • SKILL.md10.6 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Custom Blocks

Analytics has one user-facing artifact type: dashboards. Build with native dashboard panels and Data Programs first. A sandboxed extension embedded in a dashboard is presented to users as a Custom Block, not as a separate Analytics artifact.

Use native chart, table, metric, section, funnel, heatmap, callout, filter, and layout capabilities whenever they can represent the request faithfully. Reusable ROI, engagement, cross-sell, and win/loss dashboards compose these native panels around real SQL or Data Program results. Use a Data Program when the durable need is reusable fetching, transformation, or computed data that native panels can render. Dual-axis charts are native (config.rightYKeys), never an extension. Do not create a Custom Block merely because a request says custom, asks for a dashboard, or would take more effort with native components.

When To Create One

Create a Custom Block only when the user explicitly asks for a genuinely bespoke or one-off visualization or interaction, the native dashboard model cannot represent it faithfully, and its intended scope is this dashboard.

  • Create it with create-extension, immediately embed it as a chartType: "extension" panel with config.extensionId, and set config.customBlock to { authoredBy: "agent", intent: "one-off", scope: "dashboard", nativeGapReason: "custom-visualization" | "custom-interaction" | "custom-layout" | "other" }. Choose the narrow categorical reason; never put prompt text, customer data, or other free text in this metadata.
  • Use the host theme CSS variables and match the dashboard typography, card spacing, and density so the sandboxed content reads as an agent-authored patch to Analytics instead of a foreign mini-app. Describe it as a sandboxed, agent-authored dashboard patch.
  • Never leave it standalone or direct the user to an Extensions page.
  • A Custom Block is a fast runtime patch, not the durable destination for reusable product behavior. If the request should work across dashboards or users, changes app chrome or business logic, adds a reusable chart type, needs native accessibility/export/governance, or explicitly asks for app code, a PR, or a native feature, call connect-builder with the request verbatim instead of creating a Custom Block. If scope is ambiguous, ask whether the user wants a one-off block for this dashboard or a reusable app feature.
  • When the user chooses Promote to app code, preserve the existing Custom Block and pass its dashboard id, panel id, extension id, and requested native placement through connect-builder; do not delete or replace the block until the native implementation is reviewed and deployed.
  • Legacy analyses and existing extension-backed dashboards remain readable and editable for compatibility.

An explicit request to build one authorizes every non-destructive step in the same turn: query or scaffold, seed extension data (extension-data-set), save, embed, and navigate. Do not leave an empty shell or ask whether to proceed.

Extension Data Boundary

Code inside a Custom Block runs in an iframe and may call only actions that are HTTP-mounted and intended for appAction. Use the canonical bigquery action for warehouse SQL; never call query-agent-native-analytics, bigquery-table-info, or another http: false agent-only action from extension code. For first-party Analytics data, prefer a native source: "first-party" panel, or query it as the agent and seed the extension data store.

Embedding An Extension As A Panel

Use chartType: "extension" to add an extension box alongside normal SQL charts. The panel skips source and sql. For ordinary requests such as "put X in this dashboard," save the author-selected extension id in config.extensionId. This makes the selection part of the shared dashboard and keeps the widget present in scheduled report captures:

{
  "id": "pipeline-widget",
  "title": "Pipeline Widget",
  "chartType": "extension",
  "width": 3,
  "config": { "extensionId": "extension-123" },
}

Direct embeds receive the dashboard id, name, description, current filters, and panel context. Embedding does not grant extension access, so share the extension with the dashboard audience (share-resource --resourceType extension ...); otherwise viewers see an "extension unavailable" placeholder.

Use a stable config.extensionSlotId only when the user explicitly wants each viewer to choose or install their own widget:

analytics.dashboard.<dashboard-id>.panel.<panel-id>

Create or choose the extension, call add-extension-slot-target with the extension id and slot id, then call install-extension with the same values. The dashboard panel is shared, while the installed extension is per-user. Empty slots show the normal install affordance instead of a broken iframe.

{
  "id": "pipeline-widget",
  "title": "Pipeline Widget",
  "chartType": "extension",
  "width": 3,
  "config": {
    "extensionSlotId": "analytics.dashboard.weekly-metrics.panel.pipeline-widget",
  },
}
  • Both direct and slot-backed extensions receive dashboard and panel context.
  • Installs and extension access are per viewer. Slot installs are per-user preferences, so different viewers can see different widgets and scheduled reports running as a service identity may show an empty slot. This is why slots are opt-in rather than the default.
  • Inspect an existing Custom Block through the get-sql-dashboard panel summaries (extensionId, extensionSlotId).

Cloning A Direct-Extension Dashboard (e.g. per-customer copies)

When the user asks for a copy of an existing extension-backed dashboard for a different customer/org (for example "make an Intuit version of the Roku usage dashboard"), follow this playbook. Extension bodies are frequently tens of thousands of characters. The reliable path is to read+transform+write the body INSIDE run-code (where workspaceRead returns the full file) and then create from that written file — never by pulling the body into chat context first or re-typing it as a content argument.

  1. get-sql-dashboard on the source dashboard (pass the extension panel's id in panelIds) and confirm the target panel is a chartType: "extension" panel with config.extensionId; grab that extension id. For a slot-backed panel, clone the dashboard panel with a new stable extensionSlotId, then target and install the desired extension into that slot instead of using this body-copy playbook.
  2. get-extension for that id with forceContent: true exactly once. Reuse that body for the rest of the turn — a second same-run read intentionally omits content and returns contentOmitted instead. That is not the content disappearing; use the copy you already have. Do NOT try to re-fetch the body with run-code (appAction('get-extension')) to page past a display truncation — the same-run omit makes it return empty content, wasting turns. If you need the full body again, read the workspace resource file (step 5) or set forceContent: true on a single native get-extension.
  3. Change ONLY the small customer-specific static config (e.g. the ACCOUNT_USAGE_STATIC block: company name, title, org-discovery filters, messaging). Prefer a focused update-extension edit/patch over regenerating the entire HTML.
  4. Call create-extension / update-extension as native tools. They are mutating actions and are NOT callable from run-code / appAction (the sandbox bridge only exposes read-only actions). Do not try to create or update an extension from inside run-code.
  5. If the source body already exists as a workspace/shared resource file (e.g. a pre-built intuit-analytics-extension.html), do the read AND the customer swap in ONE run-code call, then create from the written file:
    • Inside run-code: const src = await workspaceRead('<source>.html') returns the WHOLE file (it auto-pages; there is no 50k cap here), do the small string-replace on the static config block, then await workspaceWrite('<target>.html', modified).
    • Then call create-extension (native) with contentFromWorkspaceFile: '<target>.html' and leave content empty — the server reads the full file verbatim. Do NOT read the source body with the resources read tool (or get-extension) first just to transform it: that display is capped and wastes a turn. And do NOT re-emit an 80k+ char body as the content argument — it gets cut off mid-stream. contentFromAttachment only sees files the user pasted into chat, not workspace resources. create-extension/update-extension are mutating and cannot run from run-code, so only the read+write+transform happens there.
  6. Finally save a new dashboard embedding the new extension panel (chartType: "extension", config.extensionId) with update-dashboard, then navigate to it.

Repairing An Existing Extension-Backed Dashboard

When the user asks to fix data loading in an existing or migrated extension-backed dashboard, treat the current extension body as user-authored design. Read the dashboard config and extension once, identify the smallest data-loading seam, and call update-extension with exactly id, operation="edit", and a payloadJson string containing focused patches/edits that change only that seam. Never send empty placeholder fields. Preserve the existing layout, CSS, copy, and interactions. Do not send a reconstructed full content body for a data-only repair.

A request that combines a visual rewrite (compacting, removing sections, renaming, changing padding) with a data repair is a broad rewrite: after inspecting the current extension, use operation="replace" with the complete replacement in payloadJson. If a focused edit fails, inspect the current body and change the target rather than retrying the same arguments.

Display truncation is cosmetic — do not chase the "missing" tail

A tool result ending in ...[truncated — full result was N chars; only first 50,000 shown] (from the resources read tool or get-extension) means only the DISPLAYED text was capped. The file is intact. run-code's workspaceRead returns the full N chars, and contentFromWorkspaceFile hosts the full file. Never read the same file twice or try to "page the rest" to recover the tail — that is the single biggest source of wasted turns on clone requests. Decide to clone, then go straight to the run-code read+transform+write path in step 5.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

a2a-assets

FreeSummary

Generate, refine, export, and place brand images or videos from an Assets library into another app. The skill covers choosing a library, handling image batches and video runs, and updating selected assets after feedback.

  • Generate brand images for slides or another app
  • Refine an existing asset after feedback
  • Create and export a video asset
BuilderIO/agent-native7.5KUpdated Oct 26, 2026

account-health

FreeSummary

Prepare a customer account health, renewal, or contract usage readout. Verify the account identity, use approved metric definitions, compare contracted capacity with actual usage, and trace findings to sources, time windows, and known gaps.

  • Preparing a named customer's account health review
  • Comparing contracted seats or capacity with usage
  • Gathering adoption signals before a renewal or QBR
BuilderIO/agent-native7.5KUpdated Oct 26, 2026

actions

FreeSummary

Create focused app actions that agents can call as tools and frontend code can use through client hooks. Use them to add provider integrations or fetch UI data, with validated inputs and structured results.

  • Adding a new app operation
  • Connecting an external provider API
  • Fetching data for a frontend view
BuilderIO/agent-native7.5KUpdated Oct 26, 2026

adding-a-feature

FreeSummary

Use this checklist when adding a feature or integration to an agent-native app. It covers the user interface, agent-callable actions, instructions, and app-state sync so both the user and agent can work with the capability consistently.

  • Planning a new feature across UI and agent actions
  • Checking that agent changes appear in the UI
  • Documenting actions and feature state for the agent
BuilderIO/agent-native7.5KUpdated Oct 26, 2026

adhoc-analysis

FreeSummary

Investigate complex questions by combining data from multiple sources, then summarize patterns, metrics, and evidence in a clear report. Save requested analyses as dashboards with instructions for future refreshes.

  • Comparing CRM deals with call records
  • Investigating customer health across systems
  • Saving a repeatable performance analysis
BuilderIO/agent-native7.5KUpdated Oct 26, 2026

admin-surfaces

FreeSummary

Use the Analytics admin surfaces to inspect and change workspace feature flags, review dashboard ownership and usage activity, or browse and edit databases connected from other agent-native apps. The skill guides administrators to the relevant view and actions.

  • Review dashboard owners, views, and edits
  • Change a workspace feature flag for an app
  • Browse or edit a connected app database
BuilderIO/agent-native7.5KUpdated Oct 26, 2026

See all skills by BuilderIO

Report a problem with this skill