@contentrain/skills 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -127,7 +127,7 @@ This reduces always-loaded context from thousands of lines to just the essential
127
127
 
128
128
  `@contentrain/skills` is kept in lockstep with the MCP tool registry via cross-package parity tests (`tests/mcp-parity.test.ts`):
129
129
 
130
- - `skills/contentrain/references/mcp-tools.md` must have an `### <tool>` heading for every tool in the MCP `TOOL_NAMES` registry (currently 17).
130
+ - `skills/contentrain/references/mcp-tools.md` must have an `### <tool>` heading for every tool in the MCP `TOOL_NAMES` registry (currently 24: 19 core + 5 media).
131
131
  - Key skills (normalize, translate) must not reference legacy `contentrain/{operation}/...` branch prefixes — MCP now emits `cr/*`.
132
132
 
133
133
  When MCP's surface changes, these tests fail until the skill docs catch up.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentrain/skills",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "license": "MIT",
5
5
  "description": "AI agent skills for Contentrain — workflow procedures, framework integration guides",
6
6
  "type": "module",
@@ -61,7 +61,7 @@
61
61
  "tsdown": "^0.21.0",
62
62
  "typescript": "^5.7.0",
63
63
  "vitest": "^3.0.0",
64
- "@contentrain/mcp": "1.5.2"
64
+ "@contentrain/mcp": "1.10.0"
65
65
  },
