@jabworks/condux 0.13.2 → 0.13.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jabworks/condux",
3
- "version": "0.13.2",
3
+ "version": "0.13.3",
4
4
  "description": "OpenCode plugin for the condux workflow toolkit — bundles the condux skills and injects the specialist agents (coder, explorer, planner, researcher) plus an opt-in plan-review listener.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -172,6 +172,24 @@ function gitRoot() {
172
172
  }
173
173
  }
174
174
 
175
+ // Write atomically. A plain writeFileSync is open(O_CREAT|O_TRUNC) then write()
176
+ // — two syscalls — so any reader polling for the file can win the gap and see a
177
+ // created, still-empty file. Writing a sibling temp and rename()-ing it into
178
+ // place is atomic on POSIX: readers see either the old file or the complete new
179
+ // one, never a truncated one. Same directory so the rename never crosses a
180
+ // filesystem. If rename is unavailable (some Windows AV/lock cases), fall back
181
+ // to the direct write rather than losing the feedback.
182
+ function writeFileAtomic(file, data) {
183
+ const tmp = file + '.' + process.pid + '.tmp';
184
+ try {
185
+ fs.writeFileSync(tmp, data);
186
+ fs.renameSync(tmp, file);
187
+ } catch (e) {
188
+ try { fs.rmSync(tmp, { force: true }); } catch (e2) { /* nothing to clean */ }
189
+ fs.writeFileSync(file, data);
190
+ }
191
+ }
192
+
175
193
  function feedbackMarkdown(decision, thread) {
176
194
  const lines = ['# Plan Review Feedback', '',
177
195
  '- **Decision:** ' + decision,
@@ -262,7 +280,7 @@ function resolveDecision(decision, thread) {
262
280
  // browser tab live-reloads when the agent revises the plan on disk. On a
263
281
  // terminal decision (Approve/Reject) the review is over, so we exit after the
264
282
  // response flushes; on "Request Revisions" we keep serving the next round.
265
- fs.writeFileSync(feedbackFile, md);
283
+ writeFileAtomic(feedbackFile, md);
266
284
  const payload = { decision: decision, feedback: md, feedbackFile: feedbackFile };
267
285
  if (decisionWaiter) {
268
286
  try { decisionWaiter.writeHead(200, { 'Content-Type': 'application/json' }); decisionWaiter.end(JSON.stringify(payload)); } catch (e) { /* client gone */ }
@@ -276,7 +294,7 @@ function resolveDecision(decision, thread) {
276
294
  setTimeout(function () { process.exit(0); }, 400);
277
295
  }
278
296
  } else {
279
- fs.writeFileSync(feedbackFile, md);
297
+ writeFileAtomic(feedbackFile, md);
280
298
  log('\n[PLAN-REVIEW FEEDBACK -> ' + feedbackFile + ']\n' + md + '\n');
281
299
  }
282
300
  }