Experimental: AI Skill for building plugins#57
Conversation
correct property name
This is the only field that actually supports this property currently, the other fields have no UI to support encrypting values
The skill previously used a placeholder @yourhandle in the metadata.json template without prompting Claude to collect this from the user, meaning plugins could be created with an unresolved placeholder. An explicit AskUserQuestion instruction now ensures the author name is always gathered at the point it is needed. The author type silently defaults to "community" to keep the interaction minimal.
The build-plugin skill mentioned that the documentation link in metadata.json should point to docs/README.md, but never explicitly instructed Claude to create the file. This made it easy to skip. The fix adds docs/README.md to the Phase 3 checklist entry, the folder structure diagram, and a new section explaining what the file should contain (prerequisites, config field descriptions, object types, known limitations). The section also explains why it matters — the file is surfaced directly in-product when users add the plugin, so it needs to work as a self-contained setup guide without relying on external links.
Was using incorrect property name
Otherwise Claude can name each perspective the same i.e. Overview, which show next to each other in the UI
Co-authored-by: Dave Clarke <02DClarke@gmail.com>
Claude skill tweaks
The build-plugin SKILL.md was a monolithic ~850-line file loaded in full on every invocation, including auth patterns, field types, pagination options, and viz configs irrelevant to the current phase. This refactors it into a 113-line orchestrator that references six topic-specific files under references/; Claude reads each file only when it reaches the corresponding build phase. A standalone deploy-plugin skill is extracted so validate/deploy/versioning can be invoked independently of the full build flow. Follows Anthropic's recommended progressive disclosure pattern for large skills, which advises keeping SKILL.md under 500 lines. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The data-streams reference steered the skill toward writing a postRequestScript for almost every stream, even when the response only needed per-column tweaks. In practice most "scripts" were just `data.items.map(...)` reshapes that `pathToData` plus column-level expressions handle without a separate JS file. The Expressions section now states up front that any string leaf under `config` is run through the expression resolver, not just the fields previously shown in examples. The former "Computed columns" subsection is rewritten as "Column expressions" covering both `valueExpression` (computes the underlying value; with `computed: true` it materialises a brand-new column, without it it overrides an existing one) and `formatExpression` (changes only the displayed string, leaves the raw value intact for sort and aggregation). The post-request scripts section gains bullets pointing per-row value transforms, display-only formatting, and simple map-and-rename scripts at the expression mechanisms instead. Also captures a real gotcha verified in the WebAPI handler: `pathToData` is silently ignored when `postRequestScript` is set (the script receives the raw response body as `data`), so the two should not be combined. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The build-plugin skill's post-request script guidance was correct
but easy to skim past. In practice, LLM invocations of the skill
keep imitating shipped plugins (UniFi, Spotify, OpenSearch) that
use `data.items.map(...)` reshape scripts where pathToData,
expandInnerObjects, and computed valueExpression would suffice.
The data-streams.md "Post-request scripts" section now leads with
an explicit "don't imitate existing plugins" warning, followed by
a "Default: no script" decision table covering the eight common
patterns that don't justify a script: path navigation, primitive
parsing, single-level flattening, constant columns, derived
columns, null coercion, array counts, and renames. The "Use
scripts for" list is tightened — "flattening" is qualified as
deeply nested or array-into-rows, since one-level nesting is
handled by expandInnerObjects.
index-defs.md gains a worked no-script example for the common
paged list shape ({ items: [{ id, attributes: { ... } }] }),
demonstrating sourceId/sourceType via computed valueExpression
and showing how the index definition references dot-notation
column names. The original script example is reframed as the
exception (nested array into rows) rather than the default.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Claude is seeing this property as being required by default due to the examples, it should be added only as needed
Could lead Claude to hallucinate that $schema is a valid property, which it currently is not and will fail validation
Load build-plugin reference content on demand
🧩 Plugin PR Summaryℹ️ No plugins were modified in this PR. |
| ``` | ||
| my-plugin/ | ||
| v1/ | ||
| metadata.json | ||
| ui.json | ||
| icon.svg | ||
| custom_types.json | ||
| configValidation.json # preferred: validates config on setup | ||
| docs/ | ||
| README.md # REQUIRED: shown in-product when users add the plugin | ||
| indexDefinitions/ | ||
| default.json | ||
| dataStreams/ | ||
| myStream.json | ||
| scripts/ | ||
| myScript.js | ||
| defaultContent/ | ||
| manifest.json | ||
| scopes.json | ||
| overviewDashboard.dash.json | ||
| deviceDashboard.dash.json # single perspective — no sub-folder needed | ||
| Installations/ # sub-folder only for multiple dashboards of the same type | ||
| manifest.json | ||
| dashboard1.dash.json |
There was a problem hiding this comment.
🔴 The Phase 3 file-structure scaffold in build-plugin/SKILL.md uses lowercase my-plugin/ as the top-level folder, but the same PR's references/metadata.md explicitly says the folder is PascalCase (e.g. MyPlugin), the documentation-link example a few lines below uses plugins/MyPlugin/v1/, and every one of the 19 existing plugin folders in this repo uses PascalCase (DigiCert, GoogleSheets, MetOffice, …). An LLM following the scaffold literally would create the plugin in a lowercase folder that breaks repo convention — change the example to MyPlugin/v1/ (the kebab-case stays only in the metadata.json name field).
Extended reasoning...
The bug
In .claude/skills/build-plugin/SKILL.md, the Phase 3 "File structure" code block starts with:
my-plugin/
v1/
metadata.json
...
This lowercase kebab-case folder name contradicts three load-bearing sources in this same PR and repo:
references/metadata.mdin this PR — under the field notes it states verbatim: "name: lowercase kebab-case (e.g.my-plugin). Folder uses PascalCase (e.g.MyPlugin) — these are separate things." The skill is contradicting its own reference doc.- The same SKILL.md file, ~20 lines below the scaffold — the documentation-link example is
https://github.com/squaredup/plugins/blob/main/plugins/MyPlugin/v1/docs/README.md. So PascalCase is already used as the intended path inside SKILL.md itself. - Existing repo convention — every plugin folder under
plugins/is PascalCase: DattoRMM, DigiCert, FantasyPremierLeague, GoogleSheets, MetOffice, OpenSearch, Phare, Postcoder, RDAP, RSS, Rootly, SendGrid, Snowflake, Spotify, Steam, SumoLogic, TransportForLondon, UniFi, UptimeRobot. There is no precedent for a lowercase folder.
How it manifests
The build-plugin skill is intended to be invoked by an AI agent that will scaffold files literally from the example. Phase 3 is the scaffolding phase; the agent reads the file-tree block and creates directories/files matching it. Because the example shows my-plugin/v1/..., the agent will run something like mkdir -p my-plugin/v1/dataStreams and write files there. By the time Phase 4 instructs the agent to read metadata.md (which contains the PascalCase rule), the folder has already been created with the wrong case — renaming a directory mid-build is a friction point the agent may simply skip, especially because the lowercase name field in metadata.json does match the folder name and looks superficially consistent.
Step-by-step proof
- User runs the skill: "I want to build a plugin for MyService."
- Agent works through the checklist; reaches Phase 3.
- Agent reads the scaffold tree and creates
my-plugin/v1/metadata.json,my-plugin/v1/ui.json, etc. - Agent moves to Phase 4, opens
references/metadata.md, sees the PascalCase rule — but only as a field note aboutnamevs. folder. The agent has already created the folder; no instruction tells it "go back and rename." - PR opens with a new plugin at
plugins/my-plugin/v1/— inconsistent with all 19 existing plugins. Reviewer asks for a rename, which involves rewriting paths in the documentation link insidemetadata.jsontoo.
Why existing code does not prevent it
There is no validator step that checks folder casing — squaredup validate validates JSON contents, not directory names. The PascalCase rule exists only in prose inside metadata.md, which is read after the folder is created. Without correcting the scaffold itself, the rule is documented but unenforced at the moment it matters.
Impact
Low severity (it is a docs/instructional bug, easily fixed by a reviewer), but real — every plugin generated by this skill is at risk of being committed with the wrong folder case, requiring a manual fix before merging. Given this is the new official AI-skill workflow, it will be the dominant pattern going forward.
Fix
Change the scaffold tree to:
MyPlugin/
v1/
metadata.json
...
Leave the name field in metadata.json as my-plugin (kebab-case) — those are separate things, exactly as metadata.md states. No other lines in the scaffold need to change.
📋 Summary
This PR introduces an experimental skill for building LCPs. It's currently under active development so expect further PRs to fix issues and enhance functionality.
To get started:
Check out the repo
Open a Claude session in the repo
Trigger the skill, e.g. I want to build a plugin for xxxxxx
Notes:
It will probably call squaredup validate and squaredup deploy for you - so make sure the CLI is logged in to the right organisation
It's focused on Web API based data streams, not PowerShell or otherwise right now
It will reference things locally like existing plugins
🔍 Scope of change
📚 Checklist