contentrain 0.4.2 → 0.4.4

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");
@@ -183,12 +205,10 @@ async function installIdeRulesAndSkills(projectRoot, ide, resolveRuleFile, resol
183
205
  } catch {}
184
206
  }
185
207
  }
186
- if (filesToAdd.length > 0) {
208
+ if (filesToAdd.length > 0) try {
187
209
  await git.add(filesToAdd);
188
- try {
189
- await git.commit(`[contentrain] install ${ide.name} rules and skills`);
190
- } catch {}
191
- }
210
+ await git.commit(`[contentrain] install ${ide.name} rules and skills`);
211
+ } catch {}
192
212
  return {
193
213
  installed,
194
214
  updated
@@ -210,18 +230,18 @@ async function addClaudeMdReference(projectRoot) {
210
230
  if (await pathExists(claudeMdPath)) {
211
231
  if (!(await readFile(claudeMdPath, "utf-8")).includes(marker)) {
212
232
  await appendFile(claudeMdPath, `\n\n${reference}\n`);
213
- await git.add(claudeMdPath);
214
233
  try {
234
+ await git.add(claudeMdPath);
215
235
  await git.commit("[contentrain] add CLAUDE.md reference");
216
236
  } catch {}
217
237
  }
218
238
  } else {
219
239
  await writeFile(claudeMdPath, `${reference}\n`, "utf-8");
220
- await git.add(claudeMdPath);
221
240
  try {
241
+ await git.add(claudeMdPath);
222
242
  await git.commit("[contentrain] add CLAUDE.md reference");
223
243
  } catch {}
224
244
  }
225
245
  }
226
246
  //#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 };
247
+ 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.4",
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-CqoDCSV5.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-Y1LFQ9rZ.mjs").then((m) => m.default),
20
+ skills: () => import("./skills-Cp8KF1af.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-_C2gUPbQ.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-_C2gUPbQ.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-_C2gUPbQ.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.4",
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",
50
- "@contentrain/mcp": "1.2.0",
49
+ "@contentrain/rules": "0.3.3",
50
+ "@contentrain/skills": "0.3.0",
51
+ "@contentrain/mcp": "1.2.1",
51
52
  "@contentrain/query": "5.1.4",
52
53
  "@contentrain/types": "0.4.2"
53
54
  },