From 9701396351b4abb2026cbbef0e9003b9dbe94f72 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 9 Oct 2026 21:04:54 +0000 Subject: [PATCH 01/11] Add optional contribute-back phase to build-plugin skill Phase 10 asks the user whether to raise a pull request contributing the finished plugin to squaredup/plugins. On a yes it reviews the plugin and its branch history for secrets and organisation-specific data, guides safe screenshots, fills the matching PR template, and opens the PR only after the user approves the files and description. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UmF4xDehy5QsrZb4ibqwyU --- .claude/skills/build-plugin/SKILL.md | 20 ++- .../build-plugin/references/contribute.md | 148 ++++++++++++++++++ AGENTS.md | 2 +- 3 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 .claude/skills/build-plugin/references/contribute.md diff --git a/.claude/skills/build-plugin/SKILL.md b/.claude/skills/build-plugin/SKILL.md index e842a27e..1e785b01 100644 --- a/.claude/skills/build-plugin/SKILL.md +++ b/.claude/skills/build-plugin/SKILL.md @@ -1,9 +1,9 @@ --- name: build-plugin -description: Guides building a SquaredUp low-code plugin for HTTP/REST APIs, from API exploration through deployment. Use when the user wants to integrate a service with SquaredUp, add a new data source, connect to a third-party tool, "pull data from", or "monitor" any service in SquaredUp. +description: Guides building a SquaredUp low-code plugin for HTTP/REST APIs, from API exploration through deployment, with an optional pull request to contribute it to the community. Use when the user wants to integrate a service with SquaredUp, add a new data source, connect to a third-party tool, "pull data from", or "monitor" any service in SquaredUp. metadata: author: SquaredUp - version: "1.0.0" + version: "1.1.0" --- # Building a SquaredUp Low-Code Plugin @@ -53,6 +53,7 @@ Create a TaskCreate task for each phase. The flow deploys early and tests as it - [ ] **Phase 7** — Build OOB default content in a sub-agent (it reads [oob-content.md](references/oob-content.md)) - [ ] **Phase 8** — Write `custom_types.json` → [common-patterns.md](references/common-patterns.md) - [ ] **Phase 9** — Finalize `docs/README.md`, then final validate and deploy → [readme.md](references/readme.md); invoke the `deploy-plugin` skill; re-index if import definitions changed since the last import +- [ ] **Phase 10 (optional)** — Ask whether to contribute the plugin back; only on a yes, review it for secrets and organisation-specific data, then raise a pull request using the matching template → [contribute.md](references/contribute.md) --- @@ -322,3 +323,18 @@ Write `custom_types.json` — for this and other reusable patterns (built-in pro Then invoke the `deploy-plugin` skill for the final validate, version bump, and deploy. **Conditional final re-index.** If `indexDefinitions/*.json` or any import stream changed since the last successful import (the Checkpoint B run, or any re-index triggered by the [re-indexing rule](#re-indexing-rule--a-definition-change-leaves-imported-objects-stale)), the deployed tenant's objects are **stale** — they still match the **old** definition and won't pick up the new shape until the next scheduled import, up to `frequencyMinutes` away (default `720` = 12 hours). So after the final deploy lands, trigger + poll one more import (the [Checkpoint B](#checkpoint-b-redeploy--run-the-first-import) trigger/wait steps) so the deployed objects match the shipped definition. Skip only if no import definition or import stream has changed since the last import. + +--- + +## Phase 10 (optional): Contribute the plugin back + +Once the final deploy has landed, **ask the user** with `AskUserQuestion` whether they'd like to contribute the plugin to the community by raising a pull request against `squaredup/plugins`. Always ask — even in autonomous mode, and even if the user waived the Phase 2 approval gate — and never raise a PR without an explicit yes. If they decline, the build is complete. + +On a yes, follow [contribute.md](references/contribute.md) step by step. In outline: + +1. Check the preconditions — `gh` logged in, a clone of `squaredup/plugins`, new plugin vs change, push access vs fork. +2. Run the **sensitivity review**: secrets, organisation-specific hosts and IDs, people and company names, and internal tools — in the files _and_ the branch's history — and resolve every finding with the user. +3. Encourage screenshots, with a checklist for keeping tenant data out of them. +4. Branch from `main`, stage only the plugin folder (plus an optional `CODEOWNERS` line), and confirm the PR touches a single plugin. +5. Fill in the matching PR template from what this build actually produced. +6. Show the user the files, title and description, and push and open the PR only once they approve. diff --git a/.claude/skills/build-plugin/references/contribute.md b/.claude/skills/build-plugin/references/contribute.md new file mode 100644 index 00000000..c4265db3 --- /dev/null +++ b/.claude/skills/build-plugin/references/contribute.md @@ -0,0 +1,148 @@ +# Phase 10 (optional) — Contribute the plugin back + +This is the **main agent's** reference for raising a pull request that contributes the finished plugin to the community repository, [`squaredup/plugins`](https://github.com/squaredup/plugins). Everything here is outward-facing: once pushed, the plugin and the PR description are **public**, and git history keeps anything that was ever committed. Treat every step below as a gate, not a formality. + +The repository's own documents stay authoritative — read them at run time rather than relying on this summary: + +- [`README.md`](../../../../README.md#how-do-i-submit-my-plugin) — "How do I submit my plugin?" +- [`REVIEW.md`](../../../../REVIEW.md#pull-requests) — the PR rules and every file convention the PR is reviewed against +- [`.github/PULL_REQUEST_TEMPLATE/`](../../../../.github/PULL_REQUEST_TEMPLATE/) — the templates the PR body must follow + +## 1. Ask first — always + +**Never start this phase without an explicit yes.** Ask with `AskUserQuestion` after Phase 9's final deploy, even in autonomous mode and even if the user waived the Phase 2 approval gate — that waiver covers planning, not publishing their work. + +Explain in one or two sentences what contributing means — the plugin is published under a public pull request, reviewed by a community moderator, and available to every SquaredUp user once merged — then offer: + +- `Yes — prepare a pull request` → continue with step 2 +- `No — keep it private` → stop; the plugin stays in their tenant only +- `Not yet` → stop, and tell them they can ask for this step later + +A plugin built for an internal-only system (a home-grown API, a single company's private service) is rarely useful to the community. If that is what was built, say so when asking — don't talk them out of it, but don't assume it fits either. + +## 2. Preconditions + +Confirm each before touching git. Stop and tell the user what's missing if any fails. + +1. **Phase 9 is done** — the final `squaredup validate --json` passes and the final deploy succeeded. CI runs the same validation and blocks the PR on the same errors. +2. **GitHub CLI** — `gh auth status` succeeds. If `gh` isn't installed or logged in, ask the user to install it and run `! gh auth login`; don't try to work around it with tokens. +3. **A clone of `squaredup/plugins`** — run `git remote -v` in the working directory. If it isn't a clone of `squaredup/plugins` (the skill may have been installed with `npx skills add` into an unrelated folder), fork-and-clone into a fresh directory (`gh repo fork squaredup/plugins --clone`) and copy the finished `plugins//v/` folder into it. +4. **New plugin or change?** — `git fetch origin main`, then `git ls-tree -d origin/main plugins/`. Absent on `main` → **new plugin**; present → **change to an existing plugin**. This decides the template (step 6) and the version rule: + - New plugin: `metadata.json` stays at `.0.0` matching its `v` folder. + - Change: `version` must be higher than on `main` — invoke `deploy-plugin` for the bump if Phase 9 didn't. +5. **Push access** — `gh repo view squaredup/plugins --json viewerPermission`. `WRITE`, `MAINTAIN` or `ADMIN` → push a branch to `squaredup/plugins` directly. Anything else → push to the user's fork (`gh repo fork squaredup/plugins --remote`, which adds it as a remote) and open the PR from there. + +## 3. Sensitivity review + +The plugin was built and tested against the user's own tenant and their own account with the vendor, so it is easy for their organisation's details to leak into the files. The repository's PR template asks the author to confirm **"No secrets or credentials included"** — this step is how that box is earned. Do it every time, in full, even for a small change. + +### What to look for + +| Category | Examples | +| --- | --- | +| **Secrets** | API keys, tokens, passwords, client secrets, bearer/basic `Authorization` values, JWTs, private keys (`-----BEGIN`), webhook URLs carrying a token, connection strings, signed URLs | +| **Organisation-specific hosts** | The user's instance URL or subdomain (`acme.atlassian.net`, `acme.zendesk.com`), internal hostnames, private IP ranges (`10.`, `172.16–31.`, `192.168.`), VPN or intranet links | +| **Identifiers from the user's tenant or vendor account** | Account, tenant, workspace, project, org or subscription IDs; SquaredUp `config-…`, `plugin-…`, `node-…`, workspace or dashboard IDs; object IDs from the import. Default content must use the [templating tokens](oob-content.md#templating-tokens) (`{{configId}}`, `{{workspaceId}}`, `{{scopes.[…]}}`), never a real ID | +| **People and organisation names** | Colleagues' names or emails, customer names, team names, the company name — in placeholders, examples, sample data, dashboard titles, tile text or README walkthroughs | +| **Internal tools and process** | References to internal wikis, Slack/Teams channels, ticket numbers, runbooks, internal-only product names, "ask IT to…" — anything a stranger can't act on | +| **Values pinned to one environment** | `ui.json` defaults or placeholders holding the user's real values instead of a generic example; filters or scopes hardcoded to their objects; dashboards that only make sense with their data | + +### How to look + +1. **Build a list of known values from this session.** You already hold most of what can leak: the `tenantName` from `squaredup status --json`, the `pluginId` and datasource `id` and `displayName` from Checkpoint A, the base URL and any non-secret config values the user entered, object names and IDs seen during testing, and the user's email domain. Grep the plugin folder for each literally (case-insensitive). Ask the user for anything else worth checking — company name, internal domains, product codenames. +2. **Pattern sweep.** Grep the plugin folder for generic secret and identifier shapes, e.g.: + + ```bash + grep -rniE "(api[_-]?key|secret|token|passw(or)?d|bearer|authorization|-----BEGIN|eyJ[a-zA-Z0-9_-]{10,}|https?://[^\"' ]*\?(.*&)?(sig|token|key)=|config-[0-9a-z]{8,}|plugin-[0-9a-z]{8,}|node-[0-9a-z]{8,}|@[a-z0-9.-]+\.(com|net|org|io|co\.uk)|\b(10|192\.168|172\.(1[6-9]|2[0-9]|3[01]))\.[0-9]+\.[0-9]+)" plugins// + ``` + + A hit is a prompt to look, not a verdict — `ui.json` field names like `apiKey` and `{{apiKey}}` header templates are expected. Only a **value** is a leak. +3. **Read every file.** Patterns miss names, internal tools and environment-specific content. Spawn **one general-purpose sub-agent**, told not to edit anything, to read every file in the plugin folder in full (dashboards are large — don't read them inline; and don't use an `Explore` agent, which skims excerpts rather than reading whole files) against the table above, including the known-values list from step 1, and return only findings as `file:line — category — the offending text — suggested generic replacement`. Images (`icon.*`, `screenshots/`) are part of this: it should view them, not skip them. +4. **Check history, not just the final files.** Anything committed on the branch is published with it, even if a later commit removed it. If the plugin has commits of its own, run `git log -p origin/main..HEAD -- plugins/` through the same sweep. If a secret appears anywhere in history, don't push that branch — build a fresh branch from `origin/main` with only the clean files (step 5), and tell the user to **rotate the credential** regardless, since it may already exist elsewhere. +5. **Check what else would ship.** The PR must contain the plugin folder plus, optionally, a `.github/CODEOWNERS` line. Personal configuration (`.claude/settings.json`, `*.local.json`, `.env`, scratch test output) must not be staged. + +### Resolve with the user + +Show every finding to the user — grouped by file, with your suggested replacement — and fix them only with their agreement. Some will be judgement calls (is that hostname the vendor's public API or their private instance?) that only the user can answer. When a value has to be generalised, replace it with a neutral example in the style [REVIEW.md](../../../../REVIEW.md) asks for (e.g. `https://organisation.atlassian.net`, `rootly_xxxxxxxxx`). + +After fixing, **re-run `squaredup validate --json`** and redeploy with `deploy-plugin` if anything other than docs changed, so the PR ships what was tested. Re-run the sweep until it comes back clean, then tell the user plainly that it did. + +## 4. Screenshots + +The *Add a new plugin* template asks for screenshots of the plugin's configuration and its default dashboards. They make review much faster, so **encourage the user to add them** — but they're also the easiest place to leak the very data step 3 just removed, because they show real tenant data. + +Ask the user whether they'd like to include screenshots and, if so, give them this checklist before they take or share any: + +- Use the default dashboards on a data source whose objects are safe to show, or crop/blur object names, hostnames, IPs, account IDs and people's names. +- Crop out the browser address bar, the SquaredUp organisation name and workspace names, and the user's avatar or email in the navigation. +- On the configuration screen, make sure credential fields are empty or masked — never a visible API key, even a revoked one. +- Check every tile, including table rows and legends, not just titles. + +If the user gives you the image files, **view each one** with `Read` and report anything sensitive you can see before they're used — don't assume they've been cropped. + +Screenshots go in the **PR description**, not the repository: `gh` can't upload images, so the user drags them into the PR's description in the GitHub web editor after it is created. Leave the template's screenshot headings in place with a short note so they know where they belong. Only add a `screenshots/` folder to the plugin if the user specifically wants images in-product. + +## 5. Branch and commit + +1. **Branch from current `main`**: `git fetch origin main && git switch -c plugins/ origin/main`, then bring in the plugin folder (it may already be on a working branch — copy or check out just `plugins//` from it rather than merging unrelated history). +2. **CODEOWNERS** — for a new plugin, the README asks contributors to add themselves to [`.github/CODEOWNERS`](../../../../.github/CODEOWNERS) so they review future changes. Ask the user for their GitHub handle (it may differ from `author.name` in `metadata.json`) and add a line in the existing style: `plugins//* @handle`. Skip it if they decline. +3. **Stage explicitly** — `git add plugins/ .github/CODEOWNERS`, never `git add -A`. Then confirm the scope the same way CI does: + + ```bash + git diff --name-only --cached origin/main | grep -oE 'plugins/[^/]+' | sort -u # must print exactly one plugin + git diff --name-only --cached origin/main # nothing outside the plugin folder and CODEOWNERS + ``` + +4. **Commit** with a message in the repository's style — `Add plugin` for a new plugin, or a short imperative summary of the change for an existing one. + +## 6. Write the PR description from the template + +The repository requires the **matching template**, and PRs raised without it may be closed. Read the template file at run time — don't write the body from memory: + +- New plugin → `.github/PULL_REQUEST_TEMPLATE/Add a new plugin.md` +- Change to an existing plugin → `.github/PULL_REQUEST_TEMPLATE/Change to an existing plugin.md` + +Keep every heading and checklist in the template's order and fill each section from what this build actually produced. Write the body to a file (e.g. in your scratch directory) so it can be shown to the user and passed to `gh`. + +| Template section | Fill it from | +| --- | --- | +| Plugin overview / Summary | `metadata.json` (`displayName`, `description`), the Phase 2 plan, and the auth mechanism from `ui.json` | +| Screenshots | Leave the headings with a note for the user to drag images in (step 4) | +| Test plan / Testing | What was **actually** run against a live, authenticated deployment: the Checkpoint A auth probe, the Checkpoint B import (object types and counts), each data stream's PASS report from Phases 5–6, and the default dashboards rendering. The template says "Validation is not testing" — don't offer `squaredup validate` as the test plan | +| Known limitations | The finalised **Known limitations** section of `docs/README.md` (Phase 9) | +| Type of change / Breaking changes / Documentation | The real diff. A removed or renamed data stream, or significantly changed UI parameters, is breaking and needs a new major version folder — see [REVIEW.md](../../../../REVIEW.md#versioning) | +| Checklist | Tick only items you have verified (single plugin, logo, dashboards, README, naming checked against REVIEW.md, no secrets — step 3). **Leave "I agree to the Code of Conduct" unticked unless the user tells you they agree** — it's their agreement, not yours | + +Take the HTML comment prompts out once a section is filled. Don't add sections the template doesn't have, and don't put tenant details, internal links or anything step 3 removed back into the description. + +**Title**: `Add plugin` for a new plugin, matching existing PRs; a short imperative summary for a change. + +## 7. Confirm, push and open the PR + +Before anything leaves the machine, show the user: + +- the list of files that will be pushed (`git diff --name-only origin/main...HEAD`), +- the PR title and the full description, +- where it will be pushed (`squaredup/plugins` or their fork), + +and ask with `AskUserQuestion`: `Open the pull request`, `Open it as a draft`, or `Make changes first`. Only on approval: + +```bash +git push -u plugins/ +gh pr create --repo squaredup/plugins --base main \ + --head :plugins/ \ + --title "" --body-file <body-file> [--draft] +``` + +`<owner>` is `squaredup` when pushing directly, or the user's GitHub login when pushing to their fork. + +## 8. Hand over + +Give the user the PR URL, then tell them: + +- **Add screenshots now** if they chose to — edit the PR description on GitHub and drag the vetted images under the screenshot headings. +- **Tick the Code of Conduct box** themselves if it's still unticked. +- **What CI will do** — `pr-run.yaml` checks the PR touches one plugin and the version is valid, runs `squaredup validate`, and deploys the plugin to a shared organisation; results appear as a comment on the PR. A community moderator then reviews it against [REVIEW.md](../../../../REVIEW.md). +- Review feedback is addressed by pushing to the same branch. A new plugin stays at `<major>.0.0` through every review round; a change needs only one version bump for the whole PR. + +Don't push further commits, respond to reviews or close the PR unless the user asks. diff --git a/AGENTS.md b/AGENTS.md index 7738f9dc..34fbd63c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ Three skills live in `.claude/skills/`. They carry the actual procedures — rea | Skill | Use it when | | --- | --- | -| `build-plugin` | Building a new plugin, or adding streams to an existing one. Nine phases from API exploration to a deployed, tested plugin with dashboards. | +| `build-plugin` | Building a new plugin, or adding streams to an existing one. Nine phases from API exploration to a deployed, tested plugin with dashboards, then an optional pull request to contribute it here. | | `deploy-plugin` | Validating or deploying a plugin, or working out the right version bump. | | `convert-dashboard` | Turning an exported platform dashboard JSON into plugin default content. | From 45729053f1a530e8eb7a2ba5fff3f1a55c61b0e2 Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Fri, 9 Oct 2026 21:09:05 +0000 Subject: [PATCH 02/11] Ask for Code of Conduct agreement, support no gh CLI, explain next steps Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UmF4xDehy5QsrZb4ibqwyU --- .claude/skills/build-plugin/SKILL.md | 5 +- .../build-plugin/references/contribute.md | 53 ++++++++++++++++--- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/.claude/skills/build-plugin/SKILL.md b/.claude/skills/build-plugin/SKILL.md index 1e785b01..67763c8e 100644 --- a/.claude/skills/build-plugin/SKILL.md +++ b/.claude/skills/build-plugin/SKILL.md @@ -332,9 +332,10 @@ Once the final deploy has landed, **ask the user** with `AskUserQuestion` whethe On a yes, follow [contribute.md](references/contribute.md) step by step. In outline: -1. Check the preconditions — `gh` logged in, a clone of `squaredup/plugins`, new plugin vs change, push access vs fork. +1. Check the preconditions — the GitHub CLI (or a git-and-browser fallback without it), a clone of `squaredup/plugins`, new plugin vs change, push access vs fork. 2. Run the **sensitivity review**: secrets, organisation-specific hosts and IDs, people and company names, and internal tools — in the files _and_ the branch's history — and resolve every finding with the user. 3. Encourage screenshots, with a checklist for keeping tenant data out of them. 4. Branch from `main`, stage only the plugin folder (plus an optional `CODEOWNERS` line), and confirm the PR touches a single plugin. -5. Fill in the matching PR template from what this build actually produced. +5. Fill in the matching PR template from what this build actually produced, and ask the user to agree to the Code of Conduct — tick that box only on an explicit yes. 6. Show the user the files, title and description, and push and open the PR only once they approve. +7. Tell the user what happens next: automated checks, CodeRabbit review, SquaredUp team review, then merge and release to all SquaredUp users. diff --git a/.claude/skills/build-plugin/references/contribute.md b/.claude/skills/build-plugin/references/contribute.md index c4265db3..fd1be5b3 100644 --- a/.claude/skills/build-plugin/references/contribute.md +++ b/.claude/skills/build-plugin/references/contribute.md @@ -25,13 +25,29 @@ A plugin built for an internal-only system (a home-grown API, a single company's Confirm each before touching git. Stop and tell the user what's missing if any fails. 1. **Phase 9 is done** — the final `squaredup validate --json` passes and the final deploy succeeded. CI runs the same validation and blocks the PR on the same errors. -2. **GitHub CLI** — `gh auth status` succeeds. If `gh` isn't installed or logged in, ask the user to install it and run `! gh auth login`; don't try to work around it with tokens. +2. **GitHub CLI** — run `gh auth status`. If it succeeds, continue. If `gh` is installed but not logged in, ask the user to run `! gh auth login`. If `gh` isn't installed, ask with `AskUserQuestion`: + - `Install the GitHub CLI (recommended)` → point them to [cli.github.com](https://cli.github.com) (`winget install --id GitHub.cli` on Windows, `brew install gh` on macOS, or their Linux package manager), then `! gh auth login`, and re-run `gh auth status`. + - `Continue without it` → follow the [without the GitHub CLI](#without-the-github-cli) path wherever a step below uses `gh`. It needs `git` able to push to GitHub, and the user finishes the PR in their browser. + + Never ask for, paste or store a GitHub token to work around a missing login. 3. **A clone of `squaredup/plugins`** — run `git remote -v` in the working directory. If it isn't a clone of `squaredup/plugins` (the skill may have been installed with `npx skills add` into an unrelated folder), fork-and-clone into a fresh directory (`gh repo fork squaredup/plugins --clone`) and copy the finished `plugins/<PluginName>/v<N>/` folder into it. 4. **New plugin or change?** — `git fetch origin main`, then `git ls-tree -d origin/main plugins/<PluginName>`. Absent on `main` → **new plugin**; present → **change to an existing plugin**. This decides the template (step 6) and the version rule: - New plugin: `metadata.json` stays at `<major>.0.0` matching its `v<major>` folder. - Change: `version` must be higher than on `main` — invoke `deploy-plugin` for the bump if Phase 9 didn't. 5. **Push access** — `gh repo view squaredup/plugins --json viewerPermission`. `WRITE`, `MAINTAIN` or `ADMIN` → push a branch to `squaredup/plugins` directly. Anything else → push to the user's fork (`gh repo fork squaredup/plugins --remote`, which adds it as a remote) and open the PR from there. +### Without the GitHub CLI + +Each `gh` step has a git-and-browser equivalent: + +| Step | Instead of `gh` | +| --- | --- | +| Clone (precondition 3) | The user forks at [github.com/squaredup/plugins/fork](https://github.com/squaredup/plugins/fork) (or skips forking if they have write access), then `git clone` that repository | +| Push access (precondition 5) | Push to `origin` in step 7. A `403` or `Permission denied` means no write access: ask the user to fork in the browser, then `git remote add fork https://github.com/<login>/plugins.git` and push there instead | +| Open the PR (step 7) | Give the user a link to the compare page instead of running `gh pr create` — see step 7 | + +If `git push` fails on authentication (not permissions), stop: the user needs working git credentials for GitHub, and installing `gh` and running `gh auth login` is the simplest way to get them. + ## 3. Sensitivity review The plugin was built and tested against the user's own tenant and their own account with the vendor, so it is easy for their organisation's details to leak into the files. The repository's PR template asks the author to confirm **"No secrets or credentials included"** — this step is how that box is earned. Do it every time, in full, even for a small change. @@ -111,10 +127,19 @@ Keep every heading and checklist in the template's order and fill each section f | Test plan / Testing | What was **actually** run against a live, authenticated deployment: the Checkpoint A auth probe, the Checkpoint B import (object types and counts), each data stream's PASS report from Phases 5–6, and the default dashboards rendering. The template says "Validation is not testing" — don't offer `squaredup validate` as the test plan | | Known limitations | The finalised **Known limitations** section of `docs/README.md` (Phase 9) | | Type of change / Breaking changes / Documentation | The real diff. A removed or renamed data stream, or significantly changed UI parameters, is breaking and needs a new major version folder — see [REVIEW.md](../../../../REVIEW.md#versioning) | -| Checklist | Tick only items you have verified (single plugin, logo, dashboards, README, naming checked against REVIEW.md, no secrets — step 3). **Leave "I agree to the Code of Conduct" unticked unless the user tells you they agree** — it's their agreement, not yours | +| Checklist | Tick only items you have verified (single plugin, logo, dashboards, README, naming checked against REVIEW.md, no secrets — step 3). The Code of Conduct item is ticked only as described below | Take the HTML comment prompts out once a section is filled. Don't add sections the template doesn't have, and don't put tenant details, internal links or anything step 3 removed back into the description. +### Code of Conduct + +The checklist's "I agree to the Code of Conduct" is the user's agreement, so **ask them for it explicitly** — a yes to contributing, or to any earlier question, is not agreement. Link [`CODE_OF_CONDUCT.md`](../../../../CODE_OF_CONDUCT.md) (offer to summarise it if they want), then ask with `AskUserQuestion`: + +- `I agree to the Code of Conduct` → tick the box +- `I don't agree` → leave it unticked and stop: the README says contributing means agreeing to it, so don't raise the PR. Tell the user the plugin is still deployed in their tenant. + +Tick the box **only** on that explicit `I agree`. Any other answer (including "Other" or a question) is not agreement — answer it and ask again. + **Title**: `Add <displayName> plugin` for a new plugin, matching existing PRs; a short imperative summary for a change. ## 7. Confirm, push and open the PR @@ -136,13 +161,27 @@ gh pr create --repo squaredup/plugins --base main \ `<owner>` is `squaredup` when pushing directly, or the user's GitHub login when pushing to their fork. -## 8. Hand over +**Without the GitHub CLI**, push with `git` as above, then give the user this link to open the PR in their browser (URL-encode the title): + +```text +https://github.com/squaredup/plugins/compare/main...<owner>:plugins/<PluginName>?expand=1&title=<title> +``` + +Print the PR description for them to paste into the description box — don't put it in the URL, which is too long for a full template — and tell them to use the arrow next to **Create pull request** if they wanted a draft. Ask them to share the PR URL once it's created. -Give the user the PR URL, then tell them: +## 8. Hand over — what happens next + +Give the user the PR URL and remind them of anything still theirs to do: - **Add screenshots now** if they chose to — edit the PR description on GitHub and drag the vetted images under the screenshot headings. -- **Tick the Code of Conduct box** themselves if it's still unticked. -- **What CI will do** — `pr-run.yaml` checks the PR touches one plugin and the version is valid, runs `squaredup validate`, and deploys the plugin to a shared organisation; results appear as a comment on the PR. A community moderator then reviews it against [REVIEW.md](../../../../REVIEW.md). -- Review feedback is addressed by pushing to the same branch. A new plugin stays at `<major>.0.0` through every review round; a change needs only one version bump for the whole PR. + +Then show them what happens next, in this order: + +1. **Automated checks** — CI checks the PR changes a single plugin with a valid version, validates it, and deploys it to a shared SquaredUp organisation for testing. The results appear as a comment on the PR. +2. **CodeRabbit review** — CodeRabbit automatically reviews the PR and leaves feedback as comments. Address each one by pushing a fix to the same branch or replying to explain why not — they can ask you for help with either. +3. **SquaredUp team review** — a member of the SquaredUp team reviews the plugin against [REVIEW.md](../../../../REVIEW.md), may ask for changes, and approves it when it's ready. +4. **Merged and released** — once approved, the PR is merged and the plugin is released, making it available to all SquaredUp users. + +Review changes go on the same branch. A new plugin stays at `<major>.0.0` through every review round; a change needs only one version bump for the whole PR. Don't push further commits, respond to reviews or close the PR unless the user asks. From 47c6f36a935943f559c5a97a05038fc04bee3831 Mon Sep 17 00:00:00 2001 From: Dave Clarke <dave.clarke@squaredup.com> Date: Tue, 13 Oct 2026 17:42:46 +0100 Subject: [PATCH 03/11] Fix CODEOWNERS --- .github/CODEOWNERS | 64 ++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4c0b7722..4c0bee05 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,38 +1,36 @@ # Root files -/*.md @squaredup/community-moderators -/LICENSE @squaredup/community-moderators -/.github/* @squaredup/community-moderators +/*.md @squaredup/community-moderators +/LICENSE @squaredup/community-moderators +/.github/ @squaredup/community-moderators # Fallback – if a plugin has no specified author -plugins/* @squaredup/community-moderators +/plugins/ @squaredup/community-moderators # Request review from original author -plugins/AutoTask/* @TimWheeler-SQUP -plugins/DattoRMM/* @TimWheeler-SQUP -plugins/DigiCert/* @shaswot77 -plugins/Huntress/* @Deenk -plugins/IncidentIO/ @noorulkhan-n -plugins/FantasyPremierLeague/* @TimWheeler-SQUP -plugins/GoogleSheets/* @kieranlangton -plugins/Ivanti-ITSM/ @shawn149 -plugins/Zscaler/ @shawn149 -plugins/MetOffice/* @blackgrouse -plugins/Phare/* @vinbab -plugins/Postcoder/* @richbenwell -plugins/RDAP/* @richbenwell -plugins/Redstor/ @TimWheeler-SQUP -plugins/RSS/* @clarkd -plugins/Rootly/* @noorulkhan-n -plugins/SendGrid/* @richbenwell -plugins/Spotify/* @jame2O -plugins/Steam/* @kmichalski0 -plugins/SumoLogic/* @richpeters -plugins/TransportForLondon/* @clarkd -plugins/UniFi/* @adamkinniburgh -plugins/UptimeRobot/* @kieranlangton -plugins/WorldCup2026/* @TimWheeler-SQUP -plugins/Notion/* @clarkd -plugins/Algolia/* @andrewmumblebee -plugins/Vercel/* @andrewmumblebee - - +/plugins/Algolia/ @andrewmumblebee +/plugins/AutoTask/ @TimWheeler-SQUP +/plugins/DattoRMM/ @TimWheeler-SQUP +/plugins/DigiCert/ @shaswot77 +/plugins/FantasyPremierLeague/ @TimWheeler-SQUP +/plugins/GoogleSheets/ @kieranlangton +/plugins/Huntress/ @Deenk +/plugins/IncidentIO/ @noorulkhan-n +/plugins/Ivanti-ITSM/ @shawn149 +/plugins/MetOffice/ @blackgrouse +/plugins/Notion/ @clarkd +/plugins/Phare/ @vinbab +/plugins/Postcoder/ @richbenwell +/plugins/RDAP/ @richbenwell +/plugins/Redstor/ @TimWheeler-SQUP +/plugins/Rootly/ @noorulkhan-n +/plugins/RSS/ @clarkd +/plugins/SendGrid/ @richbenwell +/plugins/Spotify/ @jame2O +/plugins/Steam/ @kmichalski0 +/plugins/SumoLogic/ @richpeters +/plugins/TransportForLondon/ @clarkd +/plugins/UniFi/ @adamkinniburgh +/plugins/UptimeRobot/ @kieranlangton +/plugins/Vercel/ @andrewmumblebee +/plugins/WorldCup2026/ @TimWheeler-SQUP +/plugins/Zscaler/ @shawn149 From 8fc3a1b0a6afcef5c7b583edbb3c6946846a493d Mon Sep 17 00:00:00 2001 From: Dave Clarke <dave.clarke@squaredup.com> Date: Tue, 13 Oct 2026 17:42:54 +0100 Subject: [PATCH 04/11] Remove cspell --- AGENTS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 34fbd63c..837a6a95 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,7 +61,6 @@ What each file is, in rough order of how universal it is: | `icon.svg` / `icon.png` | The plugin logo. | | `configValidation.json` | The check run when a user saves their configuration, backed by a data stream. | | `custom_types.json` | Display names and icons for the object types the plugin imports. | -| `cspell.json` | Per-plugin spellcheck word list, for product names the root list doesn't cover. | | `correlationRules/`, `preRequest.js`, `screenshots/` | Rare extras, used by one or two plugins each. | ## Tooling From 0e12890e567eaa28f3e76adba4019efdcf381986 Mon Sep 17 00:00:00 2001 From: Dave Clarke <dave.clarke@squaredup.com> Date: Tue, 13 Oct 2026 17:43:07 +0100 Subject: [PATCH 05/11] Tidy up auto contribute flow --- .../build-plugin/references/contribute.md | 39 ++++--------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/.claude/skills/build-plugin/references/contribute.md b/.claude/skills/build-plugin/references/contribute.md index fd1be5b3..931553e4 100644 --- a/.claude/skills/build-plugin/references/contribute.md +++ b/.claude/skills/build-plugin/references/contribute.md @@ -83,25 +83,10 @@ Show every finding to the user — grouped by file, with your suggested replacem After fixing, **re-run `squaredup validate --json`** and redeploy with `deploy-plugin` if anything other than docs changed, so the PR ships what was tested. Re-run the sweep until it comes back clean, then tell the user plainly that it did. -## 4. Screenshots - -The *Add a new plugin* template asks for screenshots of the plugin's configuration and its default dashboards. They make review much faster, so **encourage the user to add them** — but they're also the easiest place to leak the very data step 3 just removed, because they show real tenant data. - -Ask the user whether they'd like to include screenshots and, if so, give them this checklist before they take or share any: - -- Use the default dashboards on a data source whose objects are safe to show, or crop/blur object names, hostnames, IPs, account IDs and people's names. -- Crop out the browser address bar, the SquaredUp organisation name and workspace names, and the user's avatar or email in the navigation. -- On the configuration screen, make sure credential fields are empty or masked — never a visible API key, even a revoked one. -- Check every tile, including table rows and legends, not just titles. - -If the user gives you the image files, **view each one** with `Read` and report anything sensitive you can see before they're used — don't assume they've been cropped. - -Screenshots go in the **PR description**, not the repository: `gh` can't upload images, so the user drags them into the PR's description in the GitHub web editor after it is created. Leave the template's screenshot headings in place with a short note so they know where they belong. Only add a `screenshots/` folder to the plugin if the user specifically wants images in-product. - -## 5. Branch and commit +## 4. Branch and commit 1. **Branch from current `main`**: `git fetch origin main && git switch -c plugins/<PluginName> origin/main`, then bring in the plugin folder (it may already be on a working branch — copy or check out just `plugins/<PluginName>/` from it rather than merging unrelated history). -2. **CODEOWNERS** — for a new plugin, the README asks contributors to add themselves to [`.github/CODEOWNERS`](../../../../.github/CODEOWNERS) so they review future changes. Ask the user for their GitHub handle (it may differ from `author.name` in `metadata.json`) and add a line in the existing style: `plugins/<PluginName>/* @handle`. Skip it if they decline. +2. **CODEOWNERS** — for a new plugin, the README asks contributors to add themselves to [`.github/CODEOWNERS`](../../../../.github/CODEOWNERS) so they review future changes. Ask the user for their GitHub handle (it may differ from `author.name` in `metadata.json`) and add a line in the existing style: `plugins/<PluginName>/ @handle`. Skip it if they decline. 3. **Stage explicitly** — `git add plugins/<PluginName> .github/CODEOWNERS`, never `git add -A`. Then confirm the scope the same way CI does: ```bash @@ -111,7 +96,7 @@ Screenshots go in the **PR description**, not the repository: `gh` can't upload 4. **Commit** with a message in the repository's style — `Add <displayName> plugin` for a new plugin, or a short imperative summary of the change for an existing one. -## 6. Write the PR description from the template +## 5. Write the PR description from the template The repository requires the **matching template**, and PRs raised without it may be closed. Read the template file at run time — don't write the body from memory: @@ -142,15 +127,9 @@ Tick the box **only** on that explicit `I agree`. Any other answer (including "O **Title**: `Add <displayName> plugin` for a new plugin, matching existing PRs; a short imperative summary for a change. -## 7. Confirm, push and open the PR - -Before anything leaves the machine, show the user: - -- the list of files that will be pushed (`git diff --name-only origin/main...HEAD`), -- the PR title and the full description, -- where it will be pushed (`squaredup/plugins` or their fork), +## 6. Confirm, push and open the PR -and ask with `AskUserQuestion`: `Open the pull request`, `Open it as a draft`, or `Make changes first`. Only on approval: +Before anything leaves the machine, ask with `AskUserQuestion`: `Open the pull request`, `Open it as a draft`, or `Make changes first`. Only on approval: ```bash git push -u <remote> plugins/<PluginName> @@ -169,13 +148,9 @@ https://github.com/squaredup/plugins/compare/main...<owner>:plugins/<PluginName> Print the PR description for them to paste into the description box — don't put it in the URL, which is too long for a full template — and tell them to use the arrow next to **Create pull request** if they wanted a draft. Ask them to share the PR URL once it's created. -## 8. Hand over — what happens next - -Give the user the PR URL and remind them of anything still theirs to do: - -- **Add screenshots now** if they chose to — edit the PR description on GitHub and drag the vetted images under the screenshot headings. +## 7. Hand over — what happens next -Then show them what happens next, in this order: +Give the user the PR URL and then show them what happens next, in this order: 1. **Automated checks** — CI checks the PR changes a single plugin with a valid version, validates it, and deploys it to a shared SquaredUp organisation for testing. The results appear as a comment on the PR. 2. **CodeRabbit review** — CodeRabbit automatically reviews the PR and leaves feedback as comments. Address each one by pushing a fix to the same branch or replying to explain why not — they can ask you for help with either. From 5304e8504e2fe12bc2a79390473cb2c6bfd65eed Mon Sep 17 00:00:00 2001 From: Claude <noreply@anthropic.com> Date: Tue, 13 Oct 2026 16:44:04 +0000 Subject: [PATCH 06/11] Fix step references after removing the screenshots step Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UmF4xDehy5QsrZb4ibqwyU --- .claude/skills/build-plugin/SKILL.md | 9 ++++----- .claude/skills/build-plugin/references/contribute.md | 10 +++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.claude/skills/build-plugin/SKILL.md b/.claude/skills/build-plugin/SKILL.md index 67763c8e..346deb29 100644 --- a/.claude/skills/build-plugin/SKILL.md +++ b/.claude/skills/build-plugin/SKILL.md @@ -334,8 +334,7 @@ On a yes, follow [contribute.md](references/contribute.md) step by step. In outl 1. Check the preconditions — the GitHub CLI (or a git-and-browser fallback without it), a clone of `squaredup/plugins`, new plugin vs change, push access vs fork. 2. Run the **sensitivity review**: secrets, organisation-specific hosts and IDs, people and company names, and internal tools — in the files _and_ the branch's history — and resolve every finding with the user. -3. Encourage screenshots, with a checklist for keeping tenant data out of them. -4. Branch from `main`, stage only the plugin folder (plus an optional `CODEOWNERS` line), and confirm the PR touches a single plugin. -5. Fill in the matching PR template from what this build actually produced, and ask the user to agree to the Code of Conduct — tick that box only on an explicit yes. -6. Show the user the files, title and description, and push and open the PR only once they approve. -7. Tell the user what happens next: automated checks, CodeRabbit review, SquaredUp team review, then merge and release to all SquaredUp users. +3. Branch from `main`, stage only the plugin folder (plus an optional `CODEOWNERS` line), and confirm the PR touches a single plugin. +4. Fill in the matching PR template from what this build actually produced, and ask the user to agree to the Code of Conduct — tick that box only on an explicit yes. +5. Push and open the PR only once the user approves. +6. Tell the user what happens next: automated checks, CodeRabbit review, SquaredUp team review, then merge and release to all SquaredUp users. diff --git a/.claude/skills/build-plugin/references/contribute.md b/.claude/skills/build-plugin/references/contribute.md index 931553e4..3234f782 100644 --- a/.claude/skills/build-plugin/references/contribute.md +++ b/.claude/skills/build-plugin/references/contribute.md @@ -31,7 +31,7 @@ Confirm each before touching git. Stop and tell the user what's missing if any f Never ask for, paste or store a GitHub token to work around a missing login. 3. **A clone of `squaredup/plugins`** — run `git remote -v` in the working directory. If it isn't a clone of `squaredup/plugins` (the skill may have been installed with `npx skills add` into an unrelated folder), fork-and-clone into a fresh directory (`gh repo fork squaredup/plugins --clone`) and copy the finished `plugins/<PluginName>/v<N>/` folder into it. -4. **New plugin or change?** — `git fetch origin main`, then `git ls-tree -d origin/main plugins/<PluginName>`. Absent on `main` → **new plugin**; present → **change to an existing plugin**. This decides the template (step 6) and the version rule: +4. **New plugin or change?** — `git fetch origin main`, then `git ls-tree -d origin/main plugins/<PluginName>`. Absent on `main` → **new plugin**; present → **change to an existing plugin**. This decides the template (step 5) and the version rule: - New plugin: `metadata.json` stays at `<major>.0.0` matching its `v<major>` folder. - Change: `version` must be higher than on `main` — invoke `deploy-plugin` for the bump if Phase 9 didn't. 5. **Push access** — `gh repo view squaredup/plugins --json viewerPermission`. `WRITE`, `MAINTAIN` or `ADMIN` → push a branch to `squaredup/plugins` directly. Anything else → push to the user's fork (`gh repo fork squaredup/plugins --remote`, which adds it as a remote) and open the PR from there. @@ -43,8 +43,8 @@ Each `gh` step has a git-and-browser equivalent: | Step | Instead of `gh` | | --- | --- | | Clone (precondition 3) | The user forks at [github.com/squaredup/plugins/fork](https://github.com/squaredup/plugins/fork) (or skips forking if they have write access), then `git clone` that repository | -| Push access (precondition 5) | Push to `origin` in step 7. A `403` or `Permission denied` means no write access: ask the user to fork in the browser, then `git remote add fork https://github.com/<login>/plugins.git` and push there instead | -| Open the PR (step 7) | Give the user a link to the compare page instead of running `gh pr create` — see step 7 | +| Push access (precondition 5) | Push to `origin` in step 6. A `403` or `Permission denied` means no write access: ask the user to fork in the browser, then `git remote add fork https://github.com/<login>/plugins.git` and push there instead | +| Open the PR (step 6) | Give the user a link to the compare page instead of running `gh pr create` — see step 6 | If `git push` fails on authentication (not permissions), stop: the user needs working git credentials for GitHub, and installing `gh` and running `gh auth login` is the simplest way to get them. @@ -74,7 +74,7 @@ The plugin was built and tested against the user's own tenant and their own acco A hit is a prompt to look, not a verdict — `ui.json` field names like `apiKey` and `{{apiKey}}` header templates are expected. Only a **value** is a leak. 3. **Read every file.** Patterns miss names, internal tools and environment-specific content. Spawn **one general-purpose sub-agent**, told not to edit anything, to read every file in the plugin folder in full (dashboards are large — don't read them inline; and don't use an `Explore` agent, which skims excerpts rather than reading whole files) against the table above, including the known-values list from step 1, and return only findings as `file:line — category — the offending text — suggested generic replacement`. Images (`icon.*`, `screenshots/`) are part of this: it should view them, not skip them. -4. **Check history, not just the final files.** Anything committed on the branch is published with it, even if a later commit removed it. If the plugin has commits of its own, run `git log -p origin/main..HEAD -- plugins/<PluginName>` through the same sweep. If a secret appears anywhere in history, don't push that branch — build a fresh branch from `origin/main` with only the clean files (step 5), and tell the user to **rotate the credential** regardless, since it may already exist elsewhere. +4. **Check history, not just the final files.** Anything committed on the branch is published with it, even if a later commit removed it. If the plugin has commits of its own, run `git log -p origin/main..HEAD -- plugins/<PluginName>` through the same sweep. If a secret appears anywhere in history, don't push that branch — build a fresh branch from `origin/main` with only the clean files (step 4), and tell the user to **rotate the credential** regardless, since it may already exist elsewhere. 5. **Check what else would ship.** The PR must contain the plugin folder plus, optionally, a `.github/CODEOWNERS` line. Personal configuration (`.claude/settings.json`, `*.local.json`, `.env`, scratch test output) must not be staged. ### Resolve with the user @@ -108,7 +108,7 @@ Keep every heading and checklist in the template's order and fill each section f | Template section | Fill it from | | --- | --- | | Plugin overview / Summary | `metadata.json` (`displayName`, `description`), the Phase 2 plan, and the auth mechanism from `ui.json` | -| Screenshots | Leave the headings with a note for the user to drag images in (step 4) | +| Screenshots | Leave the headings in place with a note for the user to drag images in on GitHub — `gh` can't upload them | | Test plan / Testing | What was **actually** run against a live, authenticated deployment: the Checkpoint A auth probe, the Checkpoint B import (object types and counts), each data stream's PASS report from Phases 5–6, and the default dashboards rendering. The template says "Validation is not testing" — don't offer `squaredup validate` as the test plan | | Known limitations | The finalised **Known limitations** section of `docs/README.md` (Phase 9) | | Type of change / Breaking changes / Documentation | The real diff. A removed or renamed data stream, or significantly changed UI parameters, is breaking and needs a new major version folder — see [REVIEW.md](../../../../REVIEW.md#versioning) | From 24f45b5463e07a46a6c8b0177b145429b0434e7d Mon Sep 17 00:00:00 2001 From: Dave Clarke <dave.clarke@squaredup.com> Date: Tue, 13 Oct 2026 22:02:05 +0100 Subject: [PATCH 07/11] Use the squaredup/plugins remote as the contribute baseline In a fork, origin is the contributor's fork and can be behind, so fetch and branch from the remote that points at squaredup/plugins. Drop the repository argument from gh repo fork --remote, which gh rejects, and check the staged CODEOWNERS diff for unrelated edits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- .../build-plugin/references/contribute.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.claude/skills/build-plugin/references/contribute.md b/.claude/skills/build-plugin/references/contribute.md index 3234f782..05f3838d 100644 --- a/.claude/skills/build-plugin/references/contribute.md +++ b/.claude/skills/build-plugin/references/contribute.md @@ -31,10 +31,12 @@ Confirm each before touching git. Stop and tell the user what's missing if any f Never ask for, paste or store a GitHub token to work around a missing login. 3. **A clone of `squaredup/plugins`** — run `git remote -v` in the working directory. If it isn't a clone of `squaredup/plugins` (the skill may have been installed with `npx skills add` into an unrelated folder), fork-and-clone into a fresh directory (`gh repo fork squaredup/plugins --clone`) and copy the finished `plugins/<PluginName>/v<N>/` folder into it. -4. **New plugin or change?** — `git fetch origin main`, then `git ls-tree -d origin/main plugins/<PluginName>`. Absent on `main` → **new plugin**; present → **change to an existing plugin**. This decides the template (step 5) and the version rule: + + From here on, **`<upstream>`** means the remote whose URL is `squaredup/plugins` itself — never a fork, which can be behind. In a plain clone it is `origin`; after `gh repo fork` it is `upstream` (the fork becomes `origin`). If no remote points at `squaredup/plugins`, add one: `git remote add upstream https://github.com/squaredup/plugins.git`. Re-check `git remote -v` whenever a step below changes the remotes. +4. **New plugin or change?** — `git fetch <upstream> main`, then `git ls-tree -d <upstream>/main plugins/<PluginName>`. Absent on `main` → **new plugin**; present → **change to an existing plugin**. This decides the template (step 5) and the version rule: - New plugin: `metadata.json` stays at `<major>.0.0` matching its `v<major>` folder. - Change: `version` must be higher than on `main` — invoke `deploy-plugin` for the bump if Phase 9 didn't. -5. **Push access** — `gh repo view squaredup/plugins --json viewerPermission`. `WRITE`, `MAINTAIN` or `ADMIN` → push a branch to `squaredup/plugins` directly. Anything else → push to the user's fork (`gh repo fork squaredup/plugins --remote`, which adds it as a remote) and open the PR from there. +5. **Push access** — `gh repo view squaredup/plugins --json viewerPermission`. `WRITE`, `MAINTAIN` or `ADMIN` → push a branch to `<upstream>` directly. Anything else → push to the user's fork and open the PR from there. If the clone has no fork remote yet, run `gh repo fork --remote` (no repository argument — `gh` rejects `--remote` alongside one) from inside the clone: it creates the fork, renames the existing `origin` to `upstream` and adds the fork as `origin`. ### Without the GitHub CLI @@ -42,7 +44,7 @@ Each `gh` step has a git-and-browser equivalent: | Step | Instead of `gh` | | --- | --- | -| Clone (precondition 3) | The user forks at [github.com/squaredup/plugins/fork](https://github.com/squaredup/plugins/fork) (or skips forking if they have write access), then `git clone` that repository | +| Clone (precondition 3) | The user forks at [github.com/squaredup/plugins/fork](https://github.com/squaredup/plugins/fork) (or skips forking if they have write access), then `git clone` that repository. A clone of the fork has no remote for `squaredup/plugins` — add `upstream` as precondition 3 describes | | Push access (precondition 5) | Push to `origin` in step 6. A `403` or `Permission denied` means no write access: ask the user to fork in the browser, then `git remote add fork https://github.com/<login>/plugins.git` and push there instead | | Open the PR (step 6) | Give the user a link to the compare page instead of running `gh pr create` — see step 6 | @@ -74,7 +76,7 @@ The plugin was built and tested against the user's own tenant and their own acco A hit is a prompt to look, not a verdict — `ui.json` field names like `apiKey` and `{{apiKey}}` header templates are expected. Only a **value** is a leak. 3. **Read every file.** Patterns miss names, internal tools and environment-specific content. Spawn **one general-purpose sub-agent**, told not to edit anything, to read every file in the plugin folder in full (dashboards are large — don't read them inline; and don't use an `Explore` agent, which skims excerpts rather than reading whole files) against the table above, including the known-values list from step 1, and return only findings as `file:line — category — the offending text — suggested generic replacement`. Images (`icon.*`, `screenshots/`) are part of this: it should view them, not skip them. -4. **Check history, not just the final files.** Anything committed on the branch is published with it, even if a later commit removed it. If the plugin has commits of its own, run `git log -p origin/main..HEAD -- plugins/<PluginName>` through the same sweep. If a secret appears anywhere in history, don't push that branch — build a fresh branch from `origin/main` with only the clean files (step 4), and tell the user to **rotate the credential** regardless, since it may already exist elsewhere. +4. **Check history, not just the final files.** Anything committed on the branch is published with it, even if a later commit removed it. If the plugin has commits of its own, run `git log -p <upstream>/main..HEAD -- plugins/<PluginName>` through the same sweep. If a secret appears anywhere in history, don't push that branch — build a fresh branch from `<upstream>/main` with only the clean files (step 4), and tell the user to **rotate the credential** regardless, since it may already exist elsewhere. 5. **Check what else would ship.** The PR must contain the plugin folder plus, optionally, a `.github/CODEOWNERS` line. Personal configuration (`.claude/settings.json`, `*.local.json`, `.env`, scratch test output) must not be staged. ### Resolve with the user @@ -85,15 +87,18 @@ After fixing, **re-run `squaredup validate --json`** and redeploy with `deploy-p ## 4. Branch and commit -1. **Branch from current `main`**: `git fetch origin main && git switch -c plugins/<PluginName> origin/main`, then bring in the plugin folder (it may already be on a working branch — copy or check out just `plugins/<PluginName>/` from it rather than merging unrelated history). +1. **Branch from current `main`**: `git fetch <upstream> main && git switch -c plugins/<PluginName> <upstream>/main`, then bring in the plugin folder (it may already be on a working branch — copy or check out just `plugins/<PluginName>/` from it rather than merging unrelated history). 2. **CODEOWNERS** — for a new plugin, the README asks contributors to add themselves to [`.github/CODEOWNERS`](../../../../.github/CODEOWNERS) so they review future changes. Ask the user for their GitHub handle (it may differ from `author.name` in `metadata.json`) and add a line in the existing style: `plugins/<PluginName>/ @handle`. Skip it if they decline. 3. **Stage explicitly** — `git add plugins/<PluginName> .github/CODEOWNERS`, never `git add -A`. Then confirm the scope the same way CI does: ```bash - git diff --name-only --cached origin/main | grep -oE 'plugins/[^/]+' | sort -u # must print exactly one plugin - git diff --name-only --cached origin/main # nothing outside the plugin folder and CODEOWNERS + git diff --name-only --cached <upstream>/main | grep -oE 'plugins/[^/]+' | sort -u # must print exactly one plugin + git diff --name-only --cached <upstream>/main # nothing outside the plugin folder and CODEOWNERS + git diff --cached <upstream>/main -- .github/CODEOWNERS # only the one owner line added ``` + If the `CODEOWNERS` diff shows anything else, the working copy had unrelated edits. Unstage it (`git restore --staged .github/CODEOWNERS`), show the user those edits, and with their agreement set them aside (`git stash push -- .github/CODEOWNERS`) before re-adding just the owner line and staging again. + 4. **Commit** with a message in the repository's style — `Add <displayName> plugin` for a new plugin, or a short imperative summary of the change for an existing one. ## 5. Write the PR description from the template From c4b9cc5ab517b0463cd36ab8f6dd6168650b5f48 Mon Sep 17 00:00:00 2001 From: Dave Clarke <dave.clarke@squaredup.com> Date: Tue, 13 Oct 2026 22:10:53 +0100 Subject: [PATCH 08/11] Make the contribute branch step survive local state Copy the plugin folder aside and stash its uncommitted changes before switching, so git can't refuse the switch, and pick an unused branch name when plugins/<PluginName> already exists locally. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- .claude/skills/build-plugin/references/contribute.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.claude/skills/build-plugin/references/contribute.md b/.claude/skills/build-plugin/references/contribute.md index 05f3838d..6801ff8f 100644 --- a/.claude/skills/build-plugin/references/contribute.md +++ b/.claude/skills/build-plugin/references/contribute.md @@ -87,7 +87,11 @@ After fixing, **re-run `squaredup validate --json`** and redeploy with `deploy-p ## 4. Branch and commit -1. **Branch from current `main`**: `git fetch <upstream> main && git switch -c plugins/<PluginName> <upstream>/main`, then bring in the plugin folder (it may already be on a working branch — copy or check out just `plugins/<PluginName>/` from it rather than merging unrelated history). +1. **Branch from current `main`** — the plugin may sit on a working branch, with uncommitted changes, so bring it across by copying only the folder, never by merging unrelated history: + 1. **Keep a copy** of the whole `plugins/<PluginName>/` folder in your scratch directory. + 2. **Clear the way** — if `git status --porcelain -- plugins/<PluginName>` prints anything, `git stash push --include-untracked -- plugins/<PluginName>` so the switch can't be refused over those paths. Tell the user the stash holds their working state. + 3. **Pick the branch name** — `plugins/<PluginName>`, unless `git rev-parse --verify --quiet refs/heads/plugins/<PluginName>` shows it already exists; then add a suffix such as `-contribution`. `<branch>` below means the name chosen. + 4. **Branch and restore** — `git fetch <upstream> main && git switch -c <branch> <upstream>/main`, then replace the folder with the copy: `rm -rf plugins/<PluginName> && cp -R <scratch>/<PluginName> plugins/`. 2. **CODEOWNERS** — for a new plugin, the README asks contributors to add themselves to [`.github/CODEOWNERS`](../../../../.github/CODEOWNERS) so they review future changes. Ask the user for their GitHub handle (it may differ from `author.name` in `metadata.json`) and add a line in the existing style: `plugins/<PluginName>/ @handle`. Skip it if they decline. 3. **Stage explicitly** — `git add plugins/<PluginName> .github/CODEOWNERS`, never `git add -A`. Then confirm the scope the same way CI does: @@ -137,9 +141,9 @@ Tick the box **only** on that explicit `I agree`. Any other answer (including "O Before anything leaves the machine, ask with `AskUserQuestion`: `Open the pull request`, `Open it as a draft`, or `Make changes first`. Only on approval: ```bash -git push -u <remote> plugins/<PluginName> +git push -u <remote> <branch> gh pr create --repo squaredup/plugins --base main \ - --head <owner>:plugins/<PluginName> \ + --head <owner>:<branch> \ --title "<title>" --body-file <body-file> [--draft] ``` @@ -148,7 +152,7 @@ gh pr create --repo squaredup/plugins --base main \ **Without the GitHub CLI**, push with `git` as above, then give the user this link to open the PR in their browser (URL-encode the title): ```text -https://github.com/squaredup/plugins/compare/main...<owner>:plugins/<PluginName>?expand=1&title=<title> +https://github.com/squaredup/plugins/compare/main...<owner>:<branch>?expand=1&title=<title> ``` Print the PR description for them to paste into the description box — don't put it in the URL, which is too long for a full template — and tell them to use the arrow next to **Create pull request** if they wanted a draft. Ask them to share the PR URL once it's created. From 87990d9a3013ca9650b4bb4a39cb075b3be492c2 Mon Sep 17 00:00:00 2001 From: Dave Clarke <dave.clarke@squaredup.com> Date: Tue, 13 Oct 2026 22:23:25 +0100 Subject: [PATCH 09/11] Drop the org prefix from gh pr create --head on direct pushes gh doesn't accept an organisation in <owner>:<branch>, so a direct push to squaredup/plugins passes the bare branch name; forks keep the login. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- .claude/skills/build-plugin/references/contribute.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude/skills/build-plugin/references/contribute.md b/.claude/skills/build-plugin/references/contribute.md index 6801ff8f..d8abf970 100644 --- a/.claude/skills/build-plugin/references/contribute.md +++ b/.claude/skills/build-plugin/references/contribute.md @@ -143,13 +143,13 @@ Before anything leaves the machine, ask with `AskUserQuestion`: `Open the pull r ```bash git push -u <remote> <branch> gh pr create --repo squaredup/plugins --base main \ - --head <owner>:<branch> \ + --head <head> \ --title "<title>" --body-file <body-file> [--draft] ``` -`<owner>` is `squaredup` when pushing directly, or the user's GitHub login when pushing to their fork. +`<head>` is just `<branch>` when pushing directly to `squaredup/plugins`, or `<login>:<branch>` when pushing to the user's fork. `gh` doesn't accept an organisation before the colon, so `squaredup:<branch>` fails. -**Without the GitHub CLI**, push with `git` as above, then give the user this link to open the PR in their browser (URL-encode the title): +**Without the GitHub CLI**, push with `git` as above, then give the user this link to open the PR in their browser. `<owner>` is `squaredup` for a direct push, or the user's login for a fork (the browser accepts either). URL-encode the title: ```text https://github.com/squaredup/plugins/compare/main...<owner>:<branch>?expand=1&title=<title> From a22209412f7b7df9f7fd9ae161b50c7c01d9cfbb Mon Sep 17 00:00:00 2001 From: Dave Clarke <dave.clarke@squaredup.com> Date: Tue, 13 Oct 2026 22:38:14 +0100 Subject: [PATCH 10/11] Spell out when to pass --draft to gh pr create The bracketed [--draft] could be copied literally; pass the flag only when the user chose a draft PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- .claude/skills/build-plugin/references/contribute.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.claude/skills/build-plugin/references/contribute.md b/.claude/skills/build-plugin/references/contribute.md index d8abf970..e54e1a2d 100644 --- a/.claude/skills/build-plugin/references/contribute.md +++ b/.claude/skills/build-plugin/references/contribute.md @@ -144,9 +144,11 @@ Before anything leaves the machine, ask with `AskUserQuestion`: `Open the pull r git push -u <remote> <branch> gh pr create --repo squaredup/plugins --base main \ --head <head> \ - --title "<title>" --body-file <body-file> [--draft] + --title "<title>" --body-file <body-file> ``` +Add `--draft` only if the user chose `Open it as a draft`. + `<head>` is just `<branch>` when pushing directly to `squaredup/plugins`, or `<login>:<branch>` when pushing to the user's fork. `gh` doesn't accept an organisation before the colon, so `squaredup:<branch>` fails. **Without the GitHub CLI**, push with `git` as above, then give the user this link to open the PR in their browser. `<owner>` is `squaredup` for a direct push, or the user's login for a fork (the browser accepts either). URL-encode the title: From e2736f99782d37549ce52f3eb8ac38e087d92ec2 Mon Sep 17 00:00:00 2001 From: Dave Clarke <dave.clarke@squaredup.com> Date: Wed, 14 Oct 2026 16:20:20 +0100 Subject: [PATCH 11/11] Trim the contribute step and ask about it once per conversation Revert the skill description so triggering is unchanged, drop the SKILL.md outline that duplicated contribute.md, and replace the template-section table with only the guidance the template can't give. Ask about contributing once, so iterative edit-and-deploy sessions aren't prompted after every deploy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- .claude/skills/build-plugin/SKILL.md | 11 +++-------- .../skills/build-plugin/references/contribute.md | 14 +++++--------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/.claude/skills/build-plugin/SKILL.md b/.claude/skills/build-plugin/SKILL.md index 346deb29..785d8a19 100644 --- a/.claude/skills/build-plugin/SKILL.md +++ b/.claude/skills/build-plugin/SKILL.md @@ -1,6 +1,6 @@ --- name: build-plugin -description: Guides building a SquaredUp low-code plugin for HTTP/REST APIs, from API exploration through deployment, with an optional pull request to contribute it to the community. Use when the user wants to integrate a service with SquaredUp, add a new data source, connect to a third-party tool, "pull data from", or "monitor" any service in SquaredUp. +description: Guides building a SquaredUp low-code plugin for HTTP/REST APIs, from API exploration through deployment. Use when the user wants to integrate a service with SquaredUp, add a new data source, connect to a third-party tool, "pull data from", or "monitor" any service in SquaredUp. metadata: author: SquaredUp version: "1.1.0" @@ -330,11 +330,6 @@ Then invoke the `deploy-plugin` skill for the final validate, version bump, and Once the final deploy has landed, **ask the user** with `AskUserQuestion` whether they'd like to contribute the plugin to the community by raising a pull request against `squaredup/plugins`. Always ask — even in autonomous mode, and even if the user waived the Phase 2 approval gate — and never raise a PR without an explicit yes. If they decline, the build is complete. -On a yes, follow [contribute.md](references/contribute.md) step by step. In outline: +Ask **once per conversation**. If the user is making a series of small changes (adding a stream, tweaking a tile), deploying each one, don't ask again after every deploy — once they've answered, they can ask for this step themselves when they're ready. -1. Check the preconditions — the GitHub CLI (or a git-and-browser fallback without it), a clone of `squaredup/plugins`, new plugin vs change, push access vs fork. -2. Run the **sensitivity review**: secrets, organisation-specific hosts and IDs, people and company names, and internal tools — in the files _and_ the branch's history — and resolve every finding with the user. -3. Branch from `main`, stage only the plugin folder (plus an optional `CODEOWNERS` line), and confirm the PR touches a single plugin. -4. Fill in the matching PR template from what this build actually produced, and ask the user to agree to the Code of Conduct — tick that box only on an explicit yes. -5. Push and open the PR only once the user approves. -6. Tell the user what happens next: automated checks, CodeRabbit review, SquaredUp team review, then merge and release to all SquaredUp users. +On a yes, follow [contribute.md](references/contribute.md). diff --git a/.claude/skills/build-plugin/references/contribute.md b/.claude/skills/build-plugin/references/contribute.md index e54e1a2d..43d0cd66 100644 --- a/.claude/skills/build-plugin/references/contribute.md +++ b/.claude/skills/build-plugin/references/contribute.md @@ -112,16 +112,12 @@ The repository requires the **matching template**, and PRs raised without it may - New plugin → `.github/PULL_REQUEST_TEMPLATE/Add a new plugin.md` - Change to an existing plugin → `.github/PULL_REQUEST_TEMPLATE/Change to an existing plugin.md` -Keep every heading and checklist in the template's order and fill each section from what this build actually produced. Write the body to a file (e.g. in your scratch directory) so it can be shown to the user and passed to `gh`. +Keep every heading and checklist in the template's order and fill each section from what this build actually produced. Write the body to a file (e.g. in your scratch directory) so it can be shown to the user and passed to `gh`. A few things the template can't tell you: -| Template section | Fill it from | -| --- | --- | -| Plugin overview / Summary | `metadata.json` (`displayName`, `description`), the Phase 2 plan, and the auth mechanism from `ui.json` | -| Screenshots | Leave the headings in place with a note for the user to drag images in on GitHub — `gh` can't upload them | -| Test plan / Testing | What was **actually** run against a live, authenticated deployment: the Checkpoint A auth probe, the Checkpoint B import (object types and counts), each data stream's PASS report from Phases 5–6, and the default dashboards rendering. The template says "Validation is not testing" — don't offer `squaredup validate` as the test plan | -| Known limitations | The finalised **Known limitations** section of `docs/README.md` (Phase 9) | -| Type of change / Breaking changes / Documentation | The real diff. A removed or renamed data stream, or significantly changed UI parameters, is breaking and needs a new major version folder — see [REVIEW.md](../../../../REVIEW.md#versioning) | -| Checklist | Tick only items you have verified (single plugin, logo, dashboards, README, naming checked against REVIEW.md, no secrets — step 3). The Code of Conduct item is ticked only as described below | +- **Testing** means what was actually run against the live deployment — the Checkpoint A auth probe, the Checkpoint B import, each stream's PASS report from Phases 5–6, the dashboards rendering. `squaredup validate` is not a test plan. +- **Known limitations** already exist — copy them from the finalised `docs/README.md`. +- **Screenshots** can't be uploaded by `gh` — leave those headings with a note for the user to drag images in on GitHub. +- **Checklist** items are ticked only once verified; "no secrets" is earned by step 3, and the Code of Conduct item only as described below. Take the HTML comment prompts out once a section is filled. Don't add sections the template doesn't have, and don't put tenant details, internal links or anything step 3 removed back into the description.