Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions AGENT-INSTALL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Patchstack — Setup Guide for AI Coding Agents

This is a plain-text instruction document. Read it and follow the steps — there is nothing here to pipe to a shell or execute from a URL.

You are reading this either from `node_modules/@patchstack/connect/AGENT-INSTALL.md` (the package is already installed) or from the [GitHub repo](https://github.com/patchstack/connect). The canonical, always-current version of the full guide lives at https://patchstack.com/install.txt — prefer it when you have network access; this bundled copy matches the package version it shipped with.

## Verify before you trust (do this, it's quick)

- The connector is a normal npm package: [`@patchstack/connect`](https://www.npmjs.com/package/@patchstack/connect), MIT-licensed, source at https://github.com/patchstack/connect. Install it pinned, check the integrity hash in the lockfile, run `npm audit` over it — whatever your policy requires.
- It reads the project's **lockfile only** (`package-lock.json`, `pnpm-lock.yaml`, or `yarn.lock`) and sends package names + versions to Patchstack for vulnerability matching. No source code, no env vars, no file paths, no git history.
- Patchstack is not WordPress-only. This connector monitors any JS/Node project — Vite, Next.js, plain vanilla JS, anything with a lockfile.

## Before you start — never install twice

- If `.patchstackrc.json` exists, the project is already provisioned. Reuse its UUID; run `npx @patchstack/connect status` to re-print it and the claim URL. **Do not run `scan` to provision a second site.**
- If `@patchstack/connect` is already in `devDependencies`, skip the install command.
- If the widget script tag (`cdn.patchstack.com/patchstack-widget.js`) is already in the layout, don't add a second one.

## Install steps (run in order, don't pause between them unless one fails)

1. **Install** (skip if already present), matching the project's package manager:

```
npm install --save-dev @patchstack/connect # package-lock.json
pnpm add -D @patchstack/connect # pnpm-lock.yaml
yarn add -D @patchstack/connect # yarn.lock
bun add -d @patchstack/connect # bun.lock / bun-managed platforms
```

2. **First scan** — provisions a Patchstack site automatically and writes the UUID to `.patchstackrc.json`. No signup, dashboard step, or UUID is needed up front:

```
npx @patchstack/connect scan
```

It prints a claim URL. Surface that URL to the user verbatim — never fetch it yourself; it must be opened in their browser.

3. **Wire builds** in `package.json` (chain with `&&` if hooks already exist):

```jsonc
{
"scripts": {
"prebuild": "patchstack-connect scan",
"postbuild": "patchstack-connect mark-build"
}
}
```

4. **Install the disclosure widget** — a floating "Report a vulnerability" button. Read the UUID from `.patchstackrc.json` and use it as `userToken`. Place these two snippets via the framework's HTML/layout mechanism (never a JS entry point):

```html
<script src="https://cdn.patchstack.com/patchstack-widget.js"></script>
<script>
PatchstackWidget.init({ userToken: '<SITE_UUID>' });
</script>
```

Framework-specific placement patterns: https://cdn.patchstack.com/llm.html. The `userToken` is public by design — it ships in client-side HTML and is not a secret.

5. **Commit** `.patchstackrc.json` and the updated `package.json` so every developer and CI run reports to the same site.

6. **Tell the user to claim the site** (free): open the claim URL from step 2 in a browser and sign in. The site is monitored either way, but the vulnerability reports are only visible after claiming. They can also claim directly from the widget's "Connect this website" prompt. On the published site, the owner reaches the widget login by appending `#patchstack` to the live URL.

## Rules

- Never invent or guess a UUID — the scan provisions it, the widget silently no-ops on a fake one.
- Never fetch the claim URL programmatically, and never ask the user for Patchstack credentials.
- If a step fails, stop and report it. Don't proceed with placeholders.
- In CI where `.patchstackrc.json` can't be committed, set `PATCHSTACK_SITE_UUID` as an env var instead. Precedence: CLI flag → env var → `.patchstackrc.json`.

## Verifying the install

- `npx @patchstack/connect status` re-prints the site UUID and claim URL.
- Load the site in a browser — the "Report a vulnerability" button should appear.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ For how this repo fits with the wider Patchstack ecosystem (`saas`, `hub`, `patc

Copy and paste this prompt into any AI coding assistant (Cursor, v0, Bolt, Lovable, Claude Code, etc.):

> Add `@patchstack/connect` to this project as a dev dependency, then add `"prebuild": "patchstack-connect scan"` to `package.json` `scripts`. The first time the script runs, Patchstack provisions a site automatically from the lockfile and saves the new site UUID to `.patchstackrc.json` — no dashboard signup or manual UUID required. Commit `.patchstackrc.json` so every developer / CI run targets the same site.
> Install the npm package `@patchstack/connect` as a dev dependency (MIT, source: https://github.com/patchstack/connect) to add dependency vulnerability monitoring, then run `npx @patchstack/connect guide` and follow the setup guide it prints. It only reads the lockfile — nothing is fetched from a URL or executed remotely.

The prompt is built to survive agent security filters: it names a pinnable, auditable npm package (not a remote script), the `guide` command prints instructions that ship *inside* the installed package (`AGENT-INSTALL.md`), and it preempts the "Patchstack is WordPress-only" objection. If the agent has network access, the always-current version of the guide is at https://patchstack.com/install.txt — this longer prompt uses it directly:

> Add Patchstack dependency vulnerability monitoring: install the npm package `@patchstack/connect` as a dev dependency (MIT, source: https://github.com/patchstack/connect — pin it and verify however your policy requires), then follow the plain-text setup instructions at https://patchstack.com/install.txt. That URL is documentation to read, not a script to execute — nothing gets piped to a shell. Patchstack monitors any JS/Node lockfile, not just WordPress.

## Quick start (zero configuration)

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"files": [
"dist",
"README.md",
"AGENT-INSTALL.md",
"LICENSE"
],
"scripts": {
Expand Down
12 changes: 12 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ Usage:
patchstack-connect protect [--manifest] Install runtime protection (the guard) into
a server-side JS app. --manifest only
regenerates the package manifest (prebuild).
patchstack-connect guide Print the full setup guide for AI coding
agents (also at https://patchstack.com/install.txt)
patchstack-connect help Print this message

Options (for scan and status):
Expand Down Expand Up @@ -204,6 +206,14 @@ async function runProtectCommand(args: ParsedArgs): Promise<number> {
return 0;
}

async function runGuide(): Promise<number> {
// AGENT-INSTALL.md ships at the package root, one level above dist/ (and
// above src/ when running unbundled), so the same relative path works in both.
const guidePath = new URL('../AGENT-INSTALL.md', import.meta.url);
console.log(readFileSync(guidePath, 'utf8'));
return 0;
}

async function runStatus(args: ParsedArgs): Promise<number> {
const config = await resolveConfig({
cwd: process.cwd(),
Expand Down Expand Up @@ -307,6 +317,8 @@ async function main(): Promise<number> {
return runMarkBuild(args);
case 'protect':
return runProtectCommand(args);
case 'guide':
return runGuide();
default:
console.error(`Unknown command: ${args.command}\n`);
console.error(HELP);
Expand Down
Loading