From e3209b74ae51ba117c5f528be896bfcbace8f458 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 10 Jul 2026 12:59:49 +0000 Subject: [PATCH] Add @probo/skills multi-agent compliance package Ship compliance skills and agent plugin wiring for Claude Code, Codex, OpenCode, and Cursor via Probo MCP (OAuth 2.0). Includes access-review and missing-signatures workflows, repo-root Git marketplaces, validation, and npm release CI. Signed-off-by: Cursor Agent Co-authored-by: Bryan FRIMIN --- .agents/plugins/marketplace.json | 30 ++++ .claude-plugin/marketplace.json | 25 +++ .github/workflows/release-npm-skills.yaml | 98 +++++++++++ AGENTS.md | 1 + contrib/claude/release/README.md | 7 +- contrib/claude/release/skills.md | 31 ++++ contrib/claude/skills.md | 128 +++++++++++++++ eslint.config.mjs | 1 + package-lock.json | 10 ++ .../skills/.agents/plugins/marketplace.json | 30 ++++ .../skills/.claude-plugin/marketplace.json | 28 ++++ packages/skills/.claude-plugin/plugin.json | 21 +++ packages/skills/.codex-plugin/plugin.json | 21 +++ packages/skills/.mcp.json | 8 + packages/skills/CHANGELOG.md | 29 ++++ packages/skills/COMPATIBILITY.md | 154 ++++++++++++++++++ packages/skills/README.md | 98 +++++++++++ packages/skills/agents/.gitkeep | 0 packages/skills/commands/access-review.md | 17 ++ .../skills/commands/missing-signatures.md | 17 ++ packages/skills/hooks/.gitkeep | 0 packages/skills/package.json | 52 ++++++ packages/skills/scripts/validate.mjs | 105 ++++++++++++ packages/skills/skills/access-review/SKILL.md | 82 ++++++++++ .../references/decision-rubric.md | 60 +++++++ .../access-review/references/mcp-tools.md | 93 +++++++++++ .../access-review/references/notes-format.md | 59 +++++++ .../skills/skills/missing-signatures/SKILL.md | 109 +++++++++++++ .../references/mcp-tools.md | 143 ++++++++++++++++ .../references/notes-format.md | 57 +++++++ .../references/report-format.md | 89 ++++++++++ .../skills/open-source-compliance/SKILL.md | 51 ++++++ .../references/workflows.md | 48 ++++++ 33 files changed, 1701 insertions(+), 1 deletion(-) create mode 100644 .agents/plugins/marketplace.json create mode 100644 .claude-plugin/marketplace.json create mode 100644 .github/workflows/release-npm-skills.yaml create mode 100644 contrib/claude/release/skills.md create mode 100644 contrib/claude/skills.md create mode 100644 packages/skills/.agents/plugins/marketplace.json create mode 100644 packages/skills/.claude-plugin/marketplace.json create mode 100644 packages/skills/.claude-plugin/plugin.json create mode 100644 packages/skills/.codex-plugin/plugin.json create mode 100644 packages/skills/.mcp.json create mode 100644 packages/skills/CHANGELOG.md create mode 100644 packages/skills/COMPATIBILITY.md create mode 100644 packages/skills/README.md create mode 100644 packages/skills/agents/.gitkeep create mode 100644 packages/skills/commands/access-review.md create mode 100644 packages/skills/commands/missing-signatures.md create mode 100644 packages/skills/hooks/.gitkeep create mode 100644 packages/skills/package.json create mode 100644 packages/skills/scripts/validate.mjs create mode 100644 packages/skills/skills/access-review/SKILL.md create mode 100644 packages/skills/skills/access-review/references/decision-rubric.md create mode 100644 packages/skills/skills/access-review/references/mcp-tools.md create mode 100644 packages/skills/skills/access-review/references/notes-format.md create mode 100644 packages/skills/skills/missing-signatures/SKILL.md create mode 100644 packages/skills/skills/missing-signatures/references/mcp-tools.md create mode 100644 packages/skills/skills/missing-signatures/references/notes-format.md create mode 100644 packages/skills/skills/missing-signatures/references/report-format.md create mode 100644 packages/skills/skills/open-source-compliance/SKILL.md create mode 100644 packages/skills/skills/open-source-compliance/references/workflows.md diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json new file mode 100644 index 000000000..685328b4e --- /dev/null +++ b/.agents/plugins/marketplace.json @@ -0,0 +1,30 @@ +{ + "name": "probo", + "interface": { + "displayName": "Probo" + }, + "plugins": [ + { + "name": "probo", + "description": "Open-source compliance workflows powered by Probo MCP", + "source": { + "source": "local", + "path": "./packages/skills" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Productivity", + "homepage": "https://github.com/getprobo/probo/tree/main/packages/skills", + "repository": "https://github.com/getprobo/probo", + "license": "MIT", + "keywords": [ + "probo", + "compliance", + "grc", + "mcp" + ] + } + ] +} diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 000000000..670be28e4 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,25 @@ +{ + "name": "probo", + "description": "Probo Claude Code plugins", + "owner": { + "name": "Probo Inc", + "email": "hello@probo.com" + }, + "plugins": [ + { + "name": "probo", + "description": "Open-source compliance workflows powered by Probo MCP", + "source": "./packages/skills", + "homepage": "https://github.com/getprobo/probo/tree/main/packages/skills", + "repository": "https://github.com/getprobo/probo", + "license": "MIT", + "keywords": [ + "probo", + "compliance", + "grc", + "mcp", + "agent-skills" + ] + } + ] +} diff --git a/.github/workflows/release-npm-skills.yaml b/.github/workflows/release-npm-skills.yaml new file mode 100644 index 000000000..cdda4bc0a --- /dev/null +++ b/.github/workflows/release-npm-skills.yaml @@ -0,0 +1,98 @@ +name: "Release skills" + +on: + push: + tags: + - "@probo/skills/v*" + +permissions: + contents: "read" + +jobs: + publish: + name: "publish" + runs-on: "ubuntu-latest" + permissions: + contents: write + id-token: write + attestations: write + steps: + - uses: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd" # v6 + with: + fetch-depth: 0 + submodules: recursive + - uses: "actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e" # v6 + with: + node-version-file: ".nvmrc" + cache: "npm" + registry-url: "https://registry.npmjs.org" + scope: "@probo" + - run: "npm install -g npm@^11.8.0" + - run: "npm ci" + - name: "Verify package.json version matches tag" + run: | + TAG_VERSION="${GITHUB_REF_NAME##*/v}" + PKG_VERSION="$(node -p "require('./packages/skills/package.json').version")" + if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then + echo "tag version ($TAG_VERSION) does not match package.json version ($PKG_VERSION)" >&2 + exit 1 + fi + - run: "npm --workspace @probo/skills run validate" + - uses: "anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610" # v0.24.0 + with: + path: ./packages/skills + format: cyclonedx-json + output-file: packages/skills/sbom.json + - uses: "anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2" # v7.4.0 + with: + path: ./packages/skills + fail-build: true + severity-cutoff: critical + - name: "Generate checksums for published files" + run: | + cd packages/skills + find .claude-plugin skills commands agents hooks bin -type f 2>/dev/null \ + | sort \ + | while read -r file; do + echo "$(sha256sum "$file" | head -c 64) $file" + done > checksums.txt + test -s checksums.txt + - run: "npm --workspace @probo/skills publish --access public --dry-run" + - run: "npm --workspace @probo/skills publish --access public" + - uses: "actions/attest-sbom@c604332985a26aa8cf1bdc465b92731239ec6b9e" # v4 + with: + subject-path: "packages/skills/**" + sbom-path: "packages/skills/sbom.json" + - uses: "actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32" # v4 + with: + subject-path: "packages/skills/**" + - name: "Extract release notes" + run: | + VERSION="${GITHUB_REF_NAME##*/v}" + awk -v ver="$VERSION" ' + /^## \[/ { if (found) exit; if ($0 ~ "\\[" ver "\\]") found=1 } + found + ' packages/skills/CHANGELOG.md > release-notes.md + - name: "Create GitHub release" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PRERELEASE_FLAG="" + if echo "${GITHUB_REF_NAME}" | grep -qE '(alpha|beta|rc)'; then + PRERELEASE_FLAG="--prerelease" + fi + + gh release delete "${GITHUB_REF_NAME}" --yes 2>/dev/null || true + + gh release create "${GITHUB_REF_NAME}" \ + --title "${GITHUB_REF_NAME}" \ + --notes-file release-notes.md \ + $PRERELEASE_FLAG \ + packages/skills/sbom.json packages/skills/checksums.txt + - uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7 + with: + name: "skills-sbom" + path: | + packages/skills/sbom.json + packages/skills/checksums.txt + retention-days: 30 diff --git a/AGENTS.md b/AGENTS.md index af8d32445..df92529a7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -41,3 +41,4 @@ Detailed guides for specific subsystems live in `contrib/claude/`: - [`contrib/claude/release/README.md`](contrib/claude/release/README.md) — Release process (per-track version bump, changelog, tag, push) - [`contrib/claude/sandbox.md`](contrib/claude/sandbox.md) — Lima sandbox environments (create, manage, access services) - [`contrib/claude/n8n.md`](contrib/claude/n8n.md) — n8n community node (resources, operations, GraphQL helpers) +- [`contrib/claude/skills.md`](contrib/claude/skills.md) — Agent skills package (`@probo/skills`, compliance workflows, Probo MCP) diff --git a/contrib/claude/release/README.md b/contrib/claude/release/README.md index c79a90dde..2a8ba8cc5 100644 --- a/contrib/claude/release/README.md +++ b/contrib/claude/release/README.md @@ -1,6 +1,6 @@ # Release -The repository ships eight independently-versioned tracks. Each has its own +The repository ships nine independently-versioned tracks. Each has its own version source, its own `CHANGELOG.md`, its own tag pattern, and its own release workflow. Cutting a release means: bump the version, write a changelog entry, commit, tag, push. @@ -14,6 +14,7 @@ changelog entry, commit, tag, push. | `probo-agent` | `probo-agent/v*` | [probo-agent.md](./probo-agent.md) | | `@probo/n8n-nodes-probo` | `@probo/n8n-nodes-probo/v*` | [n8n-nodes-probo.md](./n8n-nodes-probo.md) | | `@probo/cookie-banner` | `@probo/cookie-banner/v*` | [cookie-banner.md](./cookie-banner.md) | +| `@probo/skills` | `@probo/skills/v*` | [skills.md](./skills.md) | | Helm chart (`probo`) | `helm/v*` | [helm.md](./helm.md) | When the user asks for a release **without specifying a track**, follow @@ -76,6 +77,10 @@ git log $(git describe --tags --abbrev=0 --match='@probo/n8n-nodes-probo/v*')..H git log $(git describe --tags --abbrev=0 --match='@probo/cookie-banner/v*')..HEAD --oneline \ -- packages/cookie-banner +# @probo/skills +git log $(git describe --tags --abbrev=0 --match='@probo/skills/v*')..HEAD --oneline \ + -- packages/skills + # helm chart git log $(git describe --tags --abbrev=0 --match='helm/v*')..HEAD --oneline \ -- contrib/helm diff --git a/contrib/claude/release/skills.md b/contrib/claude/release/skills.md new file mode 100644 index 000000000..d1080544c --- /dev/null +++ b/contrib/claude/release/skills.md @@ -0,0 +1,31 @@ +# Release `@probo/skills` + +After confirming commits below, follow the +[common steps](./README.md#3-common-steps-every-track). + +## Track facts + +- **Tag pattern**: `@probo/skills/v*` +- **Version source**: `packages/skills/package.json` +- **Version bump**: `npm --workspace @probo/skills version --no-git-tag-version` +- **Validate**: `npm --workspace @probo/skills run validate` +- **Changelog**: `packages/skills/CHANGELOG.md` +- **Files to stage**: `packages/skills/package.json`, + `packages/skills/CHANGELOG.md`, `package-lock.json` +- **Workflow**: `.github/workflows/release-npm-skills.yaml` +- **Path filter**: `packages/skills` + +## Detect commits + +```shell +git log $(git describe --tags --abbrev=0 --match='@probo/skills/v*')..HEAD --oneline \ + -- packages/skills +``` + +If empty or non-user-facing only, do not release this track. + +## Notes + +There is no build step. Run `validate` after the version bump to catch manifest +or structural errors before tagging. CI runs the same validation, publishes to +npm with provenance + SBOM, and creates a GitHub Release. diff --git a/contrib/claude/skills.md b/contrib/claude/skills.md new file mode 100644 index 000000000..3366a1d22 --- /dev/null +++ b/contrib/claude/skills.md @@ -0,0 +1,128 @@ +# Agent skills (`packages/skills`) + +npm package [`@probo/skills`](../../packages/skills) ships multi-agent +compliance skills and agent plugin wiring powered by the Probo MCP API. +Compatible with **Claude Code**, **Codex**, **OpenCode**, and **Cursor** (via +MCP + skills). See [`COMPATIBILITY.md`](../../packages/skills/COMPATIBILITY.md). + +## What this package ships + +A **skills package** bundling: + +| Component | Role | +| --- | --- | +| `skills/` | Agent Skills–compatible workflow instructions | +| `.mcp.json` | Connects agents to Probo (`/mcp/v1`, OAuth 2.0) | +| `commands/` | Explicit slash commands (e.g. `access-review`, Claude Code only) | +| `agents/` | Optional specialized subagents | +| `hooks/` | Optional event automation | + +Individual capabilities are namespaced under `probo`: + +- Skills: `/probo:` (e.g. `/probo:open-source-compliance`, + `/probo:missing-signatures`, `/probo:access-review`) +- Commands: `/probo:` (e.g. `/probo:access-review`, + `/probo:missing-signatures`) + +Published to npm as `@probo/skills`. Agent-specific manifests (`.claude-plugin/`, +`.codex-plugin/`) ship inside the same package. + +## Directory structure + +``` +.claude-plugin/marketplace.json # repo root — Claude catalog for getprobo/probo +.agents/plugins/marketplace.json # repo root — Codex catalog for getprobo/probo + +packages/skills/ + .claude-plugin/ + plugin.json # Claude Code manifest (required) + marketplace.json # Claude marketplace catalog (npm) + .agents/plugins/ + marketplace.json # Codex catalog when marketplace root is the package + .codex-plugin/ + plugin.json # Codex manifest + .mcp.json # Probo MCP server wiring + skills/ + / + SKILL.md + references/ + commands/ + agents/ + hooks/ + scripts/validate.mjs + package.json + CHANGELOG.md +``` + +Only `plugin.json` belongs inside `.claude-plugin/`. All other directories +must sit at the package root. + +## plugin.json rules + +Claude Code validates the manifest strictly. Common pitfalls: + +| Field | Expected type | Notes | +| --- | --- | --- | +| `name` | string | Skill namespace (`probo` → `/probo:open-source-compliance`) | +| `repository` | string URL | **Not** the npm-style `{ type, url }` object | +| `bugs` | string URL | **Not** the npm-style `{ url }` object | +| `version` | string | Bump on every release when using explicit versioning | + +Run `npm --workspace @probo/skills run validate` before publishing. + +## Probo MCP configuration + +The package `.mcp.json` expects one environment variable: + +- `PROBO_BASE_URL` — instance root URL + +Authentication is OAuth 2.0 only. Users complete sign-in via `/mcp` or +`claude mcp login probo`. Do not document API keys or bearer tokens in the +plugin config — a pre-set `Authorization` header prevents Claude Code from +starting the OAuth flow. + +## Adding a skill + +1. Create `skills//SKILL.md` with YAML frontmatter (`name`, `description`). +2. Add `references/` for detailed workflow docs loaded on demand. +3. Validate and test: + +```bash +npm --workspace @probo/skills run validate +claude --plugin-dir ./packages/skills +/probo: +``` + +4. Update `packages/skills/CHANGELOG.md` under `## Unreleased`. + +Skills must be self-contained — npm installs do not include `contrib/claude/` +from the monorepo. + +## Adding a command + +Use commands for explicit, user-invoked workflows on Claude Code only. Pair a +thin `commands/.md` with a shared `skills//SKILL.md` so Codex and +OpenCode load the same workflow. Reference docs live under +`skills//references/` using paths relative to the skill directory (not +`${CLAUDE_PLUGIN_ROOT}`). + +1. Create `commands/.md` with frontmatter (`description`, + `argument-hint`, `disable-model-invocation: true` when writes are involved). +2. Add reference docs under `skills//references/`. +3. Register paths in `scripts/validate.mjs`. +4. Test: `/probo: ` after `claude --plugin-dir ./packages/skills`. + +## Distribution + +Published to npm as `@probo/skills`. Claude marketplace entry: + +```json +{ + "source": { + "source": "npm", + "package": "@probo/skills" + } +} +``` + +Release process: [`contrib/claude/release/skills.md`](release/skills.md). diff --git a/eslint.config.mjs b/eslint.config.mjs index 2df23b042..1fce7c0ad 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -17,6 +17,7 @@ export default defineConfig([ "pkg/**", "packages/coredata/**", "packages/cookie-banner/**", + "packages/skills/**", "packages/emails/**", "packages/eslint-relay-plugin-types/**", "packages/helpers/**", diff --git a/package-lock.json b/package-lock.json index 85815512e..d41bf8ae6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4820,6 +4820,10 @@ "resolved": "packages/routes", "link": true }, + "node_modules/@probo/skills": { + "resolved": "packages/skills", + "link": true + }, "node_modules/@probo/trust": { "resolved": "apps/trust", "link": true @@ -19347,6 +19351,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -21151,6 +21156,11 @@ "relay-runtime": "^21.0.1" } }, + "packages/skills": { + "name": "@probo/skills", + "version": "0.1.0", + "license": "MIT" + }, "packages/tsconfig": { "name": "@probo/tsconfig", "version": "0.0.1", diff --git a/packages/skills/.agents/plugins/marketplace.json b/packages/skills/.agents/plugins/marketplace.json new file mode 100644 index 000000000..77b561d0a --- /dev/null +++ b/packages/skills/.agents/plugins/marketplace.json @@ -0,0 +1,30 @@ +{ + "name": "probo", + "interface": { + "displayName": "Probo" + }, + "plugins": [ + { + "name": "probo", + "description": "Open-source compliance workflows powered by Probo MCP", + "source": { + "source": "local", + "path": "./" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Productivity", + "homepage": "https://github.com/getprobo/probo/tree/main/packages/skills", + "repository": "https://github.com/getprobo/probo", + "license": "MIT", + "keywords": [ + "probo", + "compliance", + "grc", + "mcp" + ] + } + ] +} diff --git a/packages/skills/.claude-plugin/marketplace.json b/packages/skills/.claude-plugin/marketplace.json new file mode 100644 index 000000000..a9bb15878 --- /dev/null +++ b/packages/skills/.claude-plugin/marketplace.json @@ -0,0 +1,28 @@ +{ + "name": "probo", + "description": "Probo Claude Code plugins", + "owner": { + "name": "Probo Inc", + "email": "hello@probo.com" + }, + "plugins": [ + { + "name": "probo", + "description": "Open-source compliance workflows powered by Probo MCP", + "source": { + "source": "npm", + "package": "@probo/skills" + }, + "homepage": "https://github.com/getprobo/probo/tree/main/packages/skills", + "repository": "https://github.com/getprobo/probo", + "license": "MIT", + "keywords": [ + "probo", + "compliance", + "grc", + "mcp", + "agent-skills" + ] + } + ] +} diff --git a/packages/skills/.claude-plugin/plugin.json b/packages/skills/.claude-plugin/plugin.json new file mode 100644 index 000000000..11eed4ed5 --- /dev/null +++ b/packages/skills/.claude-plugin/plugin.json @@ -0,0 +1,21 @@ +{ + "name": "probo", + "version": "0.1.0", + "description": "Open-source compliance workflows powered by Probo MCP (Claude Code, Codex, OpenCode)", + "author": { + "name": "Probo Inc", + "email": "hello@probo.com" + }, + "homepage": "https://github.com/getprobo/probo/tree/main/packages/skills", + "repository": "https://github.com/getprobo/probo", + "license": "MIT", + "keywords": [ + "probo", + "compliance", + "grc", + "mcp", + "open-source" + ], + "skills": "./skills/", + "mcpServers": "./.mcp.json" +} diff --git a/packages/skills/.codex-plugin/plugin.json b/packages/skills/.codex-plugin/plugin.json new file mode 100644 index 000000000..9102729db --- /dev/null +++ b/packages/skills/.codex-plugin/plugin.json @@ -0,0 +1,21 @@ +{ + "name": "probo", + "version": "0.1.0", + "description": "Open-source compliance workflows powered by Probo MCP (Codex, OpenCode, Claude Code)", + "author": { + "name": "Probo Inc", + "email": "hello@probo.com" + }, + "homepage": "https://github.com/getprobo/probo/tree/main/packages/skills", + "repository": "https://github.com/getprobo/probo", + "license": "MIT", + "keywords": [ + "probo", + "compliance", + "grc", + "mcp", + "open-source" + ], + "skills": "./skills/", + "mcpServers": "./.mcp.json" +} diff --git a/packages/skills/.mcp.json b/packages/skills/.mcp.json new file mode 100644 index 000000000..108a93004 --- /dev/null +++ b/packages/skills/.mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "probo": { + "type": "http", + "url": "${PROBO_BASE_URL}/mcp/v1" + } + } +} diff --git a/packages/skills/CHANGELOG.md b/packages/skills/CHANGELOG.md new file mode 100644 index 000000000..aab63a95b --- /dev/null +++ b/packages/skills/CHANGELOG.md @@ -0,0 +1,29 @@ +# Changelog + +All notable changes to the `@probo/skills` package will be documented in +this file. + +## Unreleased + +### Added + +- `@probo/skills` npm package with multi-agent manifests (`.claude-plugin/`, + `.codex-plugin/`, `.agents/`) and Probo MCP wiring via `.mcp.json` + (`PROBO_BASE_URL`, OAuth 2.0 sign-in) +- Skills: `open-source-compliance`, `access-review`, `missing-signatures` +- Commands: `access-review`, `missing-signatures` (Claude Code slash commands + delegating to shared skills) +- Portable relative `references/` paths in skill bodies (no + `${CLAUDE_PLUGIN_ROOT}`) +- Resumable session notes for `access-review` and `missing-signatures` +- Repo-root marketplace catalogs for Git installs (`getprobo/probo`): + `.claude-plugin/marketplace.json` (Claude Code) and + `.agents/plugins/marketplace.json` (Codex) +- Package-local marketplace catalogs under `packages/skills/` +- `COMPATIBILITY.md`, validation script, and release workflow + (`release-npm-skills.yaml`, tag `@probo/skills/v*`) + +### Changed + +- Package published as `@probo/skills` in `packages/skills/` (multi-agent + scope, not Claude-specific) diff --git a/packages/skills/COMPATIBILITY.md b/packages/skills/COMPATIBILITY.md new file mode 100644 index 000000000..90ad173c2 --- /dev/null +++ b/packages/skills/COMPATIBILITY.md @@ -0,0 +1,154 @@ +# Multi-agent compatibility + +`@probo/skills` targets **Claude Code**, **Codex**, **OpenCode**, and +other MCP-capable agents (including **Cursor**). The portable core is Probo +MCP plus Agent Skills–compatible `SKILL.md` files. + +## What works where + +| Component | Claude Code | Codex | OpenCode | Cursor | +| --- | --- | --- | --- | --- | +| Probo MCP (OAuth) | ✅ Plugin `.mcp.json` | ✅ `.codex-plugin` + `.mcp.json` | ✅ Manual MCP config | ✅ IDE MCP settings | +| Skills (`SKILL.md`) | ✅ `skills/` | ✅ `skills/` via `.codex-plugin` | ✅ `.opencode/skills/` or `.claude/skills/` | ✅ Copy/symlink to `.cursor/skills/` | +| Commands | ✅ `commands/` → `/probo:…` | ⚠️ Use skills instead | ⚠️ Native `skill` tool | ❌ Use skill or rules | +| Plugin manifest | `.claude-plugin/` | `.codex-plugin/` | Discovery paths (no manifest) | No native manifest | +| Marketplace catalog | `.claude-plugin/marketplace.json` (repo root or package) | `.agents/plugins/marketplace.json` (repo root or package) | — | — | + +## Probo MCP (all agents) + +Set the instance URL: + +```bash +export PROBO_BASE_URL="https://your-probo-instance.example.com" +``` + +Endpoint: `${PROBO_BASE_URL}/mcp/v1` (HTTP, OAuth 2.0). Do not configure a +static bearer token — OAuth discovery uses +`/.well-known/oauth-protected-resource`. + +### Claude Code + +**From the monorepo or GitHub** (repo-root catalog at +`.claude-plugin/marketplace.json`): + +```bash +claude plugin marketplace add getprobo/probo +# or, from a local clone: +claude plugin marketplace add . +claude plugin install probo@probo +claude mcp login probo # or /mcp in session +/probo:access-review Q3 GitHub review +``` + +**From the package directory** (catalog resolves `@probo/skills` from npm): + +```bash +claude plugin marketplace add ./packages/skills/.claude-plugin +claude plugin install probo@probo +claude mcp login probo +``` + +Or install the plugin directory directly: + +```bash +claude --plugin-dir ./packages/skills +``` + +### Codex + +**From the monorepo or GitHub** (repo-root catalog at +`.agents/plugins/marketplace.json`): + +```bash +codex plugin marketplace add getprobo/probo +# or, from a local clone: +codex plugin marketplace add . +codex plugin install probo@probo +codex mcp login probo +``` + +**From the package directory** (catalog at +`packages/skills/.agents/plugins/marketplace.json`): + +```bash +codex plugin marketplace add ./packages/skills +codex plugin install probo@probo +codex mcp login probo +``` + +Or install the plugin directory directly: + +```bash +codex plugin install ./packages/skills +codex mcp login probo +``` + +Skills load from `./skills/` via `.codex-plugin/plugin.json`. The repo-root +marketplace `source.path` is `./packages/skills`; the package-level +catalog uses `./` (plugin package root). + +### OpenCode + +OpenCode discovers skills at `.opencode/skills/`, `.claude/skills/`, and +`~/.config/opencode/skills/`. Options: + +**Option A — symlink from this package:** + +```bash +mkdir -p .opencode/skills +ln -s ../../packages/skills/skills/access-review .opencode/skills/access-review +ln -s ../../packages/skills/skills/open-source-compliance .opencode/skills/open-source-compliance +``` + +**Option B — Claude Code bridge:** install +[`opencode-claude-code-bridge`](https://www.npmjs.com/package/opencode-claude-code-bridge) +to import Claude plugins and MCP configs into OpenCode. + +Configure Probo MCP in `opencode.json` or global OpenCode MCP settings, then +authenticate. Invoke via the native `skill` tool (`access-review`). + +### Cursor + +1. Add Probo MCP in Cursor settings (HTTP URL: `${PROBO_BASE_URL}/mcp/v1`, + OAuth). +2. Copy or symlink skills into `.cursor/skills/`: + +```bash +mkdir -p .cursor/skills +cp -r packages/skills/skills/access-review .cursor/skills/ +``` + +Reference the skill in chat or add a Cursor rule pointing at the skill. + +## Portable vs agent-specific paths + +| Path | Portable? | +| --- | --- | +| `skills//SKILL.md` | ✅ Agent Skills standard | +| `skills//references/*.md` | ✅ Relative to skill directory | +| `.mcp.json` with `${PROBO_BASE_URL}` | ✅ Standard env var | +| `${CLAUDE_PLUGIN_ROOT}` | ❌ Claude Code only — avoid in skill bodies | +| `commands/*.md` | Claude Code slash commands only | + +Skill bodies use **relative** `references/` paths so they work once the skill +directory is discovered, regardless of which agent loads it. + +## npm package layout + +``` +@probo/skills/ + .claude-plugin/plugin.json # Claude Code manifest + .claude-plugin/marketplace.json # Claude marketplace (npm) + .codex-plugin/plugin.json # Codex manifest + .agents/plugins/marketplace.json # Codex marketplace (package-local) + .mcp.json # Shared MCP wiring + skills/ # Shared skills (all agents) + commands/ # Claude Code commands only +``` + +Repo root (monorepo / `getprobo/probo` Git installs): + +``` +.claude-plugin/marketplace.json # Claude marketplace → packages/skills +.agents/plugins/marketplace.json # Codex marketplace → packages/skills +``` diff --git a/packages/skills/README.md b/packages/skills/README.md new file mode 100644 index 000000000..1dd24c590 --- /dev/null +++ b/packages/skills/README.md @@ -0,0 +1,98 @@ +# @probo/skills + +Multi-agent compliance skills for open-source GRC workflows. Ships Agent +Skills–compatible instructions and wires agents to the [Probo MCP +API](https://github.com/getprobo/probo/tree/main/pkg/server/api/mcp/v1) via +OAuth 2.0. + +**Supported agents:** Claude Code, Codex, OpenCode, Cursor (MCP + skills). + +Marketplace catalogs: `.claude-plugin/marketplace.json` at the repo root or +under `packages/skills/` (Claude Code), `.agents/plugins/marketplace.json` at +the repo root or under `packages/skills/` (Codex). See +[COMPATIBILITY.md](./COMPATIBILITY.md). + +## Install + +### From GitHub or npm + +**From GitHub** (repo-root catalog at `.claude-plugin/marketplace.json`): + +```bash +claude plugin marketplace add getprobo/probo +# or, from a local clone: +claude plugin marketplace add . +claude plugin install probo@probo +``` + +**From the package directory** (catalog at +`packages/skills/.claude-plugin/marketplace.json`, resolves `@probo/skills` +from npm): + +```bash +claude plugin marketplace add ./packages/skills/.claude-plugin +claude plugin install probo@probo +``` + +When consuming the published package, the package-level marketplace entry +resolves `@probo/skills` from npm (see +`packages/skills/.claude-plugin/marketplace.json`). + +### Configure Probo MCP + +Set your Probo instance URL before starting Claude Code: + +```bash +export PROBO_BASE_URL="https://your-probo-instance.example.com" +``` + +The plugin `.mcp.json` connects to `${PROBO_BASE_URL}/mcp/v1`. Probo MCP +authenticates with **OAuth 2.0** — no API token or bearer header is required in +the plugin config. On first use, sign in from Claude Code: + +```text +/mcp +``` + +Or from your shell: + +```bash +claude mcp login probo +``` + +Claude Code discovers Probo's authorization server via +`/.well-known/oauth-protected-resource` and stores tokens securely. + +### Local development + +```bash +claude --plugin-dir ./packages/skills +``` + +## What's included + +| Component | Location | Purpose | +| --- | --- | --- | +| MCP | `.mcp.json` | Probo API connection | +| Skills | `skills/` | Compliance workflows | +| Commands | `commands/` | `access-review`, `missing-signatures` — semi-auto workflows | +| Agents | `agents/` | Reserved | +| Hooks | `hooks/` | Reserved | + +Skills: `/probo:` (e.g. `/probo:open-source-compliance`, `/probo:missing-signatures`). + +Commands: `/probo:` (e.g. `/probo:access-review`, `/probo:missing-signatures`). + +## Adding content + +See [`contrib/claude/skills.md`](../../contrib/claude/skills.md). + +```bash +npm --workspace @probo/skills run validate +claude --plugin-dir ./packages/skills +``` + +## Release + +Published to npm as `@probo/skills`. See +[`contrib/claude/release/skills.md`](../../contrib/claude/release/skills.md). diff --git a/packages/skills/agents/.gitkeep b/packages/skills/agents/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/skills/commands/access-review.md b/packages/skills/commands/access-review.md new file mode 100644 index 000000000..00e3715e2 --- /dev/null +++ b/packages/skills/commands/access-review.md @@ -0,0 +1,17 @@ +--- +description: Run a semi-automated access review on a Probo campaign (Claude Code slash command). +argument-hint: [campaign name or id] +disable-model-invocation: true +--- + +# Access review command + +Execute the `access-review` skill for campaign `$ARGUMENTS`. + +1. Load `skills/access-review/SKILL.md` from the plugin package root. +2. Load reference docs from `skills/access-review/references/` as directed by + the skill. +3. Follow the skill workflow exactly. + +Do not duplicate skill logic here — the skill is the canonical workflow shared +with Codex and OpenCode. diff --git a/packages/skills/commands/missing-signatures.md b/packages/skills/commands/missing-signatures.md new file mode 100644 index 000000000..e3e4299a1 --- /dev/null +++ b/packages/skills/commands/missing-signatures.md @@ -0,0 +1,17 @@ +--- +description: Report missing document signatures and pending quorum approvals per person (Claude Code slash command). +argument-hint: [organization name or id] +disable-model-invocation: true +--- + +# Missing signatures command + +Execute the `missing-signatures` skill for organization `$ARGUMENTS`. + +1. Load `skills/missing-signatures/SKILL.md` from the plugin package root. +2. Load reference docs from `skills/missing-signatures/references/` as directed + by the skill. +3. Follow the skill workflow exactly. + +Do not duplicate skill logic here — the skill is the canonical workflow shared +with Codex and OpenCode. diff --git a/packages/skills/hooks/.gitkeep b/packages/skills/hooks/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/skills/package.json b/packages/skills/package.json new file mode 100644 index 000000000..7d69673fe --- /dev/null +++ b/packages/skills/package.json @@ -0,0 +1,52 @@ +{ + "name": "@probo/skills", + "version": "0.1.0", + "description": "Multi-agent compliance skills for open-source GRC (Claude Code, Codex, OpenCode) powered by Probo MCP", + "private": false, + "scripts": { + "validate": "node scripts/validate.mjs", + "release": "npm run validate && npm publish --access public", + "prepublishOnly": "npm run validate" + }, + "author": { + "name": "Probo Inc", + "email": "hello@probo.com" + }, + "files": [ + ".claude-plugin", + ".codex-plugin", + ".agents", + "skills", + "commands", + "agents", + "hooks", + "bin", + ".mcp.json", + "README.md", + "COMPATIBILITY.md" + ], + "keywords": [ + "claude-code", + "agent-skills", + "codex", + "opencode", + "probo", + "compliance", + "grc", + "mcp", + "open-source" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/getprobo/probo.git", + "directory": "packages/skills" + }, + "bugs": { + "url": "https://github.com/getprobo/probo/issues" + }, + "homepage": "https://github.com/getprobo/probo/tree/main/packages/skills#readme", + "publishConfig": { + "access": "public" + } +} diff --git a/packages/skills/scripts/validate.mjs b/packages/skills/scripts/validate.mjs new file mode 100644 index 000000000..023f8fad7 --- /dev/null +++ b/packages/skills/scripts/validate.mjs @@ -0,0 +1,105 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +import { existsSync, readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); + +const requiredPaths = [ + ".claude-plugin/plugin.json", + ".codex-plugin/plugin.json", + ".agents/plugins/marketplace.json", + ".mcp.json", + "commands/access-review.md", + "commands/missing-signatures.md", + "skills/access-review/SKILL.md", + "skills/missing-signatures/SKILL.md", + "skills/open-source-compliance/SKILL.md", + "skills/access-review/references/mcp-tools.md", + "skills/access-review/references/decision-rubric.md", + "skills/access-review/references/notes-format.md", + "skills/missing-signatures/references/mcp-tools.md", + "skills/missing-signatures/references/report-format.md", + "skills/missing-signatures/references/notes-format.md", + "COMPATIBILITY.md", +]; + +let failed = false; + +for (const relativePath of requiredPaths) { + const absolutePath = join(root, relativePath); + if (!existsSync(absolutePath)) { + console.error(`missing required file: ${relativePath}`); + failed = true; + } +} + +const manifestPath = join(root, ".claude-plugin/plugin.json"); +const codexManifestPath = join(root, ".codex-plugin/plugin.json"); + +for (const [label, path] of [ + ["plugin.json", manifestPath], + [".codex-plugin/plugin.json", codexManifestPath], +]) { + if (!existsSync(path)) { + continue; + } + try { + const manifest = JSON.parse(readFileSync(path, "utf8")); + if (typeof manifest.name !== "string" || manifest.name.length === 0) { + console.error(`${label}: name must be a non-empty string`); + failed = true; + } + if (manifest.repository != null && typeof manifest.repository !== "string") { + console.error( + `${label}: repository must be a string URL, not an object`, + ); + failed = true; + } + if (manifest.bugs != null && typeof manifest.bugs !== "string") { + console.error(`${label}: bugs must be a string URL, not an object`); + failed = true; + } + } catch (error) { + console.error(`${label} is not valid JSON: ${error.message}`); + failed = true; + } +} + +const mcpPath = join(root, ".mcp.json"); +if (existsSync(mcpPath)) { + try { + const mcpConfig = JSON.parse(readFileSync(mcpPath, "utf8")); + const servers = mcpConfig.mcpServers ?? {}; + for (const [name, config] of Object.entries(servers)) { + if (config?.headers?.Authorization != null) { + console.error( + `.mcp.json: ${name} must use OAuth 2.0, not headers.Authorization`, + ); + failed = true; + } + } + } catch (error) { + console.error(`.mcp.json is not valid JSON: ${error.message}`); + failed = true; + } +} + +if (failed) { + process.exit(1); +} + +console.log("@probo/skills validation passed"); diff --git a/packages/skills/skills/access-review/SKILL.md b/packages/skills/skills/access-review/SKILL.md new file mode 100644 index 000000000..64b1b99bc --- /dev/null +++ b/packages/skills/skills/access-review/SKILL.md @@ -0,0 +1,82 @@ +--- +name: access-review +description: Run a semi-automated Probo access review campaign. Use when the user wants to review access entries, decide approve/revoke/escalate, or resume an in-progress campaign with MCP and .probo/access-reviews/ notes. +compatibility: Requires Probo MCP (OAuth 2.0) and file write access for .probo/access-reviews/ +--- + +# Access review + +Run a **semi-automated** access review for campaign `$ARGUMENTS` (or ask the +user for the campaign name). Review entries only — do not create, start, +cancel, or close campaigns. + +Before executing, read these files **relative to this skill directory**: + +- `references/mcp-tools.md` — MCP tool names, inputs, pagination +- `references/decision-rubric.md` — semi-auto decision rules +- `references/notes-format.md` — working memory file schema + +## Preconditions + +1. Probo MCP must be connected. If tools fail with auth errors, stop and tell + the user to complete OAuth sign-in for the Probo MCP server in their agent + (Claude Code: `/mcp` or `claude mcp login probo`; Codex: `codex mcp login + probo`; OpenCode/Cursor: configure MCP in settings then authenticate). +2. Resolve the campaign from `$ARGUMENTS` (name match or GID). If ambiguous, + list `listAccessReviewCampaigns` results and ask the user to pick one. +3. Campaign `status` must be `IN_PROGRESS` or `PENDING_ACTIONS`. Stop with a + clear message for `DRAFT`, `COMPLETED`, or `CANCELLED`. + +## Working notes file + +Create or resume `.probo/access-reviews/.md` per +`references/notes-format.md`. Create `.probo/access-reviews/` if missing. + +## Workflow + +### 1. Orient + +- Call `getAccessReviewStatistics` for the campaign. +- Summarize totals and pending count for the user. +- If no pending entries, report completion and stop. + +### 2. Fetch batch + +- Call `listAccessEntries` with `campaign_id`, `filter.decision: PENDING`, + `size: 50`. +- Use `last_cursor` from the notes file when resuming. + +### 3. Classify each entry + +Apply `references/decision-rubric.md`: + +| Class | Action | +| --- | --- | +| **Auto** | Queue for `recordAccessReviewEntryDecisions` | +| **Ambiguous** | Present to user; do not write yet | +| **Skip** | Log in notes only | + +Append each auto decision to the notes file before writing. + +### 4. Write auto decisions + +- Batch via `recordAccessReviewEntryDecisions` when possible. +- Non-`APPROVED` decisions **must** include `decision_note`. +- On MCP error, stop and do not advance `last_cursor`. + +### 5. Present ambiguous entries + +Show email, roles, flags, proposed decision, rationale. Record only after +explicit user confirmation. + +### 6. Checkpoint + +Update notes: `last_cursor`, session log, `updated_at`. Ask to continue if +`next_cursor` is set. + +## Hard rules + +- Never call `closeAccessReviewCampaign` or campaign setup mutations unless + the user explicitly requests setup work outside this skill. +- Never invent entry IDs or decisions — use MCP responses only. +- Never record non-`APPROVED` without `decision_note`. diff --git a/packages/skills/skills/access-review/references/decision-rubric.md b/packages/skills/skills/access-review/references/decision-rubric.md new file mode 100644 index 000000000..e881d0229 --- /dev/null +++ b/packages/skills/skills/access-review/references/decision-rubric.md @@ -0,0 +1,60 @@ +# Semi-auto decision rubric + +Classify each `PENDING` entry before writing to Probo. + +## Auto — record without asking + +Apply the **first matching rule** (top to bottom). Always set `decision_note` +for non-`APPROVED` decisions. + +| Condition | Decision | decision_note template | +| --- | --- | --- | +| `TERMINATED_USER` flag | `REVOKE` | Terminated user — access no longer required | +| `CONTRACTOR_EXPIRED` flag | `REVOKE` | Contractor engagement ended | +| `active === false` and no `NEW` tag | `REVOKE` | Account inactive at source | +| `ORPHANED` flag and `active === false` | `REVOKE` | Orphaned inactive account | +| `ORPHANED` flag only, `active !== false` | `ESCALATE` | Orphaned account still active — needs owner | +| `SHARED_ACCOUNT` flag | `ESCALATE` | Shared account — assign individual owner | +| `SOD_CONFLICT` flag | `ESCALATE` | Segregation of duties conflict | +| `PRIVILEGED_ACCESS` or `ROLE_CREEP` flag | `ESCALATE` | Privileged access requires explicit approval | +| `is_admin === true` and (`DORMANT` flag or last_login very stale) | `ESCALATE` | Admin access dormant — confirm business need | +| `account_type === SERVICE_ACCOUNT`, active, no danger flags | `APPROVED` | Service account with expected access | +| Active user, no flags (or only `NONE`), not admin, `incremental_tag !== NEW` | `APPROVED` | Routine access reaffirmed | + +"Very stale" last_login: no login in 90+ days when `last_login` is present. + +## Ambiguous — show user, do not write + +| Condition | Suggested default | Why ambiguous | +| --- | --- | --- | +| `incremental_tag === NEW` | `ESCALATE` | New access since last campaign | +| `NO_BUSINESS_JUSTIFICATION` flag | `ESCALATE` or `REVOKE` | Needs human judgment | +| `OUT_OF_DEPARTMENT` flag | `ESCALATE` | Role/department mismatch | +| `EXCESSIVE` or `ROLE_MISMATCH` flag | `ESCALATE` | Role change needs context | +| `is_admin === true` without dormant signals | `ESCALATE` | Admin approvals need explicit sign-off | +| `mfa_status === DISABLED` and (`is_admin` or privileged flags) | `ESCALATE` | MFA gap on sensitive access | +| `auth_method === API_KEY` or `PASSWORD` on production-like roles | `ESCALATE` | Non-SSO auth on sensitive access | +| Multiple conflicting flags | `ESCALATE` | Rubric rules disagree | +| `active === null` with revoke-leaning flags | `ESCALATE` | Unknown activity state | + +Present the suggested decision; wait for explicit user confirmation. + +## Flagging before decision + +Do not auto-flag unless the user asks. When reviewing ambiguous entries, you +may **suggest** `flagAccessReviewEntry` if Probo shows `NONE` but signals are +obvious (e.g. admin + 180d no login → suggest `DORMANT`). + +## Decision notes + +- `APPROVED` — `decision_note` optional +- `REVOKE`, `DEFER`, `ESCALATE` — `decision_note` **required** (MCP rejects empty) +- Keep notes short, factual, auditable. Reference flags and activity signals. + +## DEFER vs ESCALATE + +- `ESCALATE` — needs another reviewer or manager (security, HR, app owner) +- `DEFER` — modify access (role change, downgrade) before final approval; use + when the user indicates access should change rather than fully revoke + +Default to `ESCALATE` when unsure. diff --git a/packages/skills/skills/access-review/references/mcp-tools.md b/packages/skills/skills/access-review/references/mcp-tools.md new file mode 100644 index 000000000..ae30d7a90 --- /dev/null +++ b/packages/skills/skills/access-review/references/mcp-tools.md @@ -0,0 +1,93 @@ +# Access review MCP tools + +All tools are on the Probo MCP server (`probo`). Read each tool schema before +calling. + +## Read + +### `listAccessReviewCampaigns` + +List campaigns for an organization. Use to resolve `$ARGUMENTS` to a campaign +when the user provides a name instead of a GID. + +Required: `organization_id` + +### `listAccessEntries` + +List entries for a campaign. Primary data source for this command. + +| Field | Usage | +| --- | --- | +| `campaign_id` | Campaign GID | +| `filter.decision` | Use `PENDING` for review batches | +| `filter.flag` | Optional — focus on a flag (e.g. `TERMINATED_USER`) | +| `filter.incremental_tag` | Optional — `NEW`, `REMOVED`, `UNCHANGED` | +| `filter.is_admin` | Optional boolean | +| `filter.active` | Optional boolean | +| `size` | Page size; use `50` per batch | +| `cursor` | Resume pagination; store in notes file as `last_cursor` | + +Returns `entries[]` and `next_cursor`. + +### `getAccessReviewStatistics` + +Required: `campaign_id` + +Returns `statistics` with `total_count`, `decision_counts`, `flag_counts`, +`incremental_tag_counts`. Call at the start of each run and after large batches. + +## Write (semi-auto command) + +### `recordAccessReviewEntryDecisions` + +Preferred for auto batch. Input `decisions[]` with: + +- `access_review_entry_id` (required) +- `decision` — `APPROVED`, `REVOKE`, `DEFER`, `ESCALATE` (not `PENDING`) +- `decision_note` — required for non-`APPROVED` + +### `recordAccessReviewEntryDecision` + +Use for single entries after user confirms an ambiguous case. + +### `flagAccessReviewEntry` + +Optional when the user agrees a flag is missing. Input: + +- `access_review_entry_id` +- `flags[]` — see rubric for valid values +- `flag_reasons[]` — optional strings + +## Out of scope for this command + +Do not call unless the user explicitly asks for campaign setup: + +- `closeAccessReviewCampaign` +- `cancelAccessReviewCampaign` +- `startAccessReviewCampaign` +- `createAccessReviewCampaign` +- `createAccessReviewSource` / source mutations + +## Entry fields (review signals) + +| Field | Review use | +| --- | --- | +| `email`, `full_name` | Identity | +| `roles`, `job_title` | Access level | +| `is_admin` | Heightened scrutiny | +| `active` | `false` often supports revoke | +| `mfa_status` | `DISABLED` on privileged access → escalate | +| `auth_method` | `API_KEY`, `SERVICE_ACCOUNT` context | +| `account_type` | `SERVICE_ACCOUNT` vs `USER` | +| `last_login` | Dormancy signal | +| `incremental_tag` | `NEW` needs extra scrutiny | +| `flags`, `flag_reasons` | Primary risk signals | +| `decision` | Target `PENDING` entries only | + +## Pagination and resume + +1. Read `last_cursor` from the notes file. +2. Pass it to `listAccessEntries` to continue where the last batch stopped. +3. Write the new `next_cursor` back after each successful batch. +4. When `next_cursor` is null/empty, the pending page is exhausted — refresh + statistics to confirm remaining `PENDING` count. diff --git a/packages/skills/skills/access-review/references/notes-format.md b/packages/skills/skills/access-review/references/notes-format.md new file mode 100644 index 000000000..d825bf205 --- /dev/null +++ b/packages/skills/skills/access-review/references/notes-format.md @@ -0,0 +1,59 @@ +# Access review notes file + +Path: `.probo/access-reviews/.md` + +`` — lowercase campaign name with non-alphanumerics replaced by +hyphens (e.g. `Q3 GitHub Review` → `q3-github-review`). + +## Template + +```markdown +# Access review: + +campaign_id: +organization_id: +campaign_status: +last_cursor: +updated_at: + +## Session log + +- — Started review. Pending: . +- — Batch complete. Auto: approved, revoked, escalated, deferred. Ambiguous: . Cursor: . + +## Entry notes + +| entry_id | email | decision | auto | rationale | +|----------|-------|----------|------|-----------| +| gid://… | user@example.com | REVOKE | yes | Terminated user flag | + +## Ambiguous (awaiting user) + +| entry_id | email | flags | suggested | question | +|----------|-------|-------|-----------|----------| +| gid://… | admin@example.com | NEW, PRIVILEGED_ACCESS | ESCALATE | New admin — approve or revoke? | +``` + +## Field rules + +| Field | Rule | +| --- | --- | +| `last_cursor` | Empty on fresh run. Set to `listAccessEntries` `next_cursor` after each successful batch. Clear when null (pagination done). | +| `updated_at` | Update on every file write | +| `auto` column | `yes` if written via semi-auto rubric; `no` if user confirmed | +| Session log | Append-only; one line per batch or major event | +| Ambiguous table | Remove rows after user confirms and decision is recorded | + +## Resume behavior + +1. If the file exists, read `campaign_id`, `last_cursor`, and ambiguous rows. +2. Confirm with the user that resuming the same campaign is intended. +3. Continue `listAccessEntries` from `last_cursor` if set; otherwise start from + the first pending page. +4. Do not duplicate entry notes for IDs already in the table with a final + decision. + +## Git + +Do not commit or push this file unless the user asks. It is working memory for +the review session. diff --git a/packages/skills/skills/missing-signatures/SKILL.md b/packages/skills/skills/missing-signatures/SKILL.md new file mode 100644 index 000000000..d9c8b06c1 --- /dev/null +++ b/packages/skills/skills/missing-signatures/SKILL.md @@ -0,0 +1,109 @@ +--- +name: missing-signatures +description: Report who is missing document signatures or quorum approvals in Probo. Use when the user wants a per-person recap of unsigned signature requests, pending approval decisions, or document signing compliance status. +compatibility: Requires Probo MCP (OAuth 2.0) and file write access for .probo/missing-signatures/ +--- + +# Missing signatures report + +Build a **read-only, per-person recap** of outstanding document obligations for +organization `$ARGUMENTS` (or ask the user for the organization name). Cover: + +1. **Signature requests** — `REQUESTED` but not `SIGNED` on a published version +2. **Quorum approvals** — `PENDING` decisions on a `PENDING` approval quorum + while the version is `PENDING_APPROVAL` + +Before executing, read these files **relative to this skill directory**: + +- `references/mcp-tools.md` — MCP tool names, inputs, pagination +- `references/report-format.md` — per-person recap and summary layout +- `references/notes-format.md` — working memory for resumable scans + +## Preconditions + +1. Probo MCP must be connected. If tools fail with auth errors, stop and tell + the user to complete OAuth sign-in for the Probo MCP server in their agent + (Claude Code: `/mcp` or `claude mcp login probo`; Codex: `codex mcp login + probo`; OpenCode/Cursor: configure MCP in settings then authenticate). +2. Resolve the organization from `$ARGUMENTS` (name match or GID). If ambiguous, + call `listOrganizations` and ask the user to pick one. +3. This skill is **reporting only**. Do not request signatures, cancel requests, + publish documents, or submit approval decisions unless the user explicitly + asks for follow-up actions outside this report. + +## Working notes file + +Create or resume `.probo/missing-signatures/.md` per +`references/notes-format.md`. Create `.probo/missing-signatures/` if missing. + +## Workflow + +### 1. Orient + +- Record `organization_id` in the notes file. +- If resuming, read `last_document_cursor` and cached `profile_cache` from + notes. +- Tell the user whether this is a fresh scan or a resume. + +### 2. Scan documents (paginated) + +For each page from `listDocuments`: + +- Skip `ARCHIVED` documents. +- Call `listDocumentVersions` for the document. +- For each version, branch on `status`: + +| Version status | What to check | +| --- | --- | +| `PUBLISHED` | `listDocumentVersionSignatures` with `filter.states: ["REQUESTED"]` | +| `PENDING_APPROVAL` | `listDocumentVersionApprovalQuorums`; for each quorum with `status: PENDING`, `listDocumentVersionApprovalDecisions` with `filter.states: ["PENDING"]` | + +Prefer the **current published** version (`current_published_major` / +`current_published_minor` on the document) when multiple published minors exist. +Still scan all `PUBLISHED` versions if the user asked for exhaustive coverage. + +Paginate every list call. Store `last_document_cursor` after each document page +so a large org can be resumed. + +### 3. Resolve people + +Collect unique `signed_by` and `approver_id` profile GIDs. Resolve each once via +`getUser` and cache `full_name` and `email_address` in the notes file. Never +invent names. + +### 4. Build per-person recap + +Aggregate findings by profile. Each item is either: + +| Type | Meaning | +| --- | --- | +| `signature` | Signature requested, not yet signed | +| `approval` | Quorum approval decision still pending | + +Use `references/report-format.md` for the output layout. Sort people by +`full_name`, then email. + +### 5. Present summary + +Show: + +- Total people with at least one outstanding item +- Count by type (signatures vs approvals) +- Count by document +- The per-person recap tables + +If the scan is incomplete (more document pages), say so and offer to continue. + +### 6. Checkpoint + +Update notes: `last_document_cursor`, `profile_cache`, session log, `updated_at`. +Clear `last_document_cursor` when the document pagination is exhausted. + +## Hard rules + +- Never call write mutations (`requestDocumentVersionSignature`, + `cancelSignatureRequest`, `publishDocument`, `voidDocumentVersionApproval`) + unless the user explicitly requests action after reviewing the report. +- Never invent profile IDs, document titles, or states — use MCP responses only. +- Include document title, version (`major.minor`), and `requested_at` or quorum + `created_at` on every line item so the report is audit-ready. diff --git a/packages/skills/skills/missing-signatures/references/mcp-tools.md b/packages/skills/skills/missing-signatures/references/mcp-tools.md new file mode 100644 index 000000000..441dc7abf --- /dev/null +++ b/packages/skills/skills/missing-signatures/references/mcp-tools.md @@ -0,0 +1,143 @@ +# Missing signatures MCP tools + +All tools are on the Probo MCP server (`probo`). Read each tool schema before +calling. + +## Organization scope + +### `listOrganizations` + +List organizations the caller can access. Use to resolve `$ARGUMENTS` when the +user provides a name instead of a GID. + +### `listUsers` + +Resolve profile display fields. `users[]` entries are `Profile` objects. + +| Field | Usage | +| --- | --- | +| `organization_id` | Organization GID | +| `size` | Page size; use `100` when prefetching is helpful | +| `cursor` | Pagination | + +Prefer `getUser` for individual lookups when building the recap; use `listUsers` +only when bulk prefetch is faster. + +### `getUser` + +Required: `id` (profile GID) + +Returns `user` with `full_name`, `email_address`, `state`. Cache results in the +notes file to avoid repeat calls. + +## Documents and versions + +### `listDocuments` + +Primary iterator for the scan. + +| Field | Usage | +| --- | --- | +| `organization_id` | Required | +| `size` | Use `25` per page | +| `cursor` | Store in notes as `last_document_cursor` | +| `filter.status` | Optional — omit archived docs in post-processing or pass active-only if supported | + +Returns `documents[]` and `next_cursor`. Each document includes +`current_published_major`, `current_published_minor`, `status`. + +### `getDocument` + +Use when you need fresh `current_published_*` fields for one document. + +### `listDocumentVersions` + +Required: `document_id` + +| Field | Usage | +| --- | --- | +| `filter.statuses` | `["PUBLISHED"]` or `["PENDING_APPROVAL"]` to narrow | +| `size` / `cursor` | Paginate when a document has many versions | + +Returns `document_versions[]` with `title`, `major`, `minor`, `status`. + +### `getDocumentVersion` + +Use when you need a single version's metadata without listing all versions. + +## Signature gaps (published versions) + +### `listDocumentVersionSignatures` + +Required: `document_version_id` + +| Field | Usage | +| --- | --- | +| `filter.states` | `["REQUESTED"]` for outstanding signature requests | +| `size` / `cursor` | Paginate | + +Returns `document_version_signatures[]`. Key fields: + +| Field | Report use | +| --- | --- | +| `signed_by` | Profile GID — group by person | +| `state` | `REQUESTED` = missing signature | +| `requested_at` | When the request was sent | +| `signed_at` | `null` while outstanding | + +### `getDocumentVersionSignature` + +Use for detail on a single signature row if needed. + +## Quorum approval gaps (pending approval versions) + +### `listDocumentVersionApprovalQuorums` + +Required: `document_version_id` + +Returns `approval_quorums[]`. Only process quorums with `status: PENDING`. + +### `getDocumentVersionApprovalQuorum` + +Use when you need quorum metadata for one version. + +### `listDocumentVersionApprovalDecisions` + +Required: `quorum_id` + +| Field | Usage | +| --- | --- | +| `filter.states` | `["PENDING"]` for outstanding approvers | +| `size` / `cursor` | Paginate | + +Returns `approval_decisions[]`. Key fields: + +| Field | Report use | +| --- | --- | +| `approver_id` | Profile GID — group by person | +| `state` | `PENDING` = missing approval | +| `decided_at` | `null` while outstanding | +| `comment` | Include if present | + +### `getDocumentVersionApprovalDecision` + +Use for detail on a single decision row if needed. + +## Out of scope for this skill + +Do not call unless the user explicitly asks after reviewing the report: + +- `requestDocumentVersionSignature` +- `cancelSignatureRequest` +- `publishDocument` +- `voidDocumentVersionApproval` + +Signing and approving are GraphQL-only today (`signDocument`, +`approveDocumentVersion`, `rejectDocumentVersion`) — not available via MCP. + +## Pagination and resume + +1. Outer loop: `listDocuments` — persist `last_document_cursor` in notes. +2. Inner loops: paginate versions, signatures, quorums, and decisions. +3. When `listDocuments` returns no `next_cursor`, clear `last_document_cursor` + and mark the scan complete in the session log. diff --git a/packages/skills/skills/missing-signatures/references/notes-format.md b/packages/skills/skills/missing-signatures/references/notes-format.md new file mode 100644 index 000000000..bc90a28ab --- /dev/null +++ b/packages/skills/skills/missing-signatures/references/notes-format.md @@ -0,0 +1,57 @@ +# Missing signatures notes file + +Path: `.probo/missing-signatures/.md` + +`` — lowercase organization name with non-alphanumerics replaced by +hyphens (e.g. `Acme Corp` → `acme-corp`). + +## Template + +```markdown +# Missing signatures: + +organization_id: +last_document_cursor: +scan_status: +updated_at: + +## Profile cache + +| profile_id | full_name | email_address | +| --- | --- | --- | +| gid://… | Jane Doe | jane@example.com | + +## Session log + +- — Started scan. +- — Processed document page. Cursor: . +- — Scan complete. people, signatures, approvals. + +## Findings (raw) + +| profile_id | type | document_id | version_id | title | version | since | record_id | +| --- | --- | --- | --- | --- | --- | --- | --- | +``` + +## Field rules + +| Field | Rule | +| --- | --- | +| `last_document_cursor` | Empty on fresh run. Set to `listDocuments` `next_cursor` after each document page. Clear when pagination is done. | +| `scan_status` | `in_progress` until all document pages are processed | +| `updated_at` | Update on every file write | +| Profile cache | Append rows as `getUser` resolves profiles; reuse on resume | +| Findings table | Append rows as gaps are discovered; dedupe by `record_id` on resume | + +## Resume behavior + +1. If the file exists, read `organization_id`, `last_document_cursor`, profile + cache, and findings. +2. Confirm with the user that resuming the same organization is intended. +3. Continue `listDocuments` from `last_document_cursor` if set. +4. Do not duplicate findings rows with the same `record_id`. + +## Git + +Do not commit or push this file unless the user asks. It is working memory for +the reporting session. diff --git a/packages/skills/skills/missing-signatures/references/report-format.md b/packages/skills/skills/missing-signatures/references/report-format.md new file mode 100644 index 000000000..f84b60dcc --- /dev/null +++ b/packages/skills/skills/missing-signatures/references/report-format.md @@ -0,0 +1,89 @@ +# Missing signatures report format + +Present the report in this order: executive summary, per-person recap, optional +document index. + +## Executive summary + +```markdown +# Missing signatures: + +organization_id: +generated_at: +scan_status: + +## Summary + +| Metric | Count | +| --- | --- | +| People with outstanding items | | +| Outstanding signature requests | | +| Pending quorum approvals | | +| Documents affected | | + + +``` + +## Per-person recap (primary view) + +Sort alphabetically by `full_name`. One section per person with outstanding +items. Omit people with zero items. + +```markdown +## () + +profile_id: +profile_state: + +| Type | Document | Version | Since | ID | +| --- | --- | --- | --- | --- | +| signature | Information Security Policy | 2.1 | 2026-06-01 | | +| approval | Data Retention Policy | 3.0 | 2026-06-10 | | +``` + +### Column rules + +| Column | Rule | +| --- | --- | +| Type | `signature` or `approval` | +| Document | `document_versions[].title` from MCP | +| Version | `major.minor` (e.g. `2.1`) | +| Since | `requested_at` for signatures; quorum `created_at` or decision `created_at` for approvals (ISO date, UTC) | +| ID | `document_version_signature.id` or `approval_decision.id` for traceability | + +### Person with multiple items + +Keep one table per person. Do not split signatures and approvals into separate +top-level sections — the per-person view is the reporting unit. + +## Document index (optional appendix) + +When the user wants a document-centric view, add after the per-person recap: + +```markdown +## By document + +### () + +| Person | Type | Version | Since | +| --- | --- | --- | --- | +| Jane Doe | signature | 2.1 | 2026-06-01 | +``` + +## Empty result + +If no outstanding signatures or approvals exist: + +```markdown +No outstanding signature requests or pending quorum approvals were found for + as of . +``` + +Still write the notes file with `scan_status: complete`. + +## Presentation tips + +- Lead with the summary table, then the per-person recap. +- Flag `INACTIVE` profiles with outstanding items — they may need escalation. +- For `PENDING_APPROVAL` versions, note that publication is blocked until the + quorum resolves (unanimous approval required). diff --git a/packages/skills/skills/open-source-compliance/SKILL.md b/packages/skills/skills/open-source-compliance/SKILL.md new file mode 100644 index 000000000..8d8df2ba4 --- /dev/null +++ b/packages/skills/skills/open-source-compliance/SKILL.md @@ -0,0 +1,51 @@ +--- +name: open-source-compliance +description: This skill should be used when the user wants to assess, track, or automate open-source compliance work in Probo — vendor reviews, control mapping, evidence collection, risk registers, or policy workflows using Probo MCP tools. +--- + +# Open-source compliance with Probo + +Use the Probo MCP server bundled with this plugin to read and write GRC data. +Do not guess entity IDs or organization scope — discover them with MCP tools +first. + +## Before you start + +1. Confirm the Probo MCP server is connected. If not, ask the user to run + `/mcp` or `claude mcp login probo` to complete the OAuth 2.0 sign-in. +2. Identify the target organization. List organizations if the user did not + provide one. +3. Prefer MCP tools over manual API calls. The Probo MCP API mirrors the + platform's GraphQL surface. + +## Common workflows + +### Vendor / third-party review + +1. List or search third parties for the organization. +2. Pull existing risk assessments and contacts. +3. Record findings and update assessment status through MCP mutations. +4. Summarize residual risk and recommended follow-ups for the user. + +### Control and obligation tracking + +1. List controls, measures, or obligations relevant to the user's question. +2. Link evidence (documents, audits) where appropriate. +3. Report gaps between required and implemented controls. + +### Evidence and documentation + +1. Locate the relevant document or audit in Probo. +2. Fetch version history or published versions as needed. +3. Draft updates; use MCP upload tools when the user asks to attach files. + +## Output expectations + +- Cite Probo entity IDs (GIDs) for anything you create or update. +- Separate facts pulled from Probo from your analysis. +- Flag missing data instead of inventing compliance status. +- Keep recommendations actionable and mapped to Probo entities where possible. + +## References + +See `references/workflows.md` for extended workflow notes. diff --git a/packages/skills/skills/open-source-compliance/references/workflows.md b/packages/skills/skills/open-source-compliance/references/workflows.md new file mode 100644 index 000000000..09d827341 --- /dev/null +++ b/packages/skills/skills/open-source-compliance/references/workflows.md @@ -0,0 +1,48 @@ +# Open-source compliance workflows + +This plugin wraps Probo's MCP API for Claude Code. Probo is a self-hostable +GRC platform; its MCP server exposes tools for third parties, controls, +obligations, risks, documents, audits, access reviews, and more. + +## Authentication + +Probo MCP uses OAuth 2.0. Users sign in once via `/mcp` or +`claude mcp login probo`; Claude Code stores and refreshes tokens +automatically. Do not ask the user for API keys or bearer tokens for MCP +access. + +| Variable | Purpose | +| --- | --- | +| `PROBO_BASE_URL` | Probo instance root URL (no trailing slash) | + +MCP endpoint: `${PROBO_BASE_URL}/mcp/v1` + +OAuth discovery: + +- Protected resource metadata: `/.well-known/oauth-protected-resource` +- Authorization server metadata: `/.well-known/oauth-authorization-server` + +## Tool discovery + +When unsure which MCP tool to use: + +1. Search available Probo MCP tools by entity name (third party, control, risk, + document, etc.). +2. Read the tool schema before calling it. +3. Paginate list operations; default page sizes may truncate results. + +## Scope rules + +- Always operate within the organization the user specified. +- Do not exfiltrate tokens or raw credentials in responses. +- Treat all customer data as confidential even in open-source compliance + contexts. + +## Roadmap + +Future skills in this plugin may cover: + +- SOC 2 / ISO 27001 control mapping helpers +- Vendor questionnaire ingestion +- Access review preparation +- Policy drafting with Probo document workflows