Agent Skills › MapleTechLabs/maple › maple-dashboard-widgets

maple-dashboard-widgets

GitHub

指导Agent通过MCP构建、修复或审查Maple仪表盘组件。涵盖面板类型、数据源结构、单位规则及验证流程,确保Widget JSON正确生成与渲染。

skills/maple-dashboard-widgets/SKILL.md MapleTechLabs/maple

Trigger Scenarios

create_dashboard add_dashboard_widget update_dashboard_widget dashboard widget JSON panel_type

Install

npx skills add MapleTechLabs/maple --skill maple-dashboard-widgets -g -y
More Options

Use without installing

npx skills use MapleTechLabs/maple@maple-dashboard-widgets

指定 Agent (Claude Code)

npx skills add MapleTechLabs/maple --skill maple-dashboard-widgets -a claude-code -g -y

安装 repo 全部 skill

npx skills add MapleTechLabs/maple --all -g -y

预览 repo 内 skill

npx skills add MapleTechLabs/maple --list

SKILL.md

Frontmatter
{
    "name": "maple-dashboard-widgets",
    "description": "Build, repair, or review Maple dashboard widgets via the MCP. Triggers on phrases like 'create_dashboard', 'add_dashboard_widget', 'update_dashboard_widget', 'dashboard widget JSON', 'panel_type', 'QueryDraft', or any session that submits widget JSON to the maple MCP. Covers the panel-type table, the kind-discriminated data source, the percent vs percent_100 unit rule, valid aggregations and group-by tokens per source, the custom whereClause grammar, the scalar reduceToValue transform, and the verification step (MCP success != chart correctness)."
}

Maple dashboard widgets via MCP

Everything below is generated from the live widget schema by bun run --cwd apps/ai mcp:docs. Do not edit this file by hand. Edit apps/ai/src/mcp/lib/dashboard-schema-doc.ts and regenerate. The same module backs the describe_dashboard_schema MCP tool, so an agent at runtime and a reader here see one truth.

When to use this skill

When constructing widget JSON for mcp__maple__create_dashboard, mcp__maple__add_dashboard_widget, mcp__maple__update_dashboard_widget or mcp__maple__replace_dashboard_widgets.

For a brand-new dashboard, prefer the simplified widgets array on create_dashboard ({ title, source, metric, group_by?, service_name?, unit? }). It fills in the traps below. Reach for raw JSON when the simplified spec can't express what you need: multi-query charts, formulas, hidden series, non-default transforms.

The three silent failures

  1. A data source is a kind-discriminated union. { "endpoint": …, "params": … } is the retired v2 shape and will not decode.
  2. percent means a 0–1 fraction; percent_100 means 0–100. Inverted from Grafana.
  3. groupBy is ignored unless addOns.groupBy is true. No error, just an ungrouped total.

Verification

MCP success is not chart correctness. The mutation tools reject queries the engine can't honor and widgets that cannot render, and return an automatic inspect_chart_data summary for everything else. Read the verdict: suspicious or broken means fix and resubmit.

Panel types

panel_type is the whole answer to “what kind of widget is this”. Pass it and the persisted visualization, the display.chartId and the raw-SQL display type are all derived for you. The legacy visualization parameter is still accepted, but it collapses line/bar/area into chart and then needs a display.chartId to tell them apart. The two columns below are what panel_type resolves to, and are what you write directly when authoring an assembled widget rather than calling add_dashboard_widget. A panel whose chartId is - takes none: the visualization alone identifies it.

panel_type Label visualization display.chartId Raw-SQL type Default w×h Requirements
line Line chart query-builder-line line 4×6 -
bar Bar chart query-builder-bar bar 4×6 -
hbar Horizontal Bar hbar query-builder-hbar hbar 4×6 needs a group-by
area Area chart query-builder-area area 4×6 -
pie Pie pie query-builder-pie pie 4×6 needs a group-by
stat Stat stat - stat 3×4 needs transform.reduceToValue
gauge Gauge gauge - stat 4×6 needs transform.reduceToValue
table Table table - table 6×5 -
list List list - - 6×5 no raw-SQL support
histogram Histogram histogram query-builder-histogram histogram 4×6 -
heatmap Heatmap heatmap query-builder-heatmap heatmap 4×6 needs a group-by
funnel Funnel funnel query-builder-funnel funnel 4×6 needs a group-by
paths Paths paths query-builder-paths - 6×5 no raw-SQL support
markdown Note markdown - - 4×5 no raw-SQL support

