aicm 0.12.0 → 0.12.1

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 CHANGED
@@ -94,7 +94,7 @@ When you run `npx aicm install`, all rules from the preset will be installed to
94
94
  ### Notes
95
95
 
96
96
  - Generated rules are always placed in a subdirectory for deterministic cleanup and easy gitignore.
97
- - Users may add `.cursor/rules/aicm/` and `.aicm/` (for Windsurf) to their `.gitignore` if they do not want to track generated rules.
97
+ - Users may add `.cursor/rules/aicm/` and `.aicm/` (for Windsurf/Codex) to their `.gitignore` if they do not want to track generated rules.
98
98
 
99
99
  ### Overriding and Disabling Rules and MCP Servers from Presets
100
100
 
@@ -219,6 +219,7 @@ Example `aicm.json`:
219
219
 
220
220
  - `"cursor"`: For the Cursor IDE
221
221
  - `"windsurf"`: For the Windsurf IDE
222
+ - `"codex"`: For the Codex Agent
222
223
 
223
224
  > **Note:** The 'ides' field is default to `["cursor"]` if not specified.
224
225
 
@@ -292,6 +293,7 @@ Rules stored locally in your project or filesystem. Any path containing slashes
292
293
 
293
294
  - **Cursor**: Rules are installed as individual `.mdc` files in the Cursor rules directory (`.cursor/rules/aicm/`), mcp servers are installed to `.cursor/mcp.json`
294
295
  - **Windsurf**: Rules are installed in the `.aicm` directory which should be added to your `.gitignore` file. Our approach for Windsurf is to create links from the `.windsurfrules` file to the respective rules in the `.aicm` directory. There is no support for local mcp servers at the moment.
296
+ - **Codex**: Rules are installed in the `.aicm` directory and referenced from `AGENTS.md` using the same markers as Windsurf.
295
297
 
296
298
  ## Commands
297
299
 
