agentleFS
DenchHQDenchHQ/ironclaw/skills/crm/reports/SKILL.md

reports

Skill1.7k starsChanged 7 months agoMITView on GitHub

Report generation with .report.json format, interactive chart types (bar, line, area, pie, donut, radar, scatter, funnel), panel sizes, filter types, inline chat reports, and post-report checklist.

7 other projects use a version of this.

Trending

Quiet since February.thin dataabout the whole repository

Dec 2025: 0 mentionsJan 2026: 0 mentionsFeb 2026: 1 mentionMar 2026: 0 mentionsApr 2026: 0 mentionsMay 2026: 0 mentionsJun 2026: 0 mentionsJul 2026: 0 mentionsAug 2026: 0 mentionsSep 2026: 0 mentionsOct 2026: 0 mentionsNov 2026: 0 mentions
Dec 2025Jun 2026Nov 2026
Mentions a month of the DenchHQ/ironclaw repository that our weekly crawl found: only 1 in all, so this is a hint, not a trend. There is no adoption data for this file yet.

Most recently taken up by

Who's talking about DenchHQ/ironclaw

The biggest 1 of 1 mention linking to the repository that our crawl found. Each line is their own headline.

The file

We split this skill into the rules it gives an agent

11 rules. 0 are common, 2 are distinctive, and 9 are very specific to this project.

  • 0

    Common

    Most of the 4 edited versions we read keep these.

  • 2

    Distinctive

    This author's own choices. Worth a look.

  • 9

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Always use the auto-generated v_{object} PIVOT views — never raw EAV queries

    line 75
  2. Very specific:

    Use DATE_TRUNC('month', created_at) for time-series grouping

    line 78
  3. Very specific:

    Always include ORDER BY for consistent chart rendering

    line 79
  4. Very specific:

    Use aggregate functions: COUNT(*), SUM(...), AVG(...), MIN(...), MAX(...)

    line 80
  5. Very specific:

    Verify the PIVOT view exists before writing report SQL: run SELECT COUNT(*) FROM v_{object} first.

    line 82
  6. Distinctive:

    Test each panel's SQL individually: Run each query to confirm it returns data before assembling the report JSON.

    line 88
  7. Distinctive:

    If a view exists but returns 0 rows, the chart will render but show nothing.

    line 92
  8. Very specific:

    Do entries have field values?

    line 95
  9. Very specific:

    When a user asks for analytics in chat (without explicitly asking to save a report), emit the report JSON inside a fenced code block with …

    line 163
  10. Very specific:

    After creating a .report.json file:

    line 181
  11. Very specific:

    When in doubt, bar is the safest default

    line 200

“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

CRM Reports

This skill covers report generation and inline chat charts. For workspace fundamentals, see the parent crm skill (crm/SKILL.md). For DuckDB queries used in report SQL, see duckdb-operations (crm/duckdb-operations/SKILL.md).


Report Generation (Analytics / Charts)

Reports are JSON config files (.report.json) that the web app renders as live interactive dashboards using Recharts. The agent creates these files to give the user visual analytics over their CRM data.

Report file format

