projecta-rrr 1.23.0 → 1.23.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/bin/install.js +3 -1
- package/package.json +1 -1
- package/rrr/lib/codex-agent-gen.js +52 -11
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ All notable changes to RRR will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
|
|
7
|
+
## [1.23.3] - 2026-04-20
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **Codex agent config self-healing** — installer now rewrites `~/.codex/agents/*.toml` from the repo source and prunes stale generated files so legacy Codex-only fields cannot survive across installs.
|
|
11
|
+
|
|
12
|
+
## [1.23.2] - 2026-04-20
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **Codex agent TOML `developer_instructions`** — Codex 0.122.0 requires this field; generator now extracts the first 3000 chars of each agent's markdown body as the system prompt using TOML literal multi-line strings (no escaping needed).
|
|
16
|
+
- **MCP 406 for Codex rmcp clients** — Codex's Rust rmcp client does not send `Accept: application/json, text/event-stream`; server now normalizes the header before the SDK validates it so the connection succeeds.
|
|
17
|
+
|
|
18
|
+
## [1.23.1] - 2026-04-20
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **Codex agent TOML generation** — removed `tier` and `fallback_model` fields from generated `.toml` files; Codex CLI rejects these unknown fields and silently ignores the entire agent definition. Generated files now contain only `name`, `description`, and `model`.
|
|
22
|
+
|
|
7
23
|
## [1.23.0] - 2026-04-20
|
|
8
24
|
|
|
9
25
|
**v1.23 Multi-Provider Compatibility — RRR workflow now works on OpenAI Codex with zero extra setup.**
|
package/bin/install.js
CHANGED
|
@@ -2219,7 +2219,9 @@ function install(isGlobal) {
|
|
|
2219
2219
|
verbose: false
|
|
2220
2220
|
});
|
|
2221
2221
|
if (agentResult.errors.length === 0) {
|
|
2222
|
-
|
|
2222
|
+
const removedCount = agentResult.removed.length;
|
|
2223
|
+
const removedText = removedCount > 0 ? `, pruned ${removedCount} stale configs` : '';
|
|
2224
|
+
console.log(` ${green}✓${reset} Installed ${agentResult.written.length} Codex agent configs to ~/.codex/agents/${removedText}`);
|
|
2223
2225
|
} else {
|
|
2224
2226
|
console.log(` ${yellow}⚠${reset} Installed ${agentResult.written.length} agent configs (${agentResult.errors.length} errors)`);
|
|
2225
2227
|
agentResult.errors.slice(0, 3).forEach(e => {
|
package/package.json
CHANGED
|
@@ -20,6 +20,8 @@ const TIER_MAP = {
|
|
|
20
20
|
inherit: { tier: 'ADAPTIVE', model: 'gpt-4o', fallback_model: 'o1' },
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
const LEGACY_TOP_LEVEL_FIELDS = ['tier', 'fallback_model'];
|
|
24
|
+
|
|
23
25
|
/**
|
|
24
26
|
* Parse the YAML frontmatter from a markdown string.
|
|
25
27
|
*
|
|
@@ -75,6 +77,21 @@ function extractField(lines, key) {
|
|
|
75
77
|
return null;
|
|
76
78
|
}
|
|
77
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Guard against old Codex-only metadata leaking into generated TOML.
|
|
82
|
+
*
|
|
83
|
+
* Current Codex CLI agent files accept only name, description, model, and
|
|
84
|
+
* developer_instructions. Legacy tier/fallback_model keys cause Codex to
|
|
85
|
+
* reject the entire agent definition, so we fail fast if they ever reappear.
|
|
86
|
+
*/
|
|
87
|
+
function assertNoLegacyTopLevelFields(toml, filename) {
|
|
88
|
+
for (const field of LEGACY_TOP_LEVEL_FIELDS) {
|
|
89
|
+
if (new RegExp(`^\\s*${field}\\s*=`, 'm').test(toml)) {
|
|
90
|
+
throw new Error(`Generated ${filename} contains unsupported field "${field}"`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
78
95
|
/**
|
|
79
96
|
* Convert a single RRR agent markdown file (Claude Code format) into
|
|
80
97
|
* a Codex-compatible TOML agent config string.
|
|
@@ -84,7 +101,7 @@ function extractField(lines, key) {
|
|
|
84
101
|
* @returns {string} Generated TOML string for ~/.codex/agents/<basename>.toml
|
|
85
102
|
*/
|
|
86
103
|
function generateCodexAgentToml(sourceMarkdown, filename) {
|
|
87
|
-
const { frontmatterLines } = parseFrontmatter(sourceMarkdown);
|
|
104
|
+
const { frontmatterLines, body } = parseFrontmatter(sourceMarkdown);
|
|
88
105
|
|
|
89
106
|
// Extract fields from frontmatter
|
|
90
107
|
const nameField = extractField(frontmatterLines, 'name');
|
|
@@ -97,23 +114,27 @@ function generateCodexAgentToml(sourceMarkdown, filename) {
|
|
|
97
114
|
// Look up tier mapping; default to STANDARD (sonnet) if model not found
|
|
98
115
|
const tierEntry = TIER_MAP[modelField] || TIER_MAP.sonnet;
|
|
99
116
|
|
|
100
|
-
//
|
|
117
|
+
// developer_instructions: body of the markdown file (the system prompt).
|
|
118
|
+
// Truncated to 3000 chars; uses TOML literal multi-line strings (''') which
|
|
119
|
+
// need zero escaping — safe as long as the body contains no ''' sequence.
|
|
120
|
+
const instructions = body.trim().slice(0, 3000);
|
|
121
|
+
|
|
122
|
+
// Build TOML — only fields Codex CLI accepts
|
|
101
123
|
const lines = [
|
|
102
124
|
'# Generated by projecta-rrr — do not edit manually',
|
|
103
125
|
`# Source: agents/${filename}`,
|
|
104
126
|
'',
|
|
105
127
|
`name = "${agentName}"`,
|
|
106
128
|
`description = "${descriptionField || agentName}"`,
|
|
107
|
-
`tier = "${tierEntry.tier}"`,
|
|
108
129
|
`model = "${tierEntry.model}"`,
|
|
130
|
+
`developer_instructions = '''`,
|
|
131
|
+
instructions,
|
|
132
|
+
`'''`,
|
|
109
133
|
];
|
|
110
134
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return lines.join('\n') + '\n';
|
|
135
|
+
const toml = lines.join('\n') + '\n';
|
|
136
|
+
assertNoLegacyTopLevelFields(toml, filename);
|
|
137
|
+
return toml;
|
|
117
138
|
}
|
|
118
139
|
|
|
119
140
|
/**
|
|
@@ -128,7 +149,7 @@ function generateCodexAgentToml(sourceMarkdown, filename) {
|
|
|
128
149
|
* @param {string} options.targetDir - Absolute path to target directory (e.g. ~/.codex/agents/)
|
|
129
150
|
* @param {boolean} [options.dryRun=false] - If true, compute paths but skip writing
|
|
130
151
|
* @param {boolean} [options.verbose=false] - If true, log each file written
|
|
131
|
-
* @returns {{ written: string[], skipped: string[], errors: Array<{file: string, error: string}> }}
|
|
152
|
+
* @returns {{ written: string[], skipped: string[], removed: string[], errors: Array<{file: string, error: string}> }}
|
|
132
153
|
*/
|
|
133
154
|
function installCodexAgents(options) {
|
|
134
155
|
const {
|
|
@@ -140,6 +161,7 @@ function installCodexAgents(options) {
|
|
|
140
161
|
|
|
141
162
|
const written = [];
|
|
142
163
|
const skipped = [];
|
|
164
|
+
const removed = [];
|
|
143
165
|
const errors = [];
|
|
144
166
|
|
|
145
167
|
// Ensure target directory exists (no-op if already exists)
|
|
@@ -187,7 +209,26 @@ function installCodexAgents(options) {
|
|
|
187
209
|
}
|
|
188
210
|
}
|
|
189
211
|
|
|
190
|
-
|
|
212
|
+
if (!dryRun) {
|
|
213
|
+
const managedFiles = new Set(written);
|
|
214
|
+
try {
|
|
215
|
+
const existingTargets = fs.readdirSync(targetDir);
|
|
216
|
+
for (const targetEntry of existingTargets) {
|
|
217
|
+
if (!targetEntry.endsWith('.toml')) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (managedFiles.has(targetEntry)) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
fs.unlinkSync(path.join(targetDir, targetEntry));
|
|
224
|
+
removed.push(targetEntry);
|
|
225
|
+
}
|
|
226
|
+
} catch (err) {
|
|
227
|
+
errors.push({ file: targetDir, error: `Cannot prune targetDir: ${err.message}` });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return { written, skipped, removed, errors };
|
|
191
232
|
}
|
|
192
233
|
|
|
193
234
|
module.exports = {
|