package/dist/index.js CHANGED
File without changes
@@ -40,6 +40,7 @@ export interface RuleContent {
40
40
  export interface RuleCollection {
41
41
  cursor: RuleContent[];
42
42
  windsurf: RuleContent[];
43
+ codex: RuleContent[];
43
44
  }
44
45
  export interface PackageInfo {
45
46
  relativePath: string;
@@ -59,6 +59,7 @@ function initRuleCollection() {
59
59
  return {
60
60
  cursor: [],
61
61
  windsurf: [],
62
+ codex: [],
62
63
  };
63
64
  }
64
65
  /**
@@ -77,6 +78,10 @@ function addRuleToCollection(collection, rule, ides) {
77
78
  !collection.windsurf.some((r) => r.name === rule.name)) {
78
79
  collection.windsurf.push(rule);
79
80
  }
81
+ else if (ide === "codex" &&
82
+ !collection.codex.some((r) => r.name === rule.name)) {
83
+ collection.codex.push(rule);
84
+ }
80
85
  }
81
86
  }
82
87
  /**
@@ -15,6 +15,7 @@ function getIdePaths() {
15
15
  return {
16
16
  cursor: node_path_1.default.join(projectDir, ".cursor", "rules", "aicm"),
17
17
  windsurf: node_path_1.default.join(projectDir, ".aicm"),
18
+ codex: node_path_1.default.join(projectDir, ".aicm"),
18
19
  };
19
20
  }
20
21
  /**
@@ -38,6 +39,15 @@ function checkRuleStatus(ruleName, ides) {
38
39
  }
39
40
  return false;
40
41
  }
42
+ if (ide === "codex") {
43
+ const ruleExists = fs_extra_1.default.existsSync(node_path_1.default.join(idePaths[ide], `${ruleName}.md`));
44
+ const codexFilePath = node_path_1.default.join(process.cwd(), "AGENTS.md");
45
+ if (fs_extra_1.default.existsSync(codexFilePath)) {
46
+ const codexContent = fs_extra_1.default.readFileSync(codexFilePath, "utf8");
47
+ return ruleExists && codexContent.includes(`.aicm/${ruleName}.md`);
48
+ }
49
+ return false;
50
+ }
41
51
  return false;
42
52
  });
43
53
  }
@@ -7,7 +7,7 @@ exports.writeRulesToTargets = writeRulesToTargets;
7
7
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
9
  const rule_status_1 = require("./rule-status");
10
- const windsurf_writer_1 = require("./windsurf-writer");
10
+ const rules_file_writer_1 = require("./rules-file-writer");
11
11
  /**
12
12
  * Write all collected rules to their respective IDE targets
13
13
  * @param collection The collection of rules to write
@@ -20,7 +20,10 @@ function writeRulesToTargets(collection) {
20
20
  }
21
21
  // Write Windsurf rules
22
22
  if (collection.windsurf.length > 0) {
23
- writeWindsurfRulesFromCollection(collection.windsurf, idePaths.windsurf);
23
+ writeRulesForFile(collection.windsurf, idePaths.windsurf, ".windsurfrules");
24
+ }
25
+ if (collection.codex.length > 0) {
26
+ writeRulesForFile(collection.codex, idePaths.codex, "AGENTS.md");
24
27
  }
25
28
  }
26
29
  /**
@@ -71,10 +74,10 @@ function writeCursorRules(rules, cursorRulesDir) {
71
74
  }
72
75
  }
73
76
  /**
74
- * Write rules to Windsurf's rules directory and update .windsurfrules file
77
+ * Write rules to a shared directory and update the given rules file
75
78
  * @param rules The rules to write
76
79
  */
77
- function writeWindsurfRulesFromCollection(rules, ruleDir) {
80
+ function writeRulesForFile(rules, ruleDir, rulesFile) {
78
81
  fs_extra_1.default.emptyDirSync(ruleDir);
79
82
  const ruleFiles = rules.map((rule) => {
80
83
  let rulePath;
@@ -94,7 +97,7 @@ function writeWindsurfRulesFromCollection(rules, ruleDir) {
94
97
  fs_extra_1.default.ensureDirSync(node_path_1.default.dirname(physicalRulePath));
95
98
  fs_extra_1.default.writeFileSync(physicalRulePath, rule.content);
96
99
  const relativeRuleDir = node_path_1.default.basename(ruleDir); // Gets '.rules'
97
- // For the Windsurf rules file, we need to maintain the same structure
100
+ // For the rules file, maintain the same structure
98
101
  let windsurfPath;
99
102
  if (rule.presetPath) {
100
103
  const namespace = extractNamespaceFromPresetPath(rule.presetPath);
@@ -112,6 +115,6 @@ function writeWindsurfRulesFromCollection(rules, ruleDir) {
112
115
  metadata: rule.metadata,
113
116
  };
114
117
  });
115
- const windsurfRulesContent = (0, windsurf_writer_1.generateWindsurfRulesContent)(ruleFiles);
116
- (0, windsurf_writer_1.writeWindsurfRules)(windsurfRulesContent);
118
+ const windsurfRulesContent = (0, rules_file_writer_1.generateRulesFileContent)(ruleFiles);
119
+ (0, rules_file_writer_1.writeRulesFile)(windsurfRulesContent, node_path_1.default.join(process.cwd(), rulesFile));
117
120
  }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Write rules to the .windsurfrules file
3
+ * This will update the content between the RULES_BEGIN and RULES_END markers
4
+ * If the file doesn't exist, it will create it
5
+ * If the markers don't exist, it will append them to the existing content
6
+ */
7
+ export declare function writeRulesFile(rulesContent: string, rulesFilePath?: string): void;
8
+ /**
9
+ * Generate the rules file content based on rule files
10
+ */
11
+ export declare function generateRulesFileContent(ruleFiles: {
12
+ name: string;
13
+ path: string;
14
+ metadata: Record<string, string | boolean | string[]>;
15
+ }[]): string;
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.writeRulesFile = writeRulesFile;
7
+ exports.generateRulesFileContent = generateRulesFileContent;
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const RULES_BEGIN = "<!-- AICM:BEGIN -->";
11
+ const RULES_END = "<!-- AICM:END -->";
12
+ const WARNING = "<!-- WARNING: Everything between these markers will be overwritten during installation -->";
13
+ /**
14
+ * Create a formatted block of content with rules markers
15
+ */
16
+ function createRulesBlock(rulesContent) {
17
+ return `${RULES_BEGIN}
18
+ ${WARNING}
19
+
20
+ ${rulesContent}
21
+
22
+ ${RULES_END}`;
23
+ }
24
+ /**
25
+ * Write rules to the .windsurfrules file
26
+ * This will update the content between the RULES_BEGIN and RULES_END markers
27
+ * If the file doesn't exist, it will create it
28
+ * If the markers don't exist, it will append them to the existing content
29
+ */
30
+ function writeRulesFile(rulesContent, rulesFilePath = path_1.default.join(process.cwd(), ".windsurfrules")) {
31
+ let fileContent;
32
+ const formattedRulesBlock = createRulesBlock(rulesContent);
33
+ // Check if file exists
34
+ if (fs_extra_1.default.existsSync(rulesFilePath)) {
35
+ const existingContent = fs_extra_1.default.readFileSync(rulesFilePath, "utf8");
36
+ // Check if our markers exist
37
+ if (existingContent.includes(RULES_BEGIN) &&
38
+ existingContent.includes(RULES_END)) {
39
+ // Replace content between markers
40
+ const beforeMarker = existingContent.split(RULES_BEGIN)[0];
41
+ const afterMarker = existingContent.split(RULES_END)[1];
42
+ fileContent = beforeMarker + formattedRulesBlock + afterMarker;
43
+ }
44
+ else {
45
+ // Preserve the existing content and append markers
46
+ // Ensure there's proper spacing between existing content and markers
47
+ let separator = "";
48
+ if (!existingContent.endsWith("\n")) {
49
+ separator += "\n";
50
+ }
51
+ // Add an extra line if the file doesn't already end with multiple newlines
52
+ if (!existingContent.endsWith("\n\n")) {
53
+ separator += "\n";
54
+ }
55
+ // Create the new file content with preserved original content
56
+ fileContent = existingContent + separator + formattedRulesBlock;
57
+ }
58
+ }
59
+ else {
60
+ // Create new file with markers and content
61
+ fileContent = formattedRulesBlock;
62
+ }
63
+ fs_extra_1.default.writeFileSync(rulesFilePath, fileContent);
64
+ }
65
+ /**
66
+ * Generate the rules file content based on rule files
67
+ */
68
+ function generateRulesFileContent(ruleFiles) {
69
+ const alwaysRules = [];
70
+ const autoAttachedRules = [];
71
+ const agentRequestedRules = [];
72
+ const manualRules = [];
73
+ ruleFiles.forEach(({ path, metadata }) => {
74
+ // Determine rule type based on metadata
75
+ if (metadata.type === "always" ||
76
+ metadata.alwaysApply === true ||
77
+ metadata.alwaysApply === "true") {
78
+ alwaysRules.push(path);
79
+ }
80
+ else if (metadata.type === "auto-attached" || metadata.globs) {
81
+ const globPattern = typeof metadata.globs === "string" || Array.isArray(metadata.globs)
82
+ ? Array.isArray(metadata.globs)
83
+ ? metadata.globs.join(", ")
84
+ : metadata.globs
85
+ : undefined;
86
+ if (globPattern !== undefined) {
87
+ autoAttachedRules.push({ path, glob: globPattern });
88
+ }
89
+ }
90
+ else if (metadata.type === "agent-requested" || metadata.description) {
91
+ agentRequestedRules.push(path);
92
+ }
93
+ else {
94
+ // Default to manual inclusion
95
+ manualRules.push(path);
96
+ }
97
+ });
98
+ // Generate the content
99
+ let content = "";
100
+ // Always rules
101
+ if (alwaysRules.length > 0) {
102
+ content +=
103
+ "The following rules always apply to all files in the project:\n";
104
+ alwaysRules.forEach((rule) => {
105
+ content += `- ${rule}\n`;
106
+ });
107
+ content += "\n";
108
+ }
109
+ // Auto Attached rules
110
+ if (autoAttachedRules.length > 0) {
111
+ content +=
112
+ "The following rules are automatically attached to matching glob patterns:\n";
113
+ autoAttachedRules.forEach((rule) => {
114
+ content += `- [${rule.glob}] ${rule.path}\n`;
115
+ });
116
+ content += "\n";
117
+ }
118
+ // Agent Requested rules
119
+ if (agentRequestedRules.length > 0) {
120
+ content +=
121
+ "The following rules are available for the AI to include when needed:\n";
122
+ agentRequestedRules.forEach((rule) => {
123
+ content += `- ${rule}\n`;
124
+ });
125
+ content += "\n";
126
+ }
127
+ // Manual rules
128
+ if (manualRules.length > 0) {
129
+ content +=
130
+ "The following rules are only included when explicitly referenced:\n";
131
+ manualRules.forEach((rule) => {
132
+ content += `- ${rule}\n`;
133
+ });
134
+ content += "\n";
135
+ }
136
+ return content.trim();
137
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aicm",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "A TypeScript CLI tool for managing AI IDE rules across different projects and teams",
5
5
  "main": "dist/api.js",
6
6
  "types": "dist/api.d.ts",