projecta-rrr 1.23.1 → 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 CHANGED
@@ -4,6 +4,17 @@ 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
+
7
18
  ## [1.23.1] - 2026-04-20
8
19
 
9
20
  ### Fixed
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
- console.log(` ${green}✓${reset} Installed ${agentResult.written.length} Codex agent configs to ~/.codex/agents/`);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projecta-rrr",
3
- "version": "1.23.1",
3
+ "version": "1.23.3",
4
4
  "description": "A meta-prompting, context engineering and spec-driven development system for Claude Code by Projecta.ai",
5
5
  "bin": {
6
6
  "projecta-rrr": "bin/install.js",
@@ -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,7 +114,12 @@ 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
- // Build TOML lines only fields Codex CLI accepts
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}`,
@@ -105,9 +127,14 @@ function generateCodexAgentToml(sourceMarkdown, filename) {
105
127
  `name = "${agentName}"`,
106
128
  `description = "${descriptionField || agentName}"`,
107
129
  `model = "${tierEntry.model}"`,
130
+ `developer_instructions = '''`,
131
+ instructions,
132
+ `'''`,
108
133
  ];
109
134
 
110
- return lines.join('\n') + '\n';
135
+ const toml = lines.join('\n') + '\n';
136
+ assertNoLegacyTopLevelFields(toml, filename);
137
+ return toml;
111
138
  }
112
139
 
113
140
  /**
@@ -122,7 +149,7 @@ function generateCodexAgentToml(sourceMarkdown, filename) {
122
149
  * @param {string} options.targetDir - Absolute path to target directory (e.g. ~/.codex/agents/)
123
150
  * @param {boolean} [options.dryRun=false] - If true, compute paths but skip writing
124
151
  * @param {boolean} [options.verbose=false] - If true, log each file written
125
- * @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}> }}
126
153
  */
127
154
  function installCodexAgents(options) {
128
155
  const {
@@ -134,6 +161,7 @@ function installCodexAgents(options) {
134
161
 
135
162
  const written = [];
136
163
  const skipped = [];
164
+ const removed = [];
137
165
  const errors = [];
138
166
 
139
167
  // Ensure target directory exists (no-op if already exists)
@@ -181,7 +209,26 @@ function installCodexAgents(options) {
181
209
  }
182
210
  }
183
211
 
184
- return { written, skipped, errors };
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 };
185
232
  }
186
233
 
187
234
  module.exports = {