fix(codex): keep commented TOML fields, follow symlinked config surfaces

readTomlMeta was dropping any field with a trailing `# comment` (common in
real agents/*.toml). readSkills/readAgents/readRules used bare
isDirectory()/isFile(), which miss symlinked entries per cc-discovery.js's
own documented Dirent quirk — reused its isDirLike/isFileLike instead
(exporting isDirLike from cc-discovery.js, which wasn't previously public).
Also swapped the "never walks outside" test for one that actually exercises
a symlink escape, and dropped the unnecessary isUnder re-export.
This commit is contained in:
2026-09-08 16:41:46 +07:00
parent 7618766861
commit 98b1921ca6
3 changed files with 41 additions and 12 deletions
+20 -9
View File
@@ -15,7 +15,13 @@
const fs = require("node:fs");
const path = require("node:path");
const { getCodexHome, getProjectCodexDir, getProjectRoot } = require("./codex-home");
const { parseFrontmatter, isUnder, MAX_FILE_BYTES } = require("./cc-discovery");
const {
parseFrontmatter,
isUnder,
isDirLike,
isFileLike,
MAX_FILE_BYTES,
} = require("./cc-discovery");
function safeReadText(absPath) {
try {
@@ -55,7 +61,7 @@ function readSkillsAt(scope, codexDir) {
const skills = [];
for (const ent of listDir(dir)) {
const skillDir = path.join(dir, ent.name);
if (!ent.isDirectory() || !isUnder(codexDir, skillDir)) continue;
if (!isDirLike(ent, skillDir) || !isUnder(codexDir, skillDir)) continue;
const skillFile = path.join(skillDir, "SKILL.md");
const read = safeReadText(skillFile);
if (!read) continue;
@@ -116,10 +122,16 @@ function readTomlMeta(text) {
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
} else if (value === "true" || value === "false") {
value = value === "true";
} else if (/^-?\d+(\.\d+)?$/.test(value)) {
value = Number(value);
} else if (value.startsWith('"') || value.startsWith("'")) {
// A quoted value with a trailing `# comment` outside the string —
// e.g. model = "gpt-5.4" # default. Strip from the closing quote on.
const quote = value[0];
const closeIdx = value.indexOf(quote, 1);
if (closeIdx > 0) value = value.slice(1, closeIdx);
else continue;
} else if (/^(true|false|-?\d+(\.\d+)?)\s*(#.*)?$/.test(value)) {
value = value.replace(/\s*#.*$/, "");
value = value === "true" ? true : value === "false" ? false : Number(value);
} else {
continue; // arrays, inline tables, etc. — not one of the shown fields
}
@@ -134,7 +146,7 @@ function readAgentsAt(scope, codexDir) {
for (const ent of listDir(dir)) {
if (!ent.name.endsWith(".toml")) continue;
const file = path.join(dir, ent.name);
if (!ent.isFile() || !isUnder(codexDir, file)) continue;
if (!isFileLike(ent, file) || !isUnder(codexDir, file)) continue;
const read = safeReadText(file);
if (!read) continue;
agents.push({
@@ -165,7 +177,7 @@ function readRulesAt(scope, codexDir) {
for (const ent of listDir(dir)) {
if (!ent.name.endsWith(".rules")) continue;
const file = path.join(dir, ent.name);
if (!ent.isFile() || !isUnder(codexDir, file)) continue;
if (!isFileLike(ent, file) || !isUnder(codexDir, file)) continue;
const read = safeReadText(file);
if (!read) continue;
rules.push({
@@ -268,5 +280,4 @@ module.exports = {
readConfig,
readOverview,
readTomlMeta,
isUnder,
};