eklavya 1.73.0 → 1.75.0

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/README.md CHANGED
@@ -136,8 +136,9 @@ it cannot parse gets a 400 without stopping the server
136
136
  Its Learning page shows your streak and its calendar under the summary tiles; a
137
137
  **To be corrected** tile there and on the Artifacts page counts missed answers still
138
138
  waiting for a correction; correcting one closes its dialog after three seconds unless you choose **Stay open**. The
139
- review queue gives each project a command to copy that starts Claude Code on its due
140
- concepts, in the project's folder when that folder is available.
139
+ review page lists due concepts as cards; opening one shows the steps to quiz that single concept in
140
+ Claude Code or Claude desktop, in the project's folder when that folder is available. Every Copy button
141
+ answers with a toast.
141
142
  Its feature tips show the archer companion and use Driver.js, bundled in `dist/` at build and served by the
142
143
  dashboard itself at `/vendor/driver-hints.js` and `/vendor/driver-hints.css`, so
143
144
  the page still requests nothing from another host. [Configuration](https://eklavya-run.web.app/docs/configuration/) lists
@@ -147,8 +148,11 @@ covers retrieval, processing, providers, privacy and sync.
147
148
  State is local under `~/.eklavya/`, including `knowledge.db` (SQLite, WAL).
148
149
  `EKLAVYA_HOME` and `EKLAVYA_DB` override those paths. Questions and memory have
149
150
  independent switches. Set `providers.observer` to `null` for local memory
150
- summaries and no prompt feedback. Each feedback item shows the project, host,
151
- session and time it came from, read from memory when you open it;
151
+ summaries and no prompt feedback. Each feedback item leads with a verdict that
152
+ says, in words and never a score, whether the prompt left each area clear, left
153
+ it out or did not need it (an older item says "Not flagged" for an area it
154
+ did not name), and shows the project, host, session and time it came
155
+ from, read from memory when you open it;
152
156
  see [your data](https://eklavya-run.web.app/docs/your-data/). Anonymous daily
153
157
  usage counts are on by default and never include paths, names or text;
154
158
  [usage analytics](https://eklavya-run.web.app/docs/usage-analytics/) lists every
@@ -714,41 +714,248 @@
714
714
  .cmd__copy:focus-visible { outline:none; box-shadow:var(--ring); }
715
715
  .cmd__note { margin:var(--space-2) 0 0; color:var(--dim); font-size:13px; }
716
716
  .starts > .cmd__note { margin-top:var(--space-3); }
717
+
718
+ /* ---------- Core: toast and dialog (use these, never a page-local popup) ---------- */
719
+ .toasts { position:fixed; right:var(--space-4); bottom:var(--space-4); z-index:90; display:flex; flex-direction:column;
720
+ gap:var(--space-3); width:min(400px, calc(100vw - 32px)); pointer-events:none; }
721
+ .toast { pointer-events:auto; display:grid; grid-template-columns:auto 1fr auto; gap:var(--space-3); align-items:start;
722
+ padding:var(--space-3) var(--space-2) var(--space-3) var(--space-3); background:var(--panel);
723
+ border:1px solid var(--line-2); border-left:4px solid var(--spot); animation:toast-in var(--duration-slow) var(--ease); }
724
+ .toast[data-k="caution"] { border-left-color:var(--warning); }
725
+ .toast[data-k="alert"] { border-left-color:var(--error); }
726
+ .toast b { display:block; font-size:14px; }
727
+ .toast p { margin:2px 0 0; color:var(--dim); font-size:13px; }
728
+ .toast__act { margin-top:var(--space-2); border:0; background:none; padding:0; color:var(--spot); font:600 12.5px var(--font-mono);
729
+ text-decoration:underline; text-underline-offset:3px; cursor:pointer; }
730
+ .toast__x { width:28px; height:28px; border:0; background:none; color:var(--dim); font-size:18px; line-height:1; cursor:pointer; }
731
+ .toast__x:hover { color:var(--ink); }
732
+ .toast__x:focus-visible, .toast__act:focus-visible { outline:none; box-shadow:var(--ring); }
733
+ .toast.is-out { opacity:0; transform:translateY(8px); transition:opacity var(--duration-base) var(--ease), transform var(--duration-base) var(--ease); }
734
+ @keyframes toast-in { from { opacity:0; transform:translateY(10px); } }
735
+ dialog.dlg { width:min(460px, calc(100vw - 32px)); margin:auto; padding:0; border:1px solid var(--line-2); border-radius:0;
736
+ background:var(--panel); color:var(--ink); }
737
+ dialog.dlg::backdrop { background:color-mix(in srgb, var(--ink) 40%, transparent); backdrop-filter:blur(6px); }
738
+ .dlg__b { display:grid; grid-template-columns:auto 1fr; gap:var(--space-4); padding:var(--space-5) var(--space-5) var(--space-2); }
739
+ .dlg h2 { margin:0 0 4px; font:900 18px var(--font-disp); }
740
+ .dlg p { margin:0; color:var(--dim); font-size:13.5px; }
741
+ .dlg textarea { grid-column:1 / -1; width:100%; min-height:80px; background:var(--mass); border:1px solid var(--line-2); color:var(--ink); padding:var(--space-3); font:inherit; }
742
+ .dlg textarea:focus-visible { outline:none; box-shadow:var(--ring); }
743
+ .dlg__f { display:flex; justify-content:flex-end; gap:var(--space-3); padding:var(--space-3) var(--space-5) var(--space-5); }
744
+ .btn--spot { background:var(--spot); border-color:var(--spot); color:var(--spot-ink); }
745
+ .btn--danger { background:var(--error); border-color:var(--error); color:var(--bg); }
746
+ @media (max-width:760px) { .toasts { right:0; left:0; bottom:0; width:100%; padding:0 var(--space-4) var(--space-4); } }
747
+ @media (prefers-reduced-motion:reduce) { .toast { animation:none; } .toast.is-out { transition:none; } }
748
+
749
+ /* ---------- Review: one concept at a time ---------- */
750
+ .rv-intro { display:grid; grid-template-columns:minmax(0,1fr) minmax(0,2fr); gap:var(--space-6); }
751
+ .rv-intro h2 { margin:0 0 var(--space-2); font:800 15px var(--font-disp); }
752
+ .rv-intro p { margin:0; color:var(--dim); font-size:13.5px; }
753
+ .rv-steps { display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:var(--space-4); margin:0; padding:0; list-style:none; counter-reset:rv; }
754
+ .rv-steps li { counter-increment:rv; position:relative; padding-top:28px; color:var(--dim); font-size:13px; }
755
+ .rv-steps li::before { content:counter(rv); position:absolute; top:0; left:0; width:20px; height:20px; border:1px solid var(--spot);
756
+ color:var(--spot); font:600 11px/18px var(--font-mono); text-align:center; }
757
+ .rv-steps b { display:block; color:var(--ink); font-weight:600; }
758
+ .rv-hide { margin-top:var(--space-3); border:0; background:none; padding:0; color:var(--faint); font:11px var(--font-mono); text-decoration:underline; text-underline-offset:3px; cursor:pointer; }
759
+ .rv-hide:focus-visible { outline:none; box-shadow:var(--ring); }
760
+ .rv-list { display:grid; gap:var(--space-3); }
761
+ .rv-first { position:relative; margin-left:132px; min-height:150px; display:flex; align-items:center; }
762
+ .rv-first > .rv-card { flex:1; }
763
+ .rv-guide { position:absolute; right:calc(100% + 6px); top:50%; transform:translateY(-50%); width:128px; display:flex; flex-direction:column; align-items:center; pointer-events:none; }
764
+ .rv-tag { font:600 10.5px var(--font-mono); letter-spacing:var(--tracking-caps); text-transform:uppercase; background:var(--spot); color:var(--spot-ink); padding:2px 8px; white-space:nowrap; }
765
+ .rv-card { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:4px var(--space-4); align-items:center; width:100%; text-align:left;
766
+ padding:var(--space-4) var(--space-5); background:var(--panel); border:1px solid var(--line); border-left:3px solid var(--line-2);
767
+ color:inherit; font:inherit; cursor:pointer; transition:border-color var(--duration-base) var(--ease), background var(--duration-base) var(--ease); }
768
+ .rv-card:hover { border-left-color:var(--spot); background:var(--mass); }
769
+ .rv-card:focus-visible { outline:none; box-shadow:var(--ring); }
770
+ .rv-first .rv-card { border-color:var(--spot); background:var(--spot-soft); }
771
+ .rv-card__n { font:700 16px var(--font-disp); }
772
+ .rv-card__m { font:12px var(--font-mono); color:var(--dim); }
773
+ .rv-card__m .warn { color:var(--warning); }
774
+ .rv-card__go { grid-row:1 / 3; grid-column:2; color:var(--spot); font:600 12px var(--font-mono); white-space:nowrap; }
775
+ .rv-more > summary { cursor:pointer; color:var(--dim); font-size:13px; padding:var(--space-2) 0; }
776
+ .rv-more > summary:hover { color:var(--spot); }
777
+ .rv-more > summary:focus-visible { outline:none; box-shadow:var(--ring); }
778
+ .rv-more[open] > summary { margin-bottom:var(--space-2); }
779
+ .rv-tips { display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:var(--space-3); }
780
+ .rv-tips div { border:1px solid var(--line); padding:var(--space-3) var(--space-4); color:var(--dim); font-size:13px; }
781
+ .rv-tips b { display:block; color:var(--ink); font-size:13.5px; margin-bottom:2px; }
782
+ dialog.sheet { position:fixed; inset:0 0 0 auto; margin:0; width:min(520px, 100vw); height:100vh; max-height:none; padding:0;
783
+ border:0; border-left:1px solid var(--line-2); border-radius:0; background:var(--bg); color:var(--ink); }
784
+ dialog.sheet::backdrop { background:color-mix(in srgb, var(--ink) 40%, transparent); }
785
+ dialog.sheet[open] { animation:sheet-in var(--duration-slow) var(--ease); }
786
+ @keyframes sheet-in { from { transform:translateX(24px); opacity:0; } }
787
+ .sheet__h { display:flex; justify-content:space-between; gap:var(--space-3); padding:var(--space-5) var(--space-5) var(--space-3); border-bottom:1px solid var(--line); }
788
+ .sheet__h h2 { margin:0; font:900 22px/1.15 var(--font-disp); }
789
+ .sheet__h p { margin:3px 0 0; color:var(--dim); font:12px var(--font-mono); }
790
+ .sheet__x { width:32px; height:32px; border:1px solid var(--line-2); background:none; color:var(--ink); font-size:18px; line-height:1; cursor:pointer; flex:none; }
791
+ .sheet__x:focus-visible { outline:none; box-shadow:var(--ring); }
792
+ .sheet__b { padding:var(--space-4) var(--space-5) var(--space-6); }
793
+ .sheet__seg { display:grid; grid-template-columns:1fr 1fr; border:1px solid var(--line-2); margin-bottom:var(--space-4); }
794
+ .sheet__seg button { border:0; background:none; color:inherit; font:inherit; text-align:left; padding:10px 12px; cursor:pointer; }
795
+ .sheet__seg button + button { border-left:1px solid var(--line-2); }
796
+ .sheet__seg button[aria-pressed="true"] { background:var(--spot-soft); box-shadow:inset 0 -2px 0 var(--spot); }
797
+ .sheet__seg button:focus-visible { outline:none; box-shadow:var(--ring); }
798
+ .sheet__seg b { display:block; font-size:13.5px; }
799
+ .sheet__seg small { font:11px var(--font-mono); color:var(--faint); }
800
+ .sheet__how { list-style:none; margin:0; padding:0; counter-reset:sh; }
801
+ .sheet__how > li { counter-increment:sh; position:relative; padding:0 0 var(--space-4) 38px; }
802
+ .sheet__how > li::before { content:counter(sh); position:absolute; left:0; top:0; width:24px; height:24px; border:1px solid var(--spot); color:var(--spot); font:600 12px/22px var(--font-mono); text-align:center; }
803
+ .sheet__how h3 { margin:0 0 2px; font-size:14px; }
804
+ .sheet__how p { margin:0 0 var(--space-2); color:var(--dim); font-size:13px; }
805
+ .sheet__how .cmd { margin-top:0; }
806
+ .sheet__why { margin-top:var(--space-4); }
807
+ .sheet__why h3 { margin:0 0 var(--space-2); font:500 10.5px var(--font-mono); letter-spacing:var(--tracking-caps); text-transform:uppercase; color:var(--faint); }
808
+ @media (max-width:760px) {
809
+ .rv-intro, .rv-steps, .rv-tips { grid-template-columns:1fr; }
810
+ .rv-first { margin-left:0; margin-top:60px; }
811
+ .rv-guide { right:auto; left:0; top:-56px; transform:none; flex-direction:row; width:auto; gap:6px; }
812
+ .rv-card { grid-template-columns:1fr; } .rv-card__go { grid-row:auto; grid-column:1; }
813
+ dialog.sheet { inset:auto 0 0 0; width:100vw; height:88vh; border-left:0; border-top:1px solid var(--line-2); }
814
+ }
717
815
  /* ---------- Feedback: one reviewed prompt at a time ---------- */
718
816
  .fb__rule { border:1px solid var(--line-2); border-left:2px solid var(--spot); background:var(--panel);
719
817
  padding:var(--space-4) var(--space-5); }
720
818
  .fb__rule p { margin:0; color:var(--ink); font-size:14px; max-width:72ch; }
721
- .fb__rule--near { margin:0 0 var(--space-4); }
722
- .fb { display:grid; gap:var(--space-6); }
723
- .fb__grid { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:var(--space-5); }
819
+ /* The item: a story column and a decision rail that stays in view; a fixed bar replaces the rail's button at 900px and below. */
820
+ .fb { display:grid; grid-template-columns:minmax(0, 1fr) 300px; gap:var(--space-6); align-items:start; }
821
+ .fb__story { display:grid; gap:var(--space-6); min-width:0; }
822
+ .fb__rail { position:sticky; top:var(--space-6); display:grid; gap:var(--space-4); min-width:0; }
823
+ .fb__rail.is-tall { position:static; }
824
+ .fb__card { padding:var(--space-5); }
724
825
  .fb__h { font-family:var(--font-mono); font-size:10.5px; letter-spacing:var(--tracking-caps); text-transform:uppercase;
725
826
  color:var(--faint); font-weight:var(--weight-semibold); margin:0 0 var(--space-2); }
726
- .fb__quote { margin:0; padding-left:var(--space-4); border-left:2px solid var(--spot); color:var(--ink);
727
- white-space:pre-wrap; overflow-wrap:anywhere; font-size:14px; }
827
+ .fb__cap { margin:var(--space-1) 0 0; color:var(--faint); font-size:12.5px; }
828
+ .fb__note { margin:0; color:var(--dim); font-size:13.5px; overflow-wrap:anywhere; }
829
+ /* ① The verdict */
830
+ .fb__verdict { display:flex; gap:var(--space-5); align-items:flex-start; }
831
+ .fb__verdict > eklavya-mascot { flex:none; }
832
+ .fb__vb { flex:1; min-width:0; }
833
+ .fb__vh { font-family:var(--font-disp); font-weight:800; font-size:var(--text-xl); line-height:1.15; letter-spacing:-0.02em;
834
+ margin:0; color:var(--ink); overflow-wrap:anywhere; }
835
+ .fb__vh--old { font-size:var(--text-md); }
836
+ .fb__vs { margin:var(--space-2) 0 0; color:var(--dim); font-size:13.5px; }
837
+ .fb__cover { list-style:none; margin:var(--space-5) 0 0; padding:0; display:grid; grid-template-columns:repeat(4, minmax(0, 1fr));
838
+ border:1px solid var(--line-2); background:var(--bg); }
839
+ .fb__cell { display:grid; grid-template-columns:auto minmax(0, 1fr); column-gap:var(--space-2); align-items:center;
840
+ padding:var(--space-3) var(--space-4); color:var(--faint); }
841
+ .fb__cell + .fb__cell { border-left:1px solid var(--line-2); }
842
+ .fb__cell svg { width:16px; height:16px; grid-row:span 2; }
843
+ .fb__cn { color:var(--ink); font-size:13px; font-weight:var(--weight-semibold); }
844
+ .fb__cs { font-family:var(--font-mono); font-size:11px; letter-spacing:0.04em; }
845
+ .fb__cell.is-miss { color:var(--warning); box-shadow:inset 0 -2px 0 var(--warning); }
846
+ .fb__cell.is-ok { color:var(--spot); box-shadow:inset 0 -2px 0 var(--spot); }
847
+ /* ② The two prompts, one block */
848
+ .fb__grid { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); border:1px solid var(--line-2); }
849
+ .fb__grid > section { padding:var(--space-5); min-width:0; }
850
+ .fb__mine { background:var(--panel); border-right:1px solid var(--line-2); }
851
+ .fb__better { background:var(--bg); }
852
+ .fb__ch { display:flex; align-items:center; justify-content:space-between; gap:var(--space-3); min-height:32px; margin-bottom:var(--space-2); }
853
+ .fb__ch .fb__h { margin:0; }
854
+ .fb__better .fb__h { color:var(--spot); }
855
+ .fb__quote { margin:0; color:var(--ink); white-space:pre-wrap; overflow-wrap:anywhere; font-size:14px; }
856
+ .fb__mine .fb__quote { color:var(--dim); }
728
857
  .cmd--block { margin-top:0; align-items:flex-start; }
729
858
  .cmd--block code { white-space:pre-wrap; overflow-wrap:anywhere; overflow-x:visible; }
730
- .fb__ds, .fb__tips { list-style:none; margin:0; padding:0; display:grid; gap:var(--space-3); }
731
- .fb__ds li { display:grid; grid-template-columns:120px minmax(0, 1fr); gap:var(--space-3); align-items:baseline; }
732
- .fb__dn { font-family:var(--font-mono); font-size:12px; color:var(--dim); }
733
- .fb__ctx { display:grid; grid-template-columns:auto minmax(0, 1fr); gap:var(--space-1) var(--space-4); margin:0; font-size:13px; }
734
- .fb__ctx dt { font-family:var(--font-mono); font-size:12px; color:var(--dim); }
735
- .fb__ctx dd { margin:0; color:var(--ink); overflow-wrap:anywhere; }
736
- .fb__note { margin:0; color:var(--dim); font-size:13.5px; overflow-wrap:anywhere; }
737
- .fb__ev { display:block; color:var(--faint); font-size:12.5px; }
738
- .fb__tips li { color:var(--dim); font-size:13.5px; padding-left:var(--space-4); position:relative; }
739
- .fb__tips li::before { content:'–'; position:absolute; left:0; color:var(--faint); }
740
- .fb__actions { display:flex; gap:var(--space-3); flex-wrap:wrap; align-items:center; }
859
+ .fb__better .cmd--block code { background:none; border:0; padding:0; color:var(--ink); font-size:13px; }
860
+ .fb__ph { background:var(--spot-soft); color:inherit; padding:0 2px; }
861
+ .fb__clamp { position:relative; overflow:hidden; transition:max-height var(--duration-base) var(--ease); }
862
+ .fb__clamp.is-clamped { max-height:15.5em; }
863
+ .fb__clamp.is-clamped::after { content:''; position:absolute; inset:auto 0 0; height:4em; pointer-events:none;
864
+ background:linear-gradient(transparent, var(--bg)); }
865
+ .fb__mine .fb__clamp.is-clamped::after { background:linear-gradient(transparent, var(--panel)); }
866
+ .fb__copy, .fb__more, .fb__del { font:inherit; font-size:13px; cursor:pointer; background:none; color:var(--dim);
867
+ border:1px solid transparent; display:inline-flex; align-items:center; gap:var(--space-2);
868
+ padding:var(--space-1) var(--space-2); }
869
+ .fb__copy svg { width:14px; height:14px; }
870
+ .fb__copy:hover, .fb__more:hover { color:var(--spot); border-color:var(--spot); }
871
+ .fb__more { margin-top:var(--space-2); padding-inline:0; color:var(--spot); }
872
+ .fb__more:hover { border-color:transparent; text-decoration:underline; }
873
+ .fb__copy:focus-visible, .fb__more:focus-visible, .fb__del:focus-visible { outline:none; box-shadow:var(--ring); }
874
+ /* ③ What worked */
875
+ .fb__worked { display:flex; gap:var(--space-4); align-items:flex-start; padding:var(--space-4) var(--space-5);
876
+ background:var(--panel); border:1px solid var(--line-2); border-left:2px solid var(--spot); }
877
+ .fb__worked > eklavya-mascot, .fb__sh > eklavya-mascot, .fb__tip > eklavya-mascot, .fb__dh > eklavya-mascot { flex:none; }
878
+ .fb__worked .fb__h { color:var(--spot); margin-bottom:var(--space-1); }
879
+ .fb__worked p { margin:0; color:var(--ink); font-size:14px; overflow-wrap:anywhere; }
880
+ /* ④ What it left out and ⑤ next time */
881
+ .fb__sec { display:grid; gap:var(--space-4); }
882
+ .fb__sh { display:flex; gap:var(--space-3); align-items:center; }
883
+ .fb__sh h3, .fb__dh h3 { font-family:var(--font-disp); font-weight:800; font-size:var(--text-md); margin:0; color:var(--ink); }
884
+ .fb__ds, .fb__tips { list-style:none; margin:0; padding:0; }
885
+ .fb__ds { border:1px solid var(--line-2); background:var(--panel); }
886
+ .fb__ds li { display:grid; grid-template-columns:44px minmax(0, 1fr); gap:var(--space-4); padding:var(--space-5) var(--space-6); }
887
+ .fb__ds li + li { border-top:1px solid var(--line); }
888
+ .fb__tile { width:44px; height:44px; display:grid; place-items:center; background:var(--warning-soft); color:var(--warning); }
889
+ .fb__tile svg { width:20px; height:20px; }
890
+ .fb__gb { display:grid; gap:var(--space-1); min-width:0; }
891
+ .fb__gt { margin:0; color:var(--ink); font-weight:var(--weight-semibold); font-size:14px; }
892
+ .fb__gt small { font-family:var(--font-mono); font-weight:400; font-size:11px; color:var(--faint); margin-left:var(--space-2); }
893
+ .fb__ev { display:flex; gap:var(--space-3); align-items:baseline; flex-wrap:wrap; margin:var(--space-2) 0 0; font-size:13px; }
894
+ .fb__evl { font-family:var(--font-mono); font-size:10.5px; letter-spacing:var(--tracking-caps); text-transform:uppercase; color:var(--faint); }
895
+ .fb__ev q { background:var(--mass); color:var(--ink); padding:var(--space-1) var(--space-2); overflow-wrap:anywhere; quotes:'“' '”'; }
896
+ .fb__tips { display:grid; gap:var(--space-3); counter-reset:t; }
897
+ .fb__tips li { counter-increment:t; display:grid; grid-template-columns:auto minmax(0, 1fr); gap:var(--space-4); align-items:baseline;
898
+ background:var(--panel); border:1px solid var(--line-2); padding:var(--space-4) var(--space-5);
899
+ color:var(--ink); font-size:14px; overflow-wrap:anywhere; }
900
+ .fb__tips li::before { content:'0' counter(t); font-family:var(--font-disp); font-weight:900; font-size:var(--text-2xl);
901
+ line-height:1; color:var(--spot); }
902
+ @media (min-width:1100px) {
903
+ .fb__tips[data-n="2"] { grid-template-columns:repeat(2, minmax(0, 1fr)); }
904
+ .fb__tips[data-n="3"] { grid-template-columns:repeat(3, minmax(0, 1fr)); }
905
+ .fb__tips[data-n="2"] li, .fb__tips[data-n="3"] li { grid-template-columns:minmax(0, 1fr); align-content:start; }
906
+ }
907
+ /* ⑥ Found a problem */
908
+ .fb__tip { display:flex; gap:var(--space-3); align-items:center; flex-wrap:wrap; padding-top:var(--space-4); border-top:1px solid var(--line-2); }
909
+ .fb__tip p { flex:1 1 260px; margin:0; color:var(--dim); font-size:13px; }
910
+ .fb__tip code { background:var(--mass); padding:1px 6px; font-size:12px; color:var(--ink); }
911
+ /* The rail */
912
+ .fb__ctx { display:grid; gap:var(--space-3); margin:0; font-size:13px; }
913
+ .fb__ctx dt { font-family:var(--font-mono); font-size:10.5px; letter-spacing:var(--tracking-caps); text-transform:uppercase; color:var(--faint); }
914
+ .fb__ctx dd { margin:var(--space-1) 0 0; color:var(--ink); overflow-wrap:anywhere; }
915
+ .fb__ctx .slug { display:block; font-family:var(--font-mono); font-size:11px; color:var(--faint); overflow-wrap:anywhere; }
916
+ .fb__decide { border-color:var(--spot); display:grid; gap:var(--space-3); }
917
+ .fb__dh { display:flex; gap:var(--space-3); align-items:center; }
918
+ .fb__dp { margin:0; color:var(--dim); font-size:13.5px; }
919
+ .fb__actions { display:grid; gap:var(--space-2); }
920
+ .fb__ack { width:100%; min-height:44px; justify-content:center; display:inline-flex; align-items:center; gap:var(--space-2); }
921
+ .fb__ack svg { width:16px; height:16px; }
922
+ .fb__del { color:var(--faint); justify-content:center; min-height:32px; }
923
+ .fb__del:hover { color:var(--error); }
741
924
  .fb__ask { display:inline-flex; gap:var(--space-3); flex-wrap:wrap; align-items:center; color:var(--dim); font-size:13.5px; }
742
925
  .fb__msg { margin:0; color:var(--warning); font-size:13px; }
926
+ .fb__msg:empty { display:none; }
927
+ .fb__foot { display:flex; justify-content:space-between; margin:0; padding-top:var(--space-3); border-top:1px solid var(--line);
928
+ font-family:var(--font-mono); font-size:11px; color:var(--faint); }
929
+ .fb__acked { margin:0; font-family:var(--font-mono); font-size:12px; color:var(--faint); }
930
+ .fb__bar { display:none; }
743
931
  .fb__sts { display:flex; gap:var(--space-2); flex-wrap:wrap; }
744
- .fb__tip h2 { font-family:var(--font-disp); font-size:var(--text-md); font-weight:var(--weight-semibold); margin:0 0 var(--space-2); }
745
- .fb__tip p { margin:0 0 var(--space-4); color:var(--dim); max-width:70ch; }
746
- .fb__tip .fb__quote { margin-bottom:var(--space-4); }
747
932
  .fb__why p { margin:0 0 var(--space-3); color:var(--dim); max-width:70ch; }
933
+ @media (max-width:900px) {
934
+ .fb { grid-template-columns:minmax(0, 1fr); }
935
+ .fb__rail { position:static; }
936
+ .fb__ctx { grid-template-columns:repeat(2, minmax(0, 1fr)); column-gap:var(--space-5); }
937
+ /* The bar holds Acknowledge here; the rail card keeps Delete and the message. */
938
+ .fb__decide .fb__ack { display:none; }
939
+ .fb__bar { display:flex; align-items:center; gap:var(--space-4); position:fixed; inset:auto 0 0; z-index:20;
940
+ background:var(--panel); border-top:1px solid var(--spot); padding:var(--space-3) var(--space-5); }
941
+ .fb__bar p { flex:1; margin:0; color:var(--dim); font-size:13.5px; }
942
+ .fb__bar .fb__ack { width:auto; flex:none; }
943
+ main:has(.fb__bar) { padding-bottom:120px; }
944
+ }
748
945
  @media (max-width:560px) {
946
+ .fb__verdict > eklavya-mascot { display:none; }
749
947
  .fb__grid { grid-template-columns:minmax(0, 1fr); }
750
- .fb__ds li { grid-template-columns:minmax(0, 1fr); gap:var(--space-1); }
751
- .fb__actions > button, .fb__actions a { flex:1 1 100%; min-height:44px; }
948
+ .fb__mine { border-right:0; border-bottom:1px solid var(--line-2); }
949
+ .fb__cover { grid-template-columns:repeat(2, minmax(0, 1fr)); }
950
+ .fb__cell + .fb__cell { border-left:0; }
951
+ .fb__cell:nth-child(even) { border-left:1px solid var(--line-2); }
952
+ .fb__cell:nth-child(n+3) { border-top:1px solid var(--line-2); }
953
+ .fb__ds li { padding:var(--space-4); gap:var(--space-3); }
954
+ .fb__ctx { grid-template-columns:minmax(0, 1fr); }
955
+ .fb__bar p { display:none; }
956
+ .fb__bar .fb__ack { flex:1; width:100%; }
957
+ .fb__copy, .fb__del, .fb__tip .set__btn { min-height:44px; }
958
+ .fb__del { width:100%; }
752
959
  .fb__ask > button { flex:1 1 40%; min-height:44px; }
753
960
  }
754
961
  .starts__more > summary { cursor:pointer; color:var(--dim); font-size:13px; padding:var(--space-2) 0; }
@@ -1036,6 +1243,16 @@
1036
1243
  </main>
1037
1244
  </div>
1038
1245
 
1246
+ <div class="toasts" id="toasts"></div>
1247
+ <dialog class="dlg" id="dlg" aria-labelledby="dlg-h"><form method="dialog">
1248
+ <div class="dlg__b"><span id="dlg-av"></span><div><h2 id="dlg-h"></h2><p id="dlg-p"></p></div><textarea id="dlg-in" hidden></textarea></div>
1249
+ <div class="dlg__f"><button class="btn" value="cancel" id="dlg-c"></button><button class="btn" value="ok" id="dlg-ok"></button></div>
1250
+ </form></dialog>
1251
+ <dialog class="sheet" id="sheet" aria-labelledby="sheet-h">
1252
+ <div class="sheet__h"><div><h2 id="sheet-h"></h2><p id="sheet-s"></p></div><button type="button" class="sheet__x" data-sheet-close aria-label="Close">×</button></div>
1253
+ <div class="sheet__b" id="sheet-b"></div>
1254
+ </dialog>
1255
+
1039
1256
  <dialog class="fix" id="fix" aria-labelledby="fix-q">
1040
1257
  <p class="fix__eyebrow">Correct your answer</p>
1041
1258
  <h2 class="fix__q" id="fix-q"></h2>
@@ -1518,6 +1735,10 @@ const isWindows = () => navigator.userAgent.includes('Windows');
1518
1735
  * to the concept's last project, or to `into` (the selected project). Slugs
1519
1736
  * are kebab-case already; anything else is dropped rather than quoted.
1520
1737
  */
1738
+ /** The shell line that enters a project folder, quoted for this machine's shell. */
1739
+ const cdTo = (path) => isWindows() ? `Set-Location -LiteralPath '${path.replace(/'/g, "''")}'`
1740
+ : `cd '${path.replace(/'/g, "'\\''")}'`;
1741
+
1521
1742
  function startCommand(concepts, into = null) {
1522
1743
  const groups = new Map();
1523
1744
  for (const c of concepts) {
@@ -1531,9 +1752,7 @@ function startCommand(concepts, into = null) {
1531
1752
  const quiz = `claude "/eklavya:quiz ${slugs.join(' ')}"`;
1532
1753
  const p = projectById(id);
1533
1754
  const here = p?.path && p.available;
1534
- const command = !here ? quiz
1535
- : isWindows() ? `Set-Location -LiteralPath '${p.path.replace(/'/g, "''")}'; ${quiz}`
1536
- : `cd '${p.path.replace(/'/g, "'\\''")}' && ${quiz}`;
1755
+ const command = !here ? quiz : `${cdTo(p.path)}${isWindows() ? '; ' : ' && '}${quiz}`;
1537
1756
  return { name: p?.name ?? base(id), count: cs.length, command,
1538
1757
  note: here ? '' : 'Run it inside the project you want these answers counted in.' };
1539
1758
  }).sort((a, b) => b.count - a.count);
@@ -1555,17 +1774,73 @@ function startBlocks(title, cmds) {
1555
1774
  <span class="sr" id="copy-live" aria-live="polite"></span></section>`;
1556
1775
  }
1557
1776
 
1777
+
1778
+ /* ---------- core components: toast and dialog ----------
1779
+ Every page uses these for "something happened" and "are you sure"; none writes its own popup.
1780
+ toast.success|caution|alert(text, { title, id, action: { label, run } }), toast.dismiss(id);
1781
+ ekDialog({ state, title, text, ok, cancel, input, danger, single }) resolves { ok, value }. */
1782
+ const TOAST_FACE = { success: 'success', caution: 'warning', alert: 'error' };
1783
+ const toast = (() => {
1784
+ const show = (kind, text, o = {}) => {
1785
+ const host = $('toasts');
1786
+ const id = o.id ?? `${kind}:${text}`;
1787
+ host.querySelectorAll('.toast').forEach((x) => { if (x.dataset.id === id) x.remove(); });
1788
+ const el = document.createElement('div');
1789
+ el.className = 'toast'; el.dataset.k = kind; el.dataset.id = id;
1790
+ el.setAttribute('role', kind === 'alert' ? 'alert' : 'status');
1791
+ const title = o.title ?? { success: 'Done', caution: 'Careful', alert: 'Something went wrong' }[kind];
1792
+ el.innerHTML = `<eklavya-mascot state="${TOAST_FACE[kind]}" variant="face" size="44"></eklavya-mascot>
1793
+ <div><b>${esc(title)}</b><p>${esc(text)}</p>${o.action ? `<button type="button" class="toast__act">${esc(o.action.label)}</button>` : ''}</div>
1794
+ <button type="button" class="toast__x" aria-label="Dismiss">×</button>`;
1795
+ let timer;
1796
+ const leave = () => { clearTimeout(timer); el.classList.add('is-out'); setTimeout(() => el.remove(), 200); };
1797
+ const arm = () => { clearTimeout(timer); timer = setTimeout(leave, kind === 'alert' ? 8000 : 4000); };
1798
+ el.querySelector('.toast__x').onclick = leave;
1799
+ if (o.action) el.querySelector('.toast__act').onclick = () => { o.action.run(); leave(); };
1800
+ el.addEventListener('mouseenter', () => clearTimeout(timer));
1801
+ el.addEventListener('focusin', () => clearTimeout(timer));
1802
+ el.addEventListener('mouseleave', arm);
1803
+ el.addEventListener('focusout', arm);
1804
+ host.append(el);
1805
+ while (host.children.length > 3) host.firstChild.remove();
1806
+ arm();
1807
+ };
1808
+ return {
1809
+ success: (t, o) => show('success', t, o), caution: (t, o) => show('caution', t, o), alert: (t, o) => show('alert', t, o),
1810
+ dismiss: (id) => $('toasts').querySelectorAll('.toast').forEach((x) => { if (x.dataset.id === id) x.remove(); }),
1811
+ };
1812
+ })();
1813
+
1814
+ function ekDialog({ state = 'info', title, text = '', ok = 'OK', cancel = 'Cancel', input = null, danger = false, single = false }) {
1815
+ const d = $('dlg'), field = $('dlg-in'), yes = $('dlg-ok'), no = $('dlg-c');
1816
+ $('dlg-av').innerHTML = `<eklavya-mascot state="${state}" variant="face" size="56"></eklavya-mascot>`;
1817
+ $('dlg-h').textContent = title; $('dlg-p').textContent = text;
1818
+ field.hidden = !input; field.value = ''; if (input) field.setAttribute('aria-label', input);
1819
+ yes.textContent = ok; yes.className = `btn ${danger ? 'btn--danger' : 'btn--spot'}`;
1820
+ no.textContent = cancel; no.hidden = single;
1821
+ d.returnValue = ''; d.showModal();
1822
+ (danger ? no : input ? field : yes).focus();
1823
+ return new Promise((done) => d.addEventListener('close', () => done({ ok: d.returnValue === 'ok', value: field.value }), { once: true }));
1824
+ }
1825
+
1558
1826
  /** Copies a block's command; if the clipboard refuses, selects it for the keyboard shortcut instead. */
1559
1827
  function copyCommand(btn) {
1560
- const code = btn.parentElement.querySelector('code');
1828
+ // `data-copy` may name the text it copies, for a button that is not beside it; its text is plain, without the marks.
1829
+ // `data-hint` says what to do next, in the toast.
1830
+ const code = btn.dataset.copy ? $(btn.dataset.copy) : btn.parentElement.querySelector('code');
1831
+ const label = btn.querySelector('span') ?? btn;
1561
1832
  const live = $('copy-live');
1833
+ const say = (t) => { if (live) live.textContent = t; };
1562
1834
  Promise.resolve().then(() => navigator.clipboard.writeText(code.textContent)).then(() => {
1563
- btn.textContent = 'Copied';
1564
- live.textContent = 'Command copied';
1565
- setTimeout(() => { btn.textContent = 'Copy'; }, 1500);
1835
+ label.textContent = 'Copied';
1836
+ say('Command copied');
1837
+ toast.success(btn.dataset.hint ?? 'It is on your clipboard.', { id: 'copy', title: 'Copied' });
1838
+ setTimeout(() => { label.textContent = 'Copy'; }, 1500);
1566
1839
  }, () => {
1567
1840
  getSelection().selectAllChildren(code);
1568
- live.textContent = `Press ${/Mac|iPhone|iPad/.test(navigator.userAgent) ? '⌘C' : 'Ctrl+C'} to copy`;
1841
+ const key = /Mac|iPhone|iPad/.test(navigator.userAgent) ? '⌘C' : 'Ctrl+C';
1842
+ say(`Press ${key} to copy`);
1843
+ toast.caution(`The browser refused the copy. The text is selected: press ${key}.`, { id: 'copy', title: 'Not copied yet' });
1569
1844
  });
1570
1845
  }
1571
1846
 
@@ -2496,6 +2771,98 @@ function viewConcept(slug) {
2496
2771
  ].join('');
2497
2772
  }
2498
2773
 
2774
+
2775
+ /* ---------- the review page: one concept at a time ---------- */
2776
+ const REVIEW_TOOL_KEY = 'eklavya-dash-review-tool';
2777
+ const REVIEW_INTRO_KEY = 'eklavya-dash-review-intro';
2778
+ const REVIEW_SHOWN = 5, REVIEW_FOLDED = 20;
2779
+ const reviewTool = () => { try { return localStorage.getItem(REVIEW_TOOL_KEY) === 'desktop' ? 'desktop' : 'code'; } catch (e) { return 'code'; } };
2780
+
2781
+ /** Where and how to start a quiz on one concept: the project, its folder if it is on this machine, and the slash command. */
2782
+ function conceptStart(c) {
2783
+ const p = projectById(PROJECT ?? pid(c.repo));
2784
+ const path = p?.path && p.available ? p.path : null;
2785
+ return { project: p?.name ?? null, path, slash: `/eklavya:quiz ${c.slug}`, cd: path ? cdTo(path) : null };
2786
+ }
2787
+
2788
+ function reviewCard(c, first) {
2789
+ const s = conceptStart(c);
2790
+ const btn = `<button type="button" class="rv-card" data-start="${esc(c.slug)}" data-focus="start:${esc(c.slug)}" aria-haspopup="dialog">
2791
+ <span class="rv-card__n">${esc(c.name)}</span>
2792
+ <span class="rv-card__m">${esc(s.project ?? 'no project recorded')} · ${esc(c.domain)} · ${esc(tierLabel(c.tier))} · <span class="warn">${plural(c.overdue_days ?? 0, 'day')} overdue</span> · ${Math.round(c.score * 100)}%</span>
2793
+ <span class="rv-card__go">${first ? 'Start' : 'Open'} →</span></button>`;
2794
+ return first
2795
+ ? `<div class="rv-first"><div class="rv-guide"><eklavya-mascot state="review" size="110"></eklavya-mascot><span class="rv-tag">Start here</span></div>${btn}</div>`
2796
+ : btn;
2797
+ }
2798
+
2799
+ function reviewPicker(due) {
2800
+ if (!due.length) return mascotMessage('<p><strong>Nothing is due.</strong> Eklavya will list a concept here the day it comes back.</p>', 'complete');
2801
+ const rest = due.slice(1 + REVIEW_SHOWN - 1);
2802
+ const folded = rest.slice(0, REVIEW_FOLDED);
2803
+ return `<section class="starts"><div class="head"><h2>Pick a concept to review</h2><p>${plural(due.length, 'concept')} due, most overdue first.</p></div>
2804
+ <div class="rv-list">${due.slice(0, REVIEW_SHOWN).map((c, i) => reviewCard(c, i === 0)).join('')}
2805
+ ${rest.length ? `<details class="rv-more" data-key="starts-more"><summary>Show ${plural(Math.min(rest.length, REVIEW_FOLDED), 'more concept')}${rest.length > REVIEW_FOLDED ? `. The queue below lists all ${due.length}` : ''}</summary>
2806
+ <div class="rv-list">${folded.map((c) => reviewCard(c, false)).join('')}</div></details>` : ''}</div>
2807
+ <p class="cmd__note">Each session quizzes that one concept. Come back for the next.</p></section>`;
2808
+ }
2809
+
2810
+ function reviewIntro() {
2811
+ try { if (localStorage.getItem(REVIEW_INTRO_KEY) === '1') return ''; } catch (e) { /* shown */ }
2812
+ return `<section class="card rv-intro" aria-labelledby="rv-h"><div><h2 id="rv-h">Bring older concepts back before they fade</h2>
2813
+ <p>Concepts you were quizzed on return on a schedule. When one is due, a short quiz firms it up and moves its next date.</p>
2814
+ <button type="button" class="rv-hide" data-rv-hide data-focus="rv-hide">Hide this, I know how it works</button></div>
2815
+ <ol class="rv-steps"><li><b>Pick one concept</b>Open a card. The one marked Start here is the longest overdue.</li>
2816
+ <li><b>Start a session for it</b>Copy one line into Claude Code or Claude desktop. The steps are on the card.</li>
2817
+ <li><b>Answer, then pick the next</b>One concept per session keeps it short.</li></ol></section>`;
2818
+ }
2819
+
2820
+ const reviewTips = () => `<div class="rv-tips"><div><b>Run it inside the project</b>Answers are counted for the folder you start the session in, so use the project named on the card.</div>
2821
+ <div><b>Short and often beats long and rare</b>A few minutes a few times a week keeps scores up better than one big catch-up.</div>
2822
+ <div><b>Skipped is not forgotten</b>A skipped question comes back a tier lower. You can quiz any concept by name from its page.</div></div>`;
2823
+
2824
+ function reviewSheetBody(c) {
2825
+ const s = conceptStart(c), tool = reviewTool();
2826
+ const cmd = (text, hint, label = 'Copy') => `<div class="cmd"><code>${esc(text)}</code><button type="button" class="cmd__copy" data-copy data-hint="${esc(hint)}" data-focus="copy:${esc(text.slice(0, 40))}">${label}</button></div>`;
2827
+ const noPath = `<div class="next"><p>This project's folder is not on this machine, so no path is shown. Open the folder where you work on it.</p></div>`;
2828
+ const who = esc(s.project ?? 'the project you ran it in');
2829
+ const steps = tool === 'code'
2830
+ ? [['Open a terminal in the project', s.cd ? 'Skip this if you are already in it.' : '', s.cd ? cmd(s.cd, 'Paste it in your terminal, then copy the next step.') : noPath],
2831
+ ['Start Claude Code with this one concept', 'It opens a session and asks the first question.', cmd(`claude "${s.slash}"`, 'Paste it in your terminal and press Enter.')],
2832
+ ['Answer in the session', `The answer is counted for ${who}. When you finish, this page updates by itself.`, '']]
2833
+ : [['Open the project in the Code tab', 'Choose this folder, then start a new session.', s.path ? cmd(s.path, 'Choose this folder in the Code tab.', 'Copy path') : noPath],
2834
+ ['Paste this in the message box and send', 'It asks the first question on this concept.', cmd(s.slash, 'Paste it in the Code tab and send.')],
2835
+ ['Answer in the chat', `The answer is counted for ${who}. When you finish, this page updates by itself.`, '']];
2836
+ return `<div class="sheet__seg" role="group" aria-label="Where do you run Claude?">
2837
+ <button type="button" data-sheet-tool="code" data-focus="tool:code" aria-pressed="${tool === 'code'}"><b>Claude Code</b><small>terminal</small></button>
2838
+ <button type="button" data-sheet-tool="desktop" data-focus="tool:desktop" aria-pressed="${tool === 'desktop'}"><b>Claude desktop</b><small>Code tab</small></button></div>
2839
+ <ol class="sheet__how">${steps.map(([h, p, x]) => `<li><h3>${h}</h3>${p ? `<p>${p}</p>` : ''}${x}</li>`).join('')}</ol>
2840
+ ${tool === 'desktop' ? `<div class="next"><p><strong>Use a new session.</strong> A long-running session keeps its old context, so the quiz is cleaner in a fresh one.</p></div>` : ''}
2841
+ <div class="sheet__why"><h3>Why it is due</h3><p class="cmd__note">${Math.round(c.score * 100)}% score, ${plural(c.overdue_days ?? 0, 'day')} overdue, ${esc(c.domain)}, ${esc(tierLabel(c.tier))}.</p></div>
2842
+ <span class="sr" id="copy-live" aria-live="polite"></span>`;
2843
+ }
2844
+
2845
+ const SHEET = { slug: null, opener: null };
2846
+ function openSheet(slug, opener) {
2847
+ const c = seenConcepts().find((x) => x.slug === slug);
2848
+ if (!c) return;
2849
+ SHEET.slug = slug; SHEET.opener = opener ?? SHEET.opener;
2850
+ $('sheet-h').textContent = c.name;
2851
+ $('sheet-s').textContent = (conceptStart(c).project ?? 'no project recorded') + ' · ' + plural(c.overdue_days ?? 0, 'day') + ' overdue';
2852
+ $('sheet-b').innerHTML = reviewSheetBody(c);
2853
+ if (!$('sheet').open) $('sheet').showModal();
2854
+ TIP.sync();
2855
+ }
2856
+ function closeSheet() { if ($('sheet').open) $('sheet').close(); }
2857
+ $('sheet').addEventListener('close', () => { $('sheet-b').innerHTML = ''; SHEET.slug = null; SHEET.opener?.focus?.(); });
2858
+ $('sheet').addEventListener('click', (e) => {
2859
+ if (e.target === $('sheet') || e.target.closest('[data-sheet-close]')) return closeSheet();
2860
+ const tool = e.target.closest('[data-sheet-tool]');
2861
+ if (tool) { try { localStorage.setItem(REVIEW_TOOL_KEY, tool.dataset.sheetTool); } catch (x) { /* this load only */ } openSheet(SHEET.slug); $('sheet').querySelector(`[data-sheet-tool="${tool.dataset.sheetTool}"]`)?.focus(); return; }
2862
+ const copy = e.target.closest('[data-copy]');
2863
+ if (copy) copyCommand(copy);
2864
+ });
2865
+
2499
2866
  const REVIEW_TABS = ['due', 'upcoming', 'skipped'];
2500
2867
 
2501
2868
  function viewReview(param) {
@@ -2554,7 +2921,10 @@ function viewReview(param) {
2554
2921
  ease. A score decays about 5% a week while a review is overdue, so this list is also the list of
2555
2922
  things quietly getting weaker. A skipped question comes back a tier lower.`),
2556
2923
 
2557
- startBlocks('Start your reviews', startCommand(due, PROJECT)),
2924
+ reviewIntro(),
2925
+ reviewPicker(due),
2926
+
2927
+ fold('review:tips', 'Tips for reviewing', '3 tips', reviewTips(), { open: true }),
2558
2928
 
2559
2929
  card('The next fortnight', null,
2560
2930
  `<div class="chart"><svg id="c-forecast" role="img" aria-label="Scheduled reviews for the next fourteen days"></svg></div>`),
@@ -4282,9 +4652,13 @@ function drawSettings(d, scope) {
4282
4652
  ============================================================ */
4283
4653
  const FB = {
4284
4654
  ruleEmpty: 'Eklavya reviews one prompt at a time. When one is ready it appears here, and the next waits until you press Acknowledge.',
4285
- rulePending: 'Feedback pauses until you acknowledge this one. Read it, then press Acknowledge to get the next.',
4655
+ rulePending: 'The next review waits until you acknowledge this one. It moves to History.',
4656
+ barRule: 'The next review waits until you acknowledge this one.',
4286
4657
  announce: 'Acknowledged. Feedback resumes at your next session.',
4287
4658
  areas: { outcome: 'Outcome', context: 'Context', scope: 'Scope', check: 'Check' },
4659
+ hints: { outcome: 'what done looks like', context: 'files, systems, prior facts', scope: 'the limits', check: 'how the agent proves it worked' },
4660
+ // `unknown` is an item from rubric 2 or older, which named only its gaps.
4661
+ status: { left_out: 'Left out', clear: 'Clear', not_needed: 'Not needed', unknown: 'Not flagged' },
4288
4662
  counted: false,
4289
4663
  observerCmd: 'eklavya config set providers.observer <model>',
4290
4664
  about: `Your prompts are already recorded by memory. After a session ends, in the background and never in the middle of your work,
@@ -4319,16 +4693,17 @@ function fbEmpty(F) {
4319
4693
  return `<div class="card fb__why" id="fb-empty"><div class="ek-mascot-message"><eklavya-mascot state="${state}" size="72"></eklavya-mascot><div>${body}</div></div></div>`;
4320
4694
  }
4321
4695
 
4696
+ /** With no item, the rule is said above the reason; with one, it sits beside Acknowledge in the rail. */
4697
+ const fbNone = (F) => `<div class="fb__rule" id="fb-rule"><p>${esc(FB.ruleEmpty)}</p></div>${fbEmpty(F)}${fbTip()}`;
4698
+
4322
4699
  function viewFeedbackDashboard() {
4323
4700
  const F = S.feedback;
4324
- const item = F.pending ? slot('fb-item', '/api/feedback', (d) => d.item ? drawFbItem(d.item) : fbEmpty(S.feedback), { label: 'Loading your feedback…' }) : '';
4701
+ const item = F.pending ? slot('fb-item', '/api/feedback', (d) => d.item ? drawFbItem(d.item) : fbNone(S.feedback), { label: 'Loading your feedback…' }) : '';
4325
4702
  if (F.pending && !FB.counted) { FB.counted = true; AFTER.push(fbOpened); }
4326
4703
  const counts = [F.pending ? '1 waiting' : 'nothing waiting', plural(F.acknowledged, 'acknowledged item')];
4327
4704
  return [
4328
4705
  pageHead('Feedback', counts, FB.about),
4329
- `<div class="fb__rule" id="fb-rule"><p>${esc(F.pending ? FB.rulePending : FB.ruleEmpty)}</p></div>`,
4330
- F.pending ? item : fbEmpty(F),
4331
- fbTip(),
4706
+ F.pending ? item : fbNone(F),
4332
4707
  `<span class="sr" id="copy-live" aria-live="polite"></span>`,
4333
4708
  ].join('');
4334
4709
  }
@@ -4342,10 +4717,10 @@ function viewFeedbackDashboard() {
4342
4717
  const FB_TIP_KEY = 'eklavya-dash-feedback-tip';
4343
4718
  function fbTip() {
4344
4719
  try { if (localStorage.getItem(FB_TIP_KEY) === '1') return ''; } catch (e) { /* shown */ }
4345
- return `<aside class="card fb__tip" id="fb-tip" aria-labelledby="fb-tip-h">
4346
- <h2 id="fb-tip-h">Found a problem with Eklavya?</h2>
4347
- <p>Tell Claude, in plain words. It drafts a GitHub issue, shows you exactly what it will post with paths, project names and code removed, and files it only after you say yes.</p>
4348
- <blockquote class="fb__quote">That question about retries was wrong. File feedback for Eklavya.</blockquote>
4720
+ return `<aside class="fb__tip" id="fb-tip" aria-labelledby="fb-tip-h">
4721
+ <eklavya-mascot state="info" variant="face" size="28"></eklavya-mascot>
4722
+ <h2 class="sr" id="fb-tip-h">Found a problem with Eklavya?</h2>
4723
+ <p>Found a problem with Eklavya itself? Tell Claude <code>File feedback for Eklavya</code>; it shows you the issue before posting.</p>
4349
4724
  <button type="button" class="set__btn" data-fb-tip-dismiss>Dismiss</button>
4350
4725
  </aside>`;
4351
4726
  }
@@ -4366,57 +4741,162 @@ function viewFeedbackItem(id) {
4366
4741
  ].join('');
4367
4742
  }
4368
4743
 
4744
+ const fbAck = (id) => `<button type="button" class="btn fb__ack" data-fb-ack="${id}">${svg(ICONS.ack)}Acknowledge</button>`;
4369
4745
  const fbActions = (id) => `<div class="fb__actions" id="fb-actions">
4370
- <button type="button" class="btn" data-fb-ack="${id}">Acknowledge</button>
4371
- <button type="button" class="set__btn" data-fb-del="${id}">Delete</button>
4746
+ ${fbAck(id)}
4747
+ <button type="button" class="fb__del" data-fb-del="${id}">Delete this feedback</button>
4372
4748
  <p class="fb__msg" id="fb-msg" role="alert"></p></div>`;
4373
4749
 
4374
4750
  const fmtWhen = (ts) => { const d = parse(ts); return d ? d.toLocaleString(undefined, { month:'short', day:'numeric', hour:'numeric', minute:'2-digit' }) : null; };
4375
4751
 
4376
- /** Where the prompt was written: project, checkout, host, session and when, so the note is not blind. */
4752
+ /** Where the prompt was written: project, checkout, session, when and from which host, so the note is not blind. */
4377
4753
  function fbContext(it) {
4378
4754
  const c = it.context ?? {};
4379
- const sessionTail = [c.began && c.ended ? `${fmtWhen(c.began)} to ${fmtWhen(c.ended)}` : '', c.prompts ? plural(c.prompts, 'prompt') : '']
4380
- .filter(Boolean).map((x) => ' · ' + x).join('');
4755
+ const line = (html) => `<span class="slug">${html}</span>`;
4756
+ const sent = [fmtWhen(c.prompted_at), c.host].filter(Boolean).join(' · ');
4381
4757
  const rows = [
4382
- ['Project', `${esc(pname(it.project))} <span class="slug">${esc(it.project)}</span>`],
4758
+ ['Project', `${esc(pname(it.project))}${line(esc(it.project))}`],
4383
4759
  c.checkout && c.checkout !== it.project ? ['Checkout', esc(c.checkout)] : null,
4760
+ c.session_id ? ['Session', `<a data-go="${esc(MH('session', c.session_id))}">${esc(c.session_id.slice(0, 8))}</a>`
4761
+ + (c.prompts ? esc(' · ' + plural(c.prompts, 'prompt')) : '')
4762
+ + (c.began && c.ended ? line(esc(`${fmtWhen(c.began)} – ${fmtWhen(c.ended)}`)) : '')] : null,
4763
+ sent ? ['Sent', esc(sent)] : null,
4384
4764
  c.topic ? ['About', esc(c.topic)] : null,
4385
- c.host ? ['Host', esc(c.host)] : null,
4386
- fmtWhen(c.prompted_at) ? ['Sent', esc(fmtWhen(c.prompted_at))] : null,
4387
- c.session_id ? ['Session', `<a data-go="${esc(MH('session', c.session_id))}">${esc(c.session_id.slice(0, 8))}</a>${esc(sessionTail)}`] : null,
4388
4765
  ].filter(Boolean);
4389
- return `<section><h2 class="fb__h">Where you said it</h2><dl class="fb__ctx">${rows.map(([k, v]) => `<dt>${k}</dt><dd>${v}</dd>`).join('')}</dl></section>`;
4766
+ return `<section class="card fb__card" aria-labelledby="fb-ctx-h"><h2 class="fb__h" id="fb-ctx-h">Where you said it</h2>
4767
+ <dl class="fb__ctx">${rows.map(([k, v]) => `<div><dt>${k}</dt><dd>${v}</dd></div>`).join('')}</dl></section>`;
4768
+ }
4769
+
4770
+ /** The order of the coverage strip. The headline lists gaps in the review's own order, costliest first. */
4771
+ const AREA_ORDER = ['outcome', 'context', 'scope', 'check'];
4772
+ const fbList = (xs) => xs.length < 2 ? xs.join('') : `${xs.slice(0, -1).join(', ')} and ${xs[xs.length - 1]}`;
4773
+
4774
+ /** An area's status: the review's word since rubric 3; before it, a gap is "left out" and anything else was never said. */
4775
+ const fbStatus = (review, a) => review.areas?.[a] ?? (review.gaps.some((g) => g.area === a) ? 'left_out' : 'unknown');
4776
+ const FB_CLS = { left_out: 'is-miss', clear: 'is-ok', not_needed: 'is-na', unknown: 'is-na' };
4777
+
4778
+ /** The conclusion first. Every word of it comes from the gap areas, never from model text, and none is a number. */
4779
+ function fbVerdict(it) {
4780
+ const { worked, gaps } = it.review;
4781
+ const date = it.context?.prompted_at ? fmtDate(it.context.prompted_at) : '';
4782
+ const label = `<p class="fb__h">Review of one prompt${date ? ' · ' + esc(date) : ''}</p>`;
4783
+ // Only an item cleared by migration 029 has no `worked` (parseReview requires one).
4784
+ if (!worked) {
4785
+ return `<section class="card fb__verdict" aria-labelledby="fb-verdict-h"><eklavya-mascot state="info" size="88"></eklavya-mascot>
4786
+ <div>${label}<h2 class="fb__vh fb__vh--old" id="fb-verdict-h">An older review: the prompt, the rewrite and the tips are below.</h2></div></section>`;
4787
+ }
4788
+ const areas = [...new Set(gaps.map((g) => g.area))].map((a) => 'the ' + (FB.areas[a] ?? a).toLowerCase());
4789
+ const head = areas.length ? `The agent had to guess ${fbList(areas)}.` : 'Nothing here left the agent guessing.';
4790
+ const cells = AREA_ORDER.map((a) => {
4791
+ const st = fbStatus(it.review, a);
4792
+ return `<li role="listitem" class="fb__cell ${FB_CLS[st]}">${svg(ICONS[a])}<span class="fb__cn">${esc(FB.areas[a])}</span> <span class="fb__cs">${esc(FB.status[st])}</span></li>`;
4793
+ }).join('');
4794
+ return `<section class="card fb__verdict" aria-labelledby="fb-verdict-h">
4795
+ <eklavya-mascot state="${gaps.length ? 'review' : 'complete'}" size="88"></eklavya-mascot>
4796
+ <div class="fb__vb">${label}<h2 class="fb__vh" id="fb-verdict-h">${esc(head)}</h2>
4797
+ ${gaps.length ? '<p class="fb__vs">Of the prompts in that session, this one cost the most rework.</p>' : ''}
4798
+ <ul class="fb__cover" role="list" aria-label="What the prompt covered">${cells}</ul></div></section>`;
4799
+ }
4800
+
4801
+ /** A long text is clamped with its own toggle; `id` names the text the toggle controls. */
4802
+ const FB_CLAMP = { better: 600, mine: 1500 };
4803
+ const fbMore = (key, id, text) => text.length > FB_CLAMP[key]
4804
+ ? `<button type="button" class="fb__more" data-fb-more="${id}" data-focus="more:${key}" aria-expanded="false" aria-controls="${id}">Show the whole prompt</button>` : '';
4805
+
4806
+ function fbToggle(btn) {
4807
+ const el = $(btn.dataset.fbMore);
4808
+ const open = btn.getAttribute('aria-expanded') !== 'true';
4809
+ if (open) {
4810
+ el.classList.remove('is-clamped');
4811
+ // A height to move to, so the expansion is a transition; then no cap, so a resize cannot cut it.
4812
+ el.style.maxHeight = el.scrollHeight + 'px';
4813
+ setTimeout(() => { if (!el.classList.contains('is-clamped')) el.style.maxHeight = 'none'; }, 400);
4814
+ } else {
4815
+ el.style.maxHeight = el.scrollHeight + 'px';
4816
+ el.getBoundingClientRect();
4817
+ el.style.maxHeight = '';
4818
+ el.classList.add('is-clamped');
4819
+ }
4820
+ btn.setAttribute('aria-expanded', String(open));
4821
+ btn.textContent = open ? 'Show less' : 'Show the whole prompt';
4822
+ }
4823
+
4824
+ /** The rewrite, escaped first; each [placeholder] it left for a detail only the developer knows is then marked. */
4825
+ const FB_PH = /\[[^\]\n]{2,200}\]/g;
4826
+
4827
+ function fbCompare(it) {
4828
+ const better = esc(it.better);
4829
+ const marked = better.replace(FB_PH, (m) => `<mark class="fb__ph">${m}</mark>`);
4830
+ const clamp = (key, text) => text.length > FB_CLAMP[key] ? ' is-clamped' : '';
4831
+ return `<div class="fb__grid">
4832
+ <section class="fb__mine" aria-labelledby="fb-mine-h"><div class="fb__ch"><h2 class="fb__h" id="fb-mine-h">Your prompt</h2></div>
4833
+ <div class="fb__clamp${clamp('mine', it.prompt)}" id="fb-mine-t"><blockquote class="fb__quote">${esc(it.prompt)}</blockquote></div>${fbMore('mine', 'fb-mine-t', it.prompt)}</section>
4834
+ <section class="fb__better" aria-labelledby="fb-better-h"><div class="fb__ch"><h2 class="fb__h" id="fb-better-h">A better prompt</h2>
4835
+ <button type="button" class="fb__copy" data-copy="fb-better-c" data-focus="copy:better">${svg(ICONS.copy)}<span>Copy</span></button></div>
4836
+ <div class="cmd cmd--block fb__clamp${clamp('better', it.better)}" id="fb-better-t"><code id="fb-better-c">${marked}</code></div>${fbMore('better', 'fb-better-t', it.better)}
4837
+ ${marked !== better ? '<p class="fb__cap">Highlighted: details only you know. Fill them in before you reuse it.</p>' : ''}</section>
4838
+ </div>`;
4839
+ }
4840
+
4841
+ /** A section's head: its mascot, its heading and one quiet line. */
4842
+ const fbHead = (state, id, title, cap) => `<div class="fb__sh"><eklavya-mascot state="${state}" variant="face" size="40"></eklavya-mascot>
4843
+ <div><h3 id="${id}">${title}</h3>${cap ? `<p class="fb__cap">${cap}</p>` : ''}</div></div>`;
4844
+
4845
+ function fbGaps(gaps) {
4846
+ if (!gaps.length) {
4847
+ return `<section class="fb__sec" aria-labelledby="fb-gaps-h">${fbHead('success', 'fb-gaps-h', 'What it left out', '')}
4848
+ <p class="fb__note">Nothing the agent had to guess.</p></section>`;
4849
+ }
4850
+ const rows = gaps.map((g) => `<li><span class="fb__tile">${svg(ICONS[g.area] ?? '')}</span><div class="fb__gb">
4851
+ <p class="fb__gt">${esc(FB.areas[g.area] ?? g.area)} <small>${esc(FB.hints[g.area] ?? '')}</small></p>
4852
+ <p class="fb__note">${esc(g.missing)}</p>
4853
+ ${g.evidence ? `<p class="fb__ev"><span class="fb__evl" aria-hidden="true">You, later</span><span class="sr">You said later: </span><q>${esc(g.evidence)}</q></p>` : ''}</div></li>`).join('');
4854
+ return `<section class="fb__sec" aria-labelledby="fb-gaps-h">${fbHead('warning', 'fb-gaps-h', 'What it left out', 'What the agent had to guess, costliest first')}
4855
+ <ol class="fb__ds">${rows}</ol></section>`;
4390
4856
  }
4391
4857
 
4392
- /** One item: your prompt, a better one, what worked, what it left out, tips. Actions only while it is pending. */
4858
+ /** The decision, beside the item: the rule, Acknowledge and Delete. Only while it is pending. */
4859
+ const fbDecide = (it) => `<section class="card fb__card fb__decide" aria-labelledby="fb-decide-h">
4860
+ <div class="fb__dh"><eklavya-mascot state="info" variant="face" size="32"></eklavya-mascot><h3 id="fb-decide-h">Done reading?</h3></div>
4861
+ <p class="fb__dp">${esc(FB.rulePending)}</p>
4862
+ ${fbActions(it.id)}
4863
+ <p class="fb__foot"><span>1 waiting</span><span>${esc(S.feedback.acknowledged + ' in history')}</span></p></section>`;
4864
+
4865
+ /**
4866
+ * One item: the verdict, the two prompts, what worked, what it left out and the
4867
+ * habits for next time, with the decision held in view beside them (a fixed bar
4868
+ * at 900px and below). Acknowledge only while it is pending.
4869
+ */
4393
4870
  function drawFbItem(it) {
4394
4871
  const pending = !it.acknowledged_at;
4395
4872
  const { worked, gaps } = it.review;
4396
- const rows = gaps.map((g) => `<li><span class="fb__dn">${esc(FB.areas[g.area] ?? g.area)}</span> `
4397
- + `<span class="fb__note">${esc(g.missing)}`
4398
- + `${g.evidence ? `<span class="fb__ev">You said later: “${esc(g.evidence)}”</span>` : ''}</span></li>`).join('');
4399
- // Only an item cleared by migration 029 has no `worked` (parseReview requires one); its prompt, rewrite and tips still show.
4400
- const review = worked
4401
- ? `<section><h2 class="fb__h">What worked</h2><p class="fb__note">${esc(worked)}</p></section>
4402
- <section><h2 class="fb__h">What it left out</h2>${rows ? `<ul class="fb__ds">${rows}</ul>` : '<p class="fb__note">Nothing the agent had to guess.</p>'}</section>`
4403
- : '';
4404
- // Said again right above the button on the very first item a reader sees.
4405
- const first = pending && S.feedback.acknowledged === 0
4406
- ? `<div class="fb__rule fb__rule--near"><p>${esc(FB.rulePending)}</p></div>` : '';
4407
- return `<article class="card fb" aria-label="Feedback on one of your prompts">
4408
- ${fbContext(it)}
4409
- <div class="fb__grid">
4410
- <section><h2 class="fb__h">Your prompt</h2><blockquote class="fb__quote">${esc(it.prompt)}</blockquote></section>
4411
- <section><h2 class="fb__h">A better prompt</h2>
4412
- <div class="cmd cmd--block"><code>${esc(it.better)}</code><button type="button" class="cmd__copy" data-copy data-focus="copy:better">Copy</button></div></section>
4873
+ chart('fb-rail', fbRailFit);
4874
+ return `<article class="fb" aria-label="Feedback on one of your prompts">
4875
+ <div class="fb__story">
4876
+ ${fbVerdict(it)}
4877
+ ${fbCompare(it)}
4878
+ ${worked ? `<section class="fb__worked" aria-labelledby="fb-worked-h"><eklavya-mascot state="success" variant="face" size="40"></eklavya-mascot>
4879
+ <div><h3 class="fb__h" id="fb-worked-h">What worked</h3><p>${esc(worked)}</p></div></section>` : ''}
4880
+ ${worked ? fbGaps(gaps) : ''}
4881
+ <section class="fb__sec" aria-labelledby="fb-tips-h">${fbHead('tip', 'fb-tips-h', 'Next time', worked ? 'Habits that would have saved those follow-ups' : 'Habits for next time')}
4882
+ <ol class="fb__tips" data-n="${Math.min(it.tips.length, 3)}">${it.tips.map((t) => `<li>${esc(t)}</li>`).join('')}</ol></section>
4883
+ ${pending ? fbTip() : ''}
4413
4884
  </div>
4414
- ${review}
4415
- <section><h2 class="fb__h">Tips</h2><ul class="fb__tips">${it.tips.map((t) => `<li>${esc(t)}</li>`).join('')}</ul></section>
4416
- ${pending ? `<section>${first}${fbActions(it.id)}</section>` : ''}
4885
+ <aside class="fb__rail" id="fb-rail" aria-label="About this prompt">
4886
+ ${fbContext(it)}
4887
+ ${pending ? fbDecide(it) : `<p class="fb__acked">Acknowledged ${esc(fmtDate(it.acknowledged_at))}</p>`}
4888
+ </aside>
4889
+ ${pending ? `<div class="fb__bar" role="region" aria-label="Acknowledge this feedback"><p>${esc(FB.barRule)}</p>${fbAck(it.id)}</div>` : ''}
4417
4890
  </article>`;
4418
4891
  }
4419
4892
 
4893
+ /** The rail stays in view while it fits the window; one taller than that would hide its own end, so it scrolls with the page. */
4894
+ function fbRailFit() {
4895
+ const rail = $('fb-rail');
4896
+ if (rail) rail.classList.toggle('is-tall', rail.offsetHeight > innerHeight - 48);
4897
+ }
4898
+ addEventListener('resize', fbRailFit);
4899
+
4420
4900
  function viewFeedbackHistory() {
4421
4901
  const p = new URLSearchParams();
4422
4902
  if (PROJECT !== null) p.set('project', PROJECT);
@@ -4472,11 +4952,18 @@ function fbFail(e) {
4472
4952
 
4473
4953
  function fbAcknowledge(btn) {
4474
4954
  if (btn.disabled) return;
4475
- btn.disabled = true;
4955
+ // The rail's button and the bar's are one action: pressing either holds both.
4956
+ const both = [...document.querySelectorAll('[data-fb-ack]')];
4957
+ both.forEach((b) => { b.disabled = true; });
4476
4958
  postJson('/api/feedback/acknowledge', { id: +btn.dataset.fbAck })
4477
4959
  // The item, its history row and the next one waiting all changed: what was read of them is gone.
4478
4960
  .then((d) => { S.feedback = d.state; respForget('/api/feedback'); fbDone(FB.announce); })
4479
- .catch((e) => { btn.disabled = false; fbFail(e); });
4961
+ .catch((e) => {
4962
+ both.forEach((b) => { b.disabled = false; });
4963
+ fbFail(e);
4964
+ // Under the bar the message is in the rail card, after the story.
4965
+ if (innerWidth <= 900) $('fb-msg')?.scrollIntoView({ block: 'nearest' });
4966
+ });
4480
4967
  }
4481
4968
 
4482
4969
  function fbAskDelete(btn) {
@@ -4610,6 +5097,13 @@ const ICONS = {
4610
5097
  history: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
4611
5098
  user: '<circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/>',
4612
5099
  project: '<path d="M3 7l9-4 9 4-9 4-9-4z"/><path d="M3 12l9 4 9-4M3 17l9 4 9-4"/>',
5100
+ // The four areas a prompt can leave the agent to guess, and the Feedback item's two actions.
5101
+ outcome: '<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/>',
5102
+ context: '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/>',
5103
+ scope: '<path d="M3 7V5a2 2 0 0 1 2-2h2"/><path d="M17 3h2a2 2 0 0 1 2 2v2"/><path d="M21 17v2a2 2 0 0 1-2 2h-2"/><path d="M7 21H5a2 2 0 0 1-2-2v-2"/>',
5104
+ check: '<circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/>',
5105
+ copy: '<rect width="14" height="14" x="8" y="8"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>',
5106
+ ack: '<path d="M20 6 9 17l-5-5"/>',
4613
5107
  };
4614
5108
  const svg = (paths, cls = '') =>
4615
5109
  `<svg${cls ? ` class="${cls}"` : ''} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"
@@ -5703,7 +6197,7 @@ function catchUp() {
5703
6197
  * opens (`detailSelector`). A control with none is not carried over a redraw, so every control the reader can Tab to has one.
5704
6198
  */
5705
6199
  const FOCUS_KEYS = ['data-focus', 'data-tab', 'data-tab-id', 'data-state', 'data-unset', 'data-go',
5706
- 'data-fb-ack', 'data-fb-del', 'data-fb-yes', 'data-fb-no', 'data-fb-tip-dismiss'];
6200
+ 'data-fb-ack', 'data-fb-del', 'data-fb-more', 'data-fb-yes', 'data-fb-no', 'data-fb-tip-dismiss'];
5707
6201
  function focusKey(el) {
5708
6202
  if (el.id) return `#${CSS.escape(el.id)}`;
5709
6203
  for (const a of FOCUS_KEYS) {
@@ -5811,6 +6305,14 @@ function onActivate(e) {
5811
6305
  if (e.target.closest('[data-slot][aria-busy="true"][data-kept]')) return;
5812
6306
  const copy = e.target.closest('[data-copy]');
5813
6307
  if (copy) return copyCommand(copy);
6308
+ const start = e.target.closest('[data-start]');
6309
+ if (start) return openSheet(start.dataset.start, start);
6310
+ if (e.target.closest('[data-rv-hide]')) {
6311
+ try { localStorage.setItem(REVIEW_INTRO_KEY, '1'); } catch (x) { /* hidden until reload */ }
6312
+ return render();
6313
+ }
6314
+ const more = e.target.closest('[data-fb-more]');
6315
+ if (more) return fbToggle(more);
5814
6316
  if (e.target.closest('[data-fb-tip-dismiss]')) return fbTipDismiss();
5815
6317
  const ack = e.target.closest('[data-fb-ack]');
5816
6318
  if (ack) return fbAcknowledge(ack);
@@ -161,6 +161,7 @@
161
161
  --spot: var(--vd-300);
162
162
  --spot-ink: #17171a; /* text ON a --spot fill */
163
163
  --spot-soft: color-mix(in srgb, var(--spot) 14%, transparent);
164
+ --warning-soft: color-mix(in srgb, var(--warning) 14%, transparent);
164
165
 
165
166
  --grid: #1f1f23; /* background rule grid */
166
167
  --glow: rgba(25,150,136,.22); /* the terminal's halo */
@@ -197,6 +198,7 @@
197
198
  --spot: var(--vd-700);
198
199
  --spot-ink: #ffffff;
199
200
  --spot-soft: color-mix(in srgb, var(--spot) 10%, transparent);
201
+ --warning-soft: color-mix(in srgb, var(--warning) 10%, transparent);
200
202
 
201
203
  --grid: #e5e1d7;
202
204
  /* a light ground carries far less glow before it reads as a stain, and
@@ -19,6 +19,8 @@ export const AREAS = {
19
19
  check: 'How the agent proves it worked: the test, command or observation that says done.',
20
20
  };
21
21
  const AREA_KEYS = Object.keys(AREAS);
22
+ /** What the review says of each area: the prompt made it clear, left it to guess, or the task did not need it. */
23
+ export const AREA_STATUS = ['clear', 'left_out', 'not_needed'];
22
24
  /** One source for the limits: the validator, the clip and the prompt all read these. */
23
25
  export const REVIEW_LIMIT = {
24
26
  note: 240,
@@ -41,9 +43,15 @@ export const REVIEW_SCHEMA = {
41
43
  review: {
42
44
  type: 'object',
43
45
  additionalProperties: false,
44
- required: ['worked', 'gaps'],
46
+ required: ['worked', 'gaps', 'areas'],
45
47
  properties: {
46
48
  worked: { type: 'string' },
49
+ areas: {
50
+ type: 'object',
51
+ additionalProperties: false,
52
+ required: AREA_KEYS,
53
+ properties: Object.fromEntries(AREA_KEYS.map((k) => [k, { type: 'string', enum: AREA_STATUS }])),
54
+ },
47
55
  gaps: {
48
56
  type: 'array',
49
57
  items: {
@@ -78,6 +86,7 @@ export const REVIEW_SYSTEM = [
78
86
  `Name its gaps, at most ${REVIEW_LIMIT.gaps}, the most costly first, each in one area:`,
79
87
  ...AREA_KEYS.map((k) => `- ${k}: ${AREAS[k]}`),
80
88
  'Name only gaps that mattered for this task: a one-line fix needs no test plan. No gaps is a valid answer for a prompt that left nothing to guess.',
89
+ '"areas" says, for each of the four areas, whether the chosen prompt made it clear ("clear"), left it for the agent to guess ("left_out") or did not need it for this task ("not_needed"). Every area you name in gaps is "left_out". Words only.',
81
90
  `"missing" says in one or two plain sentences, under ${REVIEW_LIMIT.note} characters, what the prompt left out and what that cost.`,
82
91
  `When a later prompt supplied it, "evidence" is a quote of fewer than ${REVIEW_LIMIT.evidenceWords} words copied exactly, character for character, from one prompt after the chosen one. Otherwise leave evidence out: never paraphrase or invent a quote.`,
83
92
  `"worked" is one plain sentence, under ${REVIEW_LIMIT.note} characters, on what the prompt did well.`,
@@ -99,11 +108,16 @@ export function renderPrompts(prompts) {
99
108
  const Gap = z
100
109
  .object({ area: z.enum(AREA_KEYS), missing: z.string().min(1).max(REVIEW_LIMIT.note), evidence: z.string().optional() })
101
110
  .strict();
111
+ const Status = z.enum(AREA_STATUS);
102
112
  const ReviewResult = z
103
113
  .object({
104
114
  chosen: z.number().int().min(1),
105
115
  review: z
106
- .object({ worked: z.string().min(1).max(REVIEW_LIMIT.note), gaps: z.array(Gap).max(REVIEW_LIMIT.gaps) })
116
+ .object({
117
+ worked: z.string().min(1).max(REVIEW_LIMIT.note),
118
+ gaps: z.array(Gap).max(REVIEW_LIMIT.gaps),
119
+ areas: z.object({ outcome: Status, context: Status, scope: Status, check: Status }).strict(),
120
+ })
107
121
  .strict(),
108
122
  better: z.string().min(1).max(REVIEW_LIMIT.better),
109
123
  tips: z.array(z.string().min(1).max(REVIEW_LIMIT.tip)).min(1).max(REVIEW_LIMIT.tips),
@@ -144,6 +158,11 @@ const normal = (s) => s.toLowerCase().replace(/\s+/g, ' ').trim();
144
158
  * still the model's reading, only the proof is gone, and failing the whole
145
159
  * review for it throws away a paid call. A score, or any key the schema does
146
160
  * not name, is a rejection: the review has no numbers.
161
+ *
162
+ * `areas` is reconciled with the gaps, and the gaps win: an area named in a gap
163
+ * is `left_out` whatever `areas` said, because the gap carries the reason. An
164
+ * area marked `left_out` with no gap stays so (the gaps are capped). A
165
+ * disagreement is corrected, never a rejection, for the same paid-call reason.
147
166
  */
148
167
  export function parseReview(raw, prompts) {
149
168
  const result = ReviewResult.safeParse(clip(raw));
@@ -159,7 +178,10 @@ export function parseReview(raw, prompts) {
159
178
  return quote && quote.split(' ').length < REVIEW_LIMIT.evidenceWords && later.some((p) => p.includes(quote));
160
179
  };
161
180
  const gaps = review.gaps.map(({ area, missing, evidence }) => quoted(evidence) ? { area, missing, evidence: evidence.trim() } : { area, missing });
162
- return { chosen, review: { worked: review.worked, gaps }, better, tips };
181
+ const areas = { ...review.areas };
182
+ for (const g of gaps)
183
+ areas[g.area] = 'left_out';
184
+ return { chosen, review: { worked: review.worked, gaps, areas }, better, tips };
163
185
  }
164
186
  /** One review of one session's prompts, on the developer's subscription. Throws `ProviderError`. */
165
187
  export async function reviewPrompts(model, prompts, opts = {}) {
@@ -1 +1 @@
1
- {"version":3,"file":"feedback-review.js","sourceRoot":"","sources":["../src/feedback-review.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"feedback-review.js","sourceRoot":"","sources":["../src/feedback-review.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAiB,MAAM,sBAAsB,CAAC;AAG3F;;;;;;GAMG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,OAAO,EAAE,+DAA+D;IACxE,OAAO,EAAE,kFAAkF;IAC3F,KAAK,EAAE,4FAA4F;IACnG,KAAK,EAAE,kFAAkF;CACjF,CAAC;AAGX,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAA4B,CAAC;AAEhE,kHAAkH;AAClH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,YAAY,CAAU,CAAC;AAGxE,wFAAwF;AACxF,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,GAAG;IACT,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,GAAG;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,kEAAkE;IAClE,OAAO,EAAE,CAAC;IACV,WAAW,EAAE,IAAI;IACjB,wCAAwC;IACxC,aAAa,EAAE,EAAE;CACT,CAAC;AAEX,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,QAAQ;IACd,oBAAoB,EAAE,KAAK;IAC3B,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC;IAChD,UAAU,EAAE;QACV,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;QAC3B,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,oBAAoB,EAAE,KAAK;YAC3B,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC;YACrC,UAAU,EAAE;gBACV,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC1B,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,oBAAoB,EAAE,KAAK;oBAC3B,QAAQ,EAAE,SAAS;oBACnB,UAAU,EAAE,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;iBACjG;gBACD,IAAI,EAAE;oBACJ,IAAI,EAAE,OAAO;oBACb,KAAK,EAAE;wBACL,IAAI,EAAE,QAAQ;wBACd,oBAAoB,EAAE,KAAK;wBAC3B,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;wBAC7B,UAAU,EAAE;4BACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE;4BACzC,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;4BAC3B,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;yBAC7B;qBACF;iBACF;aACF;SACF;QACD,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;KACnD;CACO,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,iHAAiH;IACjH,mLAAmL;IACnL,+QAA+Q;IAC/Q,yOAAyO;IACzO,0BAA0B,YAAY,CAAC,IAAI,4CAA4C;IACvF,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,mJAAmJ;IACnJ,kPAAkP;IAClP,uDAAuD,YAAY,CAAC,IAAI,2DAA2D;IACnI,wEAAwE,YAAY,CAAC,aAAa,yJAAyJ;IAC3P,yCAAyC,YAAY,CAAC,IAAI,2CAA2C;IACrG,+FAA+F,YAAY,CAAC,MAAM,qUAAqU;IACvb,oBAAoB,YAAY,CAAC,IAAI,qCAAqC,YAAY,CAAC,GAAG,wJAAwJ;IAClP,2FAA2F;IAC3F,8BAA8B;CAC/B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,CAAC,MAAM,WAAW,GAAa,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;AAEtF,yFAAyF;AACzF,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAEzC,iFAAiF;AACjF,MAAM,UAAU,aAAa,CAAC,OAAiB;IAC7C,MAAM,IAAI,GAAG,OAAO;SACjB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,EAAE,UAAU,CAAC,aAAa,CAAC;SAChF,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO,cAAc,IAAI,cAAc,CAAC;AAC1C,CAAC;AAED,MAAM,GAAG,GAAG,CAAC;KACV,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC;KACvH,MAAM,EAAE,CAAC;AAEZ,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAEnC,MAAM,YAAY,GAAG,CAAC;KACnB,MAAM,CAAC;IACN,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,MAAM,EAAE,CAAC;SACN,MAAM,CAAC;QACN,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC;QAChD,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC;QACzC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;KAC7F,CAAC;SACD,MAAM,EAAE;IACX,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC;IAClD,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC;CACrF,CAAC;KACD,MAAM,EAAE,CAAC;AAUZ,MAAM,GAAG,GAAG,CAAC,CAAU,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7G,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAgC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAEjH;;;;;GAKG;AACH,SAAS,IAAI,CAAC,GAAY;IACxB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAC/B,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC;IACrB,MAAM,MAAM,GACV,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;QAClC,CAAC,CAAC;YACE,GAAG,CAAC;YACJ,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC;YACxC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SAC9H;QACH,CAAC,CAAC,CAAC,CAAC;IACR,OAAO;QACL,GAAG,GAAG;QACN,MAAM;QACN,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC;QAC5C,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI;KACrH,CAAC;AACJ,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AAE1E;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CAAC,GAAY,EAAE,OAAiB;IACzD,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,aAAa,CAAC,WAAW,EAAE,iCAAiC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9G,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IACrD,IAAI,MAAM,GAAG,OAAO,CAAC,MAAM;QAAE,MAAM,IAAI,aAAa,CAAC,WAAW,EAAE,6CAA6C,CAAC,CAAC;IAEjH,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,CAAC,QAA4B,EAAE,EAAE;QAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;QACrC,OAAO,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,YAAY,CAAC,aAAa,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/G,CAAC,CAAC;IACF,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAC3D,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAS,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CACrF,CAAC;IACF,MAAM,KAAK,GAAG,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,IAAI;QAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC;IACjD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAClF,CAAC;AAED,oGAAoG;AACpG,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAAa,EACb,OAAiB,EACjB,OAAuE,EAAE;IAEzE,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;IAC9F,OAAO,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;AAClD,CAAC"}
package/dist/feedback.js CHANGED
@@ -11,8 +11,10 @@ import { HELPERS, HOST_PROMPT, SLASH, TASK_PROMPT_CHARS } from './prompt-text.js
11
11
  /**
12
12
  * Which wording of the review an item was written against. Items from rubric 1
13
13
  * had their notes cleared by migration 029; their prompt, rewrite and tips stay.
14
+ * Rubric 3 adds `review.areas`; older items have none and the page says
15
+ * "Not flagged" for an area their gaps do not name.
14
16
  */
15
- export const FEEDBACK_RUBRIC = 2;
17
+ export const FEEDBACK_RUBRIC = 3;
16
18
  const parse = (r) => ({ ...r, review: JSON.parse(r.review), tips: JSON.parse(r.tips) });
17
19
  /**
18
20
  * The single place the switch and its dependency are combined. Feedback reads
@@ -1 +1 @@
1
- {"version":3,"file":"feedback.js","sourceRoot":"","sources":["../src/feedback.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"feedback.js","sourceRoot":"","sources":["../src/feedback.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eklavya",
3
3
  "displayName": "Eklavya",
4
- "version": "1.73.0",
4
+ "version": "1.75.0",
5
5
  "description": "Learn while your agent works. Turns coding-agent generation time into adaptive, Socratic learning grounded in the code being written.",
6
6
  "author": {
7
7
  "name": "Ajay Kumar"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eklavya",
3
- "version": "1.73.0",
3
+ "version": "1.75.0",
4
4
  "description": "Learn while your agent works — local knowledge graph, spaced repetition, and quiz gating for agent-assisted development.",
5
5
  "license": "MIT",
6
6
  "repository": {