contentrain 0.7.13 → 0.8.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.
@@ -33,6 +33,8 @@ const OLD_RULE_FILES = [
33
33
  "contentrain-workflow-rules.md",
34
34
  "contentrain-normalize-rules.md"
35
35
  ];
36
+ /** First heading of the essentials rule file — marks our block in a shared file. */
37
+ const GUARDRAILS_MARKER = "# Contentrain — Essential Rules";
36
38
  const IDE_CONFIGS = {
37
39
  "claude-code": {
38
40
  name: "Claude Code",
@@ -58,7 +60,15 @@ const IDE_CONFIGS = {
58
60
  name: "GitHub Copilot",
59
61
  rulesDir: ".github",
60
62
  skillsDir: ".agents/skills",
61
- guardrailsFileName: "copilot-instructions.md"
63
+ guardrailsFileName: "copilot-instructions.md",
64
+ sharedInstructionsFile: true
65
+ },
66
+ codex: {
67
+ name: "OpenAI Codex",
68
+ rulesDir: ".",
69
+ skillsDir: ".agents/skills",
70
+ guardrailsFileName: "AGENTS.md",
71
+ sharedInstructionsFile: true
62
72
  }
63
73
  };
64
74
  const MCP_CONFIGS = {
@@ -66,7 +76,8 @@ const MCP_CONFIGS = {
66
76
  cursor: ".cursor/mcp.json",
67
77
  windsurf: ".windsurf/mcp.json",
68
78
  vscode: ".vscode/mcp.json",
69
- copilot: ".vscode/mcp.json"
79
+ copilot: ".vscode/mcp.json",
80
+ codex: ".codex/config.toml"
70
81
  };
71
82
  const STANDARD_MCP_CONFIG = { mcpServers: { contentrain: {
72
83
  command: "npx",
@@ -76,14 +87,49 @@ const STANDARD_MCP_CONFIG = { mcpServers: { contentrain: {
76
87
  "--stdio"
77
88
  ]
78
89
  } } };
90
+ /** Codex configures MCP servers in TOML, not JSON. */
91
+ const CODEX_MCP_BLOCK = [
92
+ "[mcp_servers.contentrain]",
93
+ "command = \"npx\"",
94
+ "args = [\"contentrain\", \"serve\", \"--stdio\"]"
95
+ ].join("\n");
79
96
  async function detectIdes(projectRoot) {
80
97
  const ides = [];
81
98
  if (await pathExists(join(projectRoot, "CLAUDE.md")) || await pathExists(join(projectRoot, ".claude"))) ides.push("claude-code");
82
99
  if (await pathExists(join(projectRoot, ".cursor"))) ides.push("cursor");
83
100
  if (await pathExists(join(projectRoot, ".windsurf"))) ides.push("windsurf");
84
101
  if (await pathExists(join(projectRoot, ".github"))) ides.push("copilot");
102
+ if (await pathExists(join(projectRoot, ".codex"))) ides.push("codex");
85
103
  return ides;
86
104
  }
105
+ /**
106
+ * Codex keeps unrelated user settings (model, approval policy, sandbox) in the
107
+ * same file, so we append a table rather than parse and rewrite: appending is
108
+ * valid TOML, needs no parser dependency, and leaves the rest of the file
109
+ * byte-for-byte intact.
110
+ */
111
+ async function writeCodexMcpConfig(projectRoot, relativePath) {
112
+ const configPath = join(projectRoot, relativePath);
113
+ if (await pathExists(configPath)) {
114
+ const existing = await readFile(configPath, "utf-8");
115
+ if (/^\s*\[mcp_servers\.contentrain\]/m.test(existing)) return {
116
+ written: false,
117
+ path: relativePath,
118
+ skipped: "Already configured"
119
+ };
120
+ await appendFile(configPath, `${existing.endsWith("\n") ? "\n" : "\n\n"}${CODEX_MCP_BLOCK}\n`);
121
+ return {
122
+ written: true,
123
+ path: relativePath
124
+ };
125
+ }
126
+ await ensureDir(join(configPath, ".."));
127
+ await writeFile(configPath, `${CODEX_MCP_BLOCK}\n`, "utf-8");
128
+ return {
129
+ written: true,
130
+ path: relativePath
131
+ };
132
+ }
87
133
  async function writeMcpConfig(projectRoot, agent) {
88
134
  const relativePath = MCP_CONFIGS[agent];
89
135
  if (!relativePath) return {
@@ -91,6 +137,7 @@ async function writeMcpConfig(projectRoot, agent) {
91
137
  path: "",
92
138
  skipped: `Unknown agent: "${agent}"`
93
139
  };
140
+ if (agent === "codex") return writeCodexMcpConfig(projectRoot, relativePath);
94
141
  const configPath = join(projectRoot, relativePath);
95
142
  if (await pathExists(configPath)) try {
96
143
  const existing = JSON.parse(await readFile(configPath, "utf-8"));
@@ -150,14 +197,17 @@ async function installIdeRulesAndSkills(projectRoot, ide, resolveRuleFile, resol
150
197
  let content = await readFile(resolveRuleFile("essential/contentrain-essentials.md"), "utf-8");
151
198
  if (ide.guardrailsFrontmatter) content = ide.guardrailsFrontmatter + content;
152
199
  if (await pathExists(guardrailsDest)) {
153
- if (forceUpdate) {
200
+ if (ide.sharedInstructionsFile) {
201
+ if (!(await readFile(guardrailsDest, "utf-8")).includes(GUARDRAILS_MARKER)) {
202
+ await appendFile(guardrailsDest, `\n\n${content}`);
203
+ installed++;
204
+ }
205
+ } else if (forceUpdate) {
154
206
  await writeFile(guardrailsDest, content, "utf-8");
155
207
  updated++;
156
208
  }
157
209
  } 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");
210
+ await writeFile(guardrailsDest, content, "utf-8");
161
211
  installed++;
162
212
  }
163
213
  filesToAdd.push(guardrailsDest);
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import { i as pc } from "./ui-B8l_LC36.mjs";
3
3
  import { defineCommand, runMain } from "citty";
4
4
  //#region package.json
5
- var version = "0.7.13";
5
+ var version = "0.8.0";
6
6
  //#endregion
7
7
  //#region src/utils/debug.ts
8
8
  /**
@@ -43,7 +43,7 @@ runMain(defineCommand({
43
43
  description: "Contentrain CLI — AI content governance infrastructure"
44
44
  },
45
45
  subCommands: {
46
- init: () => import("./init-1fl35oaD.mjs").then((m) => m.default),
46
+ init: () => import("./init-BuoJKTt1.mjs").then((m) => m.default),
47
47
  status: () => import("./status-CKHWrsUI.mjs").then((m) => m.default),
48
48
  doctor: () => import("./doctor-MOf_LQEL.mjs").then((m) => m.default),
49
49
  validate: () => import("./validate-CW8FCCd4.mjs").then((m) => m.default),
@@ -55,8 +55,8 @@ runMain(defineCommand({
55
55
  describe: () => import("./describe-DJ5H9Cxm.mjs").then((m) => m.default),
56
56
  "describe-format": () => import("./describe-format-CUAqB0C4.mjs").then((m) => m.default),
57
57
  scaffold: () => import("./scaffold-irxW-vdX.mjs").then((m) => m.default),
58
- setup: () => import("./setup-ZnQXTEP-.mjs").then((m) => m.default),
59
- skills: () => import("./skills-DeliTaHV.mjs").then((m) => m.default),
58
+ setup: () => import("./setup-DdoWlLFE.mjs").then((m) => m.default),
59
+ skills: () => import("./skills-_LEBO_GH.mjs").then((m) => m.default),
60
60
  studio: () => import("./studio-DVVkj4QL.mjs").then((m) => m.default)
61
61
  }
62
62
  }));
@@ -1,6 +1,6 @@
1
1
  import { i as pc } from "./ui-B8l_LC36.mjs";
2
2
  import { r as resolveProjectRoot, t as loadProjectContext } from "./context-OskMGy8y.mjs";
3
- import { a as createPackageResolver, c as writeMcpConfig, i as addClaudeMdReference, n as IDE_CONFIGS, o as detectIdes, s as installIdeRulesAndSkills } from "./ide-DPDVYiaq.mjs";
3
+ import { a as createPackageResolver, c as writeMcpConfig, i as addClaudeMdReference, n as IDE_CONFIGS, o as detectIdes, s as installIdeRulesAndSkills } from "./ide-CQ9vEsPi.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  import { confirm, intro, isCancel, log, multiselect, outro, select, spinner } from "@clack/prompts";
6
6
  import { join } from "node:path";
@@ -1,6 +1,6 @@
1
1
  import { i as pc } from "./ui-B8l_LC36.mjs";
2
2
  import { r as resolveProjectRoot, t as loadProjectContext } from "./context-OskMGy8y.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-DPDVYiaq.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-CQ9vEsPi.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  import { intro, log, outro, spinner } from "@clack/prompts";
6
6
  //#region src/commands/setup.ts
@@ -39,6 +39,7 @@ var setup_default = defineCommand({
39
39
  log.message(` ${pc.cyan("contentrain setup vscode")}`);
40
40
  log.message(` ${pc.cyan("contentrain setup windsurf")}`);
41
41
  log.message(` ${pc.cyan("contentrain setup copilot")}`);
42
+ log.message(` ${pc.cyan("contentrain setup codex")}`);
42
43
  log.message(` ${pc.cyan("contentrain setup --all")}`);
43
44
  outro("");
44
45
  return;
@@ -1,6 +1,6 @@
1
1
  import { i as pc } from "./ui-B8l_LC36.mjs";
2
2
  import { r as resolveProjectRoot } from "./context-OskMGy8y.mjs";
3
- import { a as createPackageResolver, n as IDE_CONFIGS, o as detectIdes, s as installIdeRulesAndSkills, t as AGENT_SKILL_NAMES } from "./ide-DPDVYiaq.mjs";
3
+ import { a as createPackageResolver, n as IDE_CONFIGS, o as detectIdes, s as installIdeRulesAndSkills, t as AGENT_SKILL_NAMES } from "./ide-CQ9vEsPi.mjs";
4
4
  import { defineCommand } from "citty";
5
5
  import { intro, log, outro, spinner } from "@clack/prompts";
6
6
  import { join } from "node:path";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contentrain",
3
- "version": "0.7.13",
3
+ "version": "0.8.0",
4
4
  "license": "MIT",
5
5
  "description": "CLI for Contentrain — AI content governance infrastructure",
6
6
  "type": "module",
@@ -48,10 +48,10 @@
48
48
  "ws": "^8.18.0",
49
49
  "zod": "^3.24.0",
50
50
  "@contentrain/rules": "0.6.0",
51
- "@contentrain/mcp": "2.2.0",
51
+ "@contentrain/mcp": "2.3.0",
52
52
  "@contentrain/query": "7.0.1",
53
53
  "@contentrain/types": "0.9.0",
54
- "@contentrain/skills": "0.6.0"
54
+ "@contentrain/skills": "0.7.0"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/ws": "^8.5.0",