@contentrain/skills 0.1.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/LICENSE +21 -0
- package/README.md +102 -0
- package/dist/index.cjs +35 -0
- package/dist/index.d.cts +13 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +13 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +35 -0
- package/dist/index.mjs.map +1 -0
- package/frameworks/astro.md +334 -0
- package/frameworks/expo.md +66 -0
- package/frameworks/next.md +296 -0
- package/frameworks/node.md +63 -0
- package/frameworks/nuxt.md +274 -0
- package/frameworks/react-native.md +80 -0
- package/frameworks/react.md +71 -0
- package/frameworks/sveltekit.md +324 -0
- package/frameworks/vue.md +76 -0
- package/package.json +62 -0
- package/workflows/contentrain-bulk.md +92 -0
- package/workflows/contentrain-content.md +154 -0
- package/workflows/contentrain-diff.md +55 -0
- package/workflows/contentrain-doctor.md +55 -0
- package/workflows/contentrain-generate.md +352 -0
- package/workflows/contentrain-init.md +91 -0
- package/workflows/contentrain-model.md +134 -0
- package/workflows/contentrain-normalize.md +294 -0
- package/workflows/contentrain-review.md +162 -0
- package/workflows/contentrain-serve.md +88 -0
- package/workflows/contentrain-translate.md +172 -0
- package/workflows/contentrain-validate-fix.md +85 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Contentrain + React + Vite
|
|
2
|
+
|
|
3
|
+
> Framework guide for consuming the generated Contentrain client in a React SPA.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Setup
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @contentrain/query
|
|
11
|
+
npx contentrain generate
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Restart Vite after the first generation so `#contentrain` imports resolve.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 2. Component Usage
|
|
19
|
+
|
|
20
|
+
In a React SPA, import directly from `#contentrain`:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { query, singleton, dictionary } from '#contentrain'
|
|
24
|
+
|
|
25
|
+
export function HomePage() {
|
|
26
|
+
const hero = singleton('hero').locale('en').get()
|
|
27
|
+
const labels = dictionary('ui-labels').locale('en').get()
|
|
28
|
+
const posts = query('blog-post').locale('en').all()
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<main>
|
|
32
|
+
<h1>{hero.title}</h1>
|
|
33
|
+
<button>{labels.cta_primary}</button>
|
|
34
|
+
<ul>
|
|
35
|
+
{posts.map(post => (
|
|
36
|
+
<li key={post.id}>{post.title}</li>
|
|
37
|
+
))}
|
|
38
|
+
</ul>
|
|
39
|
+
</main>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Rules:
|
|
45
|
+
|
|
46
|
+
- queries are synchronous
|
|
47
|
+
- no `await query(...)`
|
|
48
|
+
- regenerated client data ships with the app bundle
|
|
49
|
+
|
|
50
|
+
### Relations
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
const posts = query('blog-post').locale('en').include('author').all()
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Documents
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { document } from '#contentrain'
|
|
60
|
+
|
|
61
|
+
const doc = document('doc-page').locale('en').bySlug('introduction')
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 3. Best Fit
|
|
67
|
+
|
|
68
|
+
Use this guide for client-rendered React apps.
|
|
69
|
+
|
|
70
|
+
For Next.js App Router projects, use the dedicated Next guide instead.
|
|
71
|
+
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# Contentrain + SvelteKit
|
|
2
|
+
|
|
3
|
+
> Framework guide for consuming Contentrain-managed content in SvelteKit applications.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Setup
|
|
8
|
+
|
|
9
|
+
### 1.1 SDK Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add @contentrain/query # or npm/yarn
|
|
13
|
+
npx contentrain generate
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The generator reads `.contentrain/models/` and produces a typed client in `.contentrain/client/`. Your `package.json` must include the subpath import:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"imports": {
|
|
21
|
+
"#contentrain": {
|
|
22
|
+
"types": "./.contentrain/client/index.d.ts",
|
|
23
|
+
"import": "./.contentrain/client/index.mjs",
|
|
24
|
+
"require": "./.contentrain/client/index.cjs",
|
|
25
|
+
"default": "./.contentrain/client/index.mjs"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The generator adds this automatically. Restart the SvelteKit dev server after initial generation.
|
|
32
|
+
|
|
33
|
+
### 1.2 Watch Mode
|
|
34
|
+
|
|
35
|
+
Run the generator alongside SvelteKit dev:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx contentrain generate --watch &
|
|
39
|
+
npx vite dev
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 2. Imports
|
|
45
|
+
|
|
46
|
+
All SDK functions are imported from the `#contentrain` subpath:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { query, singleton, dictionary, document } from '#contentrain'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This import works in server load functions (`+page.server.ts`, `+layout.server.ts`), API routes (`+server.ts`), and any server-side module. The SDK reads JSON files from the file system — no runtime API calls.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 3. Querying Content
|
|
57
|
+
|
|
58
|
+
### 3.1 Collections
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
const posts = query('blog-post').locale('en').all()
|
|
62
|
+
const featured = query('blog-post').locale('en').where('featured', true).all()
|
|
63
|
+
const sorted = query('blog-post').locale('en').sort('publishedAt', 'desc').all()
|
|
64
|
+
const post = query('blog-post').locale('en').where('id', 'abc-123').first()
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3.2 Singletons
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
const hero = singleton('hero').locale('en').get()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 3.3 Dictionaries
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const label = dictionary('ui-labels').locale('en').get('submit_button')
|
|
77
|
+
const allLabels = dictionary('ui-labels').locale('en').get()
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3.4 Documents
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
const article = document('blog-article').locale('en').bySlug('getting-started')
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 3.5 Relations
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const posts = query('blog-post').locale('en').include('author', 'tags').all()
|
|
90
|
+
// post.author → full author object
|
|
91
|
+
// post.tags → array of full tag objects
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 4. SvelteKit Integration Patterns
|
|
97
|
+
|
|
98
|
+
### 4.1 Server Load Functions
|
|
99
|
+
|
|
100
|
+
Content loading belongs in `+page.server.ts` or `+layout.server.ts`:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
// src/routes/blog/+page.server.ts
|
|
104
|
+
import { query } from '#contentrain'
|
|
105
|
+
import type { PageServerLoad } from './$types'
|
|
106
|
+
|
|
107
|
+
export const load: PageServerLoad = () => {
|
|
108
|
+
const posts = query('blog-post').locale('en').sort('publishedAt', 'desc').all()
|
|
109
|
+
return { posts }
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The corresponding page component receives the data:
|
|
114
|
+
|
|
115
|
+
```svelte
|
|
116
|
+
<!-- src/routes/blog/+page.svelte -->
|
|
117
|
+
<script lang="ts">
|
|
118
|
+
import type { PageData } from './$types'
|
|
119
|
+
export let data: PageData
|
|
120
|
+
</script>
|
|
121
|
+
|
|
122
|
+
<ul>
|
|
123
|
+
{#each data.posts as post}
|
|
124
|
+
<li><a href="/blog/{post.slug}">{post.title}</a></li>
|
|
125
|
+
{/each}
|
|
126
|
+
</ul>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 4.2 Dynamic Routes
|
|
130
|
+
|
|
131
|
+
For `src/routes/blog/[slug]/+page.server.ts`:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { document } from '#contentrain'
|
|
135
|
+
import { error } from '@sveltejs/kit'
|
|
136
|
+
import type { PageServerLoad } from './$types'
|
|
137
|
+
|
|
138
|
+
export const load: PageServerLoad = ({ params }) => {
|
|
139
|
+
const post = document('blog-article').locale('en').bySlug(params.slug)
|
|
140
|
+
if (!post) throw error(404, 'Post not found')
|
|
141
|
+
return { post }
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 4.3 Layout Data
|
|
146
|
+
|
|
147
|
+
Provide global content (navigation, footer) from a layout load function:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
// src/routes/+layout.server.ts
|
|
151
|
+
import { singleton, dictionary } from '#contentrain'
|
|
152
|
+
import type { LayoutServerLoad } from './$types'
|
|
153
|
+
|
|
154
|
+
export const load: LayoutServerLoad = () => {
|
|
155
|
+
const nav = singleton('navigation').locale('en').get()
|
|
156
|
+
const footerLabels = dictionary('footer-labels').locale('en').get()
|
|
157
|
+
return { nav, footerLabels }
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### 4.4 API Routes
|
|
162
|
+
|
|
163
|
+
SDK works in SvelteKit API routes:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
// src/routes/api/posts/+server.ts
|
|
167
|
+
import { query } from '#contentrain'
|
|
168
|
+
import { json } from '@sveltejs/kit'
|
|
169
|
+
|
|
170
|
+
export function GET() {
|
|
171
|
+
const posts = query('blog-post').locale('en').all()
|
|
172
|
+
return json(posts)
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 5. i18n Integration
|
|
179
|
+
|
|
180
|
+
### 5.1 Locale from Route Parameters
|
|
181
|
+
|
|
182
|
+
For `src/routes/[locale]/blog/+page.server.ts`:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { query } from '#contentrain'
|
|
186
|
+
import type { PageServerLoad } from './$types'
|
|
187
|
+
|
|
188
|
+
export const load: PageServerLoad = ({ params }) => {
|
|
189
|
+
const posts = query('blog-post').locale(params.locale).sort('publishedAt', 'desc').all()
|
|
190
|
+
return { posts, locale: params.locale }
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 5.2 Locale from locals
|
|
195
|
+
|
|
196
|
+
Set locale in a hook and access it via `locals`:
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
// src/hooks.server.ts
|
|
200
|
+
import type { Handle } from '@sveltejs/kit'
|
|
201
|
+
|
|
202
|
+
export const handle: Handle = ({ event, resolve }) => {
|
|
203
|
+
const locale = event.params.locale ?? 'en'
|
|
204
|
+
event.locals.locale = locale
|
|
205
|
+
return resolve(event)
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Then in load functions:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
export const load: PageServerLoad = ({ locals }) => {
|
|
213
|
+
const posts = query('blog-post').locale(locals.locale).all()
|
|
214
|
+
return { posts }
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 5.3 With sveltekit-i18n or Paraglide
|
|
219
|
+
|
|
220
|
+
Contentrain dictionaries can supplement i18n libraries. Use `dictionary()` for content-driven strings and the i18n library for app-level translations, or use Contentrain as the single source for all strings.
|
|
221
|
+
|
|
222
|
+
### 5.4 Dictionary Strings in Components
|
|
223
|
+
|
|
224
|
+
```svelte
|
|
225
|
+
<script lang="ts">
|
|
226
|
+
import type { PageData } from './$types'
|
|
227
|
+
export let data: PageData
|
|
228
|
+
</script>
|
|
229
|
+
|
|
230
|
+
<button>{data.labels.submit_button}</button>
|
|
231
|
+
<p>{data.labels.welcome_message}</p>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## 6. Markdown and Document Content
|
|
237
|
+
|
|
238
|
+
### 6.1 Rendering Markdown
|
|
239
|
+
|
|
240
|
+
For document-kind entries with markdown bodies, use `mdsvex` or a markdown renderer:
|
|
241
|
+
|
|
242
|
+
```svelte
|
|
243
|
+
<script lang="ts">
|
|
244
|
+
import type { PageData } from './$types'
|
|
245
|
+
export let data: PageData
|
|
246
|
+
</script>
|
|
247
|
+
|
|
248
|
+
<article>
|
|
249
|
+
<h1>{data.post.title}</h1>
|
|
250
|
+
{@html data.post.bodyHtml}
|
|
251
|
+
</article>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### 6.2 mdsvex Integration
|
|
255
|
+
|
|
256
|
+
If using `mdsvex` for `.svelte.md` files, you can point it at Contentrain markdown content or use the SDK to load document metadata and render bodies separately.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 7. Prerendering and Deployment
|
|
261
|
+
|
|
262
|
+
### 7.1 Static Pages
|
|
263
|
+
|
|
264
|
+
Mark pages for prerendering:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
// src/routes/blog/+page.server.ts
|
|
268
|
+
export const prerender = true
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Or prerender the entire site:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
// src/routes/+layout.server.ts
|
|
275
|
+
export const prerender = true
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### 7.2 Entry Generator
|
|
279
|
+
|
|
280
|
+
For dynamic routes, provide all possible parameter values:
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
// src/routes/blog/[slug]/+page.server.ts
|
|
284
|
+
import { query } from '#contentrain'
|
|
285
|
+
|
|
286
|
+
export const prerender = true
|
|
287
|
+
|
|
288
|
+
export function entries() {
|
|
289
|
+
const posts = query('blog-post').locale('en').all()
|
|
290
|
+
return posts.map(post => ({ slug: post.slug }))
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### 7.3 Build and Deploy
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
npx vite build
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Use the appropriate SvelteKit adapter for your platform (`adapter-static`, `adapter-vercel`, `adapter-netlify`, `adapter-cloudflare`).
|
|
301
|
+
|
|
302
|
+
### 7.4 Deployment Flow
|
|
303
|
+
|
|
304
|
+
1. Content changes are committed to Git via MCP tools.
|
|
305
|
+
2. Push triggers platform rebuild.
|
|
306
|
+
3. `vite build` runs, SDK reads `.contentrain/` content.
|
|
307
|
+
4. Static or server output is produced with embedded content.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## 8. Type Safety
|
|
312
|
+
|
|
313
|
+
The generated client provides full TypeScript types:
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
import { query } from '#contentrain'
|
|
317
|
+
|
|
318
|
+
// Fully typed — autocomplete for all fields
|
|
319
|
+
const posts = query('blog-post').locale('en').all()
|
|
320
|
+
posts[0].title // string
|
|
321
|
+
posts[0].author // Author type when using .include('author')
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Keep the generator running in watch mode during development to stay in sync with model changes.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Contentrain + Vue 3 + Vite
|
|
2
|
+
|
|
3
|
+
> Framework guide for consuming the generated Contentrain client in a Vue SPA.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Setup
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @contentrain/query
|
|
11
|
+
npx contentrain generate
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Restart the Vite dev server after the first generate.
|
|
15
|
+
|
|
16
|
+
The generated `#contentrain` import resolves to the ESM client under `.contentrain/client/index.mjs`.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 2. SFC Usage
|
|
21
|
+
|
|
22
|
+
In a Vue SPA, direct SFC imports are valid:
|
|
23
|
+
|
|
24
|
+
```vue
|
|
25
|
+
<script setup lang="ts">
|
|
26
|
+
import { query, singleton, dictionary } from '#contentrain'
|
|
27
|
+
|
|
28
|
+
const hero = singleton('hero').locale('en').get()
|
|
29
|
+
const labels = dictionary('ui-labels').locale('en').get()
|
|
30
|
+
const posts = query('blog-post').locale('en').sort('publishedAt', 'desc').all()
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<main>
|
|
35
|
+
<h1>{{ hero.title }}</h1>
|
|
36
|
+
<button>{{ labels.cta_primary }}</button>
|
|
37
|
+
|
|
38
|
+
<ul>
|
|
39
|
+
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
|
|
40
|
+
</ul>
|
|
41
|
+
</main>
|
|
42
|
+
</template>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Rules:
|
|
46
|
+
|
|
47
|
+
- queries are synchronous
|
|
48
|
+
- content is bundled as static generated modules
|
|
49
|
+
- re-run `contentrain generate` after model/content changes
|
|
50
|
+
|
|
51
|
+
### Relations
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const posts = query('blog-post').locale('en').include('author', 'tags').all()
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Documents
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { document } from '#contentrain'
|
|
61
|
+
|
|
62
|
+
const article = document('blog-article').locale('en').bySlug('getting-started')
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 3. Best Fit
|
|
68
|
+
|
|
69
|
+
Use this pattern for:
|
|
70
|
+
|
|
71
|
+
- marketing SPAs
|
|
72
|
+
- dashboard/admin SPAs
|
|
73
|
+
- static Vite sites
|
|
74
|
+
|
|
75
|
+
For Nuxt SSR/hybrid projects, use the dedicated Nuxt guide instead.
|
|
76
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@contentrain/skills",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "AI agent skills for Contentrain — workflow procedures, framework integration guides",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/contentrain/contentrain-ai.git",
|
|
10
|
+
"directory": "packages/skills"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/contentrain/contentrain-ai/tree/main/packages/skills",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/contentrain/contentrain-ai/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"contentrain",
|
|
18
|
+
"ai",
|
|
19
|
+
"skills",
|
|
20
|
+
"mcp",
|
|
21
|
+
"workflow",
|
|
22
|
+
"vue",
|
|
23
|
+
"react",
|
|
24
|
+
"nuxt",
|
|
25
|
+
"nextjs",
|
|
26
|
+
"astro",
|
|
27
|
+
"sveltekit",
|
|
28
|
+
"expo",
|
|
29
|
+
"react-native",
|
|
30
|
+
"nodejs"
|
|
31
|
+
],
|
|
32
|
+
"sideEffects": false,
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"import": "./dist/index.mjs",
|
|
40
|
+
"require": "./dist/index.cjs",
|
|
41
|
+
"default": "./dist/index.mjs"
|
|
42
|
+
},
|
|
43
|
+
"./workflows/*": "./workflows/*",
|
|
44
|
+
"./frameworks/*": "./frameworks/*"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"workflows",
|
|
49
|
+
"frameworks"
|
|
50
|
+
],
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "^22.0.0",
|
|
53
|
+
"tsdown": "^0.21.0",
|
|
54
|
+
"typescript": "^5.7.0",
|
|
55
|
+
"vitest": "^3.0.0"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsdown src/index.ts --format esm,cjs --dts",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"typecheck": "tsc --noEmit"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Skill: Run Bulk Content Operations
|
|
2
|
+
|
|
3
|
+
> Apply safe batch operations to existing content entries.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
Use this when the user wants to:
|
|
10
|
+
|
|
11
|
+
- copy one locale to another
|
|
12
|
+
- update status on many entries
|
|
13
|
+
- delete multiple collection entries
|
|
14
|
+
|
|
15
|
+
This skill is for existing content, not for schema design.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Steps
|
|
20
|
+
|
|
21
|
+
### 1. Inspect the Model
|
|
22
|
+
|
|
23
|
+
Call `contentrain_status` and `contentrain_describe(model: "<model-id>")` first.
|
|
24
|
+
|
|
25
|
+
Confirm:
|
|
26
|
+
|
|
27
|
+
- model kind
|
|
28
|
+
- whether `i18n` is enabled
|
|
29
|
+
- which locales are supported
|
|
30
|
+
|
|
31
|
+
### 2. Pick the Correct Bulk Operation
|
|
32
|
+
|
|
33
|
+
- `copy_locale`: clone one locale to another for i18n-enabled `collection`, `singleton`, or `dictionary` models
|
|
34
|
+
- `update_status`: update metadata state for many collection entries
|
|
35
|
+
- `delete_entries`: remove many collection entries at once
|
|
36
|
+
|
|
37
|
+
### 3. Apply Safety Rules
|
|
38
|
+
|
|
39
|
+
- never use `copy_locale` on non-i18n models
|
|
40
|
+
- `update_status` and `delete_entries` are collection-only
|
|
41
|
+
- confirm entry IDs before delete operations
|
|
42
|
+
- batch only related entries together
|
|
43
|
+
|
|
44
|
+
### 4. Execute with `contentrain_bulk`
|
|
45
|
+
|
|
46
|
+
Examples:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"operation": "copy_locale",
|
|
51
|
+
"model": "ui-labels",
|
|
52
|
+
"source_locale": "en",
|
|
53
|
+
"target_locale": "tr"
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"operation": "update_status",
|
|
60
|
+
"model": "blog-post",
|
|
61
|
+
"entry_ids": ["post_001", "post_002"],
|
|
62
|
+
"status": "in_review"
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"operation": "delete_entries",
|
|
69
|
+
"model": "faq-items",
|
|
70
|
+
"entry_ids": ["faq_01", "faq_02"],
|
|
71
|
+
"confirm": true
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 5. Validate When Needed
|
|
76
|
+
|
|
77
|
+
After `copy_locale` or status-heavy cleanup, call `contentrain_validate` if content correctness may have changed.
|
|
78
|
+
|
|
79
|
+
### 6. Submit
|
|
80
|
+
|
|
81
|
+
Bulk operations still create branches/commits. Call `contentrain_submit` if the project is in review flow.
|
|
82
|
+
|
|
83
|
+
### 7. Final Summary
|
|
84
|
+
|
|
85
|
+
Report:
|
|
86
|
+
|
|
87
|
+
- operation performed
|
|
88
|
+
- model affected
|
|
89
|
+
- locale or entry count affected
|
|
90
|
+
- validation outcome if run
|
|
91
|
+
- submit status
|
|
92
|
+
|