@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 +34 -9
- package/bin/install.mjs +252 -52
- package/package.json +4 -3
- package/skills/{content-blog → supatent-content-blog}/SKILL.md +3 -3
- package/skills/supatent-content-blog/blog-sections.md +99 -0
- package/skills/{content-landing → supatent-content-landing}/SKILL.md +3 -3
- package/skills/supatent-content-landing/landing-sections.md +360 -0
- package/skills/{core → supatent-core}/SKILL.md +4 -4
- package/skills/supatent-references/schema-reference.md +289 -0
- package/skills/supatent-references/workflow-reference.md +345 -0
- package/skills-codex/supatent-content-blog/SKILL.md +298 -0
- package/skills-codex/supatent-content-landing/SKILL.md +384 -0
- package/skills-codex/supatent-core/SKILL.md +236 -0
- package/.claude-plugin/plugin.json +0 -5
- /package/{skills/content-blog → skills-codex/supatent-content-blog}/blog-sections.md +0 -0
- /package/{skills/content-landing → skills-codex/supatent-content-landing}/landing-sections.md +0 -0
- /package/{skills/references → skills-codex/supatent-references}/schema-reference.md +0 -0
- /package/{skills/references → skills-codex/supatent-references}/workflow-reference.md +0 -0
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
|
|
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
|
-
|
|
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 |
|
|
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
|
|
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
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
182
|
-
// -----------------------------------------------------------------------
|
|
183
|
-
let claudeExists = false;
|
|
288
|
+
async function ensureDirectoryExists(dirPath, missingMessage, { force }) {
|
|
289
|
+
let exists = false;
|
|
184
290
|
try {
|
|
185
|
-
const s = await stat(
|
|
186
|
-
|
|
291
|
+
const s = await stat(dirPath);
|
|
292
|
+
exists = s.isDirectory();
|
|
187
293
|
} catch {
|
|
188
294
|
// does not exist
|
|
189
295
|
}
|
|
190
296
|
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
|
323
|
+
// Step a: Build source map
|
|
204
324
|
// -----------------------------------------------------------------------
|
|
205
|
-
const sourceMap = await buildSourceMap(
|
|
325
|
+
const sourceMap = await buildSourceMap(sourceDir);
|
|
206
326
|
const sourceFiles = Object.keys(sourceMap);
|
|
207
327
|
|
|
208
328
|
// -----------------------------------------------------------------------
|
|
209
|
-
// Step
|
|
329
|
+
// Step b: Read existing manifest
|
|
210
330
|
// -----------------------------------------------------------------------
|
|
211
331
|
const manifest = await readManifest(manifestPath);
|
|
212
332
|
|
|
213
333
|
// -----------------------------------------------------------------------
|
|
214
|
-
// Step
|
|
334
|
+
// Step c: Compute diff
|
|
215
335
|
// -----------------------------------------------------------------------
|
|
216
336
|
const diff = await computeDiff(sourceMap, manifest, targetDir);
|
|
217
337
|
|
|
218
338
|
// -----------------------------------------------------------------------
|
|
219
|
-
// Step
|
|
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(
|
|
359
|
+
console.log(` ${startHint}`);
|
|
240
360
|
return;
|
|
241
361
|
}
|
|
242
362
|
|
|
243
363
|
// -----------------------------------------------------------------------
|
|
244
|
-
// Step
|
|
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
|
|
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
|
|
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 ${
|
|
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 ${
|
|
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(
|
|
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.
|
|
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
|
-
"
|
|
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
|
|