66
66
  "scripts": {
67
67
  "build": "tsdown src/index.ts --format esm,cjs --dts",
@@ -357,3 +357,67 @@ Run git-backed batch operations on existing content entries.
357
357
  - `copy_locale` MUST NOT be used on non-i18n models
358
358
  - Bulk operations create branches and commits like other write tools
359
359
  - Validate afterward when content shape may have changed
360
+
361
+ ## Media Tools
362
+
363
+ Media tools are a deterministic passthrough to the provider's media stack. They are **registered only when the provider exposes one** (e.g. Studio MCP Cloud) — local stdio servers and plain GitHub/GitLab providers never list them. The flow: list assets → pick a `media/...` path → reference it in media/image/file fields via `contentrain_content_save` (paths are normalized to absolute delivery URLs when the provider sets `mediaBaseUrl`).
364
+
365
+ ### contentrain_media_list
366
+
367
+ List media assets with optional filtering and cursor pagination. Read-only.
368
+
369
+ | Parameter | Type | Required | Description |
370
+ |-----------|------|----------|-------------|
371
+ | `search` | string | No | Substring match on filename/path/alt |
372
+ | `tag` | string | No | Only assets carrying this tag |
373
+ | `limit` | number | No | Page size (1–100, provider may clamp) |
374
+ | `cursor` | string | No | Opaque cursor from a previous response |
375
+
376
+ Returns `{ assets[], next_cursor?, total? }`. Each asset carries `id`, `path` (`media/...`), optional `url`, `mime`, `size`, `alt`, `tags`, `createdAt`, `meta`.
377
+
378
+ ### contentrain_media_get
379
+
380
+ Get a single media asset by id. Read-only.
381
+
382
+ | Parameter | Type | Required | Description |
383
+ |-----------|------|----------|-------------|
384
+ | `id` | string | Yes | Asset id from `contentrain_media_list` |
385
+
386
+ Returns `{ asset }` or an error with a discovery hint for unknown ids.
387
+
388
+ ### contentrain_media_ingest
389
+
390
+ Ingest an asset from a source URL. The provider fetches the URL server-side under its own SSRF/MIME/size policy — MCP never fetches it. The only open-world tool (`openWorldHint: true`).
391
+
392
+ | Parameter | Type | Required | Description |
393
+ |-----------|------|----------|-------------|
394
+ | `url` | string | Yes | Source URL to fetch server-side |
395
+ | `filename` | string | No | Target filename override |
396
+ | `alt` | string | No | Alt text |
397
+ | `tags` | string[] | No | Tags to attach (max 20) |
398
+
399
+ Returns `{ status: "ingested", asset }` — use `asset.path` in content fields afterward.
400
+
401
+ ### contentrain_media_update
402
+
403
+ Update asset metadata. Never touches the binary.
404
+
405
+ | Parameter | Type | Required | Description |
406
+ |-----------|------|----------|-------------|
407
+ | `id` | string | Yes | Asset id to update |
408
+ | `alt` | string | No | New alt text |
409
+ | `tags` | string[] | No | Replacement tag list (max 20) |
410
+ | `filename` | string | No | New filename |
411
+
412
+ Returns `{ status: "updated", asset }`.
413
+
414
+ ### contentrain_media_delete
415
+
416
+ Delete an asset from the media stack. Destructive — content entries referencing its path are NOT rewritten; check references first with `contentrain_content_list`.
417
+
418
+ | Parameter | Type | Required | Description |
419
+ |-----------|------|----------|-------------|
420
+ | `id` | string | Yes | Asset id to delete |
421
+ | `confirm` | boolean | Yes | Must be `true` to confirm |
422
+
423
+ Returns `{ status: "deleted", id }`.
@@ -10,6 +10,18 @@ Follow these guidelines when populating image, video, or file fields in any cont
10
10
 
11
11
  ---
12
12
 
13
+ ## Storage & Reference Models
14
+
15
+ Media references are stored in one of two models. Identify which one the project uses before populating image, video, or file fields. The asset rules below (dimensions, formats, size limits, naming) apply to **both**; only the **Asset Organization** section (paths under `assets_path`, relative references) is specific to the local-file model.
16
+
17
+ 1. **Local file model (default / OSS).** Media lives on disk under `config.json > assets_path` (default `.contentrain/assets/`) and is referenced by a **relative path from the project root** (e.g. `.contentrain/assets/blog/hero.webp`). To turn relative paths into URLs at read time, set `config.json > cdn.url` (or `contentrain generate --cdnBaseUrl <base>`) and use the `media()` resolver baked into the `@contentrain/query` generated client — `media('media/...') → {cdn.url}/{path}`.
18
+
19
+ 2. **Studio CDN model.** Media lives in the Studio CDN/object store and is referenced by a relative storage path (`media/...`) returned by the media library. Pass either that `media/...` path or its URL to `contentrain_content_save`: in **cloud mode** the value is **automatically normalized to an absolute public delivery URL** on save (`{base}/{path}`), in both media fields and markdown bodies, so the committed content renders in a browser anywhere with no SDK. In **local mode** the relative path is kept verbatim. The rewrite is idempotent and never touches external URLs (`http(s)://`, `//`, `data:`) or already-absolute delivery URLs — so for external images (a CDN or Unsplash URL), just save the URL directly.
20
+
21
+ Never hand-build delivery URLs or guess the CDN base. Save the path or URL the media library gives you; the platform resolves it.
22
+
23
+ ---
24
+
13
25
  ## Image Dimensions (Recommended)
14
26
 
15
27
  1. Use these standard dimensions unless the project's `context.json` specifies overrides:
@@ -78,6 +78,22 @@ const doc = document('blog-post').bySlug('getting-started')
78
78
  | `.where(field, op, value)` | Operator filter (same ops as QueryBuilder) |
79
79
  | `.include(relation)` | Resolve relation fields |
80
80
 
81
+ ### Media
82
+
83
+ Media / image / file field values are plain strings. How you turn them into a URL depends on the storage model:
84
+
85
+ - **Studio-CDN content** already carries absolute delivery URLs — the write path normalizes `media/...` references on save — so use the field value directly, no resolution needed.
86
+ - **Relative-path content** (OSS / local-file model, values like `media/...`) resolves through an optional `media()` helper baked into the generated client. Set `config.json > cdn.url` (or run `contentrain generate --cdnBaseUrl <base>`) and the client exports it:
87
+
88
+ ```typescript
89
+ import { media } from '#contentrain'
90
+
91
+ media('media/original/hero.webp') // → '{cdn.url}/media/original/hero.webp'
92
+ media('https://images.unsplash.com/x.jpg') // → unchanged (external pass-through)
93
+ ```
94
+
95
+ `media()` is omitted when no base is configured, and is idempotent — external URLs (`http(s)://`, `//`, `data:`) and already-absolute delivery URLs pass through untouched. It is the **local-mode** counterpart of CDN mode's `client.media()` (which returns a `MediaAccessor` over the media manifest, below).
96
+
81
97
  ## CDN Mode (Remote Data)
82
98
 
83
99
  For server-side or client-side apps that fetch content from Contentrain Studio CDN:
@@ -151,6 +167,7 @@ const history = await conv.history(response.conversationId)
151
167
  - Locale fallback chain: explicit → config default → first available
152
168
  - Generated files in `.contentrain/client/` are **immutable** — always regenerate, never edit
153
169
  - Run `contentrain generate` after any model change
170
+ - **Media**: Studio-CDN fields already carry absolute URLs; for relative `media/...` values set `config.cdn.url` (or `generate --cdnBaseUrl`) and resolve with the generated `media()` helper
154
171
 
155
172
  ## Framework Integration
156
173