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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"name": "docent",
"source": "./plugins/docent",
"description": "Docent AI analysis tools for Claude Code",
"version": "0.1.8",
"version": "0.1.9",
"author": {
"name": "TransluceAI"
},
Expand Down
100 changes: 100 additions & 0 deletions .github/workflows/plugin-sanity.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Plugin sanity

on:
push:
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
sanity:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Validate Claude Code plugin package
run: |
python - <<'PY'
import json
import re
from pathlib import Path

root = Path.cwd()

def fail(message: str) -> None:
raise SystemExit(message)

def load_json(path: Path) -> dict:
try:
return json.loads(path.read_text(encoding="utf-8"))
except Exception as exc:
fail(f"{path} is not valid JSON: {exc}")

marketplace = load_json(root / ".claude-plugin" / "marketplace.json")
entries = marketplace.get("plugins")
if not isinstance(entries, list):
fail("marketplace plugins must be a list")

docent_entries = [entry for entry in entries if entry.get("name") == "docent"]
if len(docent_entries) != 1:
fail("marketplace must contain exactly one docent plugin entry")

entry = docent_entries[0]
plugin_dir = root / entry.get("source", "")
if not plugin_dir.is_dir():
fail(f"marketplace source does not exist: {plugin_dir}")

manifest = load_json(plugin_dir / ".claude-plugin" / "plugin.json")
if manifest.get("name") != "docent":
fail("plugin manifest name must be docent")

version = manifest.get("version")
if not isinstance(version, str) or not re.fullmatch(r"\d+\.\d+\.\d+", version):
fail("plugin manifest version must be plain major.minor.patch")
if entry.get("version") != version:
fail("marketplace docent version must match plugin manifest version")

required_files = [
".claude-plugin/plugin.json",
".mcp.json",
"skills/docent/SKILL.md",
"skills/docent/analysis.md",
"skills/docent/dql-reference.md",
"skills/docent/ingestion-reference.md",
"skills/docent/ingestion.md",
"skills/docent/readings-reference.md",
"skills/docent/report.md",
]
for rel_path in required_files:
path = plugin_dir / rel_path
if not path.is_file():
fail(f"required plugin file is missing: {rel_path}")
if path.suffix == ".md" and not path.read_text(encoding="utf-8").strip():
fail(f"markdown file is empty: {rel_path}")

mcp = load_json(plugin_dir / ".mcp.json")
server = mcp.get("mcpServers", {}).get("docent")
if not isinstance(server, dict):
fail(".mcp.json must define mcpServers.docent")
if server.get("type") != "stdio" or server.get("command") != "uv":
fail("docent MCP server must run as uv stdio")
args = server.get("args")
if not isinstance(args, list) or "--from" not in args:
fail("docent MCP server args must include --from")
package = args[args.index("--from") + 1]
if package != "docent-python>=0.1.73":
fail("docent MCP server must require docent-python>=0.1.73")

forbidden_names = {".mcp.local.json", "docent.env"}
for path in plugin_dir.rglob("*"):
if path.name in forbidden_names or path.name.startswith("docent.env."):
fail(f"local credential/config file must not be published: {path}")

print("Claude Code plugin sanity checks passed")
PY
2 changes: 1 addition & 1 deletion plugins/docent/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "docent",
"version": "0.1.8",
"version": "0.1.9",
"description": "Docent AI analysis tools"
}
2 changes: 1 addition & 1 deletion plugins/docent/.mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"docent": {
"type": "stdio",
"command": "uv",
"args": ["tool", "run", "--from", "docent-python", "docent-mcp"]
"args": ["tool", "run", "--from", "docent-python>=0.1.73", "docent-mcp"]
}
}
}
14 changes: 5 additions & 9 deletions plugins/docent/skills/docent/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
---
name: docent
description: Unified skill for the Docent AI platform. Includes instructions on how to analyze, report on, and ingest AI agent runs, as well as API references.
description: Docent is a platform for analyzing AI agent behavior. Always load this skill before interacting with the Docent platform.
alwaysApply: true
---

# Docent

This is the root skill for all Docent work. Use it whenever the user wants to analyze runs, ingest data, create or update reports, or look up how the Docent SDK works.
This is the root skill for all Docent work. This file is just a table of contents. In most cases you should read one of the guides below before starting to work with docent. Choose the guide that best matches your task.

## Choose the right guide

- For analyzing or answering questions about agent runs, exploring collections: `./analysis.md`
- For exploring a collection of agent runs, analyzing data, answering questions about agent behavior: `./analysis.md`
- For ingestion workflows that convert local logs or eval traces into Docent data: `./ingestion.md`
- If the user is asking to manipulate data in the platform through code or the command line, see the SDK reference.

## API references
## Other available documentation

- For the Readings API (`client.read`, `client.query`, batching, prompts, clustering): `./readings-reference.md`
- For DQL syntax, schemas, quirks, and example queries: `./dql-reference.md`
- For the reports API: `./report.md` (only if the user explicitly asks for a report)
- For ingestion-side data-model and conversion examples: the reference and pattern sections in `./ingestion.md`
- For ingestion-side data-model and conversion examples: `./ingestion-reference.md`
- SDK reference is available by visiting [our online documentation](https://docs.transluce.org/llms.txt)

Open only the sibling docs that match the user's task; do not load everything by default.
Loading
Loading