Store reports as .report.json files in {{WORKSPACE_PATH}}/** (wherever appropriate / create directories if you need for better structure). The JSON schema:

{
  "version": 1,
  "title": "Report Title",
  "description": "Brief description of what this report shows",
  "panels": [
    {
      "id": "unique-panel-id",
      "title": "Panel Title",
      "type": "bar",
      "sql": "SELECT ... FROM v_{object} ...",
      "mapping": { "xAxis": "column_name", "yAxis": ["value_column"] },
      "size": "half"
    }
  ],
  "filters": [
    {
      "id": "filter-id",
      "type": "dateRange",
      "label": "Date Range",
      "column": "created_at"
    }
  ]
}

Chart types

TypeBest forRequired mapping
barComparing categoriesxAxis, yAxis
lineTrends over timexAxis, yAxis
areaVolume trendsxAxis, yAxis
pieDistribution/sharenameKey, valueKey
donutDistribution (with center)nameKey, valueKey
radarMulti-dimensional comparisonxAxis (or nameKey), yAxis
scatterCorrelationxAxis, yAxis
funnelPipeline/conversionnameKey, valueKey

Panel sizes

  • "full" — spans full width (6 columns)
  • "half" — spans half width (3 columns) — default
  • "third" — spans one third (2 columns)

Filter types

  • dateRange — date picker (from/to), filters on column
  • select — single-select dropdown, needs sql to fetch options
  • multiSelect — multi-select chips, needs sql to fetch options
  • number — min/max numeric range

SQL query rules for reports

  • Always use the auto-generated v_{object} PIVOT views — never raw EAV queries
  • SQL must be SELECT-only (no INSERT/UPDATE/DELETE)
  • Cast numeric fields: "Amount"::NUMERIC or CAST("Amount" AS NUMERIC)
  • Use DATE_TRUNC('month', created_at) for time-series grouping
  • Always include ORDER BY for consistent chart rendering
  • Use aggregate functions: COUNT(*), SUM(...), AVG(...), MIN(...), MAX(...)
  • Double-quote field names with spaces: "Full Name", "Email Address", "Assigned To"
  • Verify the PIVOT view exists before writing report SQL: run SELECT COUNT(*) FROM v_{object} first. If the view doesn't exist, create it (see duckdb-operations skill).

Before writing a report

  1. Verify the PIVOT view exists: duckdb {{WORKSPACE_PATH}}/workspace.duckdb "SELECT COUNT(*) FROM v_{object}" — if this fails, the view needs to be created first.
  2. Check the view columns: duckdb {{WORKSPACE_PATH}}/workspace.duckdb -json "SELECT * FROM v_{object} LIMIT 1" — use the actual column names in your SQL (they are case-sensitive and may contain spaces).
  3. Test each panel's SQL individually: Run each query to confirm it returns data before assembling the report JSON.

Handling empty data

If a view exists but returns 0 rows, the chart will render but show nothing. This is fine — no special handling needed. However, if the user asks "why is the chart empty?", check:

  • Are there entries in the object? SELECT COUNT(*) FROM entries WHERE object_id = (SELECT id FROM objects WHERE name = '{object}')
  • Do entries have field values? SELECT COUNT(*) FROM entry_fields WHERE entry_id IN (SELECT id FROM entries WHERE object_id = ...)

Example Reports

Pipeline Funnel

{
  "version": 1,
  "title": "Deal Pipeline",
  "description": "Deal count and value by stage",
  "panels": [
    {
      "id": "deals-by-stage",
      "title": "Deals by Stage",
      "type": "funnel",
      "sql": "SELECT \"Stage\", COUNT(*) as count FROM v_deal GROUP BY \"Stage\" ORDER BY count DESC",
      "mapping": { "nameKey": "Stage", "valueKey": "count" },
      "size": "half"
    },
    {
      "id": "revenue-by-stage",
      "title": "Revenue by Stage",
      "type": "bar",
      "sql": "SELECT \"Stage\", SUM(\"Amount\"::NUMERIC) as total FROM v_deal GROUP BY \"Stage\" ORDER BY total DESC",
      "mapping": { "xAxis": "Stage", "yAxis": ["total"] },
      "size": "half"
    }
  ],
  "filters": [
    { "id": "date", "type": "dateRange", "label": "Created", "column": "created_at" },
    {
      "id": "assignee",
      "type": "select",
      "label": "Assigned To",
      "sql": "SELECT DISTINCT \"Assigned To\" as value FROM v_deal WHERE \"Assigned To\" IS NOT NULL",
      "column": "Assigned To"
    }
  ]
}

Contact Growth

{
  "version": 1,
  "title": "Contact Growth",
  "description": "New contacts over time",
  "panels": [
    {
      "id": "growth-trend",
      "title": "Contacts Over Time",
      "type": "area",
      "sql": "SELECT DATE_TRUNC('month', created_at) as month, COUNT(*) as count FROM v_people GROUP BY month ORDER BY month",
      "mapping": { "xAxis": "month", "yAxis": ["count"] },
      "size": "full"
    }
  ]
}

Inline Chat Reports

When a user asks for analytics in chat (without explicitly asking to save a report), emit the report JSON inside a fenced code block with language report-json. The web UI will render interactive charts inline:

Here's your pipeline analysis:

```report-json
{"version":1,"title":"Deals by Stage","panels":[{"id":"p1","title":"Deal Count","type":"bar","sql":"SELECT \"Stage\", COUNT(*) as count FROM v_deal GROUP BY \"Stage\" ORDER BY count DESC","mapping":{"xAxis":"Stage","yAxis":["count"]},"size":"full"}]}
```

Most deals are currently in the Discovery stage.

The user can then "Pin" the inline report to save it as a .report.json file.


Post-Report Checklist

After creating a .report.json file:

  • Verify the PIVOT view exists: duckdb {{WORKSPACE_PATH}}/workspace.duckdb "SELECT COUNT(*) FROM v_{object}" — create the view if missing
  • Test each panel's SQL individually: Run each query against the DB to confirm it returns valid data
  • Verify the report JSON is valid (proper JSON syntax, no trailing commas)
  • Choose which directory the report should be created in {{WORKSPACE_PATH}} based on the context of the conversation, if nothing very relevant, create/use the {{WORKSPACE_PATH}}/reports/ directory.
  • Write the file: {{WORKSPACE_PATH}}/**/{slug}.report.json
  • Tell the user they can view it in the workspace sidebar under whichever directory it was rightfully placed in based on the context.

Choosing the Right Chart Type

  • Comparing categories (status breakdown, source distribution): bar or pie
  • Time series (growth, trends, revenue over time): line or area
  • Pipeline/conversion (deal stages, lead funnel): funnel
  • Distribution/proportion (market share, segment split): pie or donut
  • Multi-metric comparison (performance scores): radar
  • Correlation (price vs. size, score vs. revenue): scatter
  • When in doubt, bar is the safest default

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.