Choosing one

  • line / area / bar: a value over time. area and bar accept display.stacked; line does not.
  • hbar: a ranked “top N by volume”. Each row is labelled with its share of the total.
  • funnel: sequential stages with a drop-off. Labels each bar as a share of the largest, so an unranked breakdown of four equal things reads “100%” four times. Use hbar for that. display.funnel.variant: "dropoff" draws the same funnel as one column per step with the loss between steps, the median time between them, and where the leavers went.
  • paths: what people do in the steps after (or before) one event or page, as a flow. Defined by display.paths alone; no query set.
  • pie: composition, few slices. Collapses a long tail into “Other”.
  • stat / gauge: one number. A gauge adds an arc; set display.gauge.min/max to match the unit.
  • table: rows and columns; set display.columns for headers and per-column units.
  • list: recent traces/logs. Configured by display.listDataSource, never by SQL.
  • heatmap / histogram: a distribution. A histogram over traces can bucket raw values client-side.
  • markdown: a static note. Takes no query at all.

Data sources

A widget's dataSource is a discriminated union over kind: query, raw_sql, route, static. Every arm requires its kind.

If you have seen { "endpoint": …, "params": … } anywhere, that is the retired v2 shape and it will not decode. A query source spreads queries/formulas at the TOP LEVEL, not under params, and requires resultShape.

kind: "query": the query builder

resultShape is required and is one of timeseries (a value over time), breakdown (one row per group) or list (raw rows). Optional: formulas, comparison, limit, defaultLimit, columns, transform.

{
  "kind": "query",
  "resultShape": "timeseries",
  "queries": [
    {
      "id": "q1",
      "name": "A",
      "enabled": true,
      "whereClause": "service.name = \"api\"",
      "aggregation": "error_rate",
      "stepInterval": "",
      "orderByDirection": "desc",
      "addOns": {
        "groupBy": true,
        "having": false,
        "orderBy": false,
        "limit": false,
        "legend": false
      },
      "groupBy": [
        "service.name"
      ],
      "having": "",
      "orderBy": "",
      "limit": "",
      "legend": "",
      "dataSource": "traces"
    }
  ]
}

kind: "raw_sql": your own ClickHouse SQL

{
  "kind": "raw_sql",
  "sql": "SELECT count() AS value FROM logs WHERE $__orgFilter",
  "displayType": "stat"
}

kind: "static": a markdown note, no request

{
  "kind": "static"
}

kind: "route": a curated built-in panel

{ "kind": "route", "endpoint": "service_overview", "params": { … } }. These back the prebuilt panels; you rarely author one by hand.

Scalar panels need a reduction

A stat or gauge reads data[0].value. Without transform.reduceToValue it renders [object Object]. add_dashboard_widget injects { field: "value", aggregate: "first" } when you omit it; set it explicitly to choose a different reducer. Valid aggregates: sum, first, count, avg, max, min: there is no last.

Which one depends on what the query returns, because the query is bucketed over time and the reducer collapses those buckets into one number:

  • A rate or a count (count, a metrics rate) → sum, for a window total.
  • A latency percentile or an average (p95_duration, avg_duration, a gauge metric) → avg for the typical value over the window, or max for the worst bucket. Not sum: adding percentiles together is meaningless, and it is the common wrong choice.
  • A current reading, where only the newest bucket matters → first.
{
  "kind": "query",
  "resultShape": "timeseries",
  "queries": [
    {
      "id": "q1",
      "name": "A",
      "enabled": true,
      "whereClause": "",
      "aggregation": "count",
      "stepInterval": "",
      "orderByDirection": "desc",
      "addOns": {
        "groupBy": false,
        "having": false,
        "orderBy": false,
        "limit": false,
        "legend": false
      },
      "groupBy": [],
      "having": "",
      "orderBy": "",
      "limit": "",
      "legend": "",
      "dataSource": "traces"
    }
  ],
  "transform": {
    "reduceToValue": {
      "field": "value",
      "aggregate": "sum"
    }
  }
}

The breakdown shape

resultShape: "breakdown" returns one row per group instead of a series over time, the shape pie, hbar, funnel and heatmap need. It requires a group-by, and limit caps the rows (honoured for 1–100).

