contentrain 0.4.2 → 0.4.3

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.
@@ -117,31 +117,53 @@ async function writeMcpConfig(projectRoot, agent) {
117
117
  path: relativePath
118
118
  };
119
119
  }
120
+ /**
121
+ * Create a resolver function for a package's files.
122
+ * Tries multiple strategies to handle npm, pnpm, and workspace layouts.
123
+ */
124
+ async function createPackageResolver(packageName, projectRoot) {
125
+ const { createRequire } = await import("node:module");
126
+ try {
127
+ const req = createRequire(import.meta.url);
128
+ req.resolve(packageName);
129
+ return (p) => req.resolve(`${packageName}/${p}`);
130
+ } catch {}
131
+ try {
132
+ const req = createRequire(join(projectRoot, "package.json"));
133
+ req.resolve(packageName);
134
+ return (p) => req.resolve(`${packageName}/${p}`);
135
+ } catch {}
136
+ const directBase = join(projectRoot, "node_modules", packageName);
137
+ if (await pathExists(directBase)) return (p) => join(directBase, p);
138
+ return null;
139
+ }
120
140
  async function installIdeRulesAndSkills(projectRoot, ide, resolveRuleFile, resolveSkillFile, forceUpdate = false) {
121
141
  let installed = 0;
122
142
  let updated = 0;
123
143
  const rulesDir = join(projectRoot, ide.rulesDir);
124
144
  const git = simpleGit(projectRoot);
125
145
  const filesToAdd = [];
126
- await ensureDir(rulesDir);
127
- const guardrailsDest = join(rulesDir, ide.guardrailsFileName);
128
- try {
129
- let content = await readFile(resolveRuleFile("essential/contentrain-essentials.md"), "utf-8");
130
- if (ide.guardrailsFrontmatter) content = ide.guardrailsFrontmatter + content;
131
- if (await pathExists(guardrailsDest)) {
132
- if (forceUpdate) {
133
- await writeFile(guardrailsDest, content, "utf-8");
134
- updated++;
146
+ if (resolveRuleFile) {
147
+ await ensureDir(rulesDir);
148
+ const guardrailsDest = join(rulesDir, ide.guardrailsFileName);
149
+ try {
150
+ let content = await readFile(resolveRuleFile("essential/contentrain-essentials.md"), "utf-8");
151
+ if (ide.guardrailsFrontmatter) content = ide.guardrailsFrontmatter + content;
152
+ if (await pathExists(guardrailsDest)) {
153
+ if (forceUpdate) {
154
+ await writeFile(guardrailsDest, content, "utf-8");
155
+ updated++;
156
+ }
157
+ } else {
158
+ if (ide.name === "GitHub Copilot" && await pathExists(guardrailsDest)) {
159
+ if (!(await readFile(guardrailsDest, "utf-8")).includes("# Contentrain")) await appendFile(guardrailsDest, `\n\n${content}`);
160
+ } else await writeFile(guardrailsDest, content, "utf-8");
161
+ installed++;
135
162
  }
136
- } else {
137
- if (ide.name === "GitHub Copilot" && await pathExists(guardrailsDest)) {
138
- if (!(await readFile(guardrailsDest, "utf-8")).includes("# Contentrain")) await appendFile(guardrailsDest, `\n\n${content}`);
139
- } else await writeFile(guardrailsDest, content, "utf-8");
140
- installed++;
141
- }
142
- filesToAdd.push(guardrailsDest);
143
- } catch {}
144
- for (const oldFile of OLD_RULE_FILES) {
163
+ filesToAdd.push(guardrailsDest);
164
+ } catch {}
165
+ }
166
+ if (await pathExists(rulesDir)) for (const oldFile of OLD_RULE_FILES) {
145
167
  const oldPath = join(rulesDir, oldFile);
146
168
  if (await pathExists(oldPath)) {
147
169
  const { unlink } = await import("node:fs/promises");
@@ -224,4 +246,4 @@ async function addClaudeMdReference(projectRoot) {
224
246
  }
225
247
  }
226
248
  //#endregion
227
- export { detectIdes as a, addClaudeMdReference as i, IDE_CONFIGS as n, installIdeRulesAndSkills as o, MCP_CONFIGS as r, writeMcpConfig as s, AGENT_SKILL_NAMES as t };
249
+ export { createPackageResolver as a, writeMcpConfig as c, addClaudeMdReference as i, IDE_CONFIGS as n, detectIdes as o, MCP_CONFIGS as r, installIdeRulesAndSkills as s, AGENT_SKILL_NAMES as t };
package/dist/index.mjs CHANGED
@@ -5,19 +5,19 @@ import { defineCommand, runMain } from "citty";
5
5
  runMain(defineCommand({
6
6
  meta: {
7
7
  name: "contentrain",
8
- version: "0.4.2",
8
+ version: "0.4.3",
9
9
  description: "Contentrain CLI — AI content governance infrastructure"
10
10
  },
11
11
  subCommands: {
12
- init: () => import("./init-DFyQ-l-E.mjs").then((m) => m.default),
12
+ init: () => import("./init-BVlisNcW.mjs").then((m) => m.default),
13
13
  status: () => import("./status-CoHgz6HN.mjs").then((m) => m.default),
14
14
  doctor: () => import("./doctor-DyKjAIKH.mjs").then((m) => m.default),
15
15
  validate: () => import("./validate-B10L7RL7.mjs").then((m) => m.default),
16
16
  serve: () => import("./serve-CNeFIQ1i.mjs").then((m) => m.default),
17
17
  generate: () => import("./generate-DrUojrk8.mjs").then((m) => m.default),
18
18
  diff: () => import("./diff--halNUpG.mjs").then((m) => m.default),
19
- setup: () => import("./setup-DJy-jNEU.mjs").then((m) => m.default),
20
- skills: () => import("./skills-D-tFY7WA.mjs").then((m) => m.default),
19
+ setup: () => import("./setup-4mS8RuCy.mjs").then((m) => m.default),
20
+ skills: () => import("./skills-4nhto0AZ.mjs").then((m) => m.default),
21
21
  studio: () => import("./studio-CJUGiIMd.mjs").then((m) => m.default)
22
22
  }
23
23
  }));
@@ -1,6 +1,6 @@
1
1
  import { r as resolveProjectRoot, t as loadProjectContext } from "./context-An3xrvcP.mjs";
2
2
  import { i as pc } from "./ui-B5mXontH.mjs";
3
- import { a as detectIdes, i as addClaudeMdReference, n as IDE_CONFIGS, o as installIdeRulesAndSkills, s as writeMcpConfig } from "./ide-Bg-Z8QZk.mjs";
3
+ import { a as createPackageResolver, c as writeMcpConfig, i as addClaudeMdReference, n as IDE_CONFIGS, o as detectIdes, s as installIdeRulesAndSkills } from "./ide-4ZGiAtYB.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  import { confirm, intro, isCancel, log, multiselect, outro, select, spinner } from "@clack/prompts";
6
6
  import { simpleGit } from "simple-git";
@@ -356,32 +356,25 @@ async function executeInit(projectRoot, opts) {
356
356
  }
357
357
  }
358
358
  async function installRules(projectRoot) {
359
- try {
360
- const { createRequire } = await import("node:module");
361
- const require = createRequire(import.meta.url);
362
- const resolveRuleFile = (p) => require.resolve(`@contentrain/rules/${p}`);
363
- let resolveSkillFile = null;
359
+ const resolveRuleFile = await createPackageResolver("@contentrain/rules", projectRoot);
360
+ const resolveSkillFile = await createPackageResolver("@contentrain/skills", projectRoot);
361
+ if (!resolveRuleFile && !resolveSkillFile) return;
362
+ const detectedIdeKeys = await detectIdes(projectRoot);
363
+ const noIDEDetected = detectedIdeKeys.length === 0;
364
+ for (const ideKey of detectedIdeKeys) {
365
+ const ide = IDE_CONFIGS[ideKey];
366
+ await installIdeRulesAndSkills(projectRoot, ide, resolveRuleFile, resolveSkillFile);
367
+ }
368
+ if (detectedIdeKeys.includes("claude-code")) await addClaudeMdReference(projectRoot);
369
+ if (noIDEDetected && resolveRuleFile) try {
370
+ const essentials = await readFile(resolveRuleFile("essential/contentrain-essentials.md"), "utf-8");
371
+ const dest = join(projectRoot, "CLAUDE.md");
372
+ await writeFile(dest, essentials, "utf-8");
373
+ const git = simpleGit(projectRoot);
374
+ await git.add(dest);
364
375
  try {
365
- const requireSkills = createRequire(import.meta.url);
366
- resolveSkillFile = (p) => requireSkills.resolve(`@contentrain/skills/${p}`);
376
+ await git.commit("[contentrain] install AI rules");
367
377
  } catch {}
368
- const detectedIdeKeys = await detectIdes(projectRoot);
369
- const noIDEDetected = detectedIdeKeys.length === 0;
370
- for (const ideKey of detectedIdeKeys) {
371
- const ide = IDE_CONFIGS[ideKey];
372
- await installIdeRulesAndSkills(projectRoot, ide, resolveRuleFile, resolveSkillFile);
373
- }
374
- if (detectedIdeKeys.includes("claude-code")) await addClaudeMdReference(projectRoot);
375
- if (noIDEDetected) {
376
- const essentials = await readFile(resolveRuleFile("essential/contentrain-essentials.md"), "utf-8");
377
- const dest = join(projectRoot, "CLAUDE.md");
378
- await writeFile(dest, essentials, "utf-8");
379
- const git = simpleGit(projectRoot);
380
- await git.add(dest);
381
- try {
382
- await git.commit("[contentrain] install AI rules");
383
- } catch {}
384
- }
385
378
  } catch {}
386
379
  }
387
380
  async function configureMcp(projectRoot) {
@@ -1,6 +1,6 @@
1
1
  import { r as resolveProjectRoot, t as loadProjectContext } from "./context-An3xrvcP.mjs";
2
2
  import { i as pc } from "./ui-B5mXontH.mjs";
3
- import { a as detectIdes, n as IDE_CONFIGS, o as installIdeRulesAndSkills, r as MCP_CONFIGS, s as writeMcpConfig } from "./ide-Bg-Z8QZk.mjs";
3
+ import { a as createPackageResolver, c as writeMcpConfig, n as IDE_CONFIGS, o as detectIdes, r as MCP_CONFIGS, s as installIdeRulesAndSkills } from "./ide-4ZGiAtYB.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  import { intro, log, outro, spinner } from "@clack/prompts";
6
6
  //#region src/commands/setup.ts
@@ -69,19 +69,21 @@ var setup_default = defineCommand({
69
69
  const mcpResult = await writeMcpConfig(projectRoot, agentKey);
70
70
  let rulesStatus = "skipped";
71
71
  const ideConfig = IDE_CONFIGS[agentKey];
72
- if (ideConfig) try {
73
- const { createRequire } = await import("node:module");
74
- const req = createRequire(import.meta.url);
75
- const resolveRuleFile = (p) => req.resolve(`@contentrain/rules/${p}`);
76
- let resolveSkillFile = null;
77
- try {
78
- resolveSkillFile = (p) => req.resolve(`@contentrain/skills/${p}`);
79
- } catch {}
80
- const result = await installIdeRulesAndSkills(projectRoot, ideConfig, resolveRuleFile, resolveSkillFile);
81
- if (result.installed > 0) rulesStatus = `${result.installed} installed`;
82
- else rulesStatus = "up to date";
83
- } catch {
84
- rulesStatus = "failed (packages not installed)";
72
+ if (ideConfig) {
73
+ const resolveRuleFile = await createPackageResolver("@contentrain/rules", projectRoot);
74
+ const resolveSkillFile = await createPackageResolver("@contentrain/skills", projectRoot);
75
+ if (!resolveRuleFile && !resolveSkillFile) rulesStatus = "failed (install @contentrain/rules @contentrain/skills)";
76
+ else try {
77
+ const result = await installIdeRulesAndSkills(projectRoot, ideConfig, resolveRuleFile, resolveSkillFile);
78
+ const parts = [];
79
+ if (result.installed > 0) parts.push(`${result.installed} installed`);
80
+ if (result.updated > 0) parts.push(`${result.updated} updated`);
81
+ rulesStatus = parts.length > 0 ? parts.join(", ") : "up to date";
82
+ if (!resolveRuleFile) rulesStatus += " (rules package not found)";
83
+ if (!resolveSkillFile) rulesStatus += " (skills package not found)";
84
+ } catch (err) {
85
+ rulesStatus = `failed (${err instanceof Error ? err.message : String(err)})`;
86
+ }
85
87
  }
86
88
  results.push({
87
89
  agent: agentKey,
@@ -1,6 +1,6 @@
1
1
  import { r as resolveProjectRoot } from "./context-An3xrvcP.mjs";
2
2
  import { i as pc } from "./ui-B5mXontH.mjs";
3
- import { a as detectIdes, n as IDE_CONFIGS, o as installIdeRulesAndSkills, t as AGENT_SKILL_NAMES } from "./ide-Bg-Z8QZk.mjs";
3
+ import { a as createPackageResolver, n as IDE_CONFIGS, o as detectIdes, s as installIdeRulesAndSkills, t as AGENT_SKILL_NAMES } from "./ide-4ZGiAtYB.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  import { intro, log, outro, spinner } from "@clack/prompts";
6
6
  import { join } from "node:path";
@@ -37,21 +37,16 @@ var skills_default = defineCommand({
37
37
  }
38
38
  const s = spinner();
39
39
  s.start("Installing skills and rules...");
40
+ const resolveRuleFile = await createPackageResolver("@contentrain/rules", projectRoot);
41
+ const resolveSkillFile = await createPackageResolver("@contentrain/skills", projectRoot);
42
+ if (!resolveRuleFile && !resolveSkillFile) {
43
+ s.stop("Failed");
44
+ log.error("Required packages not found. Install them:");
45
+ log.message(pc.cyan(" pnpm add -D @contentrain/skills @contentrain/rules"));
46
+ outro("");
47
+ return;
48
+ }
40
49
  try {
41
- const { createRequire } = await import("node:module");
42
- const require = createRequire(import.meta.url);
43
- const resolveRuleFile = (p) => require.resolve(`@contentrain/rules/${p}`);
44
- let resolveSkillFile = null;
45
- try {
46
- const requireSkills = createRequire(import.meta.url);
47
- resolveSkillFile = (p) => requireSkills.resolve(`@contentrain/skills/${p}`);
48
- } catch {
49
- s.stop("Failed");
50
- log.error("@contentrain/skills package not found. Install it:");
51
- log.message(pc.cyan(" pnpm add -D @contentrain/skills @contentrain/rules"));
52
- outro("");
53
- return;
54
- }
55
50
  const detectedIdes = await detectIdes(projectRoot);
56
51
  if (detectedIdes.length === 0) detectedIdes.push("claude-code");
57
52
  let totalInstalled = 0;
@@ -66,6 +61,8 @@ var skills_default = defineCommand({
66
61
  if (totalUpdated > 0) log.success(`Updated ${totalUpdated} skill(s)`);
67
62
  if (totalInstalled > 0) log.success(`Installed ${totalInstalled} new skill(s)`);
68
63
  if (totalInstalled === 0 && totalUpdated === 0) log.info("All skills are up to date");
64
+ if (!resolveRuleFile) log.warning("@contentrain/rules package not found — rules skipped");
65
+ if (!resolveSkillFile) log.warning("@contentrain/skills package not found — skills skipped");
69
66
  outro("");
70
67
  } catch (error) {
71
68
  s.stop("Failed");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contentrain",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "license": "MIT",
5
5
  "description": "CLI for Contentrain — AI content governance infrastructure",
6
6
  "type": "module",
@@ -46,8 +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.2",
49
+ "@contentrain/rules": "0.3.3",
50
50
  "@contentrain/mcp": "1.2.0",
51
+ "@contentrain/skills": "0.3.0",
51
52
  "@contentrain/query": "5.1.4",
52
53
  "@contentrain/types": "0.4.2"
53
54
  },