@supatent/skills 0.3.0 → 0.5.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
@@ -1,10 +1,10 @@
1
1
  # @supatent/skills
2
2
 
3
- Claude Code content authoring skills for [Supatent CMS](https://supatent.ai).
3
+ Claude Code and Codex CLI content authoring skills for [Supatent CMS](https://supatent.ai).
4
4
 
5
5
  ## What this does
6
6
 
7
- Installs AI-powered content authoring skills into your Claude Code environment. Once installed, Claude Code can create and manage CMS content through natural conversation — blog posts, landing pages, and more.
7
+ Installs AI-powered content authoring skills for Claude Code and Codex CLI. Once installed, agents can create and manage CMS content through natural conversation — blog posts, landing pages, and more.
8
8
 
9
9
  ## Quick start
10
10
 
@@ -12,15 +12,40 @@ Installs AI-powered content authoring skills into your Claude Code environment.
12
12
  npx @supatent/skills
13
13
  ```
14
14
 
15
- This installs skills as a Claude Code plugin to `.claude/plugins/supatent/` in your project. Then use `/supatent:core` in Claude Code to get started.
15
+ In interactive terminals, the installer prompts for a target:
16
+
17
+ - `claude`
18
+ - `codex`
19
+ - `both`
20
+
21
+ In non-interactive environments (CI), the default is `claude`.
22
+
23
+ ### Explicit target selection
24
+
25
+ ```bash
26
+ # Claude Code
27
+ npx @supatent/skills --target claude
28
+
29
+ # Codex CLI
30
+ npx @supatent/skills --target codex
31
+
32
+ # Both
33
+ npx @supatent/skills --target both
34
+ ```
35
+
36
+ Use `--codex-home` to override Codex install location (default: `$CODEX_HOME` or `~/.codex`):
37
+
38
+ ```bash
39
+ npx @supatent/skills --target codex --codex-home /path/to/codex-home
40
+ ```
16
41
 
17
42
  ## Included skills
18
43
 
19
- | Skill | Command | Description |
20
- |-------|---------|-------------|
21
- | Core | `/supatent:core` | Schema management, content operations, validation |
22
- | Blog | `/supatent:blog` | Create and edit blog posts with structured fields |
23
- | Landing | `/supatent:landing` | Build landing pages with configurable sections |
44
+ | Skill | Claude Code | Codex CLI | Description |
45
+ |-------|-------------|-----------|-------------|
46
+ | Core | `/supatent:core` | `supatent-core` | Schema management, content operations, validation |
47
+ | Blog | `/supatent:content-blog` | `supatent-content-blog` | Create and edit blog posts with structured fields |
48
+ | Landing | `/supatent:content-landing` | `supatent-content-landing` | Build landing pages with configurable sections |
24
49
 
25
50
  ## Updating
26
51
 
@@ -36,7 +61,7 @@ npx @supatent/skills --force
36
61
 
37
62
  - Node.js >= 20
38
63
  - A Supatent CMS project with `@supatent/cli` configured
39
- - Claude Code
64
+ - Claude Code and/or Codex CLI
40
65
 
41
66
  ## License
42
67
 
package/bin/install.mjs CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // bin/install.mjs - Self-contained zero-dependency installer for @supatent/skills
4
- // Copies bundled skill files to .claude/skills/supatent/ with manifest tracking.
4
+ // Copies bundled skill files to Claude Code and/or Codex CLI skill directories.
5
5
 
6
6
  import { readFile, writeFile, mkdir, copyFile, readdir, stat } from 'node:fs/promises';
7
7
  import { realpathSync } from 'node:fs';
8
8
  import { createHash } from 'node:crypto';
9
9
  import { join, dirname, relative, resolve } from 'node:path';
10
10
  import { fileURLToPath } from 'node:url';
11
+ import { homedir } from 'node:os';
11
12
  import { createInterface } from 'node:readline/promises';
12
13
 
13
14
  // ---------------------------------------------------------------------------
@@ -16,10 +17,10 @@ import { createInterface } from 'node:readline/promises';
16
17
 
17
18
  const __filename = fileURLToPath(import.meta.url);
18
19
  const __dirname = dirname(__filename);
19
- const SKILLS_SOURCE_DIR = join(__dirname, '..', 'skills');
20
- const PLUGIN_JSON_SOURCE = join(__dirname, '..', '.claude-plugin', 'plugin.json');
21
- const PLUGIN_ROOT_SUBDIR = join('.claude', 'plugins', 'supatent');
22
- const TARGET_SUBDIR = join(PLUGIN_ROOT_SUBDIR, 'skills');
20
+ const CLAUDE_SKILLS_SOURCE_DIR = join(__dirname, '..', 'skills');
21
+ const CODEX_SKILLS_SOURCE_DIR = join(__dirname, '..', 'skills-codex');
22
+ const CLAUDE_TARGET_SUBDIR = join('.claude', 'skills');
23
+ const VALID_TARGETS = new Set(['claude', 'codex', 'both']);
23
24
 
24
25
  // ---------------------------------------------------------------------------
25
26
  // Exported helpers (for testability)
@@ -67,6 +68,109 @@ export async function confirm(message, { force = false } = {}) {
67
68
  }
68
69
  }
69
70
 
71
+ /**
72
+ * Prompt install target when not explicitly provided.
73
+ * Defaults to "claude" when stdin is non-interactive.
74
+ */
75
+ export async function promptInstallTarget({ force = false } = {}) {
76
+ if (force || !process.stdin.isTTY) return 'claude';
77
+
78
+ const rl = createInterface({
79
+ input: process.stdin,
80
+ output: process.stdout,
81
+ });
82
+
83
+ try {
84
+ console.log('Select installation target:');
85
+ console.log(' 1) Claude Code');
86
+ console.log(' 2) Codex CLI');
87
+ console.log(' 3) Both');
88
+
89
+ while (true) {
90
+ const answer = (await rl.question('Target [1/2/3] (default: 1): ')).trim().toLowerCase();
91
+
92
+ if (answer === '' || answer === '1' || answer === 'claude') {
93
+ return 'claude';
94
+ }
95
+ if (answer === '2' || answer === 'codex') {
96
+ return 'codex';
97
+ }
98
+ if (answer === '3' || answer === 'both') {
99
+ return 'both';
100
+ }
101
+
102
+ console.log('Invalid choice. Enter 1, 2, 3, claude, codex, or both.');
103
+ }
104
+ } finally {
105
+ rl.close();
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Parse command line args.
111
+ */
112
+ export function parseArgs(argv) {
113
+ const parsed = {
114
+ force: false,
115
+ help: false,
116
+ target: null,
117
+ codexHome: null,
118
+ };
119
+
120
+ for (let i = 0; i < argv.length; i += 1) {
121
+ const arg = argv[i];
122
+
123
+ if (arg === '--force' || arg === '-f' || arg === '--yes' || arg === '-y') {
124
+ parsed.force = true;
125
+ continue;
126
+ }
127
+
128
+ if (arg === '--help' || arg === '-h') {
129
+ parsed.help = true;
130
+ continue;
131
+ }
132
+
133
+ if (arg === '--target') {
134
+ i += 1;
135
+ if (!argv[i]) {
136
+ throw new Error('Missing value for --target. Use: claude, codex, or both.');
137
+ }
138
+ parsed.target = argv[i];
139
+ continue;
140
+ }
141
+
142
+ if (arg.startsWith('--target=')) {
143
+ parsed.target = arg.slice('--target='.length);
144
+ continue;
145
+ }
146
+
147
+ if (arg === '--codex-home') {
148
+ i += 1;
149
+ if (!argv[i]) {
150
+ throw new Error('Missing value for --codex-home.');
151
+ }
152
+ parsed.codexHome = argv[i];
153
+ continue;
154
+ }
155
+
156
+ if (arg.startsWith('--codex-home=')) {
157
+ parsed.codexHome = arg.slice('--codex-home='.length);
158
+ continue;
159
+ }
160
+
161
+ throw new Error(`Unknown argument: ${arg}`);
162
+ }
163
+
164
+ if (parsed.target) {
165
+ parsed.target = parsed.target.toLowerCase();
166
+ if (!VALID_TARGETS.has(parsed.target)) {
167
+ throw new Error(`Invalid --target value: ${parsed.target}. Use: claude, codex, or both.`);
168
+ }
169
+ }
170
+
171
+ return parsed;
172
+ }
173
+
70
174
  /**
71
175
  * Read and parse .manifest.json. Returns null if missing or invalid.
72
176
  */
@@ -163,60 +267,76 @@ export async function getVersion() {
163
267
  return pkg.version;
164
268
  }
165
269
 
166
- // ---------------------------------------------------------------------------
167
- // Main installer flow
168
- // ---------------------------------------------------------------------------
169
-
170
- async function main() {
171
- const args = process.argv.slice(2);
172
- const force = args.includes('--force');
270
+ /**
271
+ * Resolve default Codex home.
272
+ */
273
+ export function getDefaultCodexHome() {
274
+ return process.env.CODEX_HOME ? resolve(process.env.CODEX_HOME) : join(homedir(), '.codex');
275
+ }
173
276
 
174
- const version = await getVersion();
175
- const cwd = process.cwd();
176
- const claudeDir = join(cwd, '.claude');
177
- const targetDir = join(cwd, TARGET_SUBDIR);
178
- const manifestPath = join(targetDir, '.manifest.json');
277
+ function printHelp() {
278
+ console.log('Usage: npx @supatent/skills [options]');
279
+ console.log('');
280
+ console.log('Options:');
281
+ console.log(' --target <claude|codex|both> Installation target (prompts when omitted in TTY)');
282
+ console.log(' --codex-home <path> Override Codex home (default: $CODEX_HOME or ~/.codex)');
283
+ console.log(' -f, --force Auto-confirm prompts and overwrite user-modified files');
284
+ console.log(' -y, --yes Alias for --force');
285
+ console.log(' -h, --help Show this help');
286
+ }
179
287
 
180
- // -----------------------------------------------------------------------
181
- // Step a: Check .claude/ directory
182
- // -----------------------------------------------------------------------
183
- let claudeExists = false;
288
+ async function ensureDirectoryExists(dirPath, missingMessage, { force }) {
289
+ let exists = false;
184
290
  try {
185
- const s = await stat(claudeDir);
186
- claudeExists = s.isDirectory();
291
+ const s = await stat(dirPath);
292
+ exists = s.isDirectory();
187
293
  } catch {
188
294
  // does not exist
189
295
  }
190
296
 
191
- if (!claudeExists) {
192
- console.log('\x1b[33m!\x1b[0m No .claude/ directory found. This installer creates skill files for Claude Code.');
193
- const ok = await confirm('Create .claude/ directory and continue?', { force });
194
- if (!ok) {
195
- console.log('Aborted.');
196
- process.exitCode = 1;
197
- return;
198
- }
199
- await mkdir(claudeDir, { recursive: true });
297
+ if (exists) return true;
298
+
299
+ if (missingMessage) {
300
+ console.log(`\x1b[33m!\x1b[0m ${missingMessage}`);
200
301
  }
201
302
 
303
+ const ok = await confirm(`Create ${dirPath} and continue?`, { force });
304
+ if (!ok) {
305
+ console.log('Aborted.');
306
+ return false;
307
+ }
308
+
309
+ await mkdir(dirPath, { recursive: true });
310
+ return true;
311
+ }
312
+
313
+ async function installSkillBundle({
314
+ sourceDir,
315
+ targetDir,
316
+ manifestPath,
317
+ targetLabel,
318
+ startHint,
319
+ force,
320
+ version,
321
+ }) {
202
322
  // -----------------------------------------------------------------------
203
- // Step b: Walk source skills/ directory and build source map
323
+ // Step a: Build source map
204
324
  // -----------------------------------------------------------------------
205
- const sourceMap = await buildSourceMap(SKILLS_SOURCE_DIR);
325
+ const sourceMap = await buildSourceMap(sourceDir);
206
326
  const sourceFiles = Object.keys(sourceMap);
207
327
 
208
328
  // -----------------------------------------------------------------------
209
- // Step c: Read existing manifest
329
+ // Step b: Read existing manifest
210
330
  // -----------------------------------------------------------------------
211
331
  const manifest = await readManifest(manifestPath);
212
332
 
213
333
  // -----------------------------------------------------------------------
214
- // Step d: Compute diff
334
+ // Step c: Compute diff
215
335
  // -----------------------------------------------------------------------
216
336
  const diff = await computeDiff(sourceMap, manifest, targetDir);
217
337
 
218
338
  // -----------------------------------------------------------------------
219
- // Step e: Handle user-modified files
339
+ // Step d: Handle user-modified files
220
340
  // -----------------------------------------------------------------------
221
341
  const filesToCopy = [...diff.newFiles, ...diff.changed];
222
342
  const skippedFiles = [];
@@ -234,14 +354,14 @@ async function main() {
234
354
  // Check if anything to do
235
355
  // -----------------------------------------------------------------------
236
356
  if (filesToCopy.length === 0 && skippedFiles.length === 0) {
237
- console.log(`\x1b[32m\u2713\x1b[0m Already up to date (v${version})`);
357
+ console.log(`\x1b[32m\u2713\x1b[0m Already up to date (${targetLabel}, v${version})`);
238
358
  console.log('');
239
- console.log(' Run \x1b[36m/supatent:core\x1b[0m to get started');
359
+ console.log(` ${startHint}`);
240
360
  return;
241
361
  }
242
362
 
243
363
  // -----------------------------------------------------------------------
244
- // Step f: Copy files
364
+ // Step e: Copy files
245
365
  // -----------------------------------------------------------------------
246
366
  const isFreshInstall = !manifest;
247
367
 
@@ -265,14 +385,7 @@ async function main() {
265
385
  }
266
386
 
267
387
  // -----------------------------------------------------------------------
268
- // Step f2: Copy plugin.json
269
- // -----------------------------------------------------------------------
270
- const pluginDir = join(cwd, PLUGIN_ROOT_SUBDIR, '.claude-plugin');
271
- await mkdir(pluginDir, { recursive: true });
272
- await copyFile(PLUGIN_JSON_SOURCE, join(pluginDir, 'plugin.json'));
273
-
274
- // -----------------------------------------------------------------------
275
- // Step g: Write manifest
388
+ // Step f: Write manifest
276
389
  // -----------------------------------------------------------------------
277
390
  const manifestFiles = {};
278
391
 
@@ -302,16 +415,103 @@ async function main() {
302
415
  await writeFile(manifestPath, JSON.stringify(manifestData, null, 2) + '\n');
303
416
 
304
417
  // -----------------------------------------------------------------------
305
- // Step h: Print summary
418
+ // Step g: Print summary
306
419
  // -----------------------------------------------------------------------
307
420
  console.log('');
308
421
  if (isFreshInstall) {
309
- console.log(`\x1b[32m\u2713\x1b[0m Installed ${filesToCopy.length} files to ${PLUGIN_ROOT_SUBDIR}/ (v${version})`);
422
+ console.log(`\x1b[32m\u2713\x1b[0m Installed ${filesToCopy.length} files to ${targetDir} (${targetLabel}, v${version})`);
310
423
  } else {
311
- console.log(`\x1b[32m\u2713\x1b[0m Updated ${filesToCopy.length} file${filesToCopy.length === 1 ? '' : 's'} in ${PLUGIN_ROOT_SUBDIR}/ (v${version})`);
424
+ console.log(`\x1b[32m\u2713\x1b[0m Updated ${filesToCopy.length} file${filesToCopy.length === 1 ? '' : 's'} in ${targetDir} (${targetLabel}, v${version})`);
312
425
  }
313
426
  console.log('');
314
- console.log(' Run \x1b[36m/supatent:core\x1b[0m to get started');
427
+ console.log(` ${startHint}`);
428
+ }
429
+
430
+ async function installClaude({ cwd, force, version }) {
431
+ const claudeDir = join(cwd, '.claude');
432
+ const targetDir = join(cwd, CLAUDE_TARGET_SUBDIR);
433
+ const manifestPath = join(targetDir, 'supatent-core', '.manifest.json');
434
+
435
+ const ok = await ensureDirectoryExists(
436
+ claudeDir,
437
+ 'No .claude/ directory found. This installer creates skill files for Claude Code.',
438
+ { force },
439
+ );
440
+ if (!ok) {
441
+ process.exitCode = 1;
442
+ return;
443
+ }
444
+
445
+ await installSkillBundle({
446
+ sourceDir: CLAUDE_SKILLS_SOURCE_DIR,
447
+ targetDir,
448
+ manifestPath,
449
+ targetLabel: 'Claude Code',
450
+ startHint: 'Run \x1b[36m/supatent:core\x1b[0m to get started',
451
+ force,
452
+ version,
453
+ });
454
+ }
455
+
456
+ async function installCodex({ force, version, codexHomeOverride = null }) {
457
+ const codexHome = codexHomeOverride ? resolve(codexHomeOverride) : getDefaultCodexHome();
458
+ const targetDir = join(codexHome, 'skills');
459
+ const manifestPath = join(targetDir, 'supatent-core', '.manifest.json');
460
+
461
+ const ok = await ensureDirectoryExists(
462
+ codexHome,
463
+ `No Codex home directory found at ${codexHome}.`,
464
+ { force },
465
+ );
466
+ if (!ok) {
467
+ process.exitCode = 1;
468
+ return;
469
+ }
470
+
471
+ await installSkillBundle({
472
+ sourceDir: CODEX_SKILLS_SOURCE_DIR,
473
+ targetDir,
474
+ manifestPath,
475
+ targetLabel: 'Codex CLI',
476
+ startHint: 'Restart Codex CLI to pick up new skills',
477
+ force,
478
+ version,
479
+ });
480
+ }
481
+
482
+ // ---------------------------------------------------------------------------
483
+ // Main installer flow
484
+ // ---------------------------------------------------------------------------
485
+
486
+ async function main() {
487
+ const args = parseArgs(process.argv.slice(2));
488
+
489
+ if (args.help) {
490
+ printHelp();
491
+ return;
492
+ }
493
+
494
+ const version = await getVersion();
495
+ const cwd = process.cwd();
496
+ const force = args.force;
497
+
498
+ const target = args.target || await promptInstallTarget({ force });
499
+
500
+ if (target === 'both') {
501
+ console.log('Installing Supatent skills for Claude Code...');
502
+ await installClaude({ cwd, force, version });
503
+ console.log('');
504
+ console.log('Installing Supatent skills for Codex CLI...');
505
+ await installCodex({ force, version, codexHomeOverride: args.codexHome });
506
+ return;
507
+ }
508
+
509
+ if (target === 'codex') {
510
+ await installCodex({ force, version, codexHomeOverride: args.codexHome });
511
+ return;
512
+ }
513
+
514
+ await installClaude({ cwd, force, version });
315
515
  }
316
516
 
317
517
  // ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@supatent/skills",
3
- "version": "0.3.0",
4
- "description": "Claude Code content authoring skills for Supatent CMS",
3
+ "version": "0.5.0",
4
+ "description": "Claude Code and Codex CLI content authoring skills for Supatent CMS",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "supatent-skills": "bin/install.mjs"
@@ -9,7 +9,7 @@
9
9
  "files": [
10
10
  "bin",
11
11
  "skills",
12
- ".claude-plugin"
12
+ "skills-codex"
13
13
  ],
14
14
  "devDependencies": {
15
15
  "eslint": "^9.16.0",
@@ -22,6 +22,7 @@
22
22
  "supatent",
23
23
  "cms",
24
24
  "claude-code",
25
+ "codex-cli",
25
26
  "skills",
26
27
  "content-authoring"
27
28
  ],
@@ -18,8 +18,8 @@ Load these files on-demand, not upfront. Read each file only when the situation
18
18
  | File | When to Read |
19
19
  |------|-------------|
20
20
  | `./blog-sections.md` | Creating or modifying blog schemas -- contains full JSON definitions for blog-post, blog-author, and blog-settings |
21
- | `../references/schema-reference.md` | Troubleshooting validation errors, checking field types, or looking up JSON-LD type details |
22
- | `../references/workflow-reference.md` | Running CLI operations (init, dev, push, pull, validate), fixing errors, or understanding dev mode behavior |
21
+ | `../supatent-references/schema-reference.md` | Troubleshooting validation errors, checking field types, or looking up JSON-LD type details |
22
+ | `../supatent-references/workflow-reference.md` | Running CLI operations (init, dev, push, pull, validate), fixing errors, or understanding dev mode behavior |
23
23
 
24
24
  ## Intent Detection
25
25
 
@@ -155,7 +155,7 @@ Read `./blog-sections.md` to get the schema definitions.
155
155
 
156
156
  If schemas already exist, skip this section entirely.
157
157
 
158
- After writing schema files, check `.supatent/.validation-status.json` to confirm validation passes. If errors appear, read `../references/workflow-reference.md` for fix instructions.
158
+ After writing schema files, check `.supatent/.validation-status.json` to confirm validation passes. If errors appear, read `../supatent-references/workflow-reference.md` for fix instructions.
159
159
 
160
160
  ## Content Generation
161
161
 
@@ -0,0 +1,99 @@
1
+ # Blog Schema Catalog
2
+
3
+ Defines the schemas created by the blog content skill. The SKILL.md references this file for schema generation.
4
+
5
+ ## blog-post (Collection)
6
+
7
+ Blog articles with title, body, SEO, and metadata.
8
+
9
+ ### Core Fields (always created)
10
+
11
+ ```json
12
+ {
13
+ "slug": "blog-post",
14
+ "name": "Blog Post",
15
+ "description": "Blog articles with title, body, SEO, and metadata",
16
+ "isSingleton": false,
17
+ "fields": [
18
+ { "slug": "title", "name": "Title", "type": "text", "interface": "textInput", "order": 0 },
19
+ { "slug": "excerpt", "name": "Excerpt", "type": "text", "interface": "textarea", "order": 1 },
20
+ { "slug": "cover-image", "name": "Cover Image", "type": "image", "interface": "singleImage", "order": 2 },
21
+ { "slug": "body", "name": "Body", "type": "markdown", "interface": "markdownEditor", "order": 3 },
22
+ { "slug": "json-ld", "name": "Structured Data", "type": "jsonLd", "interface": "jsonLdEditor", "order": 4 }
23
+ ]
24
+ }
25
+ ```
26
+
27
+ ### Optional Fields (suggest to user, add if they agree)
28
+
29
+ Append these after core fields, adjusting `order` values accordingly:
30
+
31
+ - `date-published` (text/textInput) -- publication date in YYYY-MM-DD format
32
+ - `author` (text/textInput) -- author slug reference (matches blog-author item slug)
33
+ - `category` (text/textInput) -- post category
34
+
35
+ ```json
36
+ { "slug": "date-published", "name": "Date Published", "type": "text", "interface": "textInput" }
37
+ { "slug": "author", "name": "Author", "type": "text", "interface": "textInput" }
38
+ { "slug": "category", "name": "Category", "type": "text", "interface": "textInput" }
39
+ ```
40
+
41
+ ## blog-author (Collection)
42
+
43
+ Blog post authors. Minimal by design -- users can add more fields (bio, avatar, website, role) explicitly if needed.
44
+
45
+ ```json
46
+ {
47
+ "slug": "blog-author",
48
+ "name": "Blog Author",
49
+ "description": "Blog post authors",
50
+ "isSingleton": false,
51
+ "fields": [
52
+ { "slug": "name", "name": "Name", "type": "text", "interface": "textInput", "order": 0 }
53
+ ]
54
+ }
55
+ ```
56
+
57
+ ## blog-settings (Singleton)
58
+
59
+ Global blog configuration. Singleton content files must use slug `default` (e.g., `default.en.json`).
60
+
61
+ ```json
62
+ {
63
+ "slug": "blog-settings",
64
+ "name": "Blog Settings",
65
+ "description": "Global blog configuration",
66
+ "isSingleton": true,
67
+ "fields": [
68
+ { "slug": "blog-title", "name": "Blog Title", "type": "text", "interface": "textInput", "order": 0 },
69
+ { "slug": "blog-description", "name": "Blog Description", "type": "text", "interface": "textarea", "order": 1 },
70
+ { "slug": "posts-per-page", "name": "Posts Per Page", "type": "number", "interface": "numberInput", "order": 2 }
71
+ ]
72
+ }
73
+ ```
74
+
75
+ ## JSON-LD Guidance (Article Type)
76
+
77
+ Blog posts use `@type: "Article"` -- NOT `BlogPosting`. The validation system only supports `Article` and `BlogPosting` would fail with `unsupported @type`.
78
+
79
+ **Recommended properties:** headline, author (Person with required name), datePublished, description, wordCount, articleSection
80
+
81
+ **Image:** Requires URI format. Omit if actual URL is not known; note as TODO for user. Do NOT use asset slugs.
82
+
83
+ ### Minimal Example
84
+
85
+ ```json
86
+ {
87
+ "@context": "https://schema.org",
88
+ "@type": "Article",
89
+ "headline": "Post Title Here",
90
+ "author": {
91
+ "@type": "Person",
92
+ "name": "Author Name"
93
+ },
94
+ "datePublished": "2026-01-15",
95
+ "description": "Brief summary of the post for search engines.",
96
+ "wordCount": 1200,
97
+ "articleSection": "Category"
98
+ }
99
+ ```
@@ -18,8 +18,8 @@ Load these files on-demand, not upfront. Read each file only when the situation
18
18
  | File | When to Read |
19
19
  |------|-------------|
20
20
  | `./landing-sections.md` | Creating or modifying landing page schemas -- contains all 12 section definitions with field schemas and image guidance |
21
- | `../references/schema-reference.md` | Troubleshooting validation errors, checking field types, or looking up JSON-LD type details |
22
- | `../references/workflow-reference.md` | Running CLI operations (init, dev, push, pull, validate), fixing errors, or understanding dev mode behavior |
21
+ | `../supatent-references/schema-reference.md` | Troubleshooting validation errors, checking field types, or looking up JSON-LD type details |
22
+ | `../supatent-references/workflow-reference.md` | Running CLI operations (init, dev, push, pull, validate), fixing errors, or understanding dev mode behavior |
23
23
 
24
24
  ## Intent Detection
25
25
 
@@ -209,7 +209,7 @@ For the `landing-metadata` schema, add conditional JSON-LD fields based on secti
209
209
 
210
210
  Singleton content files use slug `default` (e.g., `default.en.json`).
211
211
 
212
- After writing schema files, check `.supatent/.validation-status.json` to confirm validation passes. If errors appear, read `../references/workflow-reference.md` for fix instructions.
212
+ After writing schema files, check `.supatent/.validation-status.json` to confirm validation passes. If errors appear, read `../supatent-references/workflow-reference.md` for fix instructions.
213
213
 
214
214
  ## Content Generation
215
215