{
  "kind": "query",
  "resultShape": "breakdown",
  "queries": [
    {
      "id": "q1",
      "name": "A",
      "enabled": true,
      "whereClause": "",
      "aggregation": "count",
      "stepInterval": "",
      "orderByDirection": "desc",
      "addOns": {
        "groupBy": true,
        "having": false,
        "orderBy": false,
        "limit": false,
        "legend": false
      },
      "groupBy": [
        "service.name"
      ],
      "having": "",
      "orderBy": "",
      "limit": "",
      "legend": "",
      "dataSource": "traces"
    }
  ],
  "limit": 10
}

Product-event funnels (panel_type: "funnel" + display.funnel.steps)

A funnel widget has two modes. Without display.funnel.steps it draws a group-by breakdown as descending stages (the shape above). With them it is a conversion funnel over product events (page views, track() events and server-side events, stitched per person), and the query set is not used at all. Set the definition on display_json.funnel and add_dashboard_widget derives the data source (kind: "route", endpoint: "product_events_funnel") for you; do not pass data_source_json.

  • steps: 1–10, in order. { kind: "event", eventName, attributeEquals? }, { kind: "page", pagePath, host? }, or (step 1 only) { kind: "session", dimension, value } with dimension one of referrerHost, utmSource, utmMedium, utmCampaign, country, host.
  • keyBy: person (default; user id, else the visitor's linked user, else the visitor), visitor, user, or session.
  • windowSeconds: the whole chain must complete within this many seconds of step 1 (default 86400). Must be positive.
  • breakdownBy: split the funnel by a session dimension (referrerHost, utmSource, utmMedium, utmCampaign, country, host) or attribute:<key>; the widget draws one bar per group per step for the top 6 groups by step-1 count.
  • filters: the population filter: only persons with a session matching these dimensions take part. { host?, pagePath?, referrerHost?, country?, deviceType?, browserName?, osName?, language?, utmSource?, utmMedium?, utmCampaign?, visitorType?: "new" | "returning" }. They are spread FLAT into the derived route params.

The step count, the step-1-only session rule and a positive windowSeconds are enforced on write: a definition that breaks one of them is rejected rather than saved, because the query engine would reject it again on every render.

Use list_product_events to see which event names exist, and query_funnel to try a definition before pinning it to a board.

{
  "id": "w-signup-funnel",
  "visualization": "funnel",
  "dataSource": {
    "kind": "route",
    "endpoint": "product_events_funnel",
    "params": {
      "steps": [
        {
          "kind": "page",
          "pagePath": "/pricing"
        },
        {
          "kind": "event",
          "eventName": "signup_completed"
        },
        {
          "kind": "event",
          "eventName": "plan_started",
          "attributeEquals": {
            "plan": "pro"
          }
        }
      ],
      "keyBy": "person",
      "windowSeconds": 604800
    }
  },
  "display": {
    "title": "Signup funnel",
    "chartId": "query-builder-funnel",
    "funnel": {
      "showStepPercent": true,
      "steps": [
        {
          "kind": "page",
          "pagePath": "/pricing"
        },
        {
          "kind": "event",
          "eventName": "signup_completed"
        },
        {
          "kind": "event",
          "eventName": "plan_started",
          "attributeEquals": {
            "plan": "pro"
          }
        }
      ],
      "keyBy": "person",
      "windowSeconds": 604800
    }
  },
  "layout": {
    "x": 0,
    "y": 0,
    "w": 6,
    "h": 4
  }
}

display.funnel.variant: "dropoff" switches the same widget to the drop-off view, which also fetches the time between steps and the most common next event of the persons who stopped before each step. Needs at least two steps to say anything; with breakdownBy it draws one cap per group and no timing or leavers.

Paths (panel_type: "paths" + display.paths)

A flow of what people did in the hops after (or, with direction: "before", before) one anchor. Product events only; there is no query set. Set the definition on display_json.paths and add_dashboard_widget derives the data source (kind: "route", endpoint: "product_events_paths"); do not pass data_source_json.

  • anchor: required. { kind: "event", eventName, attributeEquals? } or { kind: "page", pagePath, host? }. A session step cannot anchor a sequence.
  • direction: "after" (default) or "before".
  • depth: hops to draw, 1–5 (default 3). branches: nodes named per step, 1–10 (default 4); the rest fold into an Other node. A sequence that stops short ends in an Ended node.
  • keyBy, windowSeconds (how far from the anchor a hop may be, default 86400) and filters: as for funnels.
  • include: "all" (default), "events" or "pages". exclude: names (event names or page paths) dropped before sequencing, e.g. ["heartbeat", "/"].

A complete widget

The sections above describe add_dashboard_widget's parameters, which it assembles into a widget for you. update_dashboard_widget, replace_dashboard_widgets and dashboard_json take the assembled object instead. This is its shape. timeRange and sectionId/tabId are the only other top-level keys, both optional.

{
  "id": "w-error-rate",
  "visualization": "chart",
  "dataSource": {
    "kind": "query",
    "resultShape": "timeseries",
    "queries": [
      {
        "id": "q1",
        "name": "A",
        "enabled": true,
        "whereClause": "service.name = \"api\"",
        "aggregation": "error_rate",
        "stepInterval": "",
        "orderByDirection": "desc",
        "addOns": {
          "groupBy": true,
          "having": false,
          "orderBy": false,
          "limit": false,
          "legend": false
        },
        "groupBy": [
          "service.name"
        ],
        "having": "",
        "orderBy": "",
        "limit": "",
        "legend": "",
        "dataSource": "traces"
      }
    ]
  },
  "display": {
    "title": "Error rate by service",
    "chartId": "query-builder-line",
    "unit": "percent",
    "chartPresentation": {
      "legend": "visible"
    }
  },
  "layout": {
    "x": 0,
    "y": 0,
    "w": 4,
    "h": 6,
    "minW": 2,
    "minH": 2
  }
}

Units (display.unit)

The one that bites: Maple's percent tokens are inverted relative to Grafana's.

  • percent expects a fraction 0–1 and multiplies by 100 on render. (Grafana calls this percentunit.)
  • percent_100 expects 0–100 and renders as-is. (Grafana calls this one percent.)

The traces error_rate aggregation returns a 0–1 ratio, so it pairs with percent. Most exporter metrics named *_percent/*_utilization already report 0–100 and want percent_100. Getting it backwards renders 100× off with no error anywhere.

Token Label Expects
none None any number; rendered like number
number Number any number; grouped thousands
percent Percent (0–1) a FRACTION 0–1; multiplied by 100 on render. error_rate is this one
percent_100 Percent (0–100) already 0–100; rendered as-is. Grafana spells this one percent
duration_ms Duration (ms) milliseconds. The query builder's *_duration aggregations are already ms
duration_s Duration (s) seconds
duration_us Duration (µs) microseconds
duration_ns Duration (ns) nanoseconds
bytes Bytes bytes; scaled decimal (1000-base), not 1024
requests_per_sec Requests/sec a per-second rate
short Short any number; rendered like number

display.unit is stored as an open string, so an unrecognised value like "ms", "%" or "GB" saves successfully and then renders as a plain number. The write tools warn when they see one and suggest the right token. The same vocabulary applies to display.yAxis.unit, display.xAxis.unit and display.columns[].unit.

A gauge's arc is independent of its unit and defaults to 0–100: on a percent gauge set display.gauge: { "min": 0, "max": 1 } or the needle sits pinned at zero.

Queries

A query draft is discriminated on dataSource (traces / logs / metrics / product_events). The metric-only fields belong solely to metrics queries; do not add them to trace or log queries:

  • metricName: required; discover real names with list_metrics.
  • metricType: required, one of sum, gauge, histogram, exponential_histogram. Anything else fails to decode.
  • signalSource: optional, one of default, meter. Omit it unless you know you need meter.
  • isMonotonic: optional; false marks a Sum as an UpDownCounter, which changes the aggregations that make sense (rate/increase assume a monotonic counter).

addOns is required, and all five keys must be present

addOns: { groupBy, having, orderBy, limit, legend }: every key, every time. A missing one fails to decode. Each flag gates whether the matching field is read at all, which is why groupBy without addOns.groupBy: true silently does nothing.

Aggregations, per source

dataSource Valid aggregation
traces count, avg_duration, p50_duration, p95_duration, p99_duration, error_rate
logs count
metrics avg, sum, min, max, count, rate, increase
product_events count, sessions, persons, users, visitors

On traces only, setting valueField: "attr.<key>" switches the query to numeric-attribute mode, where the aggregation is one of avg, sum, min, max, p50, p95, p99. This is the only place a bare p50/p95/p99 is valid; latency percentiles are spelled p95_duration. Metrics never accept percentiles.

Group-by tokens

groupBy is ignored unless addOns.groupBy is true. This is the single most common silent failure: the array is present, the chart shows an ungrouped total, and nothing errors.

dataSource Literal tokens Prefixed
traces service, service.name, service_name, span, span.name, span_name, status, status.code, status_code, http.method, none, all attr.<key>
logs service, service.name, service_name, severity, severity_text, none, all none
metrics service, service.name, none, all attr.<key>, resource.<key>
product_events event, event.name, event_name, kind, event.kind, source, host, page, page.path, page_path, service, service.name, service_name, group, group.id, none, all attr.<key>

Anything outside the literal list must use a supported prefix; unrecognised tokens are dropped, which makes the write tools reject the widget rather than save a mis-scoped chart.

whereClause is a custom grammar, not SQL

Operators (the only ones): =, !=, >, <, >=, <=, contains, !contains, exists, !exists. Clauses join with AND; there is no OR and no parentheses. Values use double quotes. There is no IS NULL / IS NOT NULL: write <key> exists or <key> !exists. exists means present and non-empty, because attributes live in ClickHouse Map columns where a missing key reads back as ''.

On traces any bare key outside the structured allowlist (service.name, span.name, deployment.environment, vcs.ref.head.revision, root_only, has_error) is treated as a span attribute, so db.system = "clickhouse" works directly. Cap: 5 attr.* plus 5 resource.* filters per query.

Formulas and hidden series

formulas: [{ id, name, expression, legend }] references queries by name (A / B), and is valid on the timeseries shape only. Marking a query hidden: true is UI-only in raw JSON, so also add transform.hideSeries.baseNames: ["A"] or the auxiliary series renders at full scale and flattens the axis.

Display config

Key Applies to Notes
title, description all
unit all See the units section: read it before choosing a percent token.
thresholds stat, gauge, charts [{ value, color, label? }]; highest matching value wins.
prefix, suffix stat, gauge Wrap the formatted value.
chartPresentation.legend charts visible | hidden | right.
chartPresentation.seriesStats charts Min/Max/Mean/Last table; costs up to 45% of tile height.
chartPresentation.tooltip charts visible | hidden.
chartPresentation.showPoints charts Omit for auto, true always, false never.
stacked area, bar Meaningless on line.
curveType line, area linear | monotone.
yAxis.logScale, softMin, softMax, fitYAxisToData charts
columns table, list [{ field, header, unit?, width?, align?, hidden? }].
listDataSource, listWhereClause, listLimit, listRootOnly list
pie pie { donut, innerRadius, showLabels, showPercent }.
gauge gauge { min, max }: defaults to 0–100, which is wrong for a percent unit.
histogram histogram { bucketCount, bucketWidth, logScaleY }.
heatmap heatmap { colorScale, scaleType }.
funnel funnel { showStepPercent, variant?, steps?, keyBy?, windowSeconds?, breakdownBy?, filters? }: with steps it is a product-event funnel (see Data sources); variant: "dropoff" is the drop-off view.
paths paths { anchor, direction?, depth?, branches?, keyBy?, windowSeconds?, include?, exclude?, filters? }: the whole definition (see Data sources).
markdown markdown { content }: the note body.
sparkline stat { enabled, dataSource? }; embeds a full nested data source.

Stored but not rendered

These decode and persist, and the chart renderer ignores them. Setting one to fix a problem will look like it worked and change nothing: yAxis.min, yAxis.max, every xAxis field, seriesMapping, colorOverrides, chartPresentation.fillNulls, gauge.style. To bound a chart's axis use yAxis.softMin/softMax.

Per-widget time range

A widget follows the dashboard's range unless it carries its own top-level timeRange: {"type":"relative","value":"30m"} or {"type":"absolute","startTime":"…","endTime":"…"}. Pin one only when the window is part of what the tile means. Because update_dashboard_widget replaces the whole widget, omitting timeRange there REMOVES an existing override.

Raw SQL widgets

Pass sql to add_dashboard_widget and the tool builds the data source for you. Call describe_warehouse_tables first: a hallucinated table or column silently produces an empty chart.

Macros

  • $__orgFilter → required; scopes the query to your org.
  • $__timeFilter(Column) → a bare column identifier, no expressions. Prefer this in WHERE.
  • $__startTime / $__endTime → toDateTime(…) literals for use outside a WHERE comparison.
  • $__interval_s → bucket size in seconds; only interpolate it if the SQL buckets time.

Conventions that catch everyone

  • Columns are PascalCase (ServiceName, Timestamp), never snake_case.
  • StatusCode / SeverityText / SpanKind values are Title Case ('Error', not 'ERROR'). Wrong casing runs fine and matches zero rows.
  • Span Duration is nanoseconds. Divide by 1e6 for ms.
  • SpanAttributes['key']: square brackets. A missing key returns '', not NULL.
  • One statement only; writes are rejected; every query is wrapped in LIMIT 1001.

What to SELECT, per panel type

The renderer is opinionated. Wrong aliases give an empty chart or [object Object].

  • line / area / bar: a DateTime bucket as the FIRST column (alias bucket) plus one or more numeric columns; each becomes a series named after the column. String columns are dropped, so multi-series must be pivoted in SQL with countIf(...); tall form (bucket, ServiceName, count()) collapses to one aggregate line.
  • stat / gauge: one scalar aliased value.
  • pie / funnel / hbar: a string column aliased name plus a numeric column. Cap at ~8–10 rows.
  • heatmap: three columns aliased x, y, value; string-cast numeric x/y.
  • histogram: one numeric column aliased value, one row per observation; add LIMIT 5000.
  • table: any rows; columns render in order, so use AS for readable headers.
  • list: not supported. A list is configured by display.listDataSource.
SELECT toStartOfInterval(Timestamp, INTERVAL $__interval_s SECOND) AS bucket,
       countIf(SeverityNumber BETWEEN 17 AND 20) AS Error,
       countIf(SeverityNumber BETWEEN 13 AND 16) AS Warn
FROM logs
WHERE $__orgFilter AND $__timeFilter(Timestamp)
GROUP BY bucket
ORDER BY bucket

granularity_seconds only matters if the SQL references $__interval_s. Either use toStartOfInterval(…, INTERVAL $__interval_s SECOND) with it, or a fixed toStartOf* without it; mixing them means the setting silently does nothing.

Version History

  • abef749 Current 2026-09-27 16:48

    修正文档中过时的路径和指令引用;更新源码路径从apps/api迁移至apps/ai;重新生成以同步最新的仪表板Schema变更。

  • 6b8690a 2026-09-22 05:48
  • 8117648 2026-09-09 00:11
  • b9cc9f8 2026-08-19 16:20

    重构仪表盘组件文档,改为从实时 Schema 自动生成,修正了百分比单位规则,并统一了面板类型定义。

  • a80abb5 2026-08-03 13:03

    新增组件独立时间范围(timeRange)功能,允许单个widget覆盖仪表盘的默认时间窗口;修复exists操作符逻辑,使其能正确排除空值属性而非仅缺失键。

  • 01a5dc6 2026-07-05 18:16

Same Skill Collection

.agents/skills/clickhouse-architecture-advisor/SKILL.md
.agents/skills/clickhouse-best-practices/SKILL.md
.agents/skills/clickhousectl-cloud-deploy/SKILL.md
.agents/skills/clickhousectl-local-dev/SKILL.md
.agents/skills/coss-particles/SKILL.md
.agents/skills/coss/SKILL.md
.agents/skills/react-doctor/SKILL.md
.agents/skills/tinybird-cli-guidelines/SKILL.md
.agents/skills/tinybird-python-sdk-guidelines/SKILL.md
.agents/skills/tinybird-typescript-sdk-guidelines/SKILL.md
.agents/skills/tinybird/SKILL.md
.context/effect/.agents/skills/grill-me/SKILL.md
.context/effect/.agents/skills/jsdocs/SKILL.md
.context/effect/.agents/skills/scratchpad/SKILL.md
.factory/skills/react-doctor/SKILL.md
apps/slack-agent/agent/skills/dashboard-builder/SKILL.md
apps/slack-agent/agent/skills/incident-investigation/SKILL.md
skills/maple-audit/SKILL.md
skills/maple-csharp-style/SKILL.md
skills/maple-effect-style/SKILL.md
skills/maple-go-style/SKILL.md
skills/maple-java-style/SKILL.md
skills/maple-kotlin-style/SKILL.md
skills/maple-nextjs-style/SKILL.md
skills/maple-nodejs-style/SKILL.md
skills/maple-onboard/SKILL.md
skills/maple-onboarding-style/SKILL.md
skills/maple-python-style/SKILL.md
skills/maple-rust-style/SKILL.md
.agents/skills/chdb-datastore/SKILL.md
.agents/skills/chdb-sql/SKILL.md
.agents/skills/maple-telemetry-conventions/SKILL.md
.agents/skills/onboarding-cro/SKILL.md
skills/maple-otel-spec-review/SKILL.md

Metadata

Files
0
Version
abef749
Hash
3796c844
Indexed
2026-07-05 18:16

ホーム - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-09-29 11:37
浙ICP备14020137号-1