当前行为 (Current Behavior)
观察到两个问题:
问题 1:Skills 始终显示 0
relayContextConfigContents 为空时,Codex++ 如预期显示 "Skills: 0"。但即便在 ~/.codex/skills/(106 个 SKILL.md)、~/.agents/skills/(48 个)、~/.codex/vendor_imports/skills/(39 个)中存在大量 Skill 文件,"工具与插件"页仍然只从 TOML [skills.*] 节中读取,完全忽略文件系统的技能定义。
问题 2:插件配置无法跨 Profile 继承
每个 Relay Profile 的 configContents 中包含独立的 [plugins.*]、[marketplaces.*]、[features]、[memories] 等配置。新建 Profile(例如 MiniMax)时不会自动从已有 Profile 复制这些上下文。切换 Profile 后,之前可用的插件可能在新的 config.toml 中完全消失。
relayCommonConfigContents 字段在全新安装时为空字符串。即便所有 Profile 的 useCommonConfig = true,空 common config 也使得共享机制形同虚设。
预期行为 (Expected Behavior)
-
Skills 应基于文件系统检测:read_live_context_entries 应扫描 ~/.codex/skills/、~/.agents/skills/ 和 ~/.codex/vendor_imports/skills/,解析 SKILL.md 的 YAML frontmatter 中的 name 和 description,与 TOML 型 skill 合并展示。
-
新 Profile 应自动继承共享配置:若 relayCommonConfigContents 非空,新 Profile 应默认 useCommonConfig = true,并在 UI 中提示"将继承 X 个插件、Y 个技能"。
-
首次启动应自动填充 relayCommonConfigContents:当该字段为空而 config.toml 已存在时,应自动调用 extract_common_config_from_config() 初始化。
复现步骤 (Reproduction Steps)
Skills 显示 0
- 安装 Codex 并安装多个 Skill(例如从 Marketplace 导入)
- 通过 Codex++ 启动器启动
- 打开 "Codex++ 管理工具" → "工具与插件" 页
- 观察到:Skills 显示 0,但实际在对话中可正常使用 @skill-name 调用
插件跨 Profile 丢失
- 配置 Profile A(含完整插件),应用后 config.toml 正常
- 创建 Profile B(新 API),
configContents 仅含 model/provider/base_url
- 切换到 Profile B → config.toml 中
[plugins] 全部消失
- 对话框 @-mention 列表无任何插件
日志 / 配置片段 (Logs / Config)
Codex++ 运行状态
Status: running
Codex App: Codex v26.527.7698.0 (Windows Store)
Codex++: v1.1.9
Debug port: 9229, Helper port: 57321
relayCommonConfigContents(修复前)
relayContextConfigContents(始终为空)
MiniMax Profile configContents(修复前示例)
model = "MiniMax-M3"
model_provider = "custom"
model_reasoning_effort = "high"
[model_providers.custom]
name = "custom"
wire_api = "responses"
requires_openai_auth = true
base_url = "https://api.minimaxi.com/v1"
experimental_bearer_token = "[REDACTED]"
[features]
# 空节——无 goals, memories
[mcp_servers.node_repl]
# ... (仅 node_repl)
DeepSeek Profile configContents(有插件但仅在单 profile 内)
model_provider = "custom"
model = "deepseek-v4-pro"
# ...
[plugins."computer-use@openai-bundled"]
enabled = true
[plugins."documents@openai-primary-runtime"]
enabled = true
# ... 共 12 个 plugin 节(全部仅存于此 profile 内)
技能目录结构
~/.codex/skills/ → 106 SKILL.md(YAML frontmatter: name + description)
~/.agents/skills/ → 48 SKILL.md
~/.codex/vendor_imports/skills/ → 39 SKILL.md
完整脱敏版 settings.json 见附件 redacted-settings.json
环境 (Environment)
| 项目 |
值 |
| OS |
Windows 11 24H2 |
| Codex 版本 |
26.527.7698.0 (Windows Store) |
| Codex++ 版本 |
1.1.9 |
| Codex++ 安装方式 |
CodexPlusPlus-1.1.9-windows-x64-setup.exe |
| wire_api |
responses |
| 复现概率 |
100% |
建议修复方案 (Proposed Fix)
Patch 1:文件系统技能检测(relay_config.rs)
新增函数扫描 ~/.codex/skills/、~/.agents/skills/ 和 ~/.codex/vendor_imports/skills/:
use std::collections::HashSet;
pub fn discover_file_based_skills(codex_home: &Path) -> Vec<CodexContextEntry> {
let agents_home = dirs_home().join(".agents").join("skills");
let vendor_home = codex_home.join("vendor_imports").join("skills");
let roots = [codex_home.join("skills"), agents_home, vendor_home];
let mut entries: Vec<CodexContextEntry> = Vec::new();
let mut seen: HashSet<String> = HashSet::new();
for root in &roots {
if !root.exists() { continue; }
if let Ok(walker) = walkdir::WalkDir::new(root).max_depth(4).into_iter() {
for entry in walker.filter_map(|e| e.ok()) {
if entry.file_name() != "SKILL.md" { continue; }
let Ok(content) = std::fs::read_to_string(entry.path()) else { continue; };
let (id, title, summary) = parse_skill_frontmatter(&content);
if !seen.insert(id.clone()) { continue; }
entries.push(CodexContextEntry {
id, kind: "skill".to_string(), title, summary,
toml_body: String::new(), enabled: true,
});
}
}
}
entries.sort_by(|a, b| a.title.cmp(&b.title));
entries
}
fn parse_skill_frontmatter(content: &str) -> (String, String, String) {
let mut name = String::new();
let mut desc = String::new();
let mut in_fm = false;
for line in content.lines() {
let t = line.trim();
if t == "---" { in_fm = !in_fm; continue; }
if !in_fm { continue; }
if let Some(v) = t.strip_prefix("name:") {
name = v.trim().trim_matches('"').to_string();
} else if let Some(v) = t.strip_prefix("description:") {
desc = v.trim().trim_matches('"').to_string();
if desc.len() > 120 { desc = format!("{}...", &desc[..117]); }
}
}
if name.is_empty() { name = "unknown".to_string(); }
(name.clone(), name, desc)
}
在 list_context_entries_from_common_config 中合并:
pub fn list_context_entries_from_common_config(
common_config: &str,
codex_home: Option<&Path>,
) -> anyhow::Result<CodexContextEntries> {
// ...existing TOML parsing...
let mut entries = CodexContextEntries { /* ... */ };
if let Some(home) = codex_home {
let file_skills = discover_file_based_skills(home);
for fs in file_skills {
if !entries.skills.iter().any(|s| s.id == fs.id) {
entries.skills.push(fs);
}
}
}
Ok(entries)
}
新增 Cargo 依赖 (crates/codex-plus-core/Cargo.toml):
Patch 2:首次启动自动初始化 relayCommonConfigContents
在 profile 应用逻辑中增加守卫:
if settings.relay_common_config_contents.trim().is_empty() {
if let Ok(live) = std::fs::read_to_string(home.join("config.toml")) {
if let Ok(common) = extract_common_config_from_config(&live) {
if !common.trim().is_empty() {
settings.relay_common_config_contents = common;
}
}
}
}
Patch 3:管理工具新建 Profile 流程
在创建新 Profile 的 UI 步骤中:
- 展示"将继承 X 个插件、Y 个 MCP、Z 个技能"预览
- 提供"从其他 Profile 复制上下文"的下拉选择
- 默认勾选
useCommonConfig = true
额外建议 (Additional Recommendations)
- 诊断页增加配置校验:检测
relayCommonConfigContents 是否为空并显示警告;对比各 Profile 的 contextSelection 差异
- 一键修复按钮:从当前
config.toml 重新提取 relayCommonConfigContents
- Profile 创建模板化:定义 Profile 创建时的最小必需字段(model, provider, base_url),其余从 common config 继承
附件 (Attachments)
redacted-settings.json:脱敏后的完整 Codex++ settings.json(含 7 个 Profile 的完整对比数据)
redacted-settings.json
当前行为 (Current Behavior)
观察到两个问题:
问题 1:Skills 始终显示 0
relayContextConfigContents为空时,Codex++ 如预期显示 "Skills: 0"。但即便在~/.codex/skills/(106 个 SKILL.md)、~/.agents/skills/(48 个)、~/.codex/vendor_imports/skills/(39 个)中存在大量 Skill 文件,"工具与插件"页仍然只从 TOML[skills.*]节中读取,完全忽略文件系统的技能定义。问题 2:插件配置无法跨 Profile 继承
每个 Relay Profile 的
configContents中包含独立的[plugins.*]、[marketplaces.*]、[features]、[memories]等配置。新建 Profile(例如 MiniMax)时不会自动从已有 Profile 复制这些上下文。切换 Profile 后,之前可用的插件可能在新的 config.toml 中完全消失。relayCommonConfigContents字段在全新安装时为空字符串。即便所有 Profile 的useCommonConfig = true,空 common config 也使得共享机制形同虚设。预期行为 (Expected Behavior)
Skills 应基于文件系统检测:
read_live_context_entries应扫描~/.codex/skills/、~/.agents/skills/和~/.codex/vendor_imports/skills/,解析 SKILL.md 的 YAML frontmatter 中的name和description,与 TOML 型 skill 合并展示。新 Profile 应自动继承共享配置:若
relayCommonConfigContents非空,新 Profile 应默认useCommonConfig = true,并在 UI 中提示"将继承 X 个插件、Y 个技能"。首次启动应自动填充
relayCommonConfigContents:当该字段为空而 config.toml 已存在时,应自动调用extract_common_config_from_config()初始化。复现步骤 (Reproduction Steps)
Skills 显示 0
插件跨 Profile 丢失
configContents仅含 model/provider/base_url[plugins]全部消失日志 / 配置片段 (Logs / Config)
Codex++ 运行状态
relayCommonConfigContents(修复前)
relayContextConfigContents(始终为空)
MiniMax Profile configContents(修复前示例)
DeepSeek Profile configContents(有插件但仅在单 profile 内)
技能目录结构
环境 (Environment)
26.527.7698.0(Windows Store)1.1.9CodexPlusPlus-1.1.9-windows-x64-setup.exeresponses建议修复方案 (Proposed Fix)
Patch 1:文件系统技能检测(
relay_config.rs)新增函数扫描
~/.codex/skills/、~/.agents/skills/和~/.codex/vendor_imports/skills/:在
list_context_entries_from_common_config中合并:新增 Cargo 依赖 (
crates/codex-plus-core/Cargo.toml):Patch 2:首次启动自动初始化
relayCommonConfigContents在 profile 应用逻辑中增加守卫:
Patch 3:管理工具新建 Profile 流程
在创建新 Profile 的 UI 步骤中:
useCommonConfig = true额外建议 (Additional Recommendations)
relayCommonConfigContents是否为空并显示警告;对比各 Profile 的contextSelection差异config.toml重新提取relayCommonConfigContents附件 (Attachments)
redacted-settings.json:脱敏后的完整 Codex++ settings.json(含 7 个 Profile 的完整对比数据)redacted-settings.json