@rely-ai/caliber 1.1.4 → 1.2.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.
Files changed (2) hide show
  1. package/dist/bin.js +414 -264
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -32,17 +32,17 @@ __export(constants_exports, {
32
32
  LEARNING_STATE_FILE: () => LEARNING_STATE_FILE,
33
33
  MANIFEST_FILE: () => MANIFEST_FILE
34
34
  });
35
- import path8 from "path";
35
+ import path9 from "path";
36
36
  import os2 from "os";
37
37
  var AUTH_DIR, CALIBER_DIR, MANIFEST_FILE, BACKUPS_DIR, LEARNING_DIR, LEARNING_SESSION_FILE, LEARNING_STATE_FILE, LEARNING_MAX_EVENTS;
38
38
  var init_constants = __esm({
39
39
  "src/constants.ts"() {
40
40
  "use strict";
41
- AUTH_DIR = path8.join(os2.homedir(), ".caliber");
41
+ AUTH_DIR = path9.join(os2.homedir(), ".caliber");
42
42
  CALIBER_DIR = ".caliber";
43
- MANIFEST_FILE = path8.join(CALIBER_DIR, "manifest.json");
44
- BACKUPS_DIR = path8.join(CALIBER_DIR, "backups");
45
- LEARNING_DIR = path8.join(CALIBER_DIR, "learning");
43
+ MANIFEST_FILE = path9.join(CALIBER_DIR, "manifest.json");
44
+ BACKUPS_DIR = path9.join(CALIBER_DIR, "backups");
45
+ LEARNING_DIR = path9.join(CALIBER_DIR, "learning");
46
46
  LEARNING_SESSION_FILE = "current-session.jsonl";
47
47
  LEARNING_STATE_FILE = "state.json";
48
48
  LEARNING_MAX_EVENTS = 500;
@@ -51,8 +51,8 @@ var init_constants = __esm({
51
51
 
52
52
  // src/cli.ts
53
53
  import { Command } from "commander";
54
- import fs25 from "fs";
55
- import path21 from "path";
54
+ import fs26 from "fs";
55
+ import path22 from "path";
56
56
  import { fileURLToPath } from "url";
57
57
 
58
58
  // src/commands/onboard.ts
@@ -60,7 +60,7 @@ import chalk5 from "chalk";
60
60
  import ora2 from "ora";
61
61
  import readline4 from "readline";
62
62
  import select2 from "@inquirer/select";
63
- import fs18 from "fs";
63
+ import fs19 from "fs";
64
64
 
65
65
  // src/fingerprint/index.ts
66
66
  import fs6 from "fs";
@@ -145,6 +145,10 @@ function readExistingConfigs(dir) {
145
145
  if (fs2.existsSync(readmeMdPath)) {
146
146
  configs.readmeMd = fs2.readFileSync(readmeMdPath, "utf-8");
147
147
  }
148
+ const agentsMdPath = path2.join(dir, "AGENTS.md");
149
+ if (fs2.existsSync(agentsMdPath)) {
150
+ configs.agentsMd = fs2.readFileSync(agentsMdPath, "utf-8");
151
+ }
148
152
  const claudeMdPath = path2.join(dir, "CLAUDE.md");
149
153
  if (fs2.existsSync(claudeMdPath)) {
150
154
  configs.claudeMd = fs2.readFileSync(claudeMdPath, "utf-8");
@@ -1172,7 +1176,7 @@ async function llmJsonCall(options) {
1172
1176
  }
1173
1177
 
1174
1178
  // src/ai/prompts.ts
1175
- var GENERATION_SYSTEM_PROMPT = `You are an expert auditor for coding agent configurations (Claude Code and Cursor).
1179
+ var GENERATION_SYSTEM_PROMPT = `You are an expert auditor for coding agent configurations (Claude Code, Cursor, and Codex).
1176
1180
 
1177
1181
  Your job depends on context:
1178
1182
  - If no existing configs exist \u2192 generate an initial setup from scratch.
@@ -1180,7 +1184,9 @@ Your job depends on context:
1180
1184
 
1181
1185
  You understand these config files:
1182
1186
  - CLAUDE.md: Project context for Claude Code \u2014 build/test commands, architecture, conventions.
1187
+ - AGENTS.md: Primary instructions file for OpenAI Codex \u2014 same purpose as CLAUDE.md but for the Codex agent. Also serves as a cross-agent coordination file.
1183
1188
  - .claude/skills/{name}/SKILL.md: Skill files following the OpenSkills standard (agentskills.io). Each skill is a directory named after the skill, containing a SKILL.md with YAML frontmatter.
1189
+ - .agents/skills/{name}/SKILL.md: Same OpenSkills format for Codex skills (Codex scans .agents/skills/ for skills).
1184
1190
  - .cursorrules: Coding rules for Cursor (deprecated legacy format \u2014 do NOT generate this).
1185
1191
  - .cursor/rules/*.mdc: Modern Cursor rules with frontmatter (description, globs, alwaysApply).
1186
1192
  - .cursor/skills/{name}/SKILL.md: Same OpenSkills format as Claude skills.
@@ -1212,7 +1218,7 @@ Omit empty categories. Keep each reason punchy and specific. End with a blank li
1212
1218
 
1213
1219
  AgentSetup schema:
1214
1220
  {
1215
- "targetAgent": "claude" | "cursor" | "both",
1221
+ "targetAgent": "claude" | "cursor" | "codex" | "both",
1216
1222
  "fileDescriptions": {
1217
1223
  "<file-path>": "reason for this change (max 80 chars)"
1218
1224
  },
@@ -1223,6 +1229,10 @@ AgentSetup schema:
1223
1229
  "claudeMd": "string (markdown content for CLAUDE.md)",
1224
1230
  "skills": [{ "name": "string (kebab-case, matches directory name)", "description": "string (what this skill does and when to use it)", "content": "string (markdown body \u2014 NO frontmatter, it will be generated from name+description)" }]
1225
1231
  },
1232
+ "codex": {
1233
+ "agentsMd": "string (markdown content for AGENTS.md \u2014 the primary Codex instructions file, same quality/structure as CLAUDE.md)",
1234
+ "skills": [{ "name": "string (kebab-case, matches directory name)", "description": "string (what this skill does and when to use it)", "content": "string (markdown body \u2014 NO frontmatter, it will be generated from name+description)" }]
1235
+ },
1226
1236
  "cursor": {
1227
1237
  "skills": [{ "name": "string (kebab-case, matches directory name)", "description": "string (what this skill does and when to use it)", "content": "string (markdown body \u2014 NO frontmatter, it will be generated from name+description)" }],
1228
1238
  "rules": [{ "filename": "string.mdc", "content": "string (with frontmatter)" }]
@@ -1237,14 +1247,15 @@ All skills follow the OpenSkills standard (agentskills.io):
1237
1247
  - The "content" field is the markdown body only \u2014 do NOT include YAML frontmatter in the content, it will be generated from the name and description fields.
1238
1248
  - Keep skill content under 500 lines. Move detailed references to separate files if needed.
1239
1249
 
1240
- The "fileDescriptions" object MUST include a one-liner for every file that will be created or modified. Use actual file paths as keys (e.g. "CLAUDE.md", ".claude/skills/my-skill/SKILL.md", ".cursor/skills/my-skill/SKILL.md", ".cursor/rules/my-rule.mdc"). Each description should explain why the change is needed, be concise and lowercase.
1250
+ The "fileDescriptions" object MUST include a one-liner for every file that will be created or modified. Use actual file paths as keys (e.g. "CLAUDE.md", "AGENTS.md", ".claude/skills/my-skill/SKILL.md", ".agents/skills/my-skill/SKILL.md", ".cursor/skills/my-skill/SKILL.md", ".cursor/rules/my-rule.mdc"). Each description should explain why the change is needed, be concise and lowercase.
1241
1251
 
1242
1252
  The "deletions" array should list files that should be removed (e.g. duplicate skills, stale configs). Include a reason for each. Omit the array or leave empty if nothing should be deleted.
1243
1253
 
1244
1254
  SCORING CRITERIA \u2014 your output is scored deterministically. Optimize for 100/100:
1245
1255
 
1246
1256
  Existence (25 pts):
1247
- - CLAUDE.md exists (6 pts) \u2014 always generate
1257
+ - CLAUDE.md exists (6 pts) \u2014 always generate for claude/both targets
1258
+ - AGENTS.md exists (6 pts) \u2014 always generate for codex target (serves as primary instructions file)
1248
1259
  - Skills configured (8 pts) \u2014 generate exactly 3 focused skills for full points (6 base + 1 per extra, cap 2). Two skills = 7 pts, three = 8 pts.
1249
1260
  - MCP servers mentioned (3 pts) \u2014 reference detected MCP integrations
1250
1261
  - For "both" target: .cursorrules/.cursor/rules/ exist (3+3 pts), cross-platform parity (2 pts)
@@ -1278,12 +1289,12 @@ Bonus (5 pts):
1278
1289
  - Hooks configured (2 pts), AGENTS.md (1 pt), OpenSkills format (2 pts) \u2014 handled by caliber
1279
1290
 
1280
1291
  OUTPUT SIZE CONSTRAINTS \u2014 these are critical:
1281
- - CLAUDE.md: MUST be under 100 lines for maximum score. Aim for 70-90 lines. Be extremely concise \u2014 only commands, architecture overview, and key conventions. Use bullet points and tables, not prose.
1292
+ - CLAUDE.md / AGENTS.md: MUST be under 100 lines for maximum score. Aim for 70-90 lines. Be extremely concise \u2014 only commands, architecture overview, and key conventions. Use bullet points and tables, not prose.
1282
1293
  - Skills: generate exactly 3 skills per target platform. Only go above 3 for large multi-framework projects.
1283
1294
  - Each skill content: max 150 lines. Focus on patterns and examples, not exhaustive docs.
1284
1295
  - Cursor rules: max 5 .mdc files.
1285
1296
  - If the project is large, prioritize depth on the 3-4 most critical tools over breadth across everything.`;
1286
- var REFINE_SYSTEM_PROMPT = `You are an expert at modifying coding agent configurations (Claude Code and Cursor).
1297
+ var REFINE_SYSTEM_PROMPT = `You are an expert at modifying coding agent configurations (Claude Code, Cursor, and Codex).
1287
1298
 
1288
1299
  You will receive the current AgentSetup JSON and a user request describing what to change.
1289
1300
 
@@ -1291,7 +1302,7 @@ Apply the requested changes to the setup and return the complete updated AgentSe
1291
1302
 
1292
1303
  AgentSetup schema:
1293
1304
  {
1294
- "targetAgent": "claude" | "cursor" | "both",
1305
+ "targetAgent": "claude" | "cursor" | "codex" | "both",
1295
1306
  "fileDescriptions": {
1296
1307
  "<file-path>": "reason for this change (max 80 chars)"
1297
1308
  },
@@ -1302,6 +1313,10 @@ AgentSetup schema:
1302
1313
  "claudeMd": "string (markdown content for CLAUDE.md)",
1303
1314
  "skills": [{ "name": "string (kebab-case)", "description": "string", "content": "string (markdown body, no frontmatter)" }]
1304
1315
  },
1316
+ "codex": {
1317
+ "agentsMd": "string (markdown content for AGENTS.md)",
1318
+ "skills": [{ "name": "string (kebab-case)", "description": "string", "content": "string (markdown body, no frontmatter)" }]
1319
+ },
1305
1320
  "cursor": {
1306
1321
  "skills": [{ "name": "string (kebab-case)", "description": "string", "content": "string (markdown body, no frontmatter)" }],
1307
1322
  "rules": [{ "filename": "string.mdc", "content": "string (with frontmatter)" }]
@@ -1318,7 +1333,7 @@ var REFRESH_SYSTEM_PROMPT = `You are an expert at maintaining coding project doc
1318
1333
 
1319
1334
  You will receive:
1320
1335
  1. Git diffs showing what code changed
1321
- 2. Current contents of documentation files (CLAUDE.md, README.md, skills, cursor skills, cursor rules)
1336
+ 2. Current contents of documentation files (CLAUDE.md, AGENTS.md, README.md, skills, cursor skills, cursor rules)
1322
1337
  3. Project context (languages, frameworks)
1323
1338
 
1324
1339
  Rules:
@@ -1329,6 +1344,7 @@ Rules:
1329
1344
  - Keep managed blocks (<!-- caliber:managed --> ... <!-- /caliber:managed -->) intact
1330
1345
  - If a doc doesn't need updating, return null for it
1331
1346
  - For CLAUDE.md: update commands, architecture notes, conventions, key files if the diffs affect them
1347
+ - For AGENTS.md: same as CLAUDE.md \u2014 this is the primary instructions file for Codex users
1332
1348
  - For README.md: update setup instructions, API docs, or feature descriptions if affected
1333
1349
  - For cursor skills: update skill content if the diffs affect their domains
1334
1350
 
@@ -1336,13 +1352,14 @@ Return a JSON object with this exact shape:
1336
1352
  {
1337
1353
  "updatedDocs": {
1338
1354
  "claudeMd": "<updated content or null>",
1355
+ "agentsMd": "<updated content or null>",
1339
1356
  "readmeMd": "<updated content or null>",
1340
1357
  "cursorRules": [{"filename": "name.mdc", "content": "..."}] or null,
1341
1358
  "cursorSkills": [{"slug": "string", "name": "string", "content": "..."}] or null,
1342
1359
  "claudeSkills": [{"filename": "name.md", "content": "..."}] or null
1343
1360
  },
1344
1361
  "changesSummary": "<1-2 sentence summary of what was updated and why>",
1345
- "docsUpdated": ["CLAUDE.md", "README.md"]
1362
+ "docsUpdated": ["CLAUDE.md", "AGENTS.md", "README.md"]
1346
1363
  }
1347
1364
 
1348
1365
  Respond with ONLY the JSON object, no markdown fences or extra text.`;
@@ -1361,7 +1378,7 @@ Your job is to reason deeply about these events and identify:
1361
1378
  From these observations, produce:
1362
1379
 
1363
1380
  ### claudeMdLearnedSection
1364
- A markdown section with concise, actionable bullet points that should be added to the project's CLAUDE.md file. Each bullet should be a concrete instruction that prevents a past mistake or encodes a discovered convention. Examples:
1381
+ A markdown section with concise, actionable bullet points that should be added to the project's primary instructions file (CLAUDE.md for Claude Code, AGENTS.md for Codex). Each bullet should be a concrete instruction that prevents a past mistake or encodes a discovered convention. Examples:
1365
1382
  - "Always run \`npm install\` before \`npm run build\` in this project"
1366
1383
  - "The test database requires \`DATABASE_URL\` to be set \u2014 use \`source .env.test\` first"
1367
1384
  - "TypeScript strict mode is enabled \u2014 never use \`any\`, use \`unknown\` with type guards"
@@ -1644,7 +1661,7 @@ function truncate(text, maxChars) {
1644
1661
  function buildGeneratePrompt(fingerprint, targetAgent, prompt, failingChecks, currentScore, passingChecks) {
1645
1662
  const parts = [];
1646
1663
  const existing = fingerprint.existingConfigs;
1647
- const hasExistingConfigs = !!(existing.claudeMd || existing.claudeSettings || existing.claudeSkills?.length || existing.readmeMd || existing.cursorrules || existing.cursorRules?.length);
1664
+ const hasExistingConfigs = !!(existing.claudeMd || existing.claudeSettings || existing.claudeSkills?.length || existing.readmeMd || existing.agentsMd || existing.cursorrules || existing.cursorRules?.length);
1648
1665
  const isTargetedFix = failingChecks && failingChecks.length > 0 && currentScore !== void 0 && currentScore >= 95;
1649
1666
  if (isTargetedFix) {
1650
1667
  parts.push(`TARGETED FIX MODE \u2014 current score: ${currentScore}/100, target: ${targetAgent}`);
@@ -1682,6 +1699,9 @@ ${tree.join("\n")}`);
1682
1699
  if (existing.claudeMd) parts.push(`
1683
1700
  Existing CLAUDE.md:
1684
1701
  ${truncate(existing.claudeMd, LIMITS.EXISTING_CONFIG_CHARS)}`);
1702
+ if (existing.agentsMd) parts.push(`
1703
+ Existing AGENTS.md:
1704
+ ${truncate(existing.agentsMd, LIMITS.EXISTING_CONFIG_CHARS)}`);
1685
1705
  if (existing.readmeMd) parts.push(`
1686
1706
  Existing README.md:
1687
1707
  ${truncate(existing.readmeMd, LIMITS.EXISTING_CONFIG_CHARS)}`);
@@ -1817,7 +1837,7 @@ Return the complete updated AgentSetup JSON incorporating the user's changes. Re
1817
1837
  }
1818
1838
 
1819
1839
  // src/writers/index.ts
1820
- import fs11 from "fs";
1840
+ import fs12 from "fs";
1821
1841
 
1822
1842
  // src/writers/claude/index.ts
1823
1843
  import fs7 from "fs";
@@ -1911,64 +1931,90 @@ function writeCursorConfig(config) {
1911
1931
  return written;
1912
1932
  }
1913
1933
 
1934
+ // src/writers/codex/index.ts
1935
+ import fs9 from "fs";
1936
+ import path8 from "path";
1937
+ function writeCodexConfig(config) {
1938
+ const written = [];
1939
+ fs9.writeFileSync("AGENTS.md", config.agentsMd);
1940
+ written.push("AGENTS.md");
1941
+ if (config.skills?.length) {
1942
+ for (const skill of config.skills) {
1943
+ const skillDir = path8.join(".agents", "skills", skill.name);
1944
+ if (!fs9.existsSync(skillDir)) fs9.mkdirSync(skillDir, { recursive: true });
1945
+ const skillPath = path8.join(skillDir, "SKILL.md");
1946
+ const frontmatter = [
1947
+ "---",
1948
+ `name: ${skill.name}`,
1949
+ `description: ${skill.description}`,
1950
+ "---",
1951
+ ""
1952
+ ].join("\n");
1953
+ fs9.writeFileSync(skillPath, frontmatter + skill.content);
1954
+ written.push(skillPath);
1955
+ }
1956
+ }
1957
+ return written;
1958
+ }
1959
+
1914
1960
  // src/writers/backup.ts
1915
1961
  init_constants();
1916
- import fs9 from "fs";
1917
- import path9 from "path";
1962
+ import fs10 from "fs";
1963
+ import path10 from "path";
1918
1964
  function createBackup(files) {
1919
1965
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1920
- const backupDir = path9.join(BACKUPS_DIR, timestamp);
1966
+ const backupDir = path10.join(BACKUPS_DIR, timestamp);
1921
1967
  for (const file of files) {
1922
- if (!fs9.existsSync(file)) continue;
1923
- const dest = path9.join(backupDir, file);
1924
- const destDir = path9.dirname(dest);
1925
- if (!fs9.existsSync(destDir)) {
1926
- fs9.mkdirSync(destDir, { recursive: true });
1968
+ if (!fs10.existsSync(file)) continue;
1969
+ const dest = path10.join(backupDir, file);
1970
+ const destDir = path10.dirname(dest);
1971
+ if (!fs10.existsSync(destDir)) {
1972
+ fs10.mkdirSync(destDir, { recursive: true });
1927
1973
  }
1928
- fs9.copyFileSync(file, dest);
1974
+ fs10.copyFileSync(file, dest);
1929
1975
  }
1930
1976
  return backupDir;
1931
1977
  }
1932
1978
  function restoreBackup(backupDir, file) {
1933
- const backupFile = path9.join(backupDir, file);
1934
- if (!fs9.existsSync(backupFile)) return false;
1935
- const destDir = path9.dirname(file);
1936
- if (!fs9.existsSync(destDir)) {
1937
- fs9.mkdirSync(destDir, { recursive: true });
1979
+ const backupFile = path10.join(backupDir, file);
1980
+ if (!fs10.existsSync(backupFile)) return false;
1981
+ const destDir = path10.dirname(file);
1982
+ if (!fs10.existsSync(destDir)) {
1983
+ fs10.mkdirSync(destDir, { recursive: true });
1938
1984
  }
1939
- fs9.copyFileSync(backupFile, file);
1985
+ fs10.copyFileSync(backupFile, file);
1940
1986
  return true;
1941
1987
  }
1942
1988
 
1943
1989
  // src/writers/manifest.ts
1944
1990
  init_constants();
1945
- import fs10 from "fs";
1991
+ import fs11 from "fs";
1946
1992
  import crypto from "crypto";
1947
1993
  function readManifest() {
1948
1994
  try {
1949
- if (!fs10.existsSync(MANIFEST_FILE)) return null;
1950
- return JSON.parse(fs10.readFileSync(MANIFEST_FILE, "utf-8"));
1995
+ if (!fs11.existsSync(MANIFEST_FILE)) return null;
1996
+ return JSON.parse(fs11.readFileSync(MANIFEST_FILE, "utf-8"));
1951
1997
  } catch {
1952
1998
  return null;
1953
1999
  }
1954
2000
  }
1955
2001
  function writeManifest(manifest) {
1956
- if (!fs10.existsSync(CALIBER_DIR)) {
1957
- fs10.mkdirSync(CALIBER_DIR, { recursive: true });
2002
+ if (!fs11.existsSync(CALIBER_DIR)) {
2003
+ fs11.mkdirSync(CALIBER_DIR, { recursive: true });
1958
2004
  }
1959
- fs10.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
2005
+ fs11.writeFileSync(MANIFEST_FILE, JSON.stringify(manifest, null, 2));
1960
2006
  }
1961
2007
  function fileChecksum(filePath) {
1962
- const content = fs10.readFileSync(filePath);
2008
+ const content = fs11.readFileSync(filePath);
1963
2009
  return crypto.createHash("sha256").update(content).digest("hex");
1964
2010
  }
1965
2011
 
1966
2012
  // src/writers/index.ts
1967
2013
  function writeSetup(setup) {
1968
2014
  const filesToWrite = getFilesToWrite(setup);
1969
- const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs11.existsSync(f));
2015
+ const filesToDelete = (setup.deletions || []).map((d) => d.filePath).filter((f) => fs12.existsSync(f));
1970
2016
  const existingFiles = [
1971
- ...filesToWrite.filter((f) => fs11.existsSync(f)),
2017
+ ...filesToWrite.filter((f) => fs12.existsSync(f)),
1972
2018
  ...filesToDelete
1973
2019
  ];
1974
2020
  const backupDir = existingFiles.length > 0 ? createBackup(existingFiles) : void 0;
@@ -1979,9 +2025,12 @@ function writeSetup(setup) {
1979
2025
  if ((setup.targetAgent === "cursor" || setup.targetAgent === "both") && setup.cursor) {
1980
2026
  written.push(...writeCursorConfig(setup.cursor));
1981
2027
  }
2028
+ if (setup.targetAgent === "codex" && setup.codex) {
2029
+ written.push(...writeCodexConfig(setup.codex));
2030
+ }
1982
2031
  const deleted = [];
1983
2032
  for (const filePath of filesToDelete) {
1984
- fs11.unlinkSync(filePath);
2033
+ fs12.unlinkSync(filePath);
1985
2034
  deleted.push(filePath);
1986
2035
  }
1987
2036
  ensureGitignore();
@@ -2011,8 +2060,8 @@ function undoSetup() {
2011
2060
  const removed = [];
2012
2061
  for (const entry of manifest.entries) {
2013
2062
  if (entry.action === "created") {
2014
- if (fs11.existsSync(entry.path)) {
2015
- fs11.unlinkSync(entry.path);
2063
+ if (fs12.existsSync(entry.path)) {
2064
+ fs12.unlinkSync(entry.path);
2016
2065
  removed.push(entry.path);
2017
2066
  }
2018
2067
  } else if ((entry.action === "modified" || entry.action === "deleted") && manifest.backupDir) {
@@ -2022,8 +2071,8 @@ function undoSetup() {
2022
2071
  }
2023
2072
  }
2024
2073
  const { MANIFEST_FILE: MANIFEST_FILE2 } = (init_constants(), __toCommonJS(constants_exports));
2025
- if (fs11.existsSync(MANIFEST_FILE2)) {
2026
- fs11.unlinkSync(MANIFEST_FILE2);
2074
+ if (fs12.existsSync(MANIFEST_FILE2)) {
2075
+ fs12.unlinkSync(MANIFEST_FILE2);
2027
2076
  }
2028
2077
  return { restored, removed };
2029
2078
  }
@@ -2048,27 +2097,33 @@ function getFilesToWrite(setup) {
2048
2097
  }
2049
2098
  if (setup.cursor.mcpServers) files.push(".cursor/mcp.json");
2050
2099
  }
2100
+ if (setup.targetAgent === "codex" && setup.codex) {
2101
+ files.push("AGENTS.md");
2102
+ if (setup.codex.skills) {
2103
+ for (const s of setup.codex.skills) files.push(`.agents/skills/${s.name}/SKILL.md`);
2104
+ }
2105
+ }
2051
2106
  return files;
2052
2107
  }
2053
2108
  function ensureGitignore() {
2054
2109
  const gitignorePath = ".gitignore";
2055
- if (fs11.existsSync(gitignorePath)) {
2056
- const content = fs11.readFileSync(gitignorePath, "utf-8");
2110
+ if (fs12.existsSync(gitignorePath)) {
2111
+ const content = fs12.readFileSync(gitignorePath, "utf-8");
2057
2112
  if (!content.includes(".caliber/")) {
2058
- fs11.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
2113
+ fs12.appendFileSync(gitignorePath, "\n# Caliber local state\n.caliber/\n");
2059
2114
  }
2060
2115
  } else {
2061
- fs11.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
2116
+ fs12.writeFileSync(gitignorePath, "# Caliber local state\n.caliber/\n");
2062
2117
  }
2063
2118
  }
2064
2119
 
2065
2120
  // src/writers/staging.ts
2066
2121
  init_constants();
2067
- import fs12 from "fs";
2068
- import path10 from "path";
2069
- var STAGED_DIR = path10.join(CALIBER_DIR, "staged");
2070
- var PROPOSED_DIR = path10.join(STAGED_DIR, "proposed");
2071
- var CURRENT_DIR = path10.join(STAGED_DIR, "current");
2122
+ import fs13 from "fs";
2123
+ import path11 from "path";
2124
+ var STAGED_DIR = path11.join(CALIBER_DIR, "staged");
2125
+ var PROPOSED_DIR = path11.join(STAGED_DIR, "proposed");
2126
+ var CURRENT_DIR = path11.join(STAGED_DIR, "current");
2072
2127
  function normalizeContent(content) {
2073
2128
  return content.split("\n").map((line) => line.trimEnd()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
2074
2129
  }
@@ -2078,20 +2133,20 @@ function stageFiles(files, projectDir) {
2078
2133
  let modifiedFiles = 0;
2079
2134
  const stagedFiles = [];
2080
2135
  for (const file of files) {
2081
- const originalPath = path10.join(projectDir, file.path);
2082
- if (fs12.existsSync(originalPath)) {
2083
- const existing = fs12.readFileSync(originalPath, "utf-8");
2136
+ const originalPath = path11.join(projectDir, file.path);
2137
+ if (fs13.existsSync(originalPath)) {
2138
+ const existing = fs13.readFileSync(originalPath, "utf-8");
2084
2139
  if (normalizeContent(existing) === normalizeContent(file.content)) {
2085
2140
  continue;
2086
2141
  }
2087
2142
  }
2088
- const proposedPath = path10.join(PROPOSED_DIR, file.path);
2089
- fs12.mkdirSync(path10.dirname(proposedPath), { recursive: true });
2090
- fs12.writeFileSync(proposedPath, file.content);
2091
- if (fs12.existsSync(originalPath)) {
2092
- const currentPath = path10.join(CURRENT_DIR, file.path);
2093
- fs12.mkdirSync(path10.dirname(currentPath), { recursive: true });
2094
- fs12.copyFileSync(originalPath, currentPath);
2143
+ const proposedPath = path11.join(PROPOSED_DIR, file.path);
2144
+ fs13.mkdirSync(path11.dirname(proposedPath), { recursive: true });
2145
+ fs13.writeFileSync(proposedPath, file.content);
2146
+ if (fs13.existsSync(originalPath)) {
2147
+ const currentPath = path11.join(CURRENT_DIR, file.path);
2148
+ fs13.mkdirSync(path11.dirname(currentPath), { recursive: true });
2149
+ fs13.copyFileSync(originalPath, currentPath);
2095
2150
  modifiedFiles++;
2096
2151
  stagedFiles.push({ relativePath: file.path, proposedPath, currentPath, originalPath, isNew: false });
2097
2152
  } else {
@@ -2102,8 +2157,8 @@ function stageFiles(files, projectDir) {
2102
2157
  return { newFiles, modifiedFiles, stagedFiles };
2103
2158
  }
2104
2159
  function cleanupStaging() {
2105
- if (fs12.existsSync(STAGED_DIR)) {
2106
- fs12.rmSync(STAGED_DIR, { recursive: true, force: true });
2160
+ if (fs13.existsSync(STAGED_DIR)) {
2161
+ fs13.rmSync(STAGED_DIR, { recursive: true, force: true });
2107
2162
  }
2108
2163
  }
2109
2164
 
@@ -2149,24 +2204,24 @@ function openDiffsInEditor(editor, files) {
2149
2204
  import { createTwoFilesPatch } from "diff";
2150
2205
 
2151
2206
  // src/lib/hooks.ts
2152
- import fs13 from "fs";
2153
- import path11 from "path";
2207
+ import fs14 from "fs";
2208
+ import path12 from "path";
2154
2209
  import { execSync as execSync5 } from "child_process";
2155
- var SETTINGS_PATH = path11.join(".claude", "settings.json");
2210
+ var SETTINGS_PATH = path12.join(".claude", "settings.json");
2156
2211
  var HOOK_COMMAND = "caliber refresh --quiet";
2157
2212
  var HOOK_DESCRIPTION = "Caliber: auto-refreshing docs based on code changes";
2158
2213
  function readSettings() {
2159
- if (!fs13.existsSync(SETTINGS_PATH)) return {};
2214
+ if (!fs14.existsSync(SETTINGS_PATH)) return {};
2160
2215
  try {
2161
- return JSON.parse(fs13.readFileSync(SETTINGS_PATH, "utf-8"));
2216
+ return JSON.parse(fs14.readFileSync(SETTINGS_PATH, "utf-8"));
2162
2217
  } catch {
2163
2218
  return {};
2164
2219
  }
2165
2220
  }
2166
2221
  function writeSettings(settings) {
2167
- const dir = path11.dirname(SETTINGS_PATH);
2168
- if (!fs13.existsSync(dir)) fs13.mkdirSync(dir, { recursive: true });
2169
- fs13.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
2222
+ const dir = path12.dirname(SETTINGS_PATH);
2223
+ if (!fs14.existsSync(dir)) fs14.mkdirSync(dir, { recursive: true });
2224
+ fs14.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
2170
2225
  }
2171
2226
  function findHookIndex(sessionEnd) {
2172
2227
  return sessionEnd.findIndex(
@@ -2224,19 +2279,19 @@ ${PRECOMMIT_END}`;
2224
2279
  function getGitHooksDir() {
2225
2280
  try {
2226
2281
  const gitDir = execSync5("git rev-parse --git-dir", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
2227
- return path11.join(gitDir, "hooks");
2282
+ return path12.join(gitDir, "hooks");
2228
2283
  } catch {
2229
2284
  return null;
2230
2285
  }
2231
2286
  }
2232
2287
  function getPreCommitPath() {
2233
2288
  const hooksDir = getGitHooksDir();
2234
- return hooksDir ? path11.join(hooksDir, "pre-commit") : null;
2289
+ return hooksDir ? path12.join(hooksDir, "pre-commit") : null;
2235
2290
  }
2236
2291
  function isPreCommitHookInstalled() {
2237
2292
  const hookPath = getPreCommitPath();
2238
- if (!hookPath || !fs13.existsSync(hookPath)) return false;
2239
- const content = fs13.readFileSync(hookPath, "utf-8");
2293
+ if (!hookPath || !fs14.existsSync(hookPath)) return false;
2294
+ const content = fs14.readFileSync(hookPath, "utf-8");
2240
2295
  return content.includes(PRECOMMIT_START);
2241
2296
  }
2242
2297
  function installPreCommitHook() {
@@ -2245,43 +2300,43 @@ function installPreCommitHook() {
2245
2300
  }
2246
2301
  const hookPath = getPreCommitPath();
2247
2302
  if (!hookPath) return { installed: false, alreadyInstalled: false };
2248
- const hooksDir = path11.dirname(hookPath);
2249
- if (!fs13.existsSync(hooksDir)) fs13.mkdirSync(hooksDir, { recursive: true });
2303
+ const hooksDir = path12.dirname(hookPath);
2304
+ if (!fs14.existsSync(hooksDir)) fs14.mkdirSync(hooksDir, { recursive: true });
2250
2305
  let content = "";
2251
- if (fs13.existsSync(hookPath)) {
2252
- content = fs13.readFileSync(hookPath, "utf-8");
2306
+ if (fs14.existsSync(hookPath)) {
2307
+ content = fs14.readFileSync(hookPath, "utf-8");
2253
2308
  if (!content.endsWith("\n")) content += "\n";
2254
2309
  content += "\n" + PRECOMMIT_BLOCK + "\n";
2255
2310
  } else {
2256
2311
  content = "#!/bin/sh\n\n" + PRECOMMIT_BLOCK + "\n";
2257
2312
  }
2258
- fs13.writeFileSync(hookPath, content);
2259
- fs13.chmodSync(hookPath, 493);
2313
+ fs14.writeFileSync(hookPath, content);
2314
+ fs14.chmodSync(hookPath, 493);
2260
2315
  return { installed: true, alreadyInstalled: false };
2261
2316
  }
2262
2317
  function removePreCommitHook() {
2263
2318
  const hookPath = getPreCommitPath();
2264
- if (!hookPath || !fs13.existsSync(hookPath)) {
2319
+ if (!hookPath || !fs14.existsSync(hookPath)) {
2265
2320
  return { removed: false, notFound: true };
2266
2321
  }
2267
- let content = fs13.readFileSync(hookPath, "utf-8");
2322
+ let content = fs14.readFileSync(hookPath, "utf-8");
2268
2323
  if (!content.includes(PRECOMMIT_START)) {
2269
2324
  return { removed: false, notFound: true };
2270
2325
  }
2271
2326
  const regex = new RegExp(`\\n?${PRECOMMIT_START}[\\s\\S]*?${PRECOMMIT_END}\\n?`);
2272
2327
  content = content.replace(regex, "\n");
2273
2328
  if (content.trim() === "#!/bin/sh" || content.trim() === "") {
2274
- fs13.unlinkSync(hookPath);
2329
+ fs14.unlinkSync(hookPath);
2275
2330
  } else {
2276
- fs13.writeFileSync(hookPath, content);
2331
+ fs14.writeFileSync(hookPath, content);
2277
2332
  }
2278
2333
  return { removed: true, notFound: false };
2279
2334
  }
2280
2335
 
2281
2336
  // src/lib/learning-hooks.ts
2282
- import fs14 from "fs";
2283
- import path12 from "path";
2284
- var SETTINGS_PATH2 = path12.join(".claude", "settings.json");
2337
+ import fs15 from "fs";
2338
+ import path13 from "path";
2339
+ var SETTINGS_PATH2 = path13.join(".claude", "settings.json");
2285
2340
  var HOOK_CONFIGS = [
2286
2341
  {
2287
2342
  event: "PostToolUse",
@@ -2300,17 +2355,17 @@ var HOOK_CONFIGS = [
2300
2355
  }
2301
2356
  ];
2302
2357
  function readSettings2() {
2303
- if (!fs14.existsSync(SETTINGS_PATH2)) return {};
2358
+ if (!fs15.existsSync(SETTINGS_PATH2)) return {};
2304
2359
  try {
2305
- return JSON.parse(fs14.readFileSync(SETTINGS_PATH2, "utf-8"));
2360
+ return JSON.parse(fs15.readFileSync(SETTINGS_PATH2, "utf-8"));
2306
2361
  } catch {
2307
2362
  return {};
2308
2363
  }
2309
2364
  }
2310
2365
  function writeSettings2(settings) {
2311
- const dir = path12.dirname(SETTINGS_PATH2);
2312
- if (!fs14.existsSync(dir)) fs14.mkdirSync(dir, { recursive: true });
2313
- fs14.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
2366
+ const dir = path13.dirname(SETTINGS_PATH2);
2367
+ if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
2368
+ fs15.writeFileSync(SETTINGS_PATH2, JSON.stringify(settings, null, 2));
2314
2369
  }
2315
2370
  function hasLearningHook(matchers, command) {
2316
2371
  return matchers.some((entry) => entry.hooks?.some((h) => h.command === command));
@@ -2367,23 +2422,23 @@ function removeLearningHooks() {
2367
2422
 
2368
2423
  // src/lib/state.ts
2369
2424
  init_constants();
2370
- import fs15 from "fs";
2371
- import path13 from "path";
2425
+ import fs16 from "fs";
2426
+ import path14 from "path";
2372
2427
  import { execSync as execSync6 } from "child_process";
2373
- var STATE_FILE = path13.join(CALIBER_DIR, ".caliber-state.json");
2428
+ var STATE_FILE = path14.join(CALIBER_DIR, ".caliber-state.json");
2374
2429
  function readState() {
2375
2430
  try {
2376
- if (!fs15.existsSync(STATE_FILE)) return null;
2377
- return JSON.parse(fs15.readFileSync(STATE_FILE, "utf-8"));
2431
+ if (!fs16.existsSync(STATE_FILE)) return null;
2432
+ return JSON.parse(fs16.readFileSync(STATE_FILE, "utf-8"));
2378
2433
  } catch {
2379
2434
  return null;
2380
2435
  }
2381
2436
  }
2382
2437
  function writeState(state) {
2383
- if (!fs15.existsSync(CALIBER_DIR)) {
2384
- fs15.mkdirSync(CALIBER_DIR, { recursive: true });
2438
+ if (!fs16.existsSync(CALIBER_DIR)) {
2439
+ fs16.mkdirSync(CALIBER_DIR, { recursive: true });
2385
2440
  }
2386
- fs15.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
2441
+ fs16.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
2387
2442
  }
2388
2443
  function getCurrentHeadSha() {
2389
2444
  try {
@@ -2649,6 +2704,9 @@ var BOTH_ONLY_CHECKS = /* @__PURE__ */ new Set([
2649
2704
  "cross_platform_parity",
2650
2705
  "no_duplicate_content"
2651
2706
  ]);
2707
+ var CODEX_ONLY_CHECKS = /* @__PURE__ */ new Set([
2708
+ "codex_agents_md_exists"
2709
+ ]);
2652
2710
  var GRADE_THRESHOLDS = [
2653
2711
  { minScore: 85, grade: "A" },
2654
2712
  { minScore: 70, grade: "B" },
@@ -2666,15 +2724,15 @@ function computeGrade(score) {
2666
2724
  // src/scoring/checks/coverage.ts
2667
2725
  import { readFileSync, readdirSync } from "fs";
2668
2726
  import { join } from "path";
2669
- function readFileOrNull(path23) {
2727
+ function readFileOrNull(path24) {
2670
2728
  try {
2671
- return readFileSync(path23, "utf-8");
2729
+ return readFileSync(path24, "utf-8");
2672
2730
  } catch {
2673
2731
  return null;
2674
2732
  }
2675
2733
  }
2676
- function readJsonOrNull(path23) {
2677
- const content = readFileOrNull(path23);
2734
+ function readJsonOrNull(path24) {
2735
+ const content = readFileOrNull(path24);
2678
2736
  if (!content) return null;
2679
2737
  try {
2680
2738
  return JSON.parse(content);
@@ -2970,8 +3028,20 @@ function checkExistence(dir) {
2970
3028
  detail: hasCursorrules ? ".cursorrules found" : cursorRulesDir ? ".cursor/rules/ found" : "No Cursor rules",
2971
3029
  suggestion: cursorRulesExist ? void 0 : "Add .cursor/rules/ for Cursor users on your team"
2972
3030
  });
3031
+ const agentsMdExists = existsSync3(join2(dir, "AGENTS.md"));
3032
+ checks.push({
3033
+ id: "codex_agents_md_exists",
3034
+ name: "AGENTS.md exists",
3035
+ category: "existence",
3036
+ maxPoints: POINTS_CLAUDE_MD_EXISTS,
3037
+ earnedPoints: agentsMdExists ? POINTS_CLAUDE_MD_EXISTS : 0,
3038
+ passed: agentsMdExists,
3039
+ detail: agentsMdExists ? "Found at project root" : "Not found",
3040
+ suggestion: agentsMdExists ? void 0 : "Create AGENTS.md with project context for Codex"
3041
+ });
2973
3042
  const claudeSkills = countFiles(join2(dir, ".claude", "skills"), /\.(md|SKILL\.md)$/);
2974
- const skillCount = claudeSkills.length;
3043
+ const codexSkills = countFiles(join2(dir, ".agents", "skills"), /SKILL\.md$/);
3044
+ const skillCount = claudeSkills.length + codexSkills.length;
2975
3045
  const skillBase = skillCount >= 1 ? POINTS_SKILLS_EXIST : 0;
2976
3046
  const skillBonus = Math.min((skillCount - 1) * POINTS_SKILLS_BONUS_PER_EXTRA, POINTS_SKILLS_BONUS_CAP);
2977
3047
  const skillPoints = skillCount >= 1 ? skillBase + Math.max(0, skillBonus) : 0;
@@ -3030,9 +3100,9 @@ function checkExistence(dir) {
3030
3100
  // src/scoring/checks/quality.ts
3031
3101
  import { readFileSync as readFileSync3 } from "fs";
3032
3102
  import { join as join3 } from "path";
3033
- function readFileOrNull2(path23) {
3103
+ function readFileOrNull2(path24) {
3034
3104
  try {
3035
- return readFileSync3(path23, "utf-8");
3105
+ return readFileSync3(path24, "utf-8");
3036
3106
  } catch {
3037
3107
  return null;
3038
3108
  }
@@ -3044,11 +3114,13 @@ function checkQuality(dir) {
3044
3114
  const checks = [];
3045
3115
  const claudeMd = readFileOrNull2(join3(dir, "CLAUDE.md"));
3046
3116
  const cursorrules = readFileOrNull2(join3(dir, ".cursorrules"));
3047
- const allContent = [claudeMd, cursorrules].filter(Boolean);
3117
+ const agentsMd = readFileOrNull2(join3(dir, "AGENTS.md"));
3118
+ const allContent = [claudeMd, cursorrules, agentsMd].filter(Boolean);
3048
3119
  const combinedContent = allContent.join("\n");
3049
- const hasCommands = claudeMd ? COMMAND_PATTERNS.some((p) => p.test(claudeMd)) : false;
3050
- const matchedCommands = claudeMd ? COMMAND_PATTERNS.filter((p) => p.test(claudeMd)).map((p) => {
3051
- const m = claudeMd.match(p);
3120
+ const primaryInstructions = claudeMd ?? agentsMd;
3121
+ const hasCommands = primaryInstructions ? COMMAND_PATTERNS.some((p) => p.test(primaryInstructions)) : false;
3122
+ const matchedCommands = primaryInstructions ? COMMAND_PATTERNS.filter((p) => p.test(primaryInstructions)).map((p) => {
3123
+ const m = primaryInstructions.match(p);
3052
3124
  return m ? m[0] : "";
3053
3125
  }).filter(Boolean) : [];
3054
3126
  checks.push({
@@ -3058,11 +3130,11 @@ function checkQuality(dir) {
3058
3130
  maxPoints: POINTS_HAS_COMMANDS,
3059
3131
  earnedPoints: hasCommands ? POINTS_HAS_COMMANDS : 0,
3060
3132
  passed: hasCommands,
3061
- detail: hasCommands ? `Found: ${matchedCommands.slice(0, 3).join(", ")}` : claudeMd ? "No build/test/lint commands detected" : "No CLAUDE.md to check",
3062
- suggestion: hasCommands ? void 0 : "Add build, test, and lint commands to CLAUDE.md"
3133
+ detail: hasCommands ? `Found: ${matchedCommands.slice(0, 3).join(", ")}` : primaryInstructions ? "No build/test/lint commands detected" : "No instructions file to check",
3134
+ suggestion: hasCommands ? void 0 : "Add build, test, and lint commands to your instructions file"
3063
3135
  });
3064
- const primaryFile = claudeMd ?? cursorrules;
3065
- const primaryName = claudeMd ? "CLAUDE.md" : cursorrules ? ".cursorrules" : null;
3136
+ const primaryFile = claudeMd ?? agentsMd ?? cursorrules;
3137
+ const primaryName = claudeMd ? "CLAUDE.md" : agentsMd ? "AGENTS.md" : cursorrules ? ".cursorrules" : null;
3066
3138
  let bloatPoints = 0;
3067
3139
  let lineCount = 0;
3068
3140
  if (primaryFile) {
@@ -3183,15 +3255,15 @@ function checkQuality(dir) {
3183
3255
  // src/scoring/checks/accuracy.ts
3184
3256
  import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync3, statSync } from "fs";
3185
3257
  import { join as join4 } from "path";
3186
- function readFileOrNull3(path23) {
3258
+ function readFileOrNull3(path24) {
3187
3259
  try {
3188
- return readFileSync4(path23, "utf-8");
3260
+ return readFileSync4(path24, "utf-8");
3189
3261
  } catch {
3190
3262
  return null;
3191
3263
  }
3192
3264
  }
3193
- function readJsonOrNull2(path23) {
3194
- const content = readFileOrNull3(path23);
3265
+ function readJsonOrNull2(path24) {
3266
+ const content = readFileOrNull3(path24);
3195
3267
  if (!content) return null;
3196
3268
  try {
3197
3269
  return JSON.parse(content);
@@ -3372,11 +3444,11 @@ function checkAccuracy(dir) {
3372
3444
  }
3373
3445
 
3374
3446
  // src/scoring/checks/freshness.ts
3375
- import { readFileSync as readFileSync5, statSync as statSync2 } from "fs";
3447
+ import { existsSync as existsSync6, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
3376
3448
  import { join as join5 } from "path";
3377
- function readFileOrNull4(path23) {
3449
+ function readFileOrNull4(path24) {
3378
3450
  try {
3379
- return readFileSync5(path23, "utf-8");
3451
+ return readFileSync5(path24, "utf-8");
3380
3452
  } catch {
3381
3453
  return null;
3382
3454
  }
@@ -3394,11 +3466,14 @@ function daysSinceModified(filePath) {
3394
3466
  function checkFreshness(dir) {
3395
3467
  const checks = [];
3396
3468
  const claudeMdPath = join5(dir, "CLAUDE.md");
3397
- const daysOld = daysSinceModified(claudeMdPath);
3469
+ const agentsMdPath = join5(dir, "AGENTS.md");
3470
+ const primaryPath = existsSync6(claudeMdPath) ? claudeMdPath : agentsMdPath;
3471
+ const primaryName = existsSync6(claudeMdPath) ? "CLAUDE.md" : "AGENTS.md";
3472
+ const daysOld = daysSinceModified(primaryPath);
3398
3473
  let freshnessPoints = 0;
3399
3474
  let freshnessDetail = "";
3400
3475
  if (daysOld === null) {
3401
- freshnessDetail = "No CLAUDE.md to check";
3476
+ freshnessDetail = "No instructions file to check";
3402
3477
  } else {
3403
3478
  const threshold = FRESHNESS_THRESHOLDS.find((t) => daysOld <= t.maxDaysOld);
3404
3479
  freshnessPoints = threshold ? threshold.points : 0;
@@ -3406,16 +3481,17 @@ function checkFreshness(dir) {
3406
3481
  }
3407
3482
  checks.push({
3408
3483
  id: "claude_md_freshness",
3409
- name: "CLAUDE.md freshness",
3484
+ name: `${primaryName} freshness`,
3410
3485
  category: "freshness",
3411
3486
  maxPoints: POINTS_FRESHNESS,
3412
3487
  earnedPoints: freshnessPoints,
3413
3488
  passed: freshnessPoints >= 4,
3414
3489
  detail: freshnessDetail,
3415
- suggestion: daysOld !== null && freshnessPoints < 4 ? `CLAUDE.md is ${daysOld} days old \u2014 run \`caliber refresh\` to update it` : void 0
3490
+ suggestion: daysOld !== null && freshnessPoints < 4 ? `${primaryName} is ${daysOld} days old \u2014 run \`caliber refresh\` to update it` : void 0
3416
3491
  });
3417
3492
  const filesToScan = [
3418
3493
  "CLAUDE.md",
3494
+ "AGENTS.md",
3419
3495
  ".cursorrules",
3420
3496
  ".claude/settings.json",
3421
3497
  ".claude/settings.local.json",
@@ -3486,9 +3562,9 @@ function checkFreshness(dir) {
3486
3562
  import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "fs";
3487
3563
  import { execSync as execSync7 } from "child_process";
3488
3564
  import { join as join6 } from "path";
3489
- function readFileOrNull5(path23) {
3565
+ function readFileOrNull5(path24) {
3490
3566
  try {
3491
- return readFileSync6(path23, "utf-8");
3567
+ return readFileSync6(path24, "utf-8");
3492
3568
  } catch {
3493
3569
  return null;
3494
3570
  }
@@ -3583,22 +3659,22 @@ function checkBonus(dir) {
3583
3659
 
3584
3660
  // src/scoring/dismissed.ts
3585
3661
  init_constants();
3586
- import fs16 from "fs";
3587
- import path14 from "path";
3588
- var DISMISSED_FILE = path14.join(CALIBER_DIR, "dismissed-checks.json");
3662
+ import fs17 from "fs";
3663
+ import path15 from "path";
3664
+ var DISMISSED_FILE = path15.join(CALIBER_DIR, "dismissed-checks.json");
3589
3665
  function readDismissedChecks() {
3590
3666
  try {
3591
- if (!fs16.existsSync(DISMISSED_FILE)) return [];
3592
- return JSON.parse(fs16.readFileSync(DISMISSED_FILE, "utf-8"));
3667
+ if (!fs17.existsSync(DISMISSED_FILE)) return [];
3668
+ return JSON.parse(fs17.readFileSync(DISMISSED_FILE, "utf-8"));
3593
3669
  } catch {
3594
3670
  return [];
3595
3671
  }
3596
3672
  }
3597
3673
  function writeDismissedChecks(checks) {
3598
- if (!fs16.existsSync(CALIBER_DIR)) {
3599
- fs16.mkdirSync(CALIBER_DIR, { recursive: true });
3674
+ if (!fs17.existsSync(CALIBER_DIR)) {
3675
+ fs17.mkdirSync(CALIBER_DIR, { recursive: true });
3600
3676
  }
3601
- fs16.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
3677
+ fs17.writeFileSync(DISMISSED_FILE, JSON.stringify(checks, null, 2) + "\n");
3602
3678
  }
3603
3679
  function getDismissedIds() {
3604
3680
  return new Set(readDismissedChecks().map((c) => c.id));
@@ -3615,10 +3691,13 @@ function sumCategory(checks, category) {
3615
3691
  function filterChecksForTarget(checks, target) {
3616
3692
  return checks.filter((c) => {
3617
3693
  if (target === "claude") {
3618
- return !CURSOR_ONLY_CHECKS.has(c.id) && !BOTH_ONLY_CHECKS.has(c.id);
3694
+ return !CURSOR_ONLY_CHECKS.has(c.id) && !CODEX_ONLY_CHECKS.has(c.id) && !BOTH_ONLY_CHECKS.has(c.id);
3619
3695
  }
3620
3696
  if (target === "cursor") {
3621
- return !CLAUDE_ONLY_CHECKS.has(c.id) && !BOTH_ONLY_CHECKS.has(c.id);
3697
+ return !CLAUDE_ONLY_CHECKS.has(c.id) && !CODEX_ONLY_CHECKS.has(c.id) && !BOTH_ONLY_CHECKS.has(c.id);
3698
+ }
3699
+ if (target === "codex") {
3700
+ return !CLAUDE_ONLY_CHECKS.has(c.id) && !CURSOR_ONLY_CHECKS.has(c.id) && !BOTH_ONLY_CHECKS.has(c.id);
3622
3701
  }
3623
3702
  return true;
3624
3703
  });
@@ -3626,7 +3705,9 @@ function filterChecksForTarget(checks, target) {
3626
3705
  function detectTargetAgent(dir) {
3627
3706
  const hasClaude = existsSync8(join7(dir, "CLAUDE.md")) || existsSync8(join7(dir, ".claude", "skills"));
3628
3707
  const hasCursor = existsSync8(join7(dir, ".cursorrules")) || existsSync8(join7(dir, ".cursor", "rules"));
3708
+ const hasCodex = existsSync8(join7(dir, ".codex")) || existsSync8(join7(dir, ".agents", "skills"));
3629
3709
  if (hasClaude && hasCursor) return "both";
3710
+ if (hasCodex && !hasClaude && !hasCursor) return "codex";
3630
3711
  if (hasCursor) return "cursor";
3631
3712
  return "claude";
3632
3713
  }
@@ -3707,7 +3788,7 @@ function formatCheck(check) {
3707
3788
  }
3708
3789
  function displayScore(result) {
3709
3790
  const gc = gradeColor(result.grade);
3710
- const agentLabel = result.targetAgent === "both" ? "Claude Code + Cursor" : result.targetAgent === "claude" ? "Claude Code" : "Cursor";
3791
+ const agentLabel = result.targetAgent === "both" ? "Claude Code + Cursor" : result.targetAgent === "claude" ? "Claude Code" : result.targetAgent === "codex" ? "Codex" : "Cursor";
3711
3792
  console.log("");
3712
3793
  console.log(chalk3.gray(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
3713
3794
  console.log(chalk3.gray(" \u2502") + " " + chalk3.gray("\u2502"));
@@ -3733,7 +3814,7 @@ function displayScore(result) {
3733
3814
  }
3734
3815
  function displayScoreSummary(result) {
3735
3816
  const gc = gradeColor(result.grade);
3736
- const agentLabel = result.targetAgent === "both" ? "Claude Code + Cursor" : result.targetAgent === "claude" ? "Claude Code" : "Cursor";
3817
+ const agentLabel = result.targetAgent === "both" ? "Claude Code + Cursor" : result.targetAgent === "claude" ? "Claude Code" : result.targetAgent === "codex" ? "Codex" : "Cursor";
3737
3818
  console.log("");
3738
3819
  console.log(
3739
3820
  chalk3.gray(" ") + gc(`${result.score}/${result.maxScore}`) + chalk3.gray(` (Grade ${result.grade})`) + chalk3.gray(` \xB7 ${agentLabel}`) + chalk3.gray(` \xB7 ${progressBar(result.score, result.maxScore, 20)}`)
@@ -3799,8 +3880,8 @@ function displayScoreDelta(before, after) {
3799
3880
  import chalk4 from "chalk";
3800
3881
  import ora from "ora";
3801
3882
  import readline3 from "readline";
3802
- import fs17 from "fs";
3803
- import path15 from "path";
3883
+ import fs18 from "fs";
3884
+ import path16 from "path";
3804
3885
 
3805
3886
  // src/mcp/search.ts
3806
3887
  var AWESOME_MCP_URL = "https://raw.githubusercontent.com/punkpeye/awesome-mcp-servers/main/README.md";
@@ -4148,26 +4229,29 @@ async function discoverAndInstallMcps(targetAgent, fingerprint, dir) {
4148
4229
  return { installed: 0, names: [] };
4149
4230
  }
4150
4231
  if (targetAgent === "claude" || targetAgent === "both") {
4151
- writeMcpJson(path15.join(dir, ".mcp.json"), mcpServers);
4232
+ writeMcpJson(path16.join(dir, ".mcp.json"), mcpServers);
4152
4233
  }
4153
4234
  if (targetAgent === "cursor" || targetAgent === "both") {
4154
- const cursorDir = path15.join(dir, ".cursor");
4155
- if (!fs17.existsSync(cursorDir)) fs17.mkdirSync(cursorDir, { recursive: true });
4156
- writeMcpJson(path15.join(cursorDir, "mcp.json"), mcpServers);
4235
+ const cursorDir = path16.join(dir, ".cursor");
4236
+ if (!fs18.existsSync(cursorDir)) fs18.mkdirSync(cursorDir, { recursive: true });
4237
+ writeMcpJson(path16.join(cursorDir, "mcp.json"), mcpServers);
4238
+ }
4239
+ if (targetAgent === "codex") {
4240
+ writeMcpJson(path16.join(dir, ".mcp.json"), mcpServers);
4157
4241
  }
4158
4242
  return { installed: installedNames.length, names: installedNames };
4159
4243
  }
4160
4244
  function writeMcpJson(filePath, mcpServers) {
4161
4245
  let existing = {};
4162
4246
  try {
4163
- if (fs17.existsSync(filePath)) {
4164
- const parsed = JSON.parse(fs17.readFileSync(filePath, "utf-8"));
4247
+ if (fs18.existsSync(filePath)) {
4248
+ const parsed = JSON.parse(fs18.readFileSync(filePath, "utf-8"));
4165
4249
  if (parsed.mcpServers) existing = parsed.mcpServers;
4166
4250
  }
4167
4251
  } catch {
4168
4252
  }
4169
4253
  const merged = { ...existing, ...mcpServers };
4170
- fs17.writeFileSync(filePath, JSON.stringify({ mcpServers: merged }, null, 2) + "\n");
4254
+ fs18.writeFileSync(filePath, JSON.stringify({ mcpServers: merged }, null, 2) + "\n");
4171
4255
  }
4172
4256
  function getExistingMcpNames(fingerprint, targetAgent) {
4173
4257
  const names = [];
@@ -4355,7 +4439,7 @@ async function initCommand(options) {
4355
4439
  }
4356
4440
  const baselineScore = computeLocalScore(process.cwd(), targetAgent);
4357
4441
  displayScoreSummary(baselineScore);
4358
- const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length);
4442
+ const hasExistingConfig = !!(fingerprint.existingConfigs.claudeMd || fingerprint.existingConfigs.claudeSettings || fingerprint.existingConfigs.claudeSkills?.length || fingerprint.existingConfigs.cursorrules || fingerprint.existingConfigs.cursorRules?.length || fingerprint.existingConfigs.agentsMd);
4359
4443
  const NON_LLM_CHECKS = /* @__PURE__ */ new Set(["hooks_configured", "agents_md_exists", "permissions_configured", "mcp_servers"]);
4360
4444
  if (hasExistingConfig && baselineScore.score === 100) {
4361
4445
  console.log(chalk5.bold.green(" Your setup is already optimal \u2014 nothing to change.\n"));
@@ -4559,7 +4643,7 @@ async function initCommand(options) {
4559
4643
  console.log("");
4560
4644
  console.log(title.bold(" Keep your configs fresh\n"));
4561
4645
  console.log(chalk5.dim(" Caliber can automatically update your agent configs when your code changes.\n"));
4562
- const hookChoice = await promptHookType(targetAgent);
4646
+ const hookChoice = targetAgent === "codex" ? await promptHookType("cursor") : await promptHookType(targetAgent);
4563
4647
  if (hookChoice === "claude" || hookChoice === "both") {
4564
4648
  const hookResult = installHook();
4565
4649
  if (hookResult.installed) {
@@ -4656,7 +4740,7 @@ async function refineLoop(currentSetup, _targetAgent, sessionHistory) {
4656
4740
  }
4657
4741
  function summarizeSetup(action, setup) {
4658
4742
  const descriptions = setup.fileDescriptions;
4659
- const files = descriptions ? Object.entries(descriptions).map(([path23, desc]) => ` ${path23}: ${desc}`).join("\n") : Object.keys(setup).filter((k) => k !== "targetAgent" && k !== "fileDescriptions").join(", ");
4743
+ const files = descriptions ? Object.entries(descriptions).map(([path24, desc]) => ` ${path24}: ${desc}`).join("\n") : Object.keys(setup).filter((k) => k !== "targetAgent" && k !== "fileDescriptions").join(", ");
4660
4744
  return `${action}. Files:
4661
4745
  ${files}`;
4662
4746
  }
@@ -4722,7 +4806,8 @@ async function promptAgent() {
4722
4806
  choices: [
4723
4807
  { name: "Claude Code", value: "claude" },
4724
4808
  { name: "Cursor", value: "cursor" },
4725
- { name: "Both", value: "both" }
4809
+ { name: "Codex (OpenAI)", value: "codex" },
4810
+ { name: "Both (Claude + Cursor)", value: "both" }
4726
4811
  ]
4727
4812
  });
4728
4813
  }
@@ -4776,8 +4861,8 @@ async function openReview(method, stagedFiles) {
4776
4861
  return;
4777
4862
  }
4778
4863
  const fileInfos = stagedFiles.map((file) => {
4779
- const proposed = fs18.readFileSync(file.proposedPath, "utf-8");
4780
- const current = file.currentPath ? fs18.readFileSync(file.currentPath, "utf-8") : "";
4864
+ const proposed = fs19.readFileSync(file.proposedPath, "utf-8");
4865
+ const current = file.currentPath ? fs19.readFileSync(file.currentPath, "utf-8") : "";
4781
4866
  const patch = createTwoFilesPatch(
4782
4867
  file.isNew ? "/dev/null" : file.relativePath,
4783
4868
  file.relativePath,
@@ -4960,7 +5045,7 @@ function printSetupSummary(setup) {
4960
5045
  };
4961
5046
  if (claude) {
4962
5047
  if (claude.claudeMd) {
4963
- const icon = fs18.existsSync("CLAUDE.md") ? chalk5.yellow("~") : chalk5.green("+");
5048
+ const icon = fs19.existsSync("CLAUDE.md") ? chalk5.yellow("~") : chalk5.green("+");
4964
5049
  const desc = getDescription("CLAUDE.md");
4965
5050
  console.log(` ${icon} ${chalk5.bold("CLAUDE.md")}`);
4966
5051
  if (desc) console.log(chalk5.dim(` ${desc}`));
@@ -4970,7 +5055,28 @@ function printSetupSummary(setup) {
4970
5055
  if (Array.isArray(skills) && skills.length > 0) {
4971
5056
  for (const skill of skills) {
4972
5057
  const skillPath = `.claude/skills/${skill.name}/SKILL.md`;
4973
- const icon = fs18.existsSync(skillPath) ? chalk5.yellow("~") : chalk5.green("+");
5058
+ const icon = fs19.existsSync(skillPath) ? chalk5.yellow("~") : chalk5.green("+");
5059
+ const desc = getDescription(skillPath);
5060
+ console.log(` ${icon} ${chalk5.bold(skillPath)}`);
5061
+ console.log(chalk5.dim(` ${desc || skill.description || skill.name}`));
5062
+ console.log("");
5063
+ }
5064
+ }
5065
+ }
5066
+ const codex = setup.codex;
5067
+ if (codex) {
5068
+ if (codex.agentsMd) {
5069
+ const icon = fs19.existsSync("AGENTS.md") ? chalk5.yellow("~") : chalk5.green("+");
5070
+ const desc = getDescription("AGENTS.md");
5071
+ console.log(` ${icon} ${chalk5.bold("AGENTS.md")}`);
5072
+ if (desc) console.log(chalk5.dim(` ${desc}`));
5073
+ console.log("");
5074
+ }
5075
+ const codexSkills = codex.skills;
5076
+ if (Array.isArray(codexSkills) && codexSkills.length > 0) {
5077
+ for (const skill of codexSkills) {
5078
+ const skillPath = `.agents/skills/${skill.name}/SKILL.md`;
5079
+ const icon = fs19.existsSync(skillPath) ? chalk5.yellow("~") : chalk5.green("+");
4974
5080
  const desc = getDescription(skillPath);
4975
5081
  console.log(` ${icon} ${chalk5.bold(skillPath)}`);
4976
5082
  console.log(chalk5.dim(` ${desc || skill.description || skill.name}`));
@@ -4980,7 +5086,7 @@ function printSetupSummary(setup) {
4980
5086
  }
4981
5087
  if (cursor) {
4982
5088
  if (cursor.cursorrules) {
4983
- const icon = fs18.existsSync(".cursorrules") ? chalk5.yellow("~") : chalk5.green("+");
5089
+ const icon = fs19.existsSync(".cursorrules") ? chalk5.yellow("~") : chalk5.green("+");
4984
5090
  const desc = getDescription(".cursorrules");
4985
5091
  console.log(` ${icon} ${chalk5.bold(".cursorrules")}`);
4986
5092
  if (desc) console.log(chalk5.dim(` ${desc}`));
@@ -4990,7 +5096,7 @@ function printSetupSummary(setup) {
4990
5096
  if (Array.isArray(cursorSkills) && cursorSkills.length > 0) {
4991
5097
  for (const skill of cursorSkills) {
4992
5098
  const skillPath = `.cursor/skills/${skill.name}/SKILL.md`;
4993
- const icon = fs18.existsSync(skillPath) ? chalk5.yellow("~") : chalk5.green("+");
5099
+ const icon = fs19.existsSync(skillPath) ? chalk5.yellow("~") : chalk5.green("+");
4994
5100
  const desc = getDescription(skillPath);
4995
5101
  console.log(` ${icon} ${chalk5.bold(skillPath)}`);
4996
5102
  console.log(chalk5.dim(` ${desc || skill.description || skill.name}`));
@@ -5001,7 +5107,7 @@ function printSetupSummary(setup) {
5001
5107
  if (Array.isArray(rules) && rules.length > 0) {
5002
5108
  for (const rule of rules) {
5003
5109
  const rulePath = `.cursor/rules/${rule.filename}`;
5004
- const icon = fs18.existsSync(rulePath) ? chalk5.yellow("~") : chalk5.green("+");
5110
+ const icon = fs19.existsSync(rulePath) ? chalk5.yellow("~") : chalk5.green("+");
5005
5111
  const desc = getDescription(rulePath);
5006
5112
  console.log(` ${icon} ${chalk5.bold(rulePath)}`);
5007
5113
  if (desc) {
@@ -5014,7 +5120,7 @@ function printSetupSummary(setup) {
5014
5120
  }
5015
5121
  }
5016
5122
  }
5017
- if (!fs18.existsSync("AGENTS.md")) {
5123
+ if (!codex && !fs19.existsSync("AGENTS.md")) {
5018
5124
  console.log(` ${chalk5.green("+")} ${chalk5.bold("AGENTS.md")}`);
5019
5125
  console.log(chalk5.dim(" Cross-agent coordination file"));
5020
5126
  console.log("");
@@ -5042,8 +5148,8 @@ function ensurePermissions() {
5042
5148
  const settingsPath = ".claude/settings.json";
5043
5149
  let settings = {};
5044
5150
  try {
5045
- if (fs18.existsSync(settingsPath)) {
5046
- settings = JSON.parse(fs18.readFileSync(settingsPath, "utf-8"));
5151
+ if (fs19.existsSync(settingsPath)) {
5152
+ settings = JSON.parse(fs19.readFileSync(settingsPath, "utf-8"));
5047
5153
  }
5048
5154
  } catch {
5049
5155
  }
@@ -5057,13 +5163,14 @@ function ensurePermissions() {
5057
5163
  "Bash(git *)"
5058
5164
  ];
5059
5165
  settings.permissions = permissions;
5060
- if (!fs18.existsSync(".claude")) fs18.mkdirSync(".claude", { recursive: true });
5061
- fs18.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5166
+ if (!fs19.existsSync(".claude")) fs19.mkdirSync(".claude", { recursive: true });
5167
+ fs19.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5062
5168
  }
5063
5169
  function collectSetupFiles(setup) {
5064
5170
  const files = [];
5065
5171
  const claude = setup.claude;
5066
5172
  const cursor = setup.cursor;
5173
+ const codex = setup.codex;
5067
5174
  if (claude) {
5068
5175
  if (claude.claudeMd) files.push({ path: "CLAUDE.md", content: claude.claudeMd });
5069
5176
  const skills = claude.skills;
@@ -5073,6 +5180,15 @@ function collectSetupFiles(setup) {
5073
5180
  }
5074
5181
  }
5075
5182
  }
5183
+ if (codex) {
5184
+ if (codex.agentsMd) files.push({ path: "AGENTS.md", content: codex.agentsMd });
5185
+ const codexSkills = codex.skills;
5186
+ if (Array.isArray(codexSkills)) {
5187
+ for (const skill of codexSkills) {
5188
+ files.push({ path: `.agents/skills/${skill.name}/SKILL.md`, content: buildSkillContent(skill) });
5189
+ }
5190
+ }
5191
+ }
5076
5192
  if (cursor) {
5077
5193
  if (cursor.cursorrules) files.push({ path: ".cursorrules", content: cursor.cursorrules });
5078
5194
  const cursorSkills = cursor.skills;
@@ -5088,7 +5204,8 @@ function collectSetupFiles(setup) {
5088
5204
  }
5089
5205
  }
5090
5206
  }
5091
- if (!fs18.existsSync("AGENTS.md")) {
5207
+ const hasCodexAgentsMd = codex && codex.agentsMd;
5208
+ if (!fs19.existsSync("AGENTS.md") && !hasCodexAgentsMd) {
5092
5209
  const agentRefs = [];
5093
5210
  if (claude) agentRefs.push("See `CLAUDE.md` for Claude Code configuration.");
5094
5211
  if (cursor) agentRefs.push("See `.cursor/rules/` for Cursor rules.");
@@ -5139,7 +5256,7 @@ function undoCommand() {
5139
5256
 
5140
5257
  // src/commands/status.ts
5141
5258
  import chalk7 from "chalk";
5142
- import fs19 from "fs";
5259
+ import fs20 from "fs";
5143
5260
  async function statusCommand(options) {
5144
5261
  const config = loadConfig();
5145
5262
  const manifest = readManifest();
@@ -5165,7 +5282,7 @@ async function statusCommand(options) {
5165
5282
  }
5166
5283
  console.log(` Files managed: ${chalk7.cyan(manifest.entries.length.toString())}`);
5167
5284
  for (const entry of manifest.entries) {
5168
- const exists = fs19.existsSync(entry.path);
5285
+ const exists = fs20.existsSync(entry.path);
5169
5286
  const icon = exists ? chalk7.green("\u2713") : chalk7.red("\u2717");
5170
5287
  console.log(` ${icon} ${entry.path} (${entry.action})`);
5171
5288
  }
@@ -5252,13 +5369,13 @@ import { mkdirSync, readFileSync as readFileSync7, readdirSync as readdirSync5,
5252
5369
  import { join as join8, dirname as dirname2 } from "path";
5253
5370
 
5254
5371
  // src/scanner/index.ts
5255
- import fs20 from "fs";
5256
- import path16 from "path";
5372
+ import fs21 from "fs";
5373
+ import path17 from "path";
5257
5374
  import crypto2 from "crypto";
5258
5375
  function scanLocalState(dir) {
5259
5376
  const items = [];
5260
- const claudeMdPath = path16.join(dir, "CLAUDE.md");
5261
- if (fs20.existsSync(claudeMdPath)) {
5377
+ const claudeMdPath = path17.join(dir, "CLAUDE.md");
5378
+ if (fs21.existsSync(claudeMdPath)) {
5262
5379
  items.push({
5263
5380
  type: "rule",
5264
5381
  platform: "claude",
@@ -5267,10 +5384,10 @@ function scanLocalState(dir) {
5267
5384
  path: claudeMdPath
5268
5385
  });
5269
5386
  }
5270
- const skillsDir = path16.join(dir, ".claude", "skills");
5271
- if (fs20.existsSync(skillsDir)) {
5272
- for (const file of fs20.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
5273
- const filePath = path16.join(skillsDir, file);
5387
+ const skillsDir = path17.join(dir, ".claude", "skills");
5388
+ if (fs21.existsSync(skillsDir)) {
5389
+ for (const file of fs21.readdirSync(skillsDir).filter((f) => f.endsWith(".md"))) {
5390
+ const filePath = path17.join(skillsDir, file);
5274
5391
  items.push({
5275
5392
  type: "skill",
5276
5393
  platform: "claude",
@@ -5280,10 +5397,10 @@ function scanLocalState(dir) {
5280
5397
  });
5281
5398
  }
5282
5399
  }
5283
- const mcpJsonPath = path16.join(dir, ".mcp.json");
5284
- if (fs20.existsSync(mcpJsonPath)) {
5400
+ const mcpJsonPath = path17.join(dir, ".mcp.json");
5401
+ if (fs21.existsSync(mcpJsonPath)) {
5285
5402
  try {
5286
- const mcpJson = JSON.parse(fs20.readFileSync(mcpJsonPath, "utf-8"));
5403
+ const mcpJson = JSON.parse(fs21.readFileSync(mcpJsonPath, "utf-8"));
5287
5404
  if (mcpJson.mcpServers) {
5288
5405
  for (const name of Object.keys(mcpJson.mcpServers)) {
5289
5406
  items.push({
@@ -5298,8 +5415,36 @@ function scanLocalState(dir) {
5298
5415
  } catch {
5299
5416
  }
5300
5417
  }
5301
- const cursorrulesPath = path16.join(dir, ".cursorrules");
5302
- if (fs20.existsSync(cursorrulesPath)) {
5418
+ const agentsMdPath = path17.join(dir, "AGENTS.md");
5419
+ if (fs21.existsSync(agentsMdPath)) {
5420
+ items.push({
5421
+ type: "rule",
5422
+ platform: "codex",
5423
+ name: "AGENTS.md",
5424
+ contentHash: hashFile(agentsMdPath),
5425
+ path: agentsMdPath
5426
+ });
5427
+ }
5428
+ const codexSkillsDir = path17.join(dir, ".agents", "skills");
5429
+ if (fs21.existsSync(codexSkillsDir)) {
5430
+ try {
5431
+ for (const name of fs21.readdirSync(codexSkillsDir)) {
5432
+ const skillFile = path17.join(codexSkillsDir, name, "SKILL.md");
5433
+ if (fs21.existsSync(skillFile)) {
5434
+ items.push({
5435
+ type: "skill",
5436
+ platform: "codex",
5437
+ name: `${name}/SKILL.md`,
5438
+ contentHash: hashFile(skillFile),
5439
+ path: skillFile
5440
+ });
5441
+ }
5442
+ }
5443
+ } catch {
5444
+ }
5445
+ }
5446
+ const cursorrulesPath = path17.join(dir, ".cursorrules");
5447
+ if (fs21.existsSync(cursorrulesPath)) {
5303
5448
  items.push({
5304
5449
  type: "rule",
5305
5450
  platform: "cursor",
@@ -5308,10 +5453,10 @@ function scanLocalState(dir) {
5308
5453
  path: cursorrulesPath
5309
5454
  });
5310
5455
  }
5311
- const cursorRulesDir = path16.join(dir, ".cursor", "rules");
5312
- if (fs20.existsSync(cursorRulesDir)) {
5313
- for (const file of fs20.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
5314
- const filePath = path16.join(cursorRulesDir, file);
5456
+ const cursorRulesDir = path17.join(dir, ".cursor", "rules");
5457
+ if (fs21.existsSync(cursorRulesDir)) {
5458
+ for (const file of fs21.readdirSync(cursorRulesDir).filter((f) => f.endsWith(".mdc"))) {
5459
+ const filePath = path17.join(cursorRulesDir, file);
5315
5460
  items.push({
5316
5461
  type: "rule",
5317
5462
  platform: "cursor",
@@ -5321,12 +5466,12 @@ function scanLocalState(dir) {
5321
5466
  });
5322
5467
  }
5323
5468
  }
5324
- const cursorSkillsDir = path16.join(dir, ".cursor", "skills");
5325
- if (fs20.existsSync(cursorSkillsDir)) {
5469
+ const cursorSkillsDir = path17.join(dir, ".cursor", "skills");
5470
+ if (fs21.existsSync(cursorSkillsDir)) {
5326
5471
  try {
5327
- for (const name of fs20.readdirSync(cursorSkillsDir)) {
5328
- const skillFile = path16.join(cursorSkillsDir, name, "SKILL.md");
5329
- if (fs20.existsSync(skillFile)) {
5472
+ for (const name of fs21.readdirSync(cursorSkillsDir)) {
5473
+ const skillFile = path17.join(cursorSkillsDir, name, "SKILL.md");
5474
+ if (fs21.existsSync(skillFile)) {
5330
5475
  items.push({
5331
5476
  type: "skill",
5332
5477
  platform: "cursor",
@@ -5339,10 +5484,10 @@ function scanLocalState(dir) {
5339
5484
  } catch {
5340
5485
  }
5341
5486
  }
5342
- const cursorMcpPath = path16.join(dir, ".cursor", "mcp.json");
5343
- if (fs20.existsSync(cursorMcpPath)) {
5487
+ const cursorMcpPath = path17.join(dir, ".cursor", "mcp.json");
5488
+ if (fs21.existsSync(cursorMcpPath)) {
5344
5489
  try {
5345
- const mcpJson = JSON.parse(fs20.readFileSync(cursorMcpPath, "utf-8"));
5490
+ const mcpJson = JSON.parse(fs21.readFileSync(cursorMcpPath, "utf-8"));
5346
5491
  if (mcpJson.mcpServers) {
5347
5492
  for (const name of Object.keys(mcpJson.mcpServers)) {
5348
5493
  items.push({
@@ -5360,7 +5505,7 @@ function scanLocalState(dir) {
5360
5505
  return items;
5361
5506
  }
5362
5507
  function hashFile(filePath) {
5363
- const text = fs20.readFileSync(filePath, "utf-8");
5508
+ const text = fs21.readFileSync(filePath, "utf-8");
5364
5509
  return crypto2.createHash("sha256").update(JSON.stringify({ text })).digest("hex");
5365
5510
  }
5366
5511
  function hashJson(obj) {
@@ -5380,13 +5525,17 @@ function getSkillPath(platform, slug) {
5380
5525
  if (platform === "cursor") {
5381
5526
  return join8(".cursor", "skills", slug, "SKILL.md");
5382
5527
  }
5528
+ if (platform === "codex") {
5529
+ return join8(".agents", "skills", slug, "SKILL.md");
5530
+ }
5383
5531
  return join8(".claude", "skills", slug, "SKILL.md");
5384
5532
  }
5385
5533
  function getInstalledSkills() {
5386
5534
  const installed = /* @__PURE__ */ new Set();
5387
5535
  const dirs = [
5388
5536
  join8(process.cwd(), ".claude", "skills"),
5389
- join8(process.cwd(), ".cursor", "skills")
5537
+ join8(process.cwd(), ".cursor", "skills"),
5538
+ join8(process.cwd(), ".agents", "skills")
5390
5539
  ];
5391
5540
  for (const dir of dirs) {
5392
5541
  try {
@@ -5834,7 +5983,8 @@ async function fetchSkillContent(rec) {
5834
5983
  const candidates = [
5835
5984
  `https://raw.githubusercontent.com/${repoPath}/HEAD/skills/${rec.slug}/SKILL.md`,
5836
5985
  `https://raw.githubusercontent.com/${repoPath}/HEAD/${rec.slug}/SKILL.md`,
5837
- `https://raw.githubusercontent.com/${repoPath}/HEAD/.claude/skills/${rec.slug}/SKILL.md`
5986
+ `https://raw.githubusercontent.com/${repoPath}/HEAD/.claude/skills/${rec.slug}/SKILL.md`,
5987
+ `https://raw.githubusercontent.com/${repoPath}/HEAD/.agents/skills/${rec.slug}/SKILL.md`
5838
5988
  ];
5839
5989
  for (const url of candidates) {
5840
5990
  try {
@@ -5936,8 +6086,8 @@ async function scoreCommand(options) {
5936
6086
  }
5937
6087
 
5938
6088
  // src/commands/refresh.ts
5939
- import fs22 from "fs";
5940
- import path18 from "path";
6089
+ import fs23 from "fs";
6090
+ import path19 from "path";
5941
6091
  import chalk11 from "chalk";
5942
6092
  import ora6 from "ora";
5943
6093
 
@@ -6014,37 +6164,37 @@ function collectDiff(lastSha) {
6014
6164
  }
6015
6165
 
6016
6166
  // src/writers/refresh.ts
6017
- import fs21 from "fs";
6018
- import path17 from "path";
6167
+ import fs22 from "fs";
6168
+ import path18 from "path";
6019
6169
  function writeRefreshDocs(docs) {
6020
6170
  const written = [];
6021
6171
  if (docs.claudeMd) {
6022
- fs21.writeFileSync("CLAUDE.md", docs.claudeMd);
6172
+ fs22.writeFileSync("CLAUDE.md", docs.claudeMd);
6023
6173
  written.push("CLAUDE.md");
6024
6174
  }
6025
6175
  if (docs.readmeMd) {
6026
- fs21.writeFileSync("README.md", docs.readmeMd);
6176
+ fs22.writeFileSync("README.md", docs.readmeMd);
6027
6177
  written.push("README.md");
6028
6178
  }
6029
6179
  if (docs.cursorrules) {
6030
- fs21.writeFileSync(".cursorrules", docs.cursorrules);
6180
+ fs22.writeFileSync(".cursorrules", docs.cursorrules);
6031
6181
  written.push(".cursorrules");
6032
6182
  }
6033
6183
  if (docs.cursorRules) {
6034
- const rulesDir = path17.join(".cursor", "rules");
6035
- if (!fs21.existsSync(rulesDir)) fs21.mkdirSync(rulesDir, { recursive: true });
6184
+ const rulesDir = path18.join(".cursor", "rules");
6185
+ if (!fs22.existsSync(rulesDir)) fs22.mkdirSync(rulesDir, { recursive: true });
6036
6186
  for (const rule of docs.cursorRules) {
6037
- const filePath = path17.join(rulesDir, rule.filename);
6038
- fs21.writeFileSync(filePath, rule.content);
6187
+ const filePath = path18.join(rulesDir, rule.filename);
6188
+ fs22.writeFileSync(filePath, rule.content);
6039
6189
  written.push(filePath);
6040
6190
  }
6041
6191
  }
6042
6192
  if (docs.claudeSkills) {
6043
- const skillsDir = path17.join(".claude", "skills");
6044
- if (!fs21.existsSync(skillsDir)) fs21.mkdirSync(skillsDir, { recursive: true });
6193
+ const skillsDir = path18.join(".claude", "skills");
6194
+ if (!fs22.existsSync(skillsDir)) fs22.mkdirSync(skillsDir, { recursive: true });
6045
6195
  for (const skill of docs.claudeSkills) {
6046
- const filePath = path17.join(skillsDir, skill.filename);
6047
- fs21.writeFileSync(filePath, skill.content);
6196
+ const filePath = path18.join(skillsDir, skill.filename);
6197
+ fs22.writeFileSync(filePath, skill.content);
6048
6198
  written.push(filePath);
6049
6199
  }
6050
6200
  }
@@ -6119,11 +6269,11 @@ function log(quiet, ...args) {
6119
6269
  function discoverGitRepos(parentDir) {
6120
6270
  const repos = [];
6121
6271
  try {
6122
- const entries = fs22.readdirSync(parentDir, { withFileTypes: true });
6272
+ const entries = fs23.readdirSync(parentDir, { withFileTypes: true });
6123
6273
  for (const entry of entries) {
6124
6274
  if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
6125
- const childPath = path18.join(parentDir, entry.name);
6126
- if (fs22.existsSync(path18.join(childPath, ".git"))) {
6275
+ const childPath = path19.join(parentDir, entry.name);
6276
+ if (fs23.existsSync(path19.join(childPath, ".git"))) {
6127
6277
  repos.push(childPath);
6128
6278
  }
6129
6279
  }
@@ -6218,7 +6368,7 @@ async function refreshCommand(options) {
6218
6368
  `));
6219
6369
  const originalDir = process.cwd();
6220
6370
  for (const repo of repos) {
6221
- const repoName = path18.basename(repo);
6371
+ const repoName = path19.basename(repo);
6222
6372
  try {
6223
6373
  process.chdir(repo);
6224
6374
  await refreshSingleRepo(repo, { ...options, label: repoName });
@@ -6465,8 +6615,8 @@ function readStdin() {
6465
6615
 
6466
6616
  // src/learner/storage.ts
6467
6617
  init_constants();
6468
- import fs23 from "fs";
6469
- import path19 from "path";
6618
+ import fs24 from "fs";
6619
+ import path20 from "path";
6470
6620
  var MAX_RESPONSE_LENGTH = 2e3;
6471
6621
  var DEFAULT_STATE = {
6472
6622
  sessionId: null,
@@ -6474,15 +6624,15 @@ var DEFAULT_STATE = {
6474
6624
  lastAnalysisTimestamp: null
6475
6625
  };
6476
6626
  function ensureLearningDir() {
6477
- if (!fs23.existsSync(LEARNING_DIR)) {
6478
- fs23.mkdirSync(LEARNING_DIR, { recursive: true });
6627
+ if (!fs24.existsSync(LEARNING_DIR)) {
6628
+ fs24.mkdirSync(LEARNING_DIR, { recursive: true });
6479
6629
  }
6480
6630
  }
6481
6631
  function sessionFilePath() {
6482
- return path19.join(LEARNING_DIR, LEARNING_SESSION_FILE);
6632
+ return path20.join(LEARNING_DIR, LEARNING_SESSION_FILE);
6483
6633
  }
6484
6634
  function stateFilePath() {
6485
- return path19.join(LEARNING_DIR, LEARNING_STATE_FILE);
6635
+ return path20.join(LEARNING_DIR, LEARNING_STATE_FILE);
6486
6636
  }
6487
6637
  function truncateResponse(response) {
6488
6638
  const str = JSON.stringify(response);
@@ -6493,50 +6643,50 @@ function appendEvent(event) {
6493
6643
  ensureLearningDir();
6494
6644
  const truncated = { ...event, tool_response: truncateResponse(event.tool_response) };
6495
6645
  const filePath = sessionFilePath();
6496
- fs23.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
6646
+ fs24.appendFileSync(filePath, JSON.stringify(truncated) + "\n");
6497
6647
  const count = getEventCount();
6498
6648
  if (count > LEARNING_MAX_EVENTS) {
6499
- const lines = fs23.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
6649
+ const lines = fs24.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
6500
6650
  const kept = lines.slice(lines.length - LEARNING_MAX_EVENTS);
6501
- fs23.writeFileSync(filePath, kept.join("\n") + "\n");
6651
+ fs24.writeFileSync(filePath, kept.join("\n") + "\n");
6502
6652
  }
6503
6653
  }
6504
6654
  function readAllEvents() {
6505
6655
  const filePath = sessionFilePath();
6506
- if (!fs23.existsSync(filePath)) return [];
6507
- const lines = fs23.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
6656
+ if (!fs24.existsSync(filePath)) return [];
6657
+ const lines = fs24.readFileSync(filePath, "utf-8").split("\n").filter(Boolean);
6508
6658
  return lines.map((line) => JSON.parse(line));
6509
6659
  }
6510
6660
  function getEventCount() {
6511
6661
  const filePath = sessionFilePath();
6512
- if (!fs23.existsSync(filePath)) return 0;
6513
- const content = fs23.readFileSync(filePath, "utf-8");
6662
+ if (!fs24.existsSync(filePath)) return 0;
6663
+ const content = fs24.readFileSync(filePath, "utf-8");
6514
6664
  return content.split("\n").filter(Boolean).length;
6515
6665
  }
6516
6666
  function clearSession() {
6517
6667
  const filePath = sessionFilePath();
6518
- if (fs23.existsSync(filePath)) fs23.unlinkSync(filePath);
6668
+ if (fs24.existsSync(filePath)) fs24.unlinkSync(filePath);
6519
6669
  }
6520
6670
  function readState2() {
6521
6671
  const filePath = stateFilePath();
6522
- if (!fs23.existsSync(filePath)) return { ...DEFAULT_STATE };
6672
+ if (!fs24.existsSync(filePath)) return { ...DEFAULT_STATE };
6523
6673
  try {
6524
- return JSON.parse(fs23.readFileSync(filePath, "utf-8"));
6674
+ return JSON.parse(fs24.readFileSync(filePath, "utf-8"));
6525
6675
  } catch {
6526
6676
  return { ...DEFAULT_STATE };
6527
6677
  }
6528
6678
  }
6529
6679
  function writeState2(state) {
6530
6680
  ensureLearningDir();
6531
- fs23.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
6681
+ fs24.writeFileSync(stateFilePath(), JSON.stringify(state, null, 2));
6532
6682
  }
6533
6683
  function resetState() {
6534
6684
  writeState2({ ...DEFAULT_STATE });
6535
6685
  }
6536
6686
 
6537
6687
  // src/learner/writer.ts
6538
- import fs24 from "fs";
6539
- import path20 from "path";
6688
+ import fs25 from "fs";
6689
+ import path21 from "path";
6540
6690
  var LEARNED_START = "<!-- caliber:learned -->";
6541
6691
  var LEARNED_END = "<!-- /caliber:learned -->";
6542
6692
  function writeLearnedContent(update) {
@@ -6556,8 +6706,8 @@ function writeLearnedContent(update) {
6556
6706
  function writeLearnedSection(content) {
6557
6707
  const claudeMdPath = "CLAUDE.md";
6558
6708
  let existing = "";
6559
- if (fs24.existsSync(claudeMdPath)) {
6560
- existing = fs24.readFileSync(claudeMdPath, "utf-8");
6709
+ if (fs25.existsSync(claudeMdPath)) {
6710
+ existing = fs25.readFileSync(claudeMdPath, "utf-8");
6561
6711
  }
6562
6712
  const section = `${LEARNED_START}
6563
6713
  ${content}
@@ -6571,15 +6721,15 @@ ${LEARNED_END}`;
6571
6721
  const separator = existing.endsWith("\n") || existing === "" ? "" : "\n";
6572
6722
  updated = existing + separator + "\n" + section + "\n";
6573
6723
  }
6574
- fs24.writeFileSync(claudeMdPath, updated);
6724
+ fs25.writeFileSync(claudeMdPath, updated);
6575
6725
  }
6576
6726
  function writeLearnedSkill(skill) {
6577
- const skillDir = path20.join(".claude", "skills", skill.name);
6578
- if (!fs24.existsSync(skillDir)) fs24.mkdirSync(skillDir, { recursive: true });
6579
- const skillPath = path20.join(skillDir, "SKILL.md");
6580
- if (!skill.isNew && fs24.existsSync(skillPath)) {
6581
- const existing = fs24.readFileSync(skillPath, "utf-8");
6582
- fs24.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
6727
+ const skillDir = path21.join(".claude", "skills", skill.name);
6728
+ if (!fs25.existsSync(skillDir)) fs25.mkdirSync(skillDir, { recursive: true });
6729
+ const skillPath = path21.join(skillDir, "SKILL.md");
6730
+ if (!skill.isNew && fs25.existsSync(skillPath)) {
6731
+ const existing = fs25.readFileSync(skillPath, "utf-8");
6732
+ fs25.writeFileSync(skillPath, existing.trimEnd() + "\n\n" + skill.content);
6583
6733
  } else {
6584
6734
  const frontmatter = [
6585
6735
  "---",
@@ -6588,14 +6738,14 @@ function writeLearnedSkill(skill) {
6588
6738
  "---",
6589
6739
  ""
6590
6740
  ].join("\n");
6591
- fs24.writeFileSync(skillPath, frontmatter + skill.content);
6741
+ fs25.writeFileSync(skillPath, frontmatter + skill.content);
6592
6742
  }
6593
6743
  return skillPath;
6594
6744
  }
6595
6745
  function readLearnedSection() {
6596
6746
  const claudeMdPath = "CLAUDE.md";
6597
- if (!fs24.existsSync(claudeMdPath)) return null;
6598
- const content = fs24.readFileSync(claudeMdPath, "utf-8");
6747
+ if (!fs25.existsSync(claudeMdPath)) return null;
6748
+ const content = fs25.readFileSync(claudeMdPath, "utf-8");
6599
6749
  const startIdx = content.indexOf(LEARNED_START);
6600
6750
  const endIdx = content.indexOf(LEARNED_END);
6601
6751
  if (startIdx === -1 || endIdx === -1) return null;
@@ -6779,20 +6929,20 @@ Learned items in CLAUDE.md: ${chalk14.cyan(String(lineCount))}`);
6779
6929
  }
6780
6930
 
6781
6931
  // src/cli.ts
6782
- var __dirname = path21.dirname(fileURLToPath(import.meta.url));
6932
+ var __dirname = path22.dirname(fileURLToPath(import.meta.url));
6783
6933
  var pkg = JSON.parse(
6784
- fs25.readFileSync(path21.resolve(__dirname, "..", "package.json"), "utf-8")
6934
+ fs26.readFileSync(path22.resolve(__dirname, "..", "package.json"), "utf-8")
6785
6935
  );
6786
6936
  var program = new Command();
6787
6937
  var displayVersion = process.env.CALIBER_LOCAL ? `${pkg.version}-local` : pkg.version;
6788
6938
  program.name(process.env.CALIBER_LOCAL ? "caloc" : "caliber").description("Configure your coding agent environment").version(displayVersion);
6789
- program.command("onboard").alias("init").description("Onboard your project for AI-assisted development").option("--agent <type>", "Target agent: claude, cursor, or both").option("--dry-run", "Preview changes without writing files").option("--force", "Overwrite existing setup without prompting").action(initCommand);
6939
+ program.command("onboard").alias("init").description("Onboard your project for AI-assisted development").option("--agent <type>", "Target agent: claude, cursor, codex, or both").option("--dry-run", "Preview changes without writing files").option("--force", "Overwrite existing setup without prompting").action(initCommand);
6790
6940
  program.command("undo").description("Revert all config changes made by Caliber").action(undoCommand);
6791
6941
  program.command("status").description("Show current Caliber setup status").option("--json", "Output as JSON").action(statusCommand);
6792
6942
  program.command("regenerate").alias("regen").alias("re").alias("update").description("Re-analyze project and regenerate setup").option("--dry-run", "Preview changes without writing files").action(regenerateCommand);
6793
6943
  program.command("config").description("Configure LLM provider, API key, and model").action(configCommand);
6794
6944
  program.command("recommend").description("Discover and install skill recommendations").option("--generate", "Force fresh recommendation search").action(recommendCommand);
6795
- program.command("score").description("Score your current agent config setup (deterministic, no network)").option("--json", "Output as JSON").option("--quiet", "One-line output for scripts/hooks").option("--agent <type>", "Target agent: claude, cursor, or both").action(scoreCommand);
6945
+ program.command("score").description("Score your current agent config setup (deterministic, no network)").option("--json", "Output as JSON").option("--quiet", "One-line output for scripts/hooks").option("--agent <type>", "Target agent: claude, cursor, codex, or both").action(scoreCommand);
6796
6946
  program.command("refresh").description("Update docs based on recent code changes").option("--quiet", "Suppress output (for use in hooks)").option("--dry-run", "Preview changes without writing files").action(refreshCommand);
6797
6947
  program.command("hooks").description("Manage auto-refresh hooks (toggle interactively)").option("--install", "Enable all hooks non-interactively").option("--remove", "Disable all hooks non-interactively").action(hooksCommand);
6798
6948
  var learn = program.command("learn").description("Session learning \u2014 observe tool usage and extract reusable instructions");
@@ -6803,22 +6953,22 @@ learn.command("remove").description("Remove learning hooks from .claude/settings
6803
6953
  learn.command("status").description("Show learning system status").action(learnStatusCommand);
6804
6954
 
6805
6955
  // src/utils/version-check.ts
6806
- import fs26 from "fs";
6807
- import path22 from "path";
6956
+ import fs27 from "fs";
6957
+ import path23 from "path";
6808
6958
  import { fileURLToPath as fileURLToPath2 } from "url";
6809
6959
  import { execSync as execSync9 } from "child_process";
6810
6960
  import chalk15 from "chalk";
6811
6961
  import ora7 from "ora";
6812
6962
  import confirm2 from "@inquirer/confirm";
6813
- var __dirname_vc = path22.dirname(fileURLToPath2(import.meta.url));
6963
+ var __dirname_vc = path23.dirname(fileURLToPath2(import.meta.url));
6814
6964
  var pkg2 = JSON.parse(
6815
- fs26.readFileSync(path22.resolve(__dirname_vc, "..", "package.json"), "utf-8")
6965
+ fs27.readFileSync(path23.resolve(__dirname_vc, "..", "package.json"), "utf-8")
6816
6966
  );
6817
6967
  function getInstalledVersion() {
6818
6968
  try {
6819
6969
  const globalRoot = execSync9("npm root -g", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
6820
- const pkgPath = path22.join(globalRoot, "@rely-ai", "caliber", "package.json");
6821
- return JSON.parse(fs26.readFileSync(pkgPath, "utf-8")).version;
6970
+ const pkgPath = path23.join(globalRoot, "@rely-ai", "caliber", "package.json");
6971
+ return JSON.parse(fs27.readFileSync(pkgPath, "utf-8")).version;
6822
6972
  } catch {
6823
6973
  return null;
6824
6974
  }