Skip to main content

cr-create

Drive an end-to-end GitBook docs review flow from Claude Code by calling the GitBook REST API directly with curl (no CLI) — create a change request, push content (update an existing page AND create a new page), request reviewers, notify Slack, then pull review comments back in, fix them, re-push, and resolve. This is the authoring-side companion to cr-review (the reviewer side over the same API). Use this whenever someone wants to run a "docs review in GitBook" loop from the terminal/agent against the raw API (curl/HTTP), mentions creating a change request via the API, pushing content into a CR, "pull in the latest comments and fix them," requesting review on docs, or showing engineers how to collaborate on GitBook docs from Claude + Slack without a CLI.

معلومات المصدر

المستودع
GitbookIO/gitbook-skills
آخر نشاط في المصدر
٢٤ سبتمبر ٢٠٢٦ في ١٣:٢١
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
١٧
التفرعات
٤

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
3 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
cr-create
metadata
{"version":"1.0"}
description
Drive an end-to-end GitBook docs review flow from Claude Code by calling the GitBook REST API directly with curl (no CLI) — create a change request, push content (update an existing page AND create a new page), request reviewers, notify Slack, then pull review comments back in, fix them, re-push, and resolve. This is the authoring-side companion to cr-review (the reviewer side over the same API). Use this whenever someone wants to run a "docs review in GitBook" loop from the terminal/agent against the raw API (curl/HTTP), mentions creating a change request via the API, pushing content into a CR, "pull in the latest comments and fix them," requesting review on docs, or showing engineers how to collaborate on GitBook docs from Claude + Slack without a CLI.
# GitBook Review Flow (direct API) Run a documentation review loop against a GitBook space entirely through the **GitBook REST API** (`https://api.gitbook.com/v1`, hit with `curl`), so an engineer never has to leave Claude Code (plus Slack) to propose docs changes and get them reviewed. This is the authoring-side companion to `cr-review` (the reviewer side over the same API). Every action here is a plain HTTP call — there is no CLI and no helper script. The same actions serve three purposes with no separate code paths: - **CR-creation demo** — create a change request and push content (one existing page updated, one new page created). - **Notify/review demo** — request reviewers, drop a Slack link, pull comments, fix, re-push, resolve. - **Real use** — the identical actions against the user's own content. Because the demo is just a scripted sequence of the real actions, it cannot show something that doesn't actually work. Keep it that way: never fake an output. ## Auth and the `gbapi` helper Every call is a Bearer-authenticated request to `https://api.gitbook.com/v1`. The token lives in **`GITBOOK_TOKEN`** in the repo-root `.env` (create one at https://app.gitbook.com/account/developer). **Never print the token; never write it to a tracked file.** If it's missing, prompt the user for it and write it to `.env`; don't invent one. Define this shell helper once per session and use it for every call below. It loads the token from `.env`, sets the base URL and headers, and — critically for the "never fake output" rule — **fails loudly on any non-2xx, printing the API's error body** (`curl --fail-with-body`, curl ≥ 7.76 / stock on current macOS): ```bash set -a; [ -f .env ] && . ./.env; set +a # load GITBOOK_TOKEN (and SLACK_WEBHOOK_URL) gbapi() { # gbapi METHOD /path [extra curl args…] local method="$1" apipath="$2"; shift 2 # NB: not `path` — in zsh that is tied to $PATH curl -sS --fail-with-body -X "$method" \ "https://api.gitbook.com/v1${apipath}" \ -H "Authorization: Bearer ${GITBOOK_TOKEN}" \ -H "Content-Type: application/json" "$@" } ``` Every response is **JSON** — pipe it through `jq` and read whole objects. **Never hand-parse by grepping/line-pairing fields** (bind the wrong title↔id and you act on the wrong space/CR). If `gbapi` exits non-zero, surface the printed error — do not report success. ## Endpoint map (verified against api.gitbook.com/openapi.json) `<space>`, `<cr>`, `<pageId>`, `<commentId>` are the relevant IDs. Base URL is `https://api.gitbook.com/v1`; all paths below are relative to it. | Step | Method + path | Notes | |------|---------------|-------| | Who am I | `GET /user` | returns `{id, displayName, email}` — your own user ID is `.id` | | List pages | `GET /spaces/<space>/content/pages` | flat-ish tree with `id`, `title`, `type` | | Get a page (base) | `GET /spaces/<space>/content/page/<pageId>?format=markdown` | current markdown of a page on the live space | | Create CR *(GATE)* | `POST /spaces/<space>/change-requests` body `{"subject":"…"}` | returns the CR object with `id` and `urls.app` (also a `Location` header) — **`urls.app` is only the editor/diff link, not a rendered preview**; see "Surfacing the preview link" | | Get CR | `GET /spaces/<space>/change-requests/<cr>` | `subject`, `status`, `createdBy`, `comments`, `urls.app` | | Push content | `POST /spaces/<space>/change-requests/<cr>/content` body `{"changes":[…]}` | 1–50 ops, applied sequentially in one new revision; all-or-nothing | | Find the site behind a space | `GET /spaces/<space>` → `.organization`; `GET /orgs/<org>/sites`; `GET /orgs/<org>/sites/<site>/site-spaces` → match `.items[].space.id` | needed only to resolve the site preview link (see below); a space isn't required to belong to a site | | Get a site (for its preview link) | `GET /orgs/<org>/sites/<site>` | `urls.preview` (draft/CR content), `urls.published` (only once live) — **not part of the change-request response at all** | | Get a page (CR side) | `GET /spaces/<space>/change-requests/<cr>/content/page/<pageId>?format=markdown` | verify what actually landed in the CR | | Request reviewers *(GATE)* | `POST /spaces/<space>/change-requests/<cr>/requested-reviewers` body `{"users":["…"]}` | array of user IDs; optional `subject`/`description` | | List comments | `GET /spaces/<space>/change-requests/<cr>/comments?format=markdown&status=all` | bodies at `body.markdown`; location under `target.page`/`target.node`; poster at `postedBy.id` | | Reply to a comment | `POST /spaces/<space>/change-requests/<cr>/comments/<commentId>/replies` body `{"body":{"markdown":"…"}}` | | | Resolve a comment *(GATE)* | `PUT /spaces/<space>/change-requests/<cr>/comments/<commentId>` body `{"resolved":true}` | resolves unconditionally — no reply-first guard (enforce it yourself) | | Reply list (verify) | `GET /spaces/<space>/change-requests/<cr>/comments/<commentId>/replies` | confirm a reply exists before resolving | Not a GitBook API operation: any Slack/Channels action. Slack is sent **separately** (see "Slack is a stopgap"). ### Content-change ops (the `changes` array) Each item in `changes` is discriminated by `operation` (OpenAPI `ChangeRequestContentChange`): - **`update_page`** — update document and/or metadata on an existing page. - Body document: `{"operation":"update_page","page":"<pageId>","document":{"markdown":"…"}}`. `document` accepts **only** `{"markdown":"…"}` — **not** the node tree that `GET …/page` returns with `format=document` (pushing that 422s). When `document` is provided it replaces the whole page body — fetch, edit, push, or you drop blocks. - **Rename / metadata** (same op, no document required): set `title` and/or `slug`, plus optional `emoji`, `icon`, `description`, `hidden`, `noIndex`, `noRobotsIndex`, `condition` (`RevisionPageUpdate`). Title-only rename: set `title`, omit `slug`. URL-only: set `slug`, omit `title`. Example: `{"operation":"update_page","page":"<pageId>","title":"New title","slug":"new-slug"}`. - **Move**: `into` (parent page id, or `null` for root) and/or `at` (zero-based sibling index). - For Git-synced spaces, also update `SUMMARY.md` when the nav title/path should change. - **`insert_page`** — `{"operation":"insert_page","title":"…","document":{"markdown":"…"}}`. `into` (parent page ID) is **optional** — omit it to insert at the space root; `at` (index) is also optional. Title can come from `title`, markdown frontmatter, or the leading heading. - **`delete_page`** — `{"operation":"delete_page","page":"<pageId>"}`. This flow never deletes; documented for completeness. - **`insert_files`** — upload images/attachments into the CR: `{"operation":"insert_files","files":[{"name":"diagram.png","url":"https://…","ref":"diagram"}]}` (or `base64` + `contentType`, max 1MB). Prefer `url`. Reference as `![Alt](/files/<fileId>)` or via batch `ref`. Do **not** rely on inline `data:` URIs in markdown. - **`revert_page`** — `{"operation":"revert_page","page":"<pageId>"}` (optional `revision`, optional `block` array). Omitting `revision` reverts to the CR base revision. **`compat` query param** on `POST …/content`: pass `?compat=false` so the response includes the affected-page list (`ChangeRequestContentUpdate`). Default is still `true` today but will flip; prefer `compat=false` now. The markdown round-trip is **LOSSY** — see "Editing an existing page safely" before you re-push an edited page. ### API behaviors to watch - **Every endpoint returns JSON.** `GET /user` is just JSON with an `.id` — pipe every response through `jq`. - **The `authors` comment filter works server-side.** `GET …/comments?authors=<id>` is a real array query param (repeat `authors=` for several). You still pull **all** comments and split human vs agent on `postedBy.id` (see "Two operations") — a filter narrows, it doesn't classify — but the server-side filter is available if you want it. - **Nothing normalizes content for you.** The API does not strip the duplicated leading H1 or collapse multi-line `{% … %}` blocks before sending. **You must do those transforms yourself** before every push (see "Editing an existing page safely"). This is the easiest thing to get wrong — don't skip it. ## Surfacing the preview link (do this every time) **This rule is transport-agnostic — it applies whether the change request was pushed via this skill's `curl` calls, or via `configure-site`/`write-docs` over the GitBook MCP server.** The underlying gap is the same in both cases: nothing in the change-request response points at the rendered preview, so it's easy to file this under "REST-only demo detail" and skip it when the push actually happened over MCP. It isn't optional in either case. If you reach this skill's docs while working from `configure-site` or `write-docs`, translate the REST calls below to their MCP equivalents (`getSpaceById`, `list_sites`/`get_site_structure`, `getSiteById` via `invoke_operation`) rather than skipping the step because the transport doesn't match. A change request's own response only ever gives you `urls.app` — the link to the **editor / diff view** in the GitBook app. It is easy to stop there and assume that's "the link" for the CR. It isn't the link most people actually want: someone who isn't going to comment or edit just wants to **see the docs rendered with this change applied**, and that's a different URL that GitBook calls the **site preview**. The site preview link is **not exposed anywhere on the change-request object** — verified against the `ChangeRequest` schema, whose `urls` only has `app` and `location`. It lives on the **`Site`** object instead, nested under `urls.preview`, which you only ever see if you separately resolve the site behind the space. Nothing in the CR-creation or content-push flow points you at it, so it's easy to never discover it exists at all. Resolve it once per space (cache the result for the session) and mention it **alongside** `urls.app` every time you create a CR or push content to one: ```bash ORG=$(gbapi GET "/spaces/<space>" | jq -r .organization) SITE=$(gbapi GET "/orgs/$ORG/sites" | jq -r '.items[].id' | while read -r s; do gbapi GET "/orgs/$ORG/sites/$s/site-spaces" \ | jq -e --arg space "<space>" '.items[] | select(.space.id == $space)' >/dev/null \ && echo "$s" && break done) if [ -n "$SITE" ]; then # A public site's published URL needs no sign-in and never expires; anything else # (unlisted, visitor-auth, not yet published) has to go through the preview host. BASE=$(gbapi GET "/orgs/$ORG/sites/$SITE" \ | jq -r 'if .visibility == "public" and .urls.published then .urls.published else .urls.preview end') NUM=$(gbapi GET "/spaces/<space>/change-requests/<cr>" | jq -r .number) echo "${BASE%/}/~/changes/${NUM}/" # ← the preview link for THIS change request fi ``` - **The `~/changes/<number>/` segment is what scopes the link to your change request.** A bare site URL — `urls.published` or `urls.preview` — renders whatever the site currently holds, so it loads fine and shows the wrong thing. Both come back from the API with a trailing slash, so strip it before appending or you emit a double slash. - **`urls.published`** — the live site URL; only present once the site has been published. Prefer it when the site is public: no sign-in, no expiry, safe to paste anywhere. - **`urls.preview`** — the site preview host. Viewers still need access to the site and are asked to sign in, so it's a worse link to hand to someone — use it only when there's no public published URL. - **Preview only exists when the space is attached to a published docs site** — not for a bare space with no site, and GitBook itself disables the preview UI for share-link / visitor-auth sites. If the site-spaces search above finds nothing, say so plainly (*"this space isn't on a published site, so there's no rendered preview link — here's the editor link"*) rather than silently only giving `urls.app`. - If a space is unexpectedly attached to more than one site, resolve and mention all of them rather than picking one. Use the CR's `number`; its `id` works too but is longer. A **draft** change request previews fine — you don't have to open it first. **Check the link before you send it.** `curl -sL -o /dev/null -w '%{http_code}\n' "<url>"`. A 404 means the wrong number or the wrong site. A 200 is necessary but *not* sufficient — an archived CR returns 200 as well — so when it matters, fetch a page the CR touched and confirm it differs from the same path on the live site. Report both links together, e.g.: *"Change request #42 created — [review the diff](…urls.app) · [preview the rendered docs](https://docs.example.com/~/changes/42/)."* Note the preview link carries the `~/changes/42/` segment; a bare site URL is not a preview of this change request. ## Prerequisites - **`curl` and `jq`** on your `PATH`, and network access to `api.gitbook.com`. - **`GITBOOK_TOKEN`** in the repo-root `.env` (see "Auth"). Confirm with `gbapi GET /user` before running actions. - The **space ID** of the target space (and, for the demo, the page ID to update and a
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub