@bojackduy/opencode-learn 1.2.4 → 1.2.5

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/dist/server.js CHANGED
@@ -649,9 +649,8 @@ function watchAndInject(client, directory, id, sessionID, buildText) {
649
649
  } catch {}
650
650
  };
651
651
  try {
652
- const w = fs.watch(dir, (_e, filename) => {
653
- if (filename === `response-${id}.json`)
654
- fire();
652
+ const w = fs.watch(dir, () => {
653
+ fire();
655
654
  });
656
655
  w.on("error", () => {});
657
656
  activeWatchers.set(id, w);
@@ -901,16 +900,16 @@ Return ONLY JSON: {"inferred":[2],"semanticCorrect":false,"reason":"...","isIDK"
901
900
  slog("classify response written", data.id, inferred.join(","));
902
901
  } catch {}
903
902
  };
903
+ const sweep = () => {
904
+ try {
905
+ for (const f of fs.readdirSync(dir).filter((f) => f.startsWith("classify-") && !f.startsWith("classify-response-"))) {
906
+ processClassify(f);
907
+ }
908
+ } catch {}
909
+ };
910
+ sweep();
904
911
  try {
905
- for (const f of fs.readdirSync(dir).filter((f) => f.startsWith("classify-") && !f.startsWith("classify-response-"))) {
906
- processClassify(f);
907
- }
908
- } catch {}
909
- try {
910
- const w = fs.watch(dir, (_e, filename) => {
911
- if (filename)
912
- processClassify(filename);
913
- });
912
+ const w = fs.watch(dir, () => sweep());
914
913
  w.on("error", () => {});
915
914
  } catch {}
916
915
  }
package/dist/tui.js CHANGED
@@ -2797,6 +2797,9 @@ var tui = async (api) => {
2797
2797
  }
2798
2798
  }
2799
2799
  } catch {}
2800
+ try {
2801
+ fs.unlinkSync(full);
2802
+ } catch {}
2800
2803
  api.ui.dialog.clear();
2801
2804
  currentBySession.delete(curSid);
2802
2805
  setTimeout(processPending, 150);
@@ -2856,6 +2859,9 @@ var tui = async (api) => {
2856
2859
  } catch {}
2857
2860
  }
2858
2861
  } catch {}
2862
+ try {
2863
+ fs.unlinkSync(full);
2864
+ } catch {}
2859
2865
  api.ui.dialog.clear();
2860
2866
  currentBySession.delete(curSid);
2861
2867
  setTimeout(processPending, 150);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@bojackduy/opencode-learn",
4
- "version": "1.2.4",
4
+ "version": "1.2.5",
5
5
  "description": "Pi learn system for OpenCode — Socratic teaching, graded quiz, Obsidian md_log, and visual makers. Port of amosblomqvist/learn (video: How I Use AI to Learn Things) to OpenCode.",
6
6
  "type": "module",
7
7
  "license": "AGPL-3.0-or-later",
@@ -875,12 +875,13 @@ export const tui: TuiPlugin = async (api) => {
875
875
  }
876
876
  }
877
877
  } catch {}
878
- // Server owns lifecycle: it unlinks the pending file after successful inject.
879
- // Do NOT delete pending here deleting it destroys recovery context if the server hasn't consumed the response yet.
878
+ // TUI owns dialog lifecycle always delete pending + close immediately so Enter never appears to "do nothing",
879
+ // regardless of whether another (possibly stale/old-code) opencode process is also running.
880
+ try { fs.unlinkSync(full) } catch {}
880
881
  api.ui.dialog.clear()
881
882
  currentBySession.delete(curSid)
882
883
  setTimeout(processPending, 150)
883
- // Watchdog: if the response is still unconsumed after 8s, re-touch to retrigger the server watch + log loudly.
884
+ // Watchdog: if the response is still unconsumed (server didn't fire) after 8s, re-touch to retrigger the watch + log loudly.
884
885
  // Safe: server claims via atomic rename, so a rewrite can never cause double-inject.
885
886
  setTimeout(() => {
886
887
  try {
@@ -910,7 +911,7 @@ export const tui: TuiPlugin = async (api) => {
910
911
  } catch {}
911
912
  }
912
913
  } catch {}
913
- // Server owns lifecycle: it unlinks the pending file after consuming the cancelled response.
914
+ try { fs.unlinkSync(full) } catch {}
914
915
  api.ui.dialog.clear()
915
916
  currentBySession.delete(curSid)
916
917
  setTimeout(processPending, 150)
package/plugins/learn.ts CHANGED
@@ -554,7 +554,7 @@ function watchAndInject(client: any, directory: string, id: string, sessionID: s
554
554
  closeWatcher()
555
555
  return
556
556
  }
557
- // Success: server owns lifecycle — consume claim + pending (TUI no longer deletes pending)
557
+ // Success: consume claim + defensively try pending too (TUI is the primary owner of pending deletion)
558
558
  try { fs.unlinkSync(claimPath) } catch {}
559
559
  for (const p of pendingCandidates) try { fs.unlinkSync(p) } catch {}
560
560
  closeWatcher()
@@ -564,7 +564,10 @@ function watchAndInject(client: any, directory: string, id: string, sessionID: s
564
564
  } catch {}
565
565
  }
566
566
  try {
567
- const w = fs.watch(dir, (_e, filename) => { if (filename === `response-${id}.json`) void fire() })
567
+ // Fire on ANY directory event, not just exact filename match atomic tmp+rename writes can report
568
+ // a different filename (or no filename) on some fs.watch backends (notably macOS FSEvents). The claim
569
+ // rename inside fire() makes this safe to call spuriously: if respPath doesn't exist yet, it just no-ops.
570
+ const w = fs.watch(dir, () => { void fire() })
568
571
  w.on("error", () => {})
569
572
  activeWatchers.set(id, w)
570
573
  } catch {}
@@ -803,14 +806,20 @@ Return ONLY JSON: {"inferred":[2],"semanticCorrect":false,"reason":"...","isIDK"
803
806
  const out = { id: data.id, inferredIndices: inferred, inferredValues, semanticCorrect, reason, isIDK, classifySessionID: (llmRes as any)?.sessionID, note: data.note, at: Date.now() }
804
807
  try { fs.writeFileSync(respPath, JSON.stringify(out), "utf8"); slog("classify response written", data.id, inferred.join(",")) } catch {}
805
808
  }
809
+ const sweep = () => {
810
+ try {
811
+ for (const f of fs.readdirSync(dir).filter(f => f.startsWith("classify-") && !f.startsWith("classify-response-"))) {
812
+ void processClassify(f)
813
+ }
814
+ } catch {}
815
+ }
806
816
  // Initial sweep
817
+ sweep()
807
818
  try {
808
- for (const f of fs.readdirSync(dir).filter(f => f.startsWith("classify-") && !f.startsWith("classify-response-"))) {
809
- void processClassify(f)
810
- }
811
- } catch {}
812
- try {
813
- const w = fs.watch(dir, (_e, filename) => { if (filename) void processClassify(filename) })
819
+ // Re-scan on ANY directory event rather than trusting the reported filename — atomic tmp+rename
820
+ // writes (used by the TUI) can report a different filename (or none) on some fs.watch backends
821
+ // (notably macOS FSEvents), which would otherwise silently drop classify requests.
822
+ const w = fs.watch(dir, () => sweep())
814
823
  w.on("error", () => {})
815
824
  // Keep watcher alive; store to avoid GC? No need.
816
825
  } catch {}