@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 +11 -12
- package/dist/tui.js +6 -0
- package/package.json +1 -1
- package/plugins/learn-tui.tsx +5 -4
- package/plugins/learn.ts +17 -8
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, (
|
|
653
|
-
|
|
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
|
-
|
|
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
|
+
"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",
|
package/plugins/learn-tui.tsx
CHANGED
|
@@ -875,12 +875,13 @@ export const tui: TuiPlugin = async (api) => {
|
|
|
875
875
|
}
|
|
876
876
|
}
|
|
877
877
|
} catch {}
|
|
878
|
-
//
|
|
879
|
-
//
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
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 {}
|