mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-25 10:42:45 +00:00
Scaffold author overviews for plugin listings (#6226)
* feat(plugins): scaffold author listing overviews * docs(plugins): refine the author overview contract * docs(plugins): require repository overviews for registry listings * docs(plugins): keep listing guidance focused on authors
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { exec, execFile } from "node:child_process";
|
||||
import { mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
||||
import { createRequire } from "node:module";
|
||||
import path from "node:path";
|
||||
@@ -9,6 +9,7 @@ import { scaffoldPluginDirectory } from "./scaffold.js";
|
||||
|
||||
const directories: string[] = [];
|
||||
const execFileAsync = promisify(execFile);
|
||||
const execAsync = promisify(exec);
|
||||
const tscPath = createRequire(import.meta.url).resolve("typescript/bin/tsc");
|
||||
|
||||
async function typecheckPlugin(directory: string): Promise<void> {
|
||||
@@ -28,6 +29,47 @@ afterEach(async () => {
|
||||
});
|
||||
|
||||
describe("plugin scaffold", () => {
|
||||
it("creates an author overview beside the manifest", async () => {
|
||||
const parent = await mkdtemp(path.join(tmpdir(), "paseo-plugin-scaffold-"));
|
||||
directories.push(parent);
|
||||
const directory = path.join(parent, "hello-plugin");
|
||||
await scaffoldPluginDirectory(directory);
|
||||
|
||||
const overview = await readFile(path.join(directory, "OVERVIEW.md"), "utf8");
|
||||
const comments = overview.match(/<!--[\s\S]*?-->/g) ?? [];
|
||||
expect(comments).toHaveLength(1);
|
||||
expect(overview.replace(/<!--[\s\S]*?-->/g, "").trim()).toBe("");
|
||||
expect(overview).toContain("required to list");
|
||||
expect(overview).toContain("README");
|
||||
expect(overview).toContain("choosing a plugin in Paseo");
|
||||
expect(overview).toContain("Length follows complexity");
|
||||
expect(overview).toContain("1. Describe what your plugin is and does");
|
||||
expect(overview).toContain("2. Explain how it works only when it is not obvious");
|
||||
expect(overview).toContain("3. Explain setup when needed");
|
||||
expect(overview).toContain("4. Explain capabilities and settings");
|
||||
expect(overview).toContain("permissions");
|
||||
expect(overview).toContain("known limits");
|
||||
expect(overview).not.toContain("This plugin entry was imported");
|
||||
const packageJson = JSON.parse(await readFile(path.join(directory, "package.json"), "utf8"));
|
||||
expect(packageJson.files).toContain("OVERVIEW.md");
|
||||
expect(await readdir(directory)).toContain("paseo-plugin.json");
|
||||
});
|
||||
|
||||
it("includes the author overview in the packed npm artifact", async () => {
|
||||
const parent = await mkdtemp(path.join(tmpdir(), "paseo-plugin-scaffold-"));
|
||||
directories.push(parent);
|
||||
const directory = path.join(parent, "packed-plugin");
|
||||
await scaffoldPluginDirectory(directory);
|
||||
|
||||
const { stdout } = await execAsync("npm pack --dry-run --ignore-scripts --json", {
|
||||
cwd: directory,
|
||||
});
|
||||
const [artifact] = JSON.parse(stdout) as { files: { path: string }[] }[];
|
||||
expect(artifact.files.map((file) => file.path)).toEqual(
|
||||
expect.arrayContaining(["OVERVIEW.md", "paseo-plugin.json"]),
|
||||
);
|
||||
});
|
||||
|
||||
it(
|
||||
"creates a standalone split-runtime project that typechecks",
|
||||
{ timeout: 15_000 },
|
||||
@@ -60,6 +102,7 @@ describe("plugin scaffold", () => {
|
||||
version: "0.0.0",
|
||||
files: [
|
||||
"paseo-plugin.json",
|
||||
"OVERVIEW.md",
|
||||
"index.client.ts",
|
||||
"index.client.tsx",
|
||||
"index.server.ts",
|
||||
|
||||
@@ -3,6 +3,34 @@ import path from "node:path";
|
||||
import { PluginIdSchema } from "@getpaseo/protocol/messages";
|
||||
import { resolveCliVersion } from "../../version.js";
|
||||
|
||||
const OVERVIEW = `<!--
|
||||
OVERVIEW.md is required to list your plugin in the registry. Keep it beside
|
||||
paseo-plugin.json in your repository at the pinned commit and in your npm package.
|
||||
Replace this comment with useful facts before publishing.
|
||||
|
||||
This overview is for people choosing a plugin in Paseo, on the plugin page under
|
||||
its install command. README assumes a GitHub audience and includes technical
|
||||
details, installation instructions, and badges. Long, AI-generated READMEs leave
|
||||
people reading past that material to understand what the plugin does.
|
||||
|
||||
Write these parts in order, including only what helps someone decide to install:
|
||||
|
||||
1. Describe what your plugin is and does in plain terms first.
|
||||
2. Explain how it works only when it is not obvious.
|
||||
3. Explain setup when needed: settings, accounts, tokens, providers, tools, or
|
||||
other plugins. Include applicable daemon version and operating system
|
||||
requirements. Setup guidance is allowed; installation instructions are not.
|
||||
4. Explain capabilities and settings worth understanding, what each option does,
|
||||
what the plugin reads or sends and where, permissions, and known limits.
|
||||
|
||||
Length follows complexity; a theme needs one paragraph. Choose headings only
|
||||
when they help. Use sentence case and plain factual language, with no em dashes.
|
||||
Omit installation commands, badges, changelog, contributing or license sections,
|
||||
marketing, and unsupported claims. Avoid implementation filler such as empty
|
||||
cleanup functions, theme-token field lists, or lists of absent features.
|
||||
-->
|
||||
`;
|
||||
|
||||
const TSCONFIG = {
|
||||
compilerOptions: {
|
||||
target: "ES2020",
|
||||
@@ -155,6 +183,7 @@ export async function scaffoldPluginDirectory(
|
||||
version: "0.0.0",
|
||||
files: [
|
||||
"paseo-plugin.json",
|
||||
"OVERVIEW.md",
|
||||
"index.client.ts",
|
||||
"index.client.tsx",
|
||||
"index.server.ts",
|
||||
@@ -179,6 +208,7 @@ export async function scaffoldPluginDirectory(
|
||||
"paseo-plugin.json",
|
||||
`${JSON.stringify({ id, requirements: { paseo: `>=${version}` } }, null, 2)}\n`,
|
||||
],
|
||||
["OVERVIEW.md", OVERVIEW],
|
||||
["package.json", `${JSON.stringify(packageJson, null, 2)}\n`],
|
||||
["tsconfig.json", `${JSON.stringify(TSCONFIG, null, 2)}\n`],
|
||||
["index.client.tsx", CLIENT_ENTRY],
|
||||
|
||||
@@ -56,6 +56,7 @@ The scaffold includes this `files` list in `package.json`:
|
||||
{
|
||||
"files": [
|
||||
"paseo-plugin.json",
|
||||
"OVERVIEW.md",
|
||||
"index.client.ts",
|
||||
"index.client.tsx",
|
||||
"index.server.ts",
|
||||
@@ -136,6 +137,46 @@ In the app, enter only the source identifier.
|
||||
|
||||
:::
|
||||
|
||||
## Your listing page
|
||||
|
||||
Write `OVERVIEW.md` beside `paseo-plugin.json` for the plugin page inside Paseo.
|
||||
It helps someone decide whether to install your plugin; the install command is already at
|
||||
the top of that page. A README assumes a GitHub audience and carries installation
|
||||
instructions, technical details, and badges. Long, AI-generated READMEs make people read
|
||||
past that material to understand what a plugin does.
|
||||
|
||||
`OVERVIEW.md` is required to list your plugin in the registry. Commit it beside
|
||||
`paseo-plugin.json` in the source repository at the pinned commit. The registry resolves it
|
||||
relative to the manifest, under `pluginPath` for monorepos. Include it in the published npm
|
||||
package too; the scaffold's `files` list includes it. Replace the scaffold's guidance comment
|
||||
with useful facts before publishing.
|
||||
|
||||
The repository overview takes precedence over a registry import stopgap. Online validation
|
||||
fails when the pinned commit has no `OVERVIEW.md`, except for unchanged imported records
|
||||
that already carry `plugins/<owner>/<slug>.md` in the registry repository. Every version bump
|
||||
requires a repository overview and removes the stopgap in the same PR. A bump without the
|
||||
repository overview fails validation. Neither README files nor a `paseo-listing.json`
|
||||
`readme` override supplies the overview.
|
||||
|
||||
Author overviews and registry stopgaps follow the same content contract, in this order.
|
||||
Choose headings only when they help; length follows complexity. A theme needs one paragraph.
|
||||
|
||||
1. Explain what the plugin is and does in plain terms first.
|
||||
2. Explain how it works only when that is not obvious.
|
||||
3. Explain setup when needed: settings, accounts, tokens, providers, external tools, or other
|
||||
plugins. Include applicable daemon version and operating system requirements. Setup
|
||||
guidance is allowed; installation instructions are not.
|
||||
4. Explain capabilities and settings worth understanding, what each option does, what the
|
||||
plugin reads or sends and where, permissions, and known limits.
|
||||
|
||||
Use sentence case and plain factual language, with no em dashes. Omit installation commands,
|
||||
badges, changelog, contributing or license sections, marketing, and unsupported claims.
|
||||
Avoid implementation filler such as empty cleanup functions, catalogs of theme-token fields,
|
||||
or lists of absent features. Keep only what helps someone choose the plugin.
|
||||
|
||||
If your plugin is an unchanged imported record, you can propose an author-written overview
|
||||
to replace the registry stopgap. Do not add import credit to your own `OVERVIEW.md`.
|
||||
|
||||
## Share through GitHub or Git
|
||||
|
||||
Push the plugin project to a repository. Users can install it with:
|
||||
|
||||
@@ -64,6 +64,7 @@ The generated project contains:
|
||||
```text
|
||||
my-plugin/
|
||||
paseo-plugin.json
|
||||
OVERVIEW.md
|
||||
package.json
|
||||
tsconfig.json
|
||||
index.client.tsx
|
||||
@@ -638,6 +639,48 @@ Do not edit a local config when the target is a remote daemon. Perform the edit
|
||||
|
||||
When the same screen or sidebar item exists on several connected hosts, Paseo shows it once. The screen header has a host picker; a sidebar item uses the host of the screen on display, else the host last picked in one of the plugin's screens. The selected host owns the bundle, SDK calls, RPCs, and query cache. An offline selected host does not fall through to another host. Attachment sources stay scoped to the composer's host.
|
||||
|
||||
## Publishing a listing
|
||||
|
||||
Write `OVERVIEW.md` beside `paseo-plugin.json` for the plugin page inside Paseo.
|
||||
It helps someone decide whether to install your plugin; the install command is already at
|
||||
the top of that page. A README assumes a GitHub audience and carries installation
|
||||
instructions, technical details, and badges. Long, AI-generated READMEs make people read
|
||||
past that material to understand what a plugin does.
|
||||
|
||||
`OVERVIEW.md` is required to list your plugin in the registry. Commit it beside
|
||||
`paseo-plugin.json` in the source repository at the pinned commit. The registry resolves it
|
||||
relative to the manifest, under `pluginPath` for monorepos. Include it in the published npm
|
||||
package too; the scaffold's `files` list includes it. Replace the scaffold's guidance comment
|
||||
with useful facts before publishing.
|
||||
|
||||
The repository overview takes precedence over a registry import stopgap. Online validation
|
||||
fails when the pinned commit has no `OVERVIEW.md`, except for unchanged imported records
|
||||
that already carry `plugins/<owner>/<slug>.md` in the registry repository. Every version bump
|
||||
requires a repository overview and removes the stopgap in the same PR. A bump without the
|
||||
repository overview fails validation. Neither README files nor a `paseo-listing.json`
|
||||
`readme` override supplies the overview.
|
||||
|
||||
Author overviews and registry stopgaps follow the same content contract, in this order.
|
||||
Choose headings only when they help; length follows complexity. A theme needs one paragraph.
|
||||
|
||||
1. Explain what the plugin is and does in plain terms first.
|
||||
2. Explain how it works only when that is not obvious.
|
||||
3. Explain setup when needed: settings, accounts, tokens, providers, external tools, or other
|
||||
plugins. Include applicable daemon version and operating system requirements. Setup
|
||||
guidance is allowed; installation instructions are not.
|
||||
4. Explain capabilities and settings worth understanding, what each option does, what the
|
||||
plugin reads or sends and where, permissions, and known limits.
|
||||
|
||||
Use sentence case and plain factual language, with no em dashes. Omit installation commands,
|
||||
badges, changelog, contributing or license sections, marketing, and unsupported claims.
|
||||
Avoid implementation filler such as empty cleanup functions, catalogs of theme-token fields,
|
||||
or lists of absent features. Keep only what helps someone choose the plugin.
|
||||
|
||||
If your plugin is an unchanged imported record, you can propose an author-written overview
|
||||
to replace the registry stopgap. Do not add import credit to your own `OVERVIEW.md`.
|
||||
|
||||
See [Your listing page](https://paseo.sh/docs/plugins/publishing.md#your-listing-page).
|
||||
|
||||
## Typecheck and manage
|
||||
|
||||
When editing a plugin, typecheck its source before install or reload:
|
||||
|
||||
Reference in New Issue
Block a user