contentrain 0.2.3 → 0.3.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/dist/index.mjs +2 -2
- package/dist/{init-BbtT9_sl.mjs → init-BARgfYUV.mjs} +129 -109
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -5,11 +5,11 @@ import { defineCommand, runMain } from "citty";
|
|
|
5
5
|
runMain(defineCommand({
|
|
6
6
|
meta: {
|
|
7
7
|
name: "contentrain",
|
|
8
|
-
version: "0.
|
|
8
|
+
version: "0.3.0",
|
|
9
9
|
description: "Contentrain CLI — AI content governance infrastructure"
|
|
10
10
|
},
|
|
11
11
|
subCommands: {
|
|
12
|
-
init: () => import("./init-
|
|
12
|
+
init: () => import("./init-BARgfYUV.mjs").then((m) => m.default),
|
|
13
13
|
status: () => import("./status-kF5miVty.mjs").then((m) => m.default),
|
|
14
14
|
doctor: () => import("./doctor-DyKjAIKH.mjs").then((m) => m.default),
|
|
15
15
|
validate: () => import("./validate-DnVamp3p.mjs").then((m) => m.default),
|
|
@@ -350,33 +350,64 @@ async function executeInit(projectRoot, opts) {
|
|
|
350
350
|
await tx.cleanup();
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
|
-
const
|
|
354
|
-
"
|
|
355
|
-
"
|
|
356
|
-
"
|
|
357
|
-
"
|
|
358
|
-
"
|
|
359
|
-
"
|
|
360
|
-
"
|
|
361
|
-
"
|
|
362
|
-
"
|
|
363
|
-
"
|
|
364
|
-
"
|
|
353
|
+
const AGENT_SKILL_NAMES = [
|
|
354
|
+
"contentrain",
|
|
355
|
+
"contentrain-normalize",
|
|
356
|
+
"contentrain-quality",
|
|
357
|
+
"contentrain-sdk",
|
|
358
|
+
"contentrain-content",
|
|
359
|
+
"contentrain-model",
|
|
360
|
+
"contentrain-init",
|
|
361
|
+
"contentrain-bulk",
|
|
362
|
+
"contentrain-validate-fix",
|
|
363
|
+
"contentrain-review",
|
|
364
|
+
"contentrain-translate",
|
|
365
|
+
"contentrain-generate",
|
|
366
|
+
"contentrain-serve",
|
|
367
|
+
"contentrain-diff",
|
|
368
|
+
"contentrain-doctor"
|
|
365
369
|
];
|
|
366
|
-
const
|
|
367
|
-
"contentrain-
|
|
368
|
-
"contentrain-
|
|
369
|
-
"contentrain-
|
|
370
|
-
"contentrain-
|
|
371
|
-
"contentrain-
|
|
372
|
-
"contentrain-
|
|
373
|
-
"contentrain-
|
|
374
|
-
"contentrain-
|
|
375
|
-
"contentrain-
|
|
376
|
-
"contentrain-
|
|
377
|
-
"contentrain-
|
|
378
|
-
"contentrain-init.md"
|
|
370
|
+
const OLD_RULE_FILES = [
|
|
371
|
+
"contentrain-content-quality.md",
|
|
372
|
+
"contentrain-seo-rules.md",
|
|
373
|
+
"contentrain-i18n-quality.md",
|
|
374
|
+
"contentrain-accessibility-rules.md",
|
|
375
|
+
"contentrain-security-rules.md",
|
|
376
|
+
"contentrain-media-rules.md",
|
|
377
|
+
"contentrain-content-conventions.md",
|
|
378
|
+
"contentrain-schema-rules.md",
|
|
379
|
+
"contentrain-mcp-usage.md",
|
|
380
|
+
"contentrain-workflow-rules.md",
|
|
381
|
+
"contentrain-normalize-rules.md"
|
|
379
382
|
];
|
|
383
|
+
const IDE_CONFIGS = {
|
|
384
|
+
"claude-code": {
|
|
385
|
+
name: "Claude Code",
|
|
386
|
+
rulesDir: ".claude/rules",
|
|
387
|
+
skillsDir: ".claude/skills",
|
|
388
|
+
guardrailsFileName: "contentrain-essentials.md"
|
|
389
|
+
},
|
|
390
|
+
cursor: {
|
|
391
|
+
name: "Cursor",
|
|
392
|
+
rulesDir: ".cursor/rules",
|
|
393
|
+
skillsDir: ".cursor/skills",
|
|
394
|
+
guardrailsFileName: "contentrain-essentials.mdc",
|
|
395
|
+
guardrailsFrontmatter: "---\ndescription: Contentrain essential content governance rules\nalwaysApply: true\n---\n\n"
|
|
396
|
+
},
|
|
397
|
+
windsurf: {
|
|
398
|
+
name: "Windsurf",
|
|
399
|
+
rulesDir: ".windsurf/rules",
|
|
400
|
+
skillsDir: ".windsurf/skills",
|
|
401
|
+
guardrailsFileName: "contentrain-essentials.md",
|
|
402
|
+
guardrailsFrontmatter: "---\ndescription: Contentrain essential content governance rules\ntrigger: always_on\n---\n\n"
|
|
403
|
+
},
|
|
404
|
+
copilot: {
|
|
405
|
+
name: "GitHub Copilot",
|
|
406
|
+
rulesDir: ".github",
|
|
407
|
+
skillsDir: ".agents/skills",
|
|
408
|
+
guardrailsFileName: "copilot-instructions.md"
|
|
409
|
+
}
|
|
410
|
+
};
|
|
380
411
|
async function installRules(projectRoot) {
|
|
381
412
|
try {
|
|
382
413
|
const { createRequire } = await import("node:module");
|
|
@@ -388,27 +419,24 @@ async function installRules(projectRoot) {
|
|
|
388
419
|
resolveSkillFile = (p) => requireSkills.resolve(`@contentrain/skills/${p}`);
|
|
389
420
|
} catch {}
|
|
390
421
|
const hasClaudeCode = await pathExists(join(projectRoot, "CLAUDE.md")) || await pathExists(join(projectRoot, ".claude"));
|
|
391
|
-
const
|
|
392
|
-
const
|
|
393
|
-
const
|
|
394
|
-
const noIDEDetected = !hasClaudeCode && !
|
|
395
|
-
|
|
396
|
-
if (
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const
|
|
402
|
-
await
|
|
403
|
-
try {
|
|
404
|
-
await git.commit("[contentrain] install Cursor rules");
|
|
405
|
-
} catch {}
|
|
422
|
+
const hasCursor = await pathExists(join(projectRoot, ".cursor"));
|
|
423
|
+
const hasWindsurf = await pathExists(join(projectRoot, ".windsurf"));
|
|
424
|
+
const hasCopilot = await pathExists(join(projectRoot, ".github"));
|
|
425
|
+
const noIDEDetected = !hasClaudeCode && !hasCursor && !hasWindsurf && !hasCopilot;
|
|
426
|
+
const detectedIdes = [];
|
|
427
|
+
if (hasClaudeCode) detectedIdes.push("claude-code");
|
|
428
|
+
if (hasCursor) detectedIdes.push("cursor");
|
|
429
|
+
if (hasWindsurf) detectedIdes.push("windsurf");
|
|
430
|
+
if (hasCopilot) detectedIdes.push("copilot");
|
|
431
|
+
for (const ideKey of detectedIdes) {
|
|
432
|
+
const ide = IDE_CONFIGS[ideKey];
|
|
433
|
+
await installIdeRulesAndSkills(projectRoot, ide, resolveRuleFile, resolveSkillFile);
|
|
406
434
|
}
|
|
407
|
-
if (
|
|
435
|
+
if (hasClaudeCode) await addClaudeMdReference(projectRoot);
|
|
408
436
|
if (noIDEDetected) {
|
|
409
|
-
const
|
|
437
|
+
const essentials = await readFile(resolveRuleFile("essential/contentrain-essentials.md"), "utf-8");
|
|
410
438
|
const dest = join(projectRoot, "CLAUDE.md");
|
|
411
|
-
await writeFile(dest,
|
|
439
|
+
await writeFile(dest, essentials, "utf-8");
|
|
412
440
|
const git = simpleGit(projectRoot);
|
|
413
441
|
await git.add(dest);
|
|
414
442
|
try {
|
|
@@ -417,32 +445,65 @@ async function installRules(projectRoot) {
|
|
|
417
445
|
}
|
|
418
446
|
} catch {}
|
|
419
447
|
}
|
|
420
|
-
async function
|
|
421
|
-
const rulesDir = join(projectRoot,
|
|
422
|
-
const skillsDir = join(projectRoot,
|
|
448
|
+
async function installIdeRulesAndSkills(projectRoot, ide, resolveRuleFile, resolveSkillFile) {
|
|
449
|
+
const rulesDir = join(projectRoot, ide.rulesDir);
|
|
450
|
+
const skillsDir = join(projectRoot, ide.skillsDir);
|
|
423
451
|
const git = simpleGit(projectRoot);
|
|
424
452
|
const filesToAdd = [];
|
|
425
453
|
await ensureDir(rulesDir);
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
if (
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
454
|
+
const guardrailsDest = join(rulesDir, ide.guardrailsFileName);
|
|
455
|
+
if (!await pathExists(guardrailsDest)) try {
|
|
456
|
+
let content = await readFile(resolveRuleFile("essential/contentrain-essentials.md"), "utf-8");
|
|
457
|
+
if (ide.guardrailsFrontmatter) content = ide.guardrailsFrontmatter + content;
|
|
458
|
+
if (ide.name === "GitHub Copilot" && await pathExists(guardrailsDest)) {
|
|
459
|
+
if (!(await readFile(guardrailsDest, "utf-8")).includes("# Contentrain")) await appendFile(guardrailsDest, `\n\n${content}`);
|
|
460
|
+
} else await writeFile(guardrailsDest, content, "utf-8");
|
|
461
|
+
filesToAdd.push(guardrailsDest);
|
|
462
|
+
} catch {}
|
|
463
|
+
for (const oldFile of OLD_RULE_FILES) {
|
|
464
|
+
const oldPath = join(rulesDir, oldFile);
|
|
465
|
+
if (await pathExists(oldPath)) {
|
|
466
|
+
const { unlink } = await import("node:fs/promises");
|
|
467
|
+
await unlink(oldPath);
|
|
468
|
+
}
|
|
434
469
|
}
|
|
435
470
|
if (resolveSkillFile) {
|
|
436
471
|
await ensureDir(skillsDir);
|
|
437
|
-
for (const
|
|
438
|
-
const
|
|
439
|
-
|
|
472
|
+
for (const skillName of AGENT_SKILL_NAMES) {
|
|
473
|
+
const skillDir = join(skillsDir, skillName);
|
|
474
|
+
const skillMd = join(skillDir, "SKILL.md");
|
|
475
|
+
if (await pathExists(skillMd)) continue;
|
|
440
476
|
try {
|
|
441
|
-
|
|
442
|
-
|
|
477
|
+
const src = resolveSkillFile(`skills/${skillName}/SKILL.md`);
|
|
478
|
+
await ensureDir(skillDir);
|
|
479
|
+
await writeFile(skillMd, await readFile(src, "utf-8"), "utf-8");
|
|
480
|
+
filesToAdd.push(skillMd);
|
|
481
|
+
const { readdirSync } = await import("node:fs");
|
|
482
|
+
try {
|
|
483
|
+
const refsDir = join(resolveSkillFile(`skills/${skillName}/SKILL.md`), "..", "references");
|
|
484
|
+
const refs = readdirSync(refsDir);
|
|
485
|
+
if (refs.length > 0) {
|
|
486
|
+
const destRefsDir = join(skillDir, "references");
|
|
487
|
+
await ensureDir(destRefsDir);
|
|
488
|
+
for (const ref of refs) {
|
|
489
|
+
const refSrc = join(refsDir, ref);
|
|
490
|
+
const refDest = join(destRefsDir, ref);
|
|
491
|
+
await writeFile(refDest, await readFile(refSrc, "utf-8"), "utf-8");
|
|
492
|
+
filesToAdd.push(refDest);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
} catch {}
|
|
443
496
|
} catch {}
|
|
444
497
|
}
|
|
445
498
|
}
|
|
499
|
+
if (filesToAdd.length > 0) {
|
|
500
|
+
await git.add(filesToAdd);
|
|
501
|
+
try {
|
|
502
|
+
await git.commit(`[contentrain] install ${ide.name} rules and skills`);
|
|
503
|
+
} catch {}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
async function addClaudeMdReference(projectRoot) {
|
|
446
507
|
const claudeMdPath = join(projectRoot, "CLAUDE.md");
|
|
447
508
|
const marker = "# Contentrain AI Rules";
|
|
448
509
|
const reference = [
|
|
@@ -450,65 +511,24 @@ async function installClaudeCodeRules(projectRoot, resolveRuleFile, resolveSkill
|
|
|
450
511
|
"",
|
|
451
512
|
"This project uses [Contentrain](https://ai.contentrain.io) for AI-driven content management.",
|
|
452
513
|
"",
|
|
453
|
-
"- **Rules** are in `.claude/rules
|
|
454
|
-
"- **Skills** are in `.claude/skills/` —
|
|
514
|
+
"- **Rules** are in `.claude/rules/contentrain-essentials.md` — auto-loaded each conversation",
|
|
515
|
+
"- **Skills** are in `.claude/skills/` — loaded on demand by the agent",
|
|
455
516
|
"- **Docs:** https://ai.contentrain.io"
|
|
456
517
|
].join("\n");
|
|
518
|
+
const git = simpleGit(projectRoot);
|
|
457
519
|
if (await pathExists(claudeMdPath)) {
|
|
458
520
|
if (!(await readFile(claudeMdPath, "utf-8")).includes(marker)) {
|
|
459
521
|
await appendFile(claudeMdPath, `\n\n${reference}\n`);
|
|
460
|
-
|
|
522
|
+
await git.add(claudeMdPath);
|
|
523
|
+
try {
|
|
524
|
+
await git.commit("[contentrain] add CLAUDE.md reference");
|
|
525
|
+
} catch {}
|
|
461
526
|
}
|
|
462
527
|
} else {
|
|
463
528
|
await writeFile(claudeMdPath, `${reference}\n`, "utf-8");
|
|
464
|
-
|
|
465
|
-
}
|
|
466
|
-
if (filesToAdd.length > 0) {
|
|
467
|
-
await git.add(filesToAdd);
|
|
468
|
-
try {
|
|
469
|
-
await git.commit("[contentrain] install Claude Code rules and skills");
|
|
470
|
-
} catch {}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
async function installCursorRules(projectRoot, resolveRuleFile) {
|
|
474
|
-
const rulesDir = join(projectRoot, ".cursor", "rules");
|
|
475
|
-
await ensureDir(rulesDir);
|
|
476
|
-
const git = simpleGit(projectRoot);
|
|
477
|
-
const filesToAdd = [];
|
|
478
|
-
for (const baseName of RULE_BASE_NAMES) {
|
|
479
|
-
const srcFile = `contentrain-${baseName}.mdc`;
|
|
480
|
-
const dest = join(rulesDir, srcFile);
|
|
481
|
-
if (await pathExists(dest)) continue;
|
|
482
|
-
try {
|
|
483
|
-
await writeFile(dest, await readFile(resolveRuleFile(`ide/cursor/rules/${srcFile}`), "utf-8"), "utf-8");
|
|
484
|
-
filesToAdd.push(dest);
|
|
485
|
-
} catch {}
|
|
486
|
-
}
|
|
487
|
-
if (filesToAdd.length > 0) {
|
|
488
|
-
await git.add(filesToAdd);
|
|
489
|
-
try {
|
|
490
|
-
await git.commit("[contentrain] install Cursor rules");
|
|
491
|
-
} catch {}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
async function installWindsurfRules(projectRoot, resolveRuleFile) {
|
|
495
|
-
const rulesDir = join(projectRoot, ".windsurf", "rules");
|
|
496
|
-
await ensureDir(rulesDir);
|
|
497
|
-
const git = simpleGit(projectRoot);
|
|
498
|
-
const filesToAdd = [];
|
|
499
|
-
for (const baseName of RULE_BASE_NAMES) {
|
|
500
|
-
const fileName = `contentrain-${baseName}.md`;
|
|
501
|
-
const dest = join(rulesDir, fileName);
|
|
502
|
-
if (await pathExists(dest)) continue;
|
|
503
|
-
try {
|
|
504
|
-
await writeFile(dest, await readFile(resolveRuleFile(`ide/windsurf/rules/${fileName}`), "utf-8"), "utf-8");
|
|
505
|
-
filesToAdd.push(dest);
|
|
506
|
-
} catch {}
|
|
507
|
-
}
|
|
508
|
-
if (filesToAdd.length > 0) {
|
|
509
|
-
await git.add(filesToAdd);
|
|
529
|
+
await git.add(claudeMdPath);
|
|
510
530
|
try {
|
|
511
|
-
await git.commit("[contentrain]
|
|
531
|
+
await git.commit("[contentrain] add CLAUDE.md reference");
|
|
512
532
|
} catch {}
|
|
513
533
|
}
|
|
514
534
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "contentrain",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "CLI for Contentrain — AI content governance infrastructure",
|
|
6
6
|
"type": "module",
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
"picocolors": "^1.1.0",
|
|
47
47
|
"simple-git": "^3.27.0",
|
|
48
48
|
"ws": "^8.18.0",
|
|
49
|
+
"@contentrain/rules": "0.3.0",
|
|
49
50
|
"@contentrain/mcp": "1.0.6",
|
|
50
|
-
"@contentrain/
|
|
51
|
-
"@contentrain/query": "5.0.2",
|
|
51
|
+
"@contentrain/query": "5.1.0",
|
|
52
52
|
"@contentrain/types": "0.2.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|