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 <cursoragent@cursor.com> Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
This commit is contained in:
30
.agents/plugins/marketplace.json
Normal file
30
.agents/plugins/marketplace.json
Normal file
@@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
25
.claude-plugin/marketplace.json
Normal file
25
.claude-plugin/marketplace.json
Normal file
@@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
98
.github/workflows/release-npm-skills.yaml
vendored
Normal file
98
.github/workflows/release-npm-skills.yaml
vendored
Normal file
@@ -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
|
||||||
@@ -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/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/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/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)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Release
|
# 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
|
version source, its own `CHANGELOG.md`, its own tag pattern, and its own
|
||||||
release workflow. Cutting a release means: bump the version, write a
|
release workflow. Cutting a release means: bump the version, write a
|
||||||
changelog entry, commit, tag, push.
|
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-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/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/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) |
|
| Helm chart (`probo`) | `helm/v*` | [helm.md](./helm.md) |
|
||||||
|
|
||||||
When the user asks for a release **without specifying a track**, follow
|
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 \
|
git log $(git describe --tags --abbrev=0 --match='@probo/cookie-banner/v*')..HEAD --oneline \
|
||||||
-- packages/cookie-banner
|
-- packages/cookie-banner
|
||||||
|
|
||||||
|
# @probo/skills
|
||||||
|
git log $(git describe --tags --abbrev=0 --match='@probo/skills/v*')..HEAD --oneline \
|
||||||
|
-- packages/skills
|
||||||
|
|
||||||
# helm chart
|
# helm chart
|
||||||
git log $(git describe --tags --abbrev=0 --match='helm/v*')..HEAD --oneline \
|
git log $(git describe --tags --abbrev=0 --match='helm/v*')..HEAD --oneline \
|
||||||
-- contrib/helm
|
-- contrib/helm
|
||||||
|
|||||||
31
contrib/claude/release/skills.md
Normal file
31
contrib/claude/release/skills.md
Normal file
@@ -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 <X.Y.Z> --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.
|
||||||
128
contrib/claude/skills.md
Normal file
128
contrib/claude/skills.md
Normal file
@@ -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:<skill-name>` (e.g. `/probo:open-source-compliance`,
|
||||||
|
`/probo:missing-signatures`, `/probo:access-review`)
|
||||||
|
- Commands: `/probo:<command-name>` (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-name>/
|
||||||
|
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/<name>/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:<name>
|
||||||
|
```
|
||||||
|
|
||||||
|
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/<name>.md` with a shared `skills/<name>/SKILL.md` so Codex and
|
||||||
|
OpenCode load the same workflow. Reference docs live under
|
||||||
|
`skills/<name>/references/` using paths relative to the skill directory (not
|
||||||
|
`${CLAUDE_PLUGIN_ROOT}`).
|
||||||
|
|
||||||
|
1. Create `commands/<name>.md` with frontmatter (`description`,
|
||||||
|
`argument-hint`, `disable-model-invocation: true` when writes are involved).
|
||||||
|
2. Add reference docs under `skills/<name>/references/`.
|
||||||
|
3. Register paths in `scripts/validate.mjs`.
|
||||||
|
4. Test: `/probo:<name> <args>` 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).
|
||||||
@@ -17,6 +17,7 @@ export default defineConfig([
|
|||||||
"pkg/**",
|
"pkg/**",
|
||||||
"packages/coredata/**",
|
"packages/coredata/**",
|
||||||
"packages/cookie-banner/**",
|
"packages/cookie-banner/**",
|
||||||
|
"packages/skills/**",
|
||||||
"packages/emails/**",
|
"packages/emails/**",
|
||||||
"packages/eslint-relay-plugin-types/**",
|
"packages/eslint-relay-plugin-types/**",
|
||||||
"packages/helpers/**",
|
"packages/helpers/**",
|
||||||
|
|||||||
10
package-lock.json
generated
10
package-lock.json
generated
@@ -4820,6 +4820,10 @@
|
|||||||
"resolved": "packages/routes",
|
"resolved": "packages/routes",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@probo/skills": {
|
||||||
|
"resolved": "packages/skills",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/@probo/trust": {
|
"node_modules/@probo/trust": {
|
||||||
"resolved": "apps/trust",
|
"resolved": "apps/trust",
|
||||||
"link": true
|
"link": true
|
||||||
@@ -19347,6 +19351,7 @@
|
|||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
],
|
],
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
}
|
}
|
||||||
@@ -21151,6 +21156,11 @@
|
|||||||
"relay-runtime": "^21.0.1"
|
"relay-runtime": "^21.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"packages/skills": {
|
||||||
|
"name": "@probo/skills",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"packages/tsconfig": {
|
"packages/tsconfig": {
|
||||||
"name": "@probo/tsconfig",
|
"name": "@probo/tsconfig",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
|
|||||||
30
packages/skills/.agents/plugins/marketplace.json
Normal file
30
packages/skills/.agents/plugins/marketplace.json
Normal file
@@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
28
packages/skills/.claude-plugin/marketplace.json
Normal file
28
packages/skills/.claude-plugin/marketplace.json
Normal file
@@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
21
packages/skills/.claude-plugin/plugin.json
Normal file
21
packages/skills/.claude-plugin/plugin.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
21
packages/skills/.codex-plugin/plugin.json
Normal file
21
packages/skills/.codex-plugin/plugin.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
8
packages/skills/.mcp.json
Normal file
8
packages/skills/.mcp.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"probo": {
|
||||||
|
"type": "http",
|
||||||
|
"url": "${PROBO_BASE_URL}/mcp/v1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
packages/skills/CHANGELOG.md
Normal file
29
packages/skills/CHANGELOG.md
Normal file
@@ -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)
|
||||||
154
packages/skills/COMPATIBILITY.md
Normal file
154
packages/skills/COMPATIBILITY.md
Normal file
@@ -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/<name>/SKILL.md` | ✅ Agent Skills standard |
|
||||||
|
| `skills/<name>/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
|
||||||
|
```
|
||||||
98
packages/skills/README.md
Normal file
98
packages/skills/README.md
Normal file
@@ -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:<skill-name>` (e.g. `/probo:open-source-compliance`, `/probo:missing-signatures`).
|
||||||
|
|
||||||
|
Commands: `/probo:<command-name>` (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).
|
||||||
0
packages/skills/agents/.gitkeep
Normal file
0
packages/skills/agents/.gitkeep
Normal file
17
packages/skills/commands/access-review.md
Normal file
17
packages/skills/commands/access-review.md
Normal file
@@ -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.
|
||||||
17
packages/skills/commands/missing-signatures.md
Normal file
17
packages/skills/commands/missing-signatures.md
Normal file
@@ -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.
|
||||||
0
packages/skills/hooks/.gitkeep
Normal file
0
packages/skills/hooks/.gitkeep
Normal file
52
packages/skills/package.json
Normal file
52
packages/skills/package.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
105
packages/skills/scripts/validate.mjs
Normal file
105
packages/skills/scripts/validate.mjs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||||
|
//
|
||||||
|
// 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");
|
||||||
82
packages/skills/skills/access-review/SKILL.md
Normal file
82
packages/skills/skills/access-review/SKILL.md
Normal file
@@ -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/<campaign-slug>.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`.
|
||||||
@@ -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.
|
||||||
93
packages/skills/skills/access-review/references/mcp-tools.md
Normal file
93
packages/skills/skills/access-review/references/mcp-tools.md
Normal file
@@ -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.
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# Access review notes file
|
||||||
|
|
||||||
|
Path: `.probo/access-reviews/<campaign-slug>.md`
|
||||||
|
|
||||||
|
`<campaign-slug>` — lowercase campaign name with non-alphanumerics replaced by
|
||||||
|
hyphens (e.g. `Q3 GitHub Review` → `q3-github-review`).
|
||||||
|
|
||||||
|
## Template
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Access review: <campaign name>
|
||||||
|
|
||||||
|
campaign_id: <gid>
|
||||||
|
organization_id: <gid>
|
||||||
|
campaign_status: <IN_PROGRESS|PENDING_ACTIONS>
|
||||||
|
last_cursor:
|
||||||
|
updated_at: <ISO-8601 UTC>
|
||||||
|
|
||||||
|
## Session log
|
||||||
|
|
||||||
|
- <ISO-8601> — Started review. Pending: <n>.
|
||||||
|
- <ISO-8601> — Batch complete. Auto: <a> approved, <r> revoked, <e> escalated, <d> deferred. Ambiguous: <u>. Cursor: <cursor or done>.
|
||||||
|
|
||||||
|
## 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.
|
||||||
109
packages/skills/skills/missing-signatures/SKILL.md
Normal file
109
packages/skills/skills/missing-signatures/SKILL.md
Normal file
@@ -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/<org-slug>.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.
|
||||||
@@ -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.
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# Missing signatures notes file
|
||||||
|
|
||||||
|
Path: `.probo/missing-signatures/<org-slug>.md`
|
||||||
|
|
||||||
|
`<org-slug>` — lowercase organization name with non-alphanumerics replaced by
|
||||||
|
hyphens (e.g. `Acme Corp` → `acme-corp`).
|
||||||
|
|
||||||
|
## Template
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Missing signatures: <organization name>
|
||||||
|
|
||||||
|
organization_id: <gid>
|
||||||
|
last_document_cursor:
|
||||||
|
scan_status: <in_progress|complete>
|
||||||
|
updated_at: <ISO-8601 UTC>
|
||||||
|
|
||||||
|
## Profile cache
|
||||||
|
|
||||||
|
| profile_id | full_name | email_address |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| gid://… | Jane Doe | jane@example.com |
|
||||||
|
|
||||||
|
## Session log
|
||||||
|
|
||||||
|
- <ISO-8601> — Started scan.
|
||||||
|
- <ISO-8601> — Processed document page. Cursor: <cursor or done>.
|
||||||
|
- <ISO-8601> — Scan complete. <n> people, <s> signatures, <a> 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.
|
||||||
@@ -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 name>
|
||||||
|
|
||||||
|
organization_id: <gid>
|
||||||
|
generated_at: <ISO-8601 UTC>
|
||||||
|
scan_status: <complete|in_progress>
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
| Metric | Count |
|
||||||
|
| --- | --- |
|
||||||
|
| People with outstanding items | <n> |
|
||||||
|
| Outstanding signature requests | <n> |
|
||||||
|
| Pending quorum approvals | <n> |
|
||||||
|
| Documents affected | <n> |
|
||||||
|
|
||||||
|
<One paragraph in plain language for auditors or managers.>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Per-person recap (primary view)
|
||||||
|
|
||||||
|
Sort alphabetically by `full_name`. One section per person with outstanding
|
||||||
|
items. Omit people with zero items.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## <full_name> (<email_address>)
|
||||||
|
|
||||||
|
profile_id: <gid>
|
||||||
|
profile_state: <ACTIVE|INACTIVE>
|
||||||
|
|
||||||
|
| Type | Document | Version | Since | ID |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| signature | Information Security Policy | 2.1 | 2026-06-01 | <signature gid> |
|
||||||
|
| approval | Data Retention Policy | 3.0 | 2026-06-10 | <decision gid> |
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
### <document title> (<document_id>)
|
||||||
|
|
||||||
|
| 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
|
||||||
|
<organization name> as of <generated_at>.
|
||||||
|
```
|
||||||
|
|
||||||
|
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).
|
||||||
51
packages/skills/skills/open-source-compliance/SKILL.md
Normal file
51
packages/skills/skills/open-source-compliance/SKILL.md
Normal file
@@ -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.
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user