revise-and-resubmit-cc 2.1.13 → 2.1.14

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/bin/rnr-tools.js +91 -55
  2. package/package.json +2 -2
package/bin/rnr-tools.js CHANGED
@@ -167,40 +167,76 @@ if (command === 'execute-tasks') {
167
167
  const classifications = JSON.parse(fs.readFileSync(classificationPath, 'utf8'));
168
168
 
169
169
  // Helper function to safely escape quotes without cmd.exe string mangling
170
- const executeAgent = (taskString) => {
170
+ const executeAgentAsync = (taskString, logLabel) => {
171
171
  const escapedPrompt = taskString.replace(/\n/g, ' ');
172
- try {
173
- // Using stdio: 'pipe' to suppress loud output from clogging the main orchestrator's context.
174
- const result = spawnSync('npx', ['@anthropic-ai/claude-code', '--dangerously-skip-permissions', '-p', escapedPrompt], { stdio: 'pipe' });
175
- if (result.status !== 0) {
176
- console.error(`Error executing subagent: ${result.stderr ? result.stderr.toString() : 'Unknown spawn error'}`);
177
- return false;
172
+ return new Promise((resolve) => {
173
+ try {
174
+ // Using stdio: 'pipe' to suppress loud output from clogging the main orchestrator's context.
175
+ const child = require('cross-spawn')('npx', ['@anthropic-ai/claude-code', '--dangerously-skip-permissions', '-p', escapedPrompt], { stdio: 'pipe' });
176
+
177
+ let stderrData = '';
178
+ if (child.stderr) {
179
+ child.stderr.on('data', (data) => {
180
+ stderrData += data.toString();
181
+ });
182
+ }
183
+
184
+ child.on('close', (code) => {
185
+ if (code !== 0) {
186
+ console.error(`❌ Error executing subagent for ${logLabel}: ${stderrData || 'Unknown spawn error'}`);
187
+ resolve(false);
188
+ } else {
189
+ resolve(true);
190
+ }
191
+ });
192
+
193
+ child.on('error', (error) => {
194
+ console.error(`❌ Error executing subagent for ${logLabel}: ${error.message}`);
195
+ resolve(false);
196
+ });
197
+ } catch (error) {
198
+ console.error(`❌ Error executing subagent for ${logLabel}: ${error.message}`);
199
+ resolve(false);
178
200
  }
179
- return true;
180
- } catch (error) {
181
- console.error(`Error executing subagent: ${error.message}`);
182
- return false;
183
- }
201
+ });
184
202
  };
185
203
 
204
+ async function processWithConcurrency(items, concurrency, processor) {
205
+ const results = [];
206
+ const executing = new Set();
207
+ for (const item of items) {
208
+ const p = processor(item).then(res => {
209
+ executing.delete(p);
210
+ return res;
211
+ });
212
+ executing.add(p);
213
+ results.push(p);
214
+ if (executing.size >= concurrency) {
215
+ await Promise.race(executing);
216
+ }
217
+ }
218
+ return Promise.all(results);
219
+ }
220
+
186
221
  console.log(`\n### Executing Tasks via Node Orchestrator ###\n`);
187
222
  const pidPath = path.join(process.cwd(), '.rnr', 'orchestrator.pid');
188
223
  if (fs.existsSync(path.dirname(pidPath))) {
189
224
  fs.writeFileSync(pidPath, process.pid.toString());
190
225
  }
191
226
 
192
- // Wave 1: Isolated
193
- if (classifications.isolated && classifications.isolated.length > 0) {
194
- console.log(`\n#### Wave 1: Executing Isolated Comments ####\n`);
195
- classifications.isolated.forEach(id => {
196
- const resolvedPath = path.join(process.cwd(), 'data', 'resolved', `COMMENT_${id}_RESOLVED.md`);
197
- if (fs.existsSync(resolvedPath)) {
198
- console.log(`✅ COMMENT_${id} already resolved. Skipping.`);
199
- return;
200
- }
201
-
202
- console.log(`⏳ Spawning subagent for COMMENT_${id}...`);
203
- const taskStr = `Task(
227
+ (async () => {
228
+ // Wave 1: Isolated
229
+ if (classifications.isolated && classifications.isolated.length > 0) {
230
+ console.log(`\n#### Wave 1: Executing Isolated Comments ####\n`);
231
+ await processWithConcurrency(classifications.isolated, 8, async (id) => {
232
+ const resolvedPath = path.join(process.cwd(), 'data', 'resolved', `COMMENT_${id}_RESOLVED.md`);
233
+ if (fs.existsSync(resolvedPath)) {
234
+ console.log(`✅ COMMENT_${id} already resolved. Skipping.`);
235
+ return true;
236
+ }
237
+
238
+ console.log(`⏳ Spawning subagent for COMMENT_${id}...`);
239
+ const taskStr = `Task(
204
240
  subagent_type=\`rnr-processor-isolated\`,
205
241
  model=\`claude-3-7-sonnet-20250219\`,
206
242
  prompt=\`
@@ -229,30 +265,30 @@ if (command === 'execute-tasks') {
229
265
  </rules>
230
266
  \`
231
267
  )`;
232
- const success = executeAgent(taskStr);
233
- if (success) {
234
- console.log(`✅ Processed COMMENT_${id} successfully.`);
235
- } else {
236
- console.log(`❌ Failed to process COMMENT_${id}.`);
237
- }
238
- });
239
- }
240
-
241
- // Wave 2: Interlaced Groups
242
- if (classifications.interlaced && classifications.interlaced.length > 0) {
243
- console.log(`\n#### Wave 2: Executing Interlaced Groups ####\n`);
244
- classifications.interlaced.forEach((group, index) => {
245
- // Check if all are resolved
246
- const allResolved = group.every(id => fs.existsSync(path.join(process.cwd(), 'data', 'resolved', `COMMENT_${id}_RESOLVED.md`)));
247
- if (allResolved) {
248
- console.log(`✅ Group ${index} already resolved. Skipping.`);
249
- return;
250
- }
251
-
252
- const filesList = group.map(id => `- data/extracted/COMMENT_${id}.md`).join('\\n ');
253
- console.log(`⏳ Spawning subagent for Group ${index} [${group.join(', ')}]...`);
268
+ const success = await executeAgentAsync(taskStr, `COMMENT_${id}`);
269
+ if (success) {
270
+ console.log(`✅ Processed COMMENT_${id} successfully.`);
271
+ }
272
+ return success;
273
+ });
274
+ }
254
275
 
255
- const taskStr = `Task(
276
+ // Wave 2: Interlaced Groups
277
+ if (classifications.interlaced && classifications.interlaced.length > 0) {
278
+ console.log(`\n#### Wave 2: Executing Interlaced Groups ####\n`);
279
+ await processWithConcurrency(classifications.interlaced, 8, async (group) => {
280
+ const groupStr = `Group [${group.join(', ')}]`;
281
+ // Check if all are resolved
282
+ const allResolved = group.every(id => fs.existsSync(path.join(process.cwd(), 'data', 'resolved', `COMMENT_${id}_RESOLVED.md`)));
283
+ if (allResolved) {
284
+ console.log(`✅ ${groupStr} already resolved. Skipping.`);
285
+ return true;
286
+ }
287
+
288
+ const filesList = group.map(id => `- data/extracted/COMMENT_${id}.md`).join('\\n ');
289
+ console.log(`⏳ Spawning subagent for ${groupStr}...`);
290
+
291
+ const taskStr = `Task(
256
292
  subagent_type=\`rnr-processor-interlaced\`,
257
293
  model=\`claude-3-7-sonnet-20250219\`,
258
294
  prompt=\`
@@ -281,14 +317,14 @@ if (command === 'execute-tasks') {
281
317
  </rules>
282
318
  \`
283
319
  )`;
284
- const success = executeAgent(taskStr);
285
- if (success) {
286
- console.log(`✅ Processed Group ${index} successfully.`);
287
- } else {
288
- console.log(`❌ Failed to process Group ${index}.`);
289
- }
290
- });
291
- }
320
+ const success = await executeAgentAsync(taskStr, groupStr);
321
+ if (success) {
322
+ console.log(`✅ Processed ${groupStr} successfully.`);
323
+ }
324
+ return success;
325
+ });
326
+ }
327
+ })();
292
328
  }
293
329
 
294
330
  if (command === 'health-check') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "revise-and-resubmit-cc",
3
- "version": "2.1.13",
3
+ "version": "2.1.14",
4
4
  "description": "An automated, agent-based academic revision ecosystem powered by Claude Code.",
5
5
  "main": "bin/install.js",
6
6
  "bin": {
@@ -30,4 +30,4 @@
30
30
  "dependencies": {
31
31
  "cross-spawn": "^7.0.6"
32
32
  }
33
- }
33
+ }