Skip to content

Commit 777e9ad

Browse files
Add CLAUDE.md and generate-openapi-from-pr Claude skill (#395)
* Add CLAUDE.md and generate-openapi-from-pr skill Add repo-level CLAUDE.md documenting the OpenAPI spec structure, CI/CD workflows, editing conventions, and SDK generation safety rules. Add a Claude skill that automates generating OpenAPI spec changes from intercom monolith PRs. The skill analyzes PR diffs (controllers, presenters, version changes, routes) and produces the corresponding YAML updates. Includes reference guides for Ruby-to-OpenAPI mapping, YAML patterns, and version propagation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add auto-inject hook, argument hint, and CLAUDE.md skill directive Add a UserPromptSubmit hook that detects intercom PR URLs and injects context telling Claude to use the generate-openapi-from-pr skill. Add user-invocable metadata and argument-hint to SKILL.md frontmatter. Update CLAUDE.md to explicitly direct Claude to use the skill. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Improve skill based on skill-creator review - Make description pushier for better trigger accuracy (catches PR URLs, "update the spec", "document API change", etc.) - Remove redundant "When to Activate" section (triggering is from description) - Slim SKILL.md from 313 to 205 lines by moving inline YAML templates to reference files and using conditional pointers instead - Add clear "read X when doing Y" guidance for reference files - Condense endpoint/schema checklists into scannable summaries Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Refine skill based on test results against real PRs Tested against intercom/intercom#474982 (field addition) and #477688 (new endpoint). Both produced correct output matching human-written PRs. Improvements based on test observations: - Add fern check fallback (python YAML validation) for environments without fern installed - Add guidance to extract descriptions from version change define_description - Add guidance to match example verbosity level of sibling endpoints - Add guidance to reuse existing example value styles (IDs, workspace IDs) - Note common YAML validation pitfalls Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Address PR review findings - Replace <uuid> placeholder with actual UUID format in error template to prevent literal output - Make version-propagation note future-proof by removing hardcoded v2.15 reference and adding "check the actual file" guidance - Consolidate CLAUDE.md skill directive into single paragraph Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix misleading version defaults table in version-propagation.md Remove the intercom_version default column that mixed two different patterns without explanation, making v2.15 look like an anomaly. Instead, point readers to check the actual file. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 223e8d9 commit 777e9ad

7 files changed

Lines changed: 1711 additions & 0 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/bash
2+
# UserPromptSubmit hook that auto-injects generate-openapi-from-pr skill instruction
3+
# when user provides an intercom/intercom PR URL or asks to generate OpenAPI docs from a PR
4+
5+
# Read hook event data from stdin
6+
hook_data=$(cat)
7+
8+
# Extract user message from the JSON
9+
user_message=$(echo "$hook_data" | jq -r '.prompt // empty' 2>/dev/null)
10+
11+
# Check if user message contains intercom PR references or OpenAPI generation keywords
12+
if echo "$user_message" | grep -qiE 'intercom/intercom(/pull/|#)[0-9]+|generate.*(openapi|open-api|spec).*(from|pr)|openapi.*(from|pr)|document.*(this|api).*(pr|change)'; then
13+
cat <<'EOF'
14+
{
15+
"hookSpecificOutput": {
16+
"hookEventName": "UserPromptSubmit",
17+
"additionalContext": "OPENAPI: The user wants to generate OpenAPI spec changes from an intercom PR. Use the generate-openapi-from-pr skill to handle this request."
18+
}
19+
}
20+
EOF
21+
else
22+
# No match - no additional context
23+
exit 0
24+
fi

.claude/settings.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"hooks": {
3+
"UserPromptSubmit": [
4+
{
5+
"hooks": [
6+
{
7+
"type": "command",
8+
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/openapi-skill-auto-inject.sh"
9+
}
10+
]
11+
}
12+
]
13+
}
14+
}
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
---
2+
name: generate-openapi-from-pr
3+
description: >
4+
Generate OpenAPI spec changes from an intercom monolith PR. Use this skill whenever a user
5+
provides an intercom/intercom PR URL or number, asks to generate or update OpenAPI docs,
6+
update the spec, document an API change, or mentions shipping API documentation from a PR.
7+
Also trigger when the user pastes a github.com/intercom/intercom/pull/ URL even without
8+
explicit instructions — they almost certainly want spec changes generated. This is the
9+
primary workflow for this repository.
10+
11+
metadata:
12+
author: team-data-foundations
13+
version: "1.0"
14+
user-invocable: true
15+
argument-hint: "<intercom-pr-url-or-number>"
16+
17+
allowed-tools: Task, Read, Glob, Grep, Write, Edit, Bash, AskUserQuestion
18+
---
19+
20+
# Generate OpenAPI Spec from Intercom PR
21+
22+
This skill takes an intercom monolith PR (from `intercom/intercom`) and generates the corresponding OpenAPI spec changes in this repo (`Intercom-OpenAPI`).
23+
24+
## Workflow
25+
26+
### Step 1: Parse Input
27+
28+
Extract the PR number from the user's input. Accept:
29+
- Full URL: `https://github.com/intercom/intercom/pull/12345`
30+
- Short reference: `intercom/intercom#12345`
31+
- Just a number: `12345` (assume intercom/intercom)
32+
33+
### Step 2: Fetch PR Details
34+
35+
```bash
36+
# Get PR description, metadata, and state
37+
gh pr view <NUMBER> --repo intercom/intercom --json title,body,files,labels,state
38+
39+
# Get the full diff (works for open and merged PRs)
40+
gh pr diff <NUMBER> --repo intercom/intercom
41+
```
42+
43+
If the diff is too large, fetch individual changed files instead:
44+
```bash
45+
gh pr view <NUMBER> --repo intercom/intercom --json files --jq '.files[].path'
46+
```
47+
48+
Then fetch specific files of interest (controllers, models, version changes, routes).
49+
50+
For merged PRs where you need the full file (not just diff), fetch from the default branch:
51+
```bash
52+
gh api repos/intercom/intercom/contents/<path> --jq '.content' | base64 -d
53+
```
54+
55+
### Step 3: Analyze the Diff
56+
57+
Scan the diff for these file patterns and extract API-relevant information:
58+
59+
#### 3a. Controllers (`app/controllers/api/v3/`)
60+
61+
Look for:
62+
- **New controller files** → new API resource with endpoints
63+
- **New actions** (`def index`, `def show`, `def create`, `def update`, `def destroy`) → new operations
64+
- **`requires_version_change`** → which version change gates this endpoint
65+
- **`render_json Api::V3::Models::XxxResponse`** → identifies the response model/presenter
66+
- **`params.slice(...).permit(...)`** or **request parser classes** (`RequestParser`, `StrongParams`) → request body fields
67+
- **Error handling** (`raise Api::V3::Errors::ApiCodedError`) → error responses
68+
- **`before_action :check_api_version!`** → version-gated endpoint
69+
70+
#### 3b. Models/Presenters (`app/presenters/api/v3/` or `app/lib/api/v3/models/`)
71+
72+
Look for:
73+
- **`serialized_attributes do`** blocks → response schema properties
74+
- **`attribute :field_name`** → schema field definition
75+
- **`stringify: true`** → field is string type (even if integer in DB)
76+
- **`from_model` method** → how the model maps from internal objects
77+
- **Conditional attributes** based on version → version-specific fields
78+
79+
#### 3c. Version Changes (`app/lib/api/versioning/changes/`)
80+
81+
Look for:
82+
- **`define_description`** → description of the API change (use in PR/commit message)
83+
- **`define_is_breaking`** → whether this is a breaking change
84+
- **`define_is_ready_for_release`** → usually `false` for new changes
85+
- **`define_transformation ... data.except(:field1, :field2)`** → these fields are NEW (removed for old versions)
86+
- **`define_transformation` with data modification** → field format/value changed between versions
87+
88+
#### 3d. Version Registration (`app/lib/api/versioning/service.rb`)
89+
90+
Look for which version block the new change is added to:
91+
- `UnstableVersion.new(changes: [...])` → goes in Unstable (version `0/`)
92+
- `Version.new(id: "2.15", changes: [...])` → goes in that specific version
93+
94+
#### 3e. Routes (`config/routes/api_v3.rb`)
95+
96+
Look for:
97+
- `resources :things` → standard CRUD: index, show, create, update, destroy
98+
- `resources :things, only: [:index, :show]` → limited operations
99+
- `member do ... end` → actions on specific resource (e.g., PUT `/things/{id}/action`)
100+
- `collection do ... end` → actions on resource collection (e.g., POST `/things/search`)
101+
- Nested resources → parent/child paths (e.g., `/contacts/{id}/tags`)
102+
103+
#### 3f. OAuth Scopes (`app/lib/policy/api_controller_routes_oauth_scope_policy.rb`)
104+
105+
Look for scope mappings to understand required auth scope for the endpoint.
106+
107+
### Step 4: Ask User for Version Targeting
108+
109+
Present the findings and ask:
110+
111+
```
112+
I found the following API changes in PR #XXXXX:
113+
- [list of changes found]
114+
115+
Which API versions should I update?
116+
- Unstable only (default for new features)
117+
- Specific versions (for bug fixes/backports)
118+
```
119+
120+
Default to Unstable (`descriptions/0/api.intercom.io.yaml`) unless the PR clearly targets specific versions.
121+
122+
### Step 5: Read Target Spec File(s)
123+
124+
Read the target spec file(s) to understand:
125+
- Existing endpoints in the same resource group (for consistent naming/style)
126+
- Existing schemas that can be reused or extended
127+
- The `intercom_version` enum (to verify version values)
128+
- Where to insert new paths/schemas (maintain alphabetical or logical grouping)
129+
- **All inline examples that reference the affected schema** — when adding a field, you must update every response example that returns that schema. Search with: `grep -n 'schemas/<name>' <spec_file>`
130+
- **Existing example values** for the same resource — reuse the same style of IDs, workspace IDs, timestamps, and names that nearby endpoints use. Consistency matters more than novelty.
131+
132+
### Step 6: Generate OpenAPI Changes
133+
134+
Read the appropriate reference file based on what the PR changes:
135+
136+
- **Adding/modifying fields or schemas?** → Read [./ruby-to-openapi-mapping.md](./ruby-to-openapi-mapping.md) for how Ruby presenter attributes map to OpenAPI types
137+
- **Adding new endpoints?** → Read [./openapi-patterns.md](./openapi-patterns.md) for concrete YAML templates (GET, POST, PUT, DELETE, search)
138+
- **Updating multiple versions?** → Read [./version-propagation.md](./version-propagation.md) for the decision tree on which files to update
139+
140+
#### The two most important rules
141+
142+
**Rule 1: Field additions require updates in TWO places.** When adding a field to a schema, you must update both the schema definition in `components/schemas` AND every inline response example that returns that schema. Find all affected examples with:
143+
```bash
144+
grep -n 'schemas/<schema_name>' descriptions/0/api.intercom.io.yaml
145+
```
146+
147+
**Rule 2: New resources need a top-level tag.** If adding an entirely new API resource, add an entry to the `tags` array at the bottom of the spec (alphabetical order). The tag name must match the `tags` on endpoints and `x-tags` on schemas. See existing tags in [./openapi-patterns.md](./openapi-patterns.md) under "Top-Level Tags".
148+
149+
#### Quick checklist for new endpoints
150+
151+
Every endpoint needs: `summary`, `description`, `operationId` (unique, camelCase), `tags`, `Intercom-Version` header parameter (`"$ref": "#/components/schemas/intercom_version"`), response with inline examples + schema `$ref`, and at minimum a `401 Unauthorized` error response. POST/PUT endpoints also need a `requestBody` with schema and examples. See [./openapi-patterns.md](./openapi-patterns.md) for complete templates.
152+
153+
**Writing good descriptions:** Extract the description from the PR's version change `define_description` if available — it's usually well-written for the changelog. Supplement with details from the controller (constraints, validations, edge cases). A good description explains what the endpoint does AND when you'd use it, not just "You can do X."
154+
155+
**Response example detail level:** Match the verbosity of existing examples for the same schema. If other ticket endpoints show a full ticket object with nested `ticket_parts`, `contacts`, and `linked_objects`, your example should too. If they're minimal (just `type` and `id`), keep yours minimal. Look at the nearest sibling endpoint for the right level of detail.
156+
157+
#### Quick checklist for new schemas
158+
159+
Every schema needs: `title` (Title Case), `type: object`, `x-tags`, `description`, and `properties` where each property has `type`, `description`, and `example`. Mark nullable fields explicitly with `nullable: true`. Timestamps use `type: integer` + `format: date-time`.
160+
161+
### Step 7: Apply Changes
162+
163+
Use the Edit tool to insert changes into the spec file(s). Be careful about:
164+
- YAML indentation (2-space indent throughout)
165+
- Inserting paths in logical order (group related endpoints together)
166+
- Inserting schemas alphabetically in `components/schemas`
167+
- Adding new top-level tags in alphabetical order in the `tags` array
168+
- Not breaking existing content
169+
170+
### Step 8: Validate
171+
172+
Run Fern validation:
173+
```bash
174+
fern check
175+
```
176+
177+
If `fern` is not installed, fall back to YAML syntax validation:
178+
```bash
179+
python3 -c "import yaml; yaml.safe_load(open('descriptions/0/api.intercom.io.yaml'))" && echo "YAML valid"
180+
```
181+
182+
If validation fails, read the error output and fix the issues. Common problems: indentation errors, missing quotes on string values that look like numbers, and duplicate keys.
183+
184+
### Step 9: Summarize
185+
186+
Report to the user:
187+
- What was added/changed (new endpoints, new schemas, new fields, new top-level tags)
188+
- Which files were modified
189+
- Which versions were updated
190+
- Any manual follow-up needed
191+
192+
#### Follow-up Checklist
193+
194+
Always remind the user of remaining manual steps:
195+
196+
1. **Review generated changes** for accuracy against the actual API behavior
197+
2. **Fern overrides** — if new endpoints were added to Unstable, check if `fern/unstable-openapi-overrides.yml` needs SDK method name entries
198+
3. **Developer-docs PR** — copy the updated spec to the `developer-docs` repo:
199+
- Copy `descriptions/0/api.intercom.io.yaml``docs/references/@Unstable/rest-api/api.intercom.io.yaml`
200+
- For stable versions: `descriptions/2.15/api.intercom.io.yaml``docs/references/@2.15/rest-api/api.intercom.io.yaml`
201+
4. **Changelog** — if the change should appear in the public changelog, update `docs/references/@<version>/changelog.md` in the developer-docs repo (newest entries at top)
202+
5. **Cross-version changes** — if this is an unversioned change (affects all versions), also update `docs/build-an-integration/learn-more/rest-apis/unversioned-changes.md` in developer-docs
203+
6. **Run `fern check`** to validate before committing
204+
205+
## Important Notes
206+
207+
- **Do NOT run `fern generate` without `--preview`** — this would auto-submit PRs to SDK repos
208+
- **Match existing examples** — before writing new example values, look at how nearby endpoints for the same resource format their examples. Reuse the same style of IDs (`'494'` not `'1'`), workspace IDs (`this_is_an_id664_that_should_be_at_least_`), timestamps (recent UNIX timestamps like `1719493065`), and names. Consistency across the spec is more important than creativity.
209+
- **Match existing style** — look at nearby endpoints for naming, formatting, and level of detail in response examples. If sibling endpoints show full nested objects, yours should too.
210+
- **Extract descriptions from the PR** — the version change's `define_description` is usually well-written. Use it as the basis for your endpoint description, then enrich with constraints and edge cases from the controller code.
211+
- **Cross-reference with existing schemas** — reuse `$ref` to existing schemas wherever possible
212+
- **Nullable fields** — always explicitly mark with `nullable: true`
213+
- **The `error` schema** is already defined — always reference it with `"$ref": "#/components/schemas/error"`
214+
- **Top-level tags** — new resources need a tag in the `tags` array at the bottom of the spec
215+
- **Servers** — the spec already defines 3 regional servers (US, EU, AU) — do not modify
216+
- **Security** — global `bearerAuth` is already configured — do not modify

0 commit comments

Comments
 (0)