1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
  | 
* Emacs Config Open Work
** TODO [#A] Add Transcription Org-capture Workflow
  :PROPERTIES:
  :CATEGORY: emacs
  :END:
*** Goal
   Integrate audio-to-text transcription into Org-capture so meeting recordings
   can be turned into timestamped notes automatically.
*** Steps
**** 1. Create CLI for transcription
    - Option A — OpenAI API (Whisper):
      #+begin_src bash
      # ~/bin/oai-transcribe
      #!/usr/bin/env bash
      set -euo pipefail
      AUDIO="$1"; LANG="${2:-en}"
      : "${OPENAI_API_KEY:?Set OPENAI_API_KEY in your environment}"
      curl -s -X POST "https://api.openai.com/v1/audio/transcriptions" \
        -H "Authorization: Bearer $OPENAI_API_KEY" \
        -F "model=whisper-1" \
        -F "response_format=text" \
        -F "language=${LANG}" \
        -F "file=@${AUDIO}"
      #+end_src
    - Option B — Local Whisper:
      #+begin_src bash
      pip install openai-whisper   # once
      # ~/bin/local-whisper
      #!/usr/bin/env bash
      set -euo pipefail
      AUDIO="$1"; MODEL="${2:-medium}"; LANG="${3:-en}"
      whisper "$AUDIO" --model "$MODEL" --language "$LANG" \
        --task transcribe --output_format txt --verbose False >/dev/null
      cat "${AUDIO%.*}.txt"
      #+end_src
**** 2. Emacs Lisp configuration
    Add to init.el:
    #+begin_src emacs-lisp
    (defcustom cj/transcribe-program
      (or (executable-find "oai-transcribe")
          (executable-find "local-whisper"))
      "Program used to transcribe audio to plain text.")
    (defcustom cj/meetings-org-dir "~/org/meetings/" "")
    (defun cj/transcribe-audio (file)
      (with-temp-buffer
        (call-process shell-file-name nil t nil shell-command-switch
                     (format "%s %s"
                             (shell-quote-argument cj/transcribe-program)
                             (shell-quote-argument (expand-file-name file))))
        (buffer-string)))
    (defun cj/org-capture-meeting-template ()
      (let* ((audio (read-file-name "Audio: "))
             (participants (read-string "Participants: " "Mark; Craig"))
             (location (read-string "Location: " "French Truck Coffee"))
             (topic (read-string "Topic: " "Contract discussion"))
             (transcript (cj/transcribe-audio audio)))
        (format "* Meeting: %s\n:PROPERTIES:\n:DATE: %s\n:LOCATION: %s\n:PARTICIPANTS: %s\n:AUDIO: %s\n:END:\n\n** Summary\n- \n\n** Transcript\n#+begin_quote\n%s\n#+end_quote\n"
                topic (format-time-string "%Y-%m-%d %H:%M")
                location participants (abbreviate-file-name audio) transcript)))
    (with-eval-after-load 'org
      (add-to-list 'org-capture-templates
                   `("m" "Meeting (transcribe audio)" plain
                     (file ,(expand-file-name (format-time-string "%Y-meetings.org")
                                              cj/meetings-org-dir))
                     "%(cj/org-capture-meeting-template)"
                     :empty-lines 1 :unnarrowed t)))
    #+end_src
**** 3. Usage
    1. Record meeting as .m4a or .wav  
    2. Run `M-x org-capture m` → select the audio file  
    3. Org inserts transcript and metadata under
       `~/org/meetings/YYYY-meetings.org`
**** 4. Optional quick recap template
    #+begin_src emacs-lisp
    (with-eval-after-load 'org
      (add-to-list 'org-capture-templates
                   '("r" "Post-meeting recap" entry
                     (file+headline
                      (lambda () (expand-file-name (format-time-string "%Y-meetings.org")
                                                   cj/meetings-org-dir))
                      "Recaps")
                     "* %U %^{Title|Meeting w/ Mark (Carrollton)}\n:LOCATION: %^{Location|French Truck (Dryades)}\n** Facts\n- %?\n** Next Steps\n- ")))
    #+end_src
**** 5. Notes
    - Keep filenames descriptive: `2025-11-03_mark-frenchtruck.m4a`
    - For privacy: use local-whisper variant to keep audio offline.
** TODO [#A] Finish Testing New Org-webclipper
** TODO [#A] Document Goals, Specs, and Architecture Docs for this Config :maintenance:
*** Emacs Config V2MOM
Misc Requirements
- Keybindings easy to change
- Keybindings have good mnemonics
**** Vision
Your personal ideal Emacs configuration allows you to work at the speed of your own thought. It's stable and never needs edge-case bug fixing. The functionality covers your workflow such that you can work all day in emacs for any of the following task areas:
- programming in any language, including emacs-lisp, common-lisp, scheme, golang, and c
- reading ebooks, including marginalia handling
- listening to music, including playlist management
- listening to  podcasts, including saving episodes
- task organizing and planning
- calendaring, including syncing with google calendar
- alerting and notifications on tasks and calendar events
- timer/stopwatch functionality, including pomodoro presets
- audio and desktop recording 
- memorization drills
- email, including email-to-task integration
- messaging, i.e., IRC
- file-management, including extensive integration,, and DWM scratchpad integration outside Emacs
- shell commands, leveraging vterm and eshell with custom methods, and DWM scratchpad integration outside Emacs
Anytime you make a change in the config, you have unit tests to tell you quickly if the change created unexpected issues. You have a test-runner that integrates with 
**** Values
- Intuitive
- Fast
- Simple
**** Methods
**** Metrics
*** Architecture Description for modules, utils, libs, and tests
*** UX and UI Conventions
**** 
Patterns I'm seeing in the config:
***** Pop Up Accessories
Simplify Ideally, I'll have just one way accessories appear, unless there's a good reason.
So what's my criteria for success:
- allows C-x 1 to make it the only window.
- can tab between this and the
- can resize
- can toggle orientation. 
I'm going to 
****** Bottom Up Pattern
Loads from the bottom and is dynamically adjusted.
If I hit q, the window disappears. 
Org-Agenda
****** Side Out Pattern
These pop up in a side window. Typically, I I'd like them to have 
- Elfeed
- EMMS
- AI Chat (can't choose q to quit) 
  
*** UI-Patterns
Windows that hold should behave in the same way
*** File Naming Conventions 
|---------+-----------------------------------------+---------+---------|
| type    | description                             | pattern | example |
|---------+-----------------------------------------+---------+---------|
|         |                                         |         |         |
|---------+-----------------------------------------+---------+---------|
| -config | -config config settings                 |         |         |
|---------+-----------------------------------------+---------+---------|
| -libs   | general purpose - extends functionality |         |         |
|---------+-----------------------------------------+---------+---------|
| -utils  | helper utilities for personal           |         |         |
|         | config or project.Not general purpose.  |         |         |
|---------+-----------------------------------------+---------+---------|
| test-   | test suite named after method           |         |         |
** TODO [#A] Org Reveal.js Presentations From Local Org        :enhancement:
*** References
https://revealjs.com/
https://github.com/hakimel/reveal.js
*** Steps from Zamansky's Videos
https://www.youtube.com/watch?v=psDpCpcIVYs&t=10s
**** Install Org Reveal
** TODO [#B] Implement additional custom operations
After analyzing all custom-* modules, the following line/paragraph operations are missing and would be useful additions to custom-line-paragraph.el:
*** Most Useful Operations (High Priority)
- [ ] Copy current line - Duplicate line to kill ring without cutting (like yy in Vim)
- [ ] Sort lines - Sort lines alphabetically, numerically, or by custom key
- [ ] Insert blank line above - Add empty line above current line without moving cursor
- [ ] Insert blank line below - Add empty line below current line without moving cursor
*** Additional Operations (Medium Priority)
- [ ] Reverse lines - Reverse the order of lines in region or buffer
- [ ] Shuffle lines - Randomly reorder lines in region or buffer
- [ ] Uniquify lines - Remove duplicate lines while preserving order (different from existing remove-duplicates which uses regex)
- [ ] Filter lines matching regex - Keep only lines matching a pattern (inverse of remove-lines-containing)
- [ ] Number/enumerate lines - Add line numbers or enumeration to lines in region
- [ ] Join with separator - Join lines with custom separator (e.g., comma, semicolon)
*** Lower Priority Operations
- [ ] Kill whole line - Delete entire line including newline (like dd in Vim)
- [ ] Wrap/unwrap at column - Hard wrap at fill-column or unwrap wrapped lines
- [ ] Indent/dedent region - Increase or decrease indentation for region
Note: Some operations like fill-paragraph, transpose-lines, and basic indentation already exist in Emacs core. This list focuses on commonly-needed operations not yet implemented in custom modules.
** TODO [#B] investigate unassociated files
- [ ] /home/cjennings/.emacs.d/assets/test/test-misspellings.org
- [ ] /home/cjennings/.emacs.d/assets/test/test.org
- [ ] /home/cjennings/.emacs.d/custom/c-boxes.el
- [ ] /home/cjennings/.emacs.d/custom/edit-indirect.el
- [ ] /home/cjennings/.emacs.d/custom/eplot.el
- [ ] /home/cjennings/.emacs.d/custom/gptel-prompts.el
- [ ] /home/cjennings/.emacs.d/custom/org-checklist.el
- [ ] /home/cjennings/.emacs.d/custom/pdf-continuous-scroll-mode.el
- [ ] /home/cjennings/.emacs.d/custom/pdf-continuous-scroll-mode-latest.el
- [ ] /home/cjennings/.emacs.d/custom/profile-dotemacs.el
- [ ] /home/cjennings/.emacs.d/custom/titlecase.el
- [ ] /home/cjennings/.emacs.d/custom/utilities/vcf-conversion-helpers.el
- [ ] /home/cjennings/.emacs.d/gptel-tools/update_text_file.el
- [ ] /home/cjennings/.emacs.d/modules/ai-conversations.el
- [ ] /home/cjennings/.emacs.d/modules/erc-config.el
- [ ] /home/cjennings/.emacs.d/modules/jumper.el
- [ ] /home/cjennings/.emacs.d/modules/ledger-config.el
- [ ] /home/cjennings/.emacs.d/modules/lipsum-generator.el
- [ ] /home/cjennings/.emacs.d/modules/lorem-generator.el
- [ ] /home/cjennings/.emacs.d/modules/mu4e-org-contacts-integration.el
- [ ] /home/cjennings/.emacs.d/modules/mu4e-org-contacts-setup.el
- [ ] /home/cjennings/.emacs.d/modules/org-noter-config.el
- [ ] /home/cjennings/.emacs.d/modules/prog-lsp.el
- [ ] /home/cjennings/.emacs.d/modules/wip.el
- [ ] /home/cjennings/.emacs.d/themes/dupre-theme.el
- [ ] /home/cjennings/.emacs.d/todo.org
** TODO [#B] Replace customization constructions in modules            :bug:
Files using defcustom or other customization-related constructions:
- media-utils.el
- external-open.el
- dashboard-config.el
- lipsum-generator.el
- ui-config.el
- ai-conversations.el
- video-audio-recording.el
- jumper.el
- local-repository.el
- lorem-generator.el
- eww-config.el
** TODO [#B] Test Failures Fixed
*** TODO [#A] Add All ERT Tests Into Separate Directory               :bug:
**** List of ERT tests that need consolidation:
Looks like there are only two files that have tests left:
modules/undead-buffers.el
73   (ert-deftest undead-buffers/kill-or-bury-when-not-in-list-kills ()
85   (ert-deftest undead-buffers/kill-or-bury-when-in-list-buries ()
107  (ert-deftest undead-buffers/kill-or-bury-adds-to-list-with-prefix ()
119  (ert-deftest undead-buffers/kill-buffer-and-window-removes-window ()
133  (ert-deftest undead-buffers/kill-other-window-deletes-that-window ()
150  (ert-deftest undead-buffers/kill-all-other-buffers-and-windows-keeps-only-current ()
themes/dupre-theme.el
137  (ert-deftest dupre-theme-default-face ()
146  (ert-deftest dupre-theme-comment-face-italic ()
**** 2025-09-13 Sat @ 12:18:26 -0500 Gave this task to Codex
Move all ERT tests out of individual files in the modules directories, and move them all to files in the test directories. The files should be named after the functions they test.  Move existing ERT tests in the test directories to these files as well, removing those original test files. Provide another elisp file that will read all ERT files, and offer to run all the ERT tests in that file, or all ERT tests from all the files.
**** Goals
Tests should be added to a separate directory rather than the end of each elisp file.
Gather them together, move them there, and make it easy to run tests per module and workflow.
*** TODO [#B] Fix all failing test files  ~/.emacs.d/tests/*.disabled :maintenance:
*** TODO [#C] Write missing tests for each module                      :bug:
** TODO [#B] AI-Config Fixes and Enhancements
*** DOING [#A] Finish Creating and Refactoring GPTel Tools and Tests :maintenance:
**** TODO Refactor setup/teardown in all library test files
Use the methods in test-tool-library-general.el
- [ ] tests/test-tool-library-fs--mode-to-permissions.el
- [ ] tests/test-tool-library-fs-directory-entries.el
- [ ] tests/test-tool-library-fs-filter-by-extension.el
- [ ] tests/test-tool-library-fs-format-file-info.el
- [ ] tests/test-tool-library-fs-get-file-info.el
- [ ] tests/test-tool-library-fs-list-directory-recursive-extra.el
- [ ] tests/test-tool-library-fs-list-directory-recursive.el
- [ ] tests/test-tool-library-fs-validate-path.el
***** 2025-09-28 Sun @ 01:33:56 -0500 Issue, Goals, and Discussion 
****** cjennings
When I ran the snippet you provided, I received a backtrace indicating there was no directory of that name. So most likely the test teardown function removed it. 
```backtrace
Debugger entered--Lisp error: (error "Path does not exist: /tmp/fs-lib-testHdl7KY")
  error("Path does not exist: %s" "/tmp/fs-lib-testHdl7KY")
  (if (file-exists-p expanded) nil (error "Path does not exist: %s" expanded))
  (let* ((expanded (expand-file-name path)) (home (expand-file-name "~")) (tmp-dir (expand-file-name temporary-file-directory))) (if (or (string-prefix-p home expanded) (string-prefix-p tmp-dir expanded)) nil (error "Path is not inside home or temp directory: %s" expanded)) (if (file-exists-p expanded) nil (error "Path does not exist: %s" expanded)) (if (file-readable-p expanded) nil (error "No read permission for path: %s" expanded)) expanded)
  cj/fs-validate-path("/tmp/fs-lib-testHdl7KY")
  (let* ((expanded (cj/fs-validate-path path)) (entries (directory-files expanded t nil t)) (filtered-entries (cl-remove-if #'(lambda (entry) (or (member ... ...) (and ... ...))) entries))) (if filter-predicate (seq-filter filter-predicate filtered-entries) filtered-entries))
  cj/fs-directory-entries("/tmp/fs-lib-testHdl7KY" t)
  (let ((entries (cj/fs-directory-entries "/tmp/fs-lib-testHdl7KY" t))) (cl-remove-if-not #'(lambda (e) (string-prefix-p "." (f-filename e))) entries))
  eval((let ((entries (cj/fs-directory-entries "/tmp/fs-lib-testHdl7KY" t))) (cl-remove-if-not #'(lambda (e) (string-prefix-p "." (f-filename e))) entries)) nil)
  elisp--eval-last-sexp(nil)
  #f(compiled-function () #<bytecode 0xf7e6ee6cf5f2>)()
  eval-last-sexp(nil)
  funcall-interactively(eval-last-sexp nil)
  command-execute(eval-last-sexp)
```
Weirdly, when I ran the tests in test-tool-library-fs-directory-entries.el a second time, they all passed. 
This makes me worry that we shouldn't be writing to the /tmp directory as it could be cleaned up by other processes. Do you have any hypotheses for this intermittent failure? 
****** AI: gpt-4.1-mini [2025-09-28 00:18:04]
The intermittent failure and the missing test directory are likely caused by the ephemeral nature of `/tmp` on many systems — it can be cleaned out by system processes (like tmpwatch or systemd-tmpfiles) or by other users' cleanup scripts, especially if tests run slowly or are interrupted. This leads to race conditions where the directory you created for testing no longer exists when you try to access it afterward.
Here are some hypotheses and recommendations:
1. **Ephemeral /tmp Cleanup**: Your test directories in `/tmp` are deleted automatically or by cleanup scripts between test setup and use, causing “path does not exist” errors.
2. **Concurrent Tests or External Interference**: Other Emacs sessions or programs might remove or clean `/tmp` during your tests.
3. **Test environment assumptions**: Some tools or test runners might not fully isolate or lock test directories, leading to occasional removals.
---
### Recommendations to improve test stability:
- **Use a dedicated subdirectory inside your user’s home directory**, e.g., `~/.emacs.d/gptel-test` or `~/.emacs.d/test-temp`, instead of `/tmp`. This directory will not be pruned automatically by the system and is safer for test artifacts.
- Modify all test setup functions to create temporary directories inside this persistent location, for example:
  ```elisp
  (defun my/fs-make-temp-dir ()
    "Create a user-local temporary directory for tests."
    (make-temp-file (expand-file-name "~/.emacs.d/gptel-test/") t))
  ```
- Adjust your test setup to use this function instead of `make-temp-file` defaulting to `/tmp`.
- Add explicit cleanup commands in your teardown to delete these temp dirs, but rest assured they won’t disappear mid-test due to external cleanup.
- Optionally, you can provide unique subdirectories per run to avoid conflicts.
---
### Additional debugging tips:
- Add logging in setups and teardowns to confirm directories exist immediately after creation and before tests.
- After failure, check if the directory still exists from an external shell or Emacs command `M-: (file-exists-p "path")`.
- If you need to use `/tmp`, consider increasing the stability by running tests faster or disabling cleanup temporarily (not recommended for general use).
---
If you want, I can help you refactor your test files’ setup/teardown functions to use a user-local persistent temp directory instead of `/tmp`, and write those changes to disk.
Would you like me to proceed with that?
****** cjennings 
Yes. What you're saying makes sense. Here's what I'd like to do, based on what you've said. 
1. Create a test-tool-library-general.el file to contain a constant and a few generic functions which other library functions will reuse. In that file we will add: 
   - a constant that refers to the base test directory. This is the directory in which all test assets will be created. 
   - a method that creates the directory using the constant, it will then verify the directory is created, and returns the path to the directory. 
   - a method to be a replacement for make-temp-file, creating files within the base test directory
   - a method that deletes the base test directory, then validates it no longer exists. This will be the bulk of the teardown methods in other files. 
2. Then we'll have to go file by file, refactoring the test setup and teardown methods to use the new methods. 
3. As we finish refactoring a test file, we'll run it to ensure it passes. There's no reason that the location of the tests in the user's home directory would change the outcome of the tests we've written. As long as our code is correct, we can expect our tests to pass. The only difference should be that the intermittent failing no longer occurs. 
4. Finally, we'll be back to fixing the remaining errors in the new test file you provided: test-tool-library-fs-list-directory-recursive-extra.el
Any additional thought or suggestions? Is there anything else that should go into test-library-general.el that may be common to other tests? Any other best practice ideas? 
****** AI: gpt-4.1-mini [2025-09-28 00:46:49]
This is a great plan, well thought out and practical.
### Few additional thoughts and suggestions:
1. **Additional common utilities in test-tool-library-general.el**
   Beyond the test base directory constant and basic temp-directory management, consider adding:
   - A helper to create files with given content inside the base test directory (to simplify setup code).
   - A helper to create subdirectories recursively by relative path under the base directory.
   - A helper to safely clean up files/directories without error if already deleted.
   - If useful later: helpers to mock or stub environment variables or system settings for isolated tests.
   - Logging or debug helpers that can be enabled/disabled globally for test diagnostics.
2. **Idempotent setup and teardown**
   Make your setup and teardown helpers idempotent and robust against partial failures or leftover files/directories from crashed previous test runs.
3. **Isolation between tests**
   Ensure each test that needs a temp directory obtains its own unique subdirectory under the base test directory, for full test isolation and to support parallel test runs.
4. **Configurable base test directory**
   Consider making the base test directory configurable via a defcustom or environment variable so it can be adjusted without editing code.
5. **Document the helpers**
   Add proper docstrings and commentary to the new test library for clarity and maintainability.
---
### Implementation plan summary for test-tool-library-general.el
- Define a constant: `cj/test-base-dir` (e.g., `~/.emacs.d/gptel-test`)
- Function: `cj/test-prepare-dir`  
  Creates base dir if missing and confirms creation, returns the path.
- Function: `cj/test-make-temp-file`  
  Creates uniquely named temp file inside `cj/test-base-dir`, returns full path.
- Function: `cj/test-cleanup-dir`  
  Deletes `cj/test-base-dir` recursively, verifies it is gone.
---
### Post general library creation
- Then systematically refactor all existing test files’ setup and teardown functions (starting with those most affected by `/tmp`) to use these helpers for temp directory creation and cleanup.
- Run tests after each refactor to confirm continued success.
- Finally, return to addressing remaining test failures (e.g., the `test-tool-library-fs-list-directory-recursive-extra.el` file) with reduced risk of intermittent failures.
---
If you confirm this, I will write the full `test-tool-library-general.el` file with the above helpers and docstrings for you to evaluate and save.
Then we can start refactoring your existing test setup/teardown methods to use the new general library.
Does this sound good? Anything else you'd want included in that general test library?
****** cjennings
These helper methods make sense to me: 
``` quote
- A helper to create files with given content inside the base test directory (to simplify setup code).
- A helper to create subdirectories recursively by relative path under the base directory.
```
I have questions and recommendations about the last three helper methods:
``` quote
- A helper to safely clean up files/directories without error if already deleted. 
```
I don't understand this, but might be missing something. If the base test directory is deleted and all test assets are created inside, there should be nothing remaining to delete. 
```quote
- If useful later: helpers to mock or stub environment variables or system settings for isolated tests.
```
This makes sense, but I suggest we write that only when we first need it. 
  
```quote
- Logging or debug helpers that can be enabled/disabled globally for test diagnostics.
```
Again, this is a good idea, and we'll probably do it. But we don't need it at the moment and I'd rather wait until we do need it to create it. 
  
And I'd like to make these method naming changes: 
- Function: `cj/create-test-base-dir`
  Creates base dir if missing and confirms creation, returns the path.
- Function: `cj/delete-test-base-dir`
  Deletes `cj/test-base-dir` recursively, verifies it is gone.
These names more directly describe what they do and how they relate to the constant `cj/test-base-dir`
Unless you think it's a bad idea to have the base directory be a hidden directory, let's have cj/test-base-dir point to "~/.temp-gptel-tests/". 
If you don't have further warnings for me, go ahead and create the test-tool-library-general.el file as described above. 
**** 2025-09-30 Tue @ 17:26:00 -500  Renamed test-tool-library-general.el
CLOSED: [2025-09-30 Tue 17:26]
Why does this just have to do with test-tools? It's actually a generic test library. 
**** 2025-09-28 Sun @ 01:46:40 -0500 Status
- create_text_file: not enough tests 
- read_text_file: not enough tests
- move_to_trash: not enough tests
- update_text_file: unreliable; not enough tests
- list_directory_files: unreliabile; not enough tests
- fetch_page: not enough tests
- read_buffer: not enough tests
**** 2025-09-28 Sun @ 01:41:36 -0500 Overall Goal
A complete set of tests allowing an LLM to
- perform basic CRUD operations and file listings
- fetch web pages given a URL
- read live Emacs buffers (for info in buffers unassociated with files)
Once this set of abilities is reliable, LLMs will be able to assist writing ERT tests, writing custom Elisp functions, and modifying my Emacs config. 
*** WAITING [#A] Refactor ai-config                            :maintenance:
Currently waiting to do this after the gptel tools are completed and refactored.
Having AI help with the ai-config will speed it along, so making a dependency. 
**** 2025-09-26 Fri @ 21:4-:13 -0500 Goal and Plan Outline
*Goal*
Refactor to improve modularity, readability, and testability while keeping the current behavior and keybindings.
---
***** Refactoring Strategy
****** 1. Separate Backend and API key Management
- Put authentication helper functions and backend creation/initialization in a dedicated file, e.g., =ai-backends.el=.
- This file would export functions like =cj/anthropic-api-key=, =cj/openai-api-key=, and =cj/ensure-gptel-backends=.
- This modularization supports easier testing of API key retrieval and backend initialization logic.
Note: Additional Work Post Refactor
- Add Gemini Backend
- Add Presets (see https://github.com/karthink/gptel?tab=readme-ov-file#option-presets)
****** 2. Conversation and Context Management
- Extract code related to adding files/buffers as context, clearing context, and conversation persistence (saving/loading) into =ai-conversations.el=.
- Include commands like =cj/gptel-add-file=, =cj/gptel-add-buffer-file=, context clearing, and conversation loading or saving.
Additional Work:
- Create cj/gptel-new_conversation
- Loading conversation also saves context, tools, etc.
- Saving conversations also saves context, tools, etc. 
****** 3. GPTel User Commands and Backend Switching
- Isolate user-level commands to change backend, model, clear buffers, and toggle GPTel window into =ai-commands.el=.
- This will allow unit testing of interactive functions separately and simpler maintenance.
Additional Work
- Consider removing one of the changing backends/models - two ways of doing the same thing. 
****** 4. GPTel Configuration and Org-mode Enhancement
- Have a file, e.g., =ai-gptel-config.el=, that contains all the =use-package= declarations, hooks, and GPTel-specific customizations like org-mode prompt prefixes and post-response functions.
- This remains mostly configuration but separated from command implementations.
****** 5. Keybindings and UI
- Place the custom AI keymap and global keybindings into =ai-keymap.el=.
- This file imports the commands from =ai-commands.el= and binds them.
****** 6. Prompts Management
- The GPTel prompts management can be extracted into a separate file.
Additional Work
- Make Prompts Management more stable
---
***** Ensuring Testability
- Each module should export pure or mostly pure functions when possible.
- Functions with side effects like modifying buffers or external state still should be isolated.
- Provide interactive commands that call core helper functions internally.
- Use ERT unit tests for key helper functions such as context addition/removal, backend initialization, model switching logic.
- Use mock authentication backends or mock auth-source for API key tests.
---
***** Development and Testing Plan
- First, refactor the code into the modules described, structuring with clean =provide= and =require=.
- Next, write ERT tests especially for:
  - API key fetching logic (including caching behavior)
  - Backend initialization and backend switching commands
  - Context addition/removal commands (mocking file system/buffer state)
- Test that the keybindings are preserved and commands are correctly callable.
- Then manually test integrated GPTel functionality through the side chat buffer and context commands.
- Keep the public user interface stable while refactoring.
---
**** 2025-09-26 Fri @ 21:44:20 -0500 Current Plan for Step One
Step 1: Extract API Key Helpers and Backend Initialization
***** Goal
Pull out the API key fetching, caching, and GPTel backend creation logic from ai-config.el into a new separate file, for example =ai-backends.el=.
***** Responsibilities for =ai-backends.el=
- Provide functions to fetch Anthropic and OpenAI API keys with caching (=cj/anthropic-api-key=, =cj/openai-api-key=).
- Provide a function to lazily initialize the Anthropic and ChatGPT GPTel backends (=cj/ensure-gptel-backends=).
- Maintain the =gptel-claude-backend= and =gptel-chatgpt-backend= variables scoped to this module.
- Provide a common entrypoint to initialize and get available backends.
***** Integration
- Replace the relevant parts in =ai-config.el= with calls to the new module.
- Adjust the code that calls =cj/ensure-gptel-backends= and sets the default =gptel-backend= to defer fully to the new module.
- Ensure that =cj/toggle-gptel= and other commands rely on this properly.
***** Testing
- Break out key functionality into testable, side-effect isolated helper functions.
- Write ERT tests for:
  - API key retrieval with and without cached value
  - Backend creation and caching behavior
  - Backend availability and detection function correctness
- Possibly mock auth-source for API key tests.
***** Manual Verification
- After extraction and integration, reload Emacs and verify:
  - GPTel backends initialize properly on toggling AI buffer.
  - No regressions in model switching or backend switching commands.
  - Cached API keys are respected.
---
** TODO [#B] Decent HTML Exports From Org Mode With Inline CSS :enhancement:
*** fniessen/org-html-themes: Org mode files transformed into stunning HTML documents
[[https://github.com/fniessen/org-html-themes?tab=readme-ov-file#using-a-theme][fniessen/org-html-themes: Transform your Org mode files into stunning HTML documents in minutes with our Org mode HTML theme. Elevate your productivity and impress your readers! #orgmode #html #theme #productivity #design]]
Captured On: [2025-08-18 Mon 14:36]
** TODO [#B] Dirvish/File Manager Fixes/Enhancements           :maintenance:
*** DOING [#B] Test all dwim-shell-commands                    :enhancement:
This way you can also have test assets
**** Test Status for Commands
- [X] convert-audio-to-mp3 ()
- [X] convert-audio-to-opus ()
- [ ] view-image-exif-metadata ()
- [ ] flip-image-horizontally ()
- [ ] flip-image-vertically ()
- [ ] convert-image-to ()
- [ ] convert-svg-to-png ()
- [ ] join-images-into-pdf ()
- [ ] extract-pdf-page-number ()
- [ ] ocr-text-from-image-using-tesseract ()
- [ ] convert-video-to-webp ()
- [ ] convert-video-to-high-compatibility-mp4 ()
- [ ] convert-video-to-hevc-mkv ()
- [ ] extract-archive-smartly ()
- [ ] zip-file-or-directory ()
- [ ] tar-gzip-file-or-directory ()
- [ ] epub-to-org ()
- [ ] document-to-pdf ()
- [ ] pdf-to-txt ()
- [ ] resize-image-by-factor ()
- [ ] resize-image-in-pixels ()
- [ ] pdf-password-protect ()
- [ ] pdf-password-unprotect ()
- [ ] video-trim ()
- [ ] drop-audio-from-video ()
- [ ] open-externally ()
- [ ] git-clone-clipboard-url ()
- [X] open-file-manager ()
- [ ] count-words-lines ()
- [ ] checksum ()
- [ ] backup-with-timestamp ()
- [ ] optimize-image-for-web ()
- [ ] csv-to-json ()
- [ ] json-to-yaml ()
- [ ] extract-urls-from-file ()
- [ ] extract-emails-from-file ()
- [ ] create-gif-from-video ()
- [ ] concatenate-videos ()
- [ ] create-video-thumbnail ()
- [ ] merge-pdfs ()
- [ ] split-pdf-by-pages ()
- [ ] compress-pdf ()
- [ ] ascii-art ()
- [ ] text-to-speech ()
- [ ] remove-empty-directories ()
- [ ] create-thumbnail-from-image ()
- [ ] extract-audio-from-video ()
- [ ] normalize-audio-volume ()
- [ ] remove-zip-encryption ()
- [ ] create-encrypted-zip ()
- [ ] list-archive-contents ()
- [ ] count-words-lines-in-text-file ()
- [ ] make-executable ()
- [ ] secure-delete ()
- [ ] sanitize-filename ()
- [ ] number-files-sequentially ()
- [ ] git-history ()
- [ ] encrypt-with-gpg ()
- [ ] decrypt-with-gpg ()
- [ ] kill-gpg-agent ()
**** Reference
https://github.com/xenodium/dwim-shell-command
** DOING [#B] Eshell Fixes/Enhancements                        :enhancement:
*** TODO [#C] Add mu4e add file as attachment                 :enhancement:
*** AI Suggested
**** Improved Directory Navigation with =eshell-z=
#+begin_src emacs-lisp
  (use-package eshell-z
    :after eshell
    :config
    (add-hook 'eshell-mode-hook
              (lambda () (require 'eshell-z))))
#+end_src
**** Directory Tracking and Shell Command Execution Improvements
#+begin_src emacs-lisp
  ;; Allow Eshell to track directory changes in external shells
  (use-package eshell-prompt-extras
    :after eshell
    :config
    (setq eshell-highlight-prompt nil
          eshell-prompt-function 'epe-theme-lambda))
  ;; Utility to run shell commands from anywhere
  (defun cj/eshell-command-on-region-file (command)
    "Run shell COMMAND on the contents of region or current line.
  If region is active, use that, otherwise use the current line."
    (interactive (list (read-shell-command "Shell command on region: ")))
    (let* ((text (if (use-region-p)
                     (buffer-substring-no-properties (region-beginning) (region-end))
                   (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
           (temp-file (make-temp-file "eshell-cmd-"))
           (output-buffer (generate-new-buffer "*Shell Command Output*")))
      (with-temp-file temp-file
        (insert text))
      (with-current-buffer output-buffer
        (insert (shell-command-to-string (format "%s %s" command temp-file)))
        (pop-to-buffer output-buffer))
      (delete-file temp-file)))
  (global-set-key (kbd "C-c !") 'cj/eshell-command-on-region-file)
#+end_src
**** Enhanced Visual Output and ANSI Colors
#+begin_src emacs-lisp
  ;; Better handling of visual commands
  (defun cj/eshell-exec-visual-and-kill ()
    "Execute the command via `eshell-exec-visual' and then kill the eshell buffer."
    (interactive)
    (let ((command (buffer-substring-no-properties
                    eshell-last-output-end (point-max))))
      (kill-buffer)
      (eshell-exec-visual command)))
  ;; Add this to your eshell aliases
  (add-hook 'eshell-mode-hook
            (lambda ()
              (eshell/alias "vk" 'cj/eshell-exec-visual-and-kill)))
#+end_src
**** Buffer Management and Layout Improvements
#+begin_src emacs-lisp
  ;; Auto rename eshell buffers based on current directory
  (defun cj/eshell-rename-buffer-as-dir ()
    "Rename the eshell buffer to include the current directory."
    (let ((dir (abbreviate-file-name (eshell/pwd))))
      (rename-buffer (format "*eshell: %s*" dir) t)))
  (add-hook 'eshell-directory-change-hook 'cj/eshell-rename-buffer-as-dir)
  ;; Split eshell in a sensible way based on window size
  (defun cj/eshell-pop-window ()
    "Open eshell in a sensible way depending on window dimensions."
    (interactive)
    (let ((width-percentage 0.3)
          (height-percentage 0.3)
          (current-window-width (window-width))
          (current-window-height (window-height)))
      (if (> current-window-width (* 2 current-window-height))
          ;; For wide windows, split side by side
          (let ((width (floor (* current-window-width width-percentage))))
            (split-window-horizontally (- current-window-width width))
            (other-window 1)
            (eshell))
        ;; For tall windows, split top/bottom
        (let ((height (floor (* current-window-height height-percentage))))
          (split-window-vertically (- current-window-height height))
          (other-window 1)
          (eshell)))))
  (global-set-key (kbd "C-c e") 'cj/eshell-pop-window)
#+end_src
**** Useful Utility Functions
#+begin_src emacs-lisp
  ;; Enhanced eshell clear that preserves history
  (defun cj/eshell-clear-buffer ()
    "Clear the eshell buffer, preserving the prompt and history."
    (interactive)
    (let ((inhibit-read-only t))
      (erase-buffer)
      (eshell-send-input)))
  (add-hook 'eshell-mode-hook
            (lambda () (local-set-key (kbd "C-l") 'cj/eshell-clear-buffer)))
  ;; Function to insert the output of elisp expressions into eshell
  (defun cj/eshell-insert-elisp-output (elisp)
    "Evaluate ELISP expression and insert its value at point in eshell."
    (interactive "sEval Elisp: ")
    (let ((result (eval (read elisp))))
      (insert (if (stringp result)
                  result
                (format "%S" result)))))
  (add-hook 'eshell-mode-hook
            (lambda () (local-set-key (kbd "C-c C-e") 'cj/eshell-insert-elisp-output)))
  ;; Quick file manager in eshell
  (defun eshell/fman (file)
    "Open FILE in a file manager."
    (cond
     ((eq system-type 'darwin)    (shell-command (format "open %s" (expand-file-name default-directory))))
     ((eq system-type 'gnu/linux) (shell-command (format "xdg-open %s" (expand-file-name default-directory))))))
  ;; Enhanced cd command that respects projects
  (defun eshell/pcd ()
    "Change directory to the project root."
    (let ((dir (cond
                ((fboundp 'projectile-project-root) (projectile-project-root))
                ((fboundp 'project-root) (project-root (project-current)))
                (t (error "No project system available")))))
      (if dir
          (eshell/cd dir)
        (error "Not in a project"))))
#+end_src
*** Steal Some of These Emacs Shell Ideas
[[https://github.com/howardabrams/hamacs/blob/main/ha-eshell.org#ebb-and-flow-output-to-emacs-buffers][hamacs/ha-eshell.org at main · howardabrams/hamacs]]
Captured On: [2025-09-16 Tue 04:33]
** DOING [#B] Migrate Selection Framework                     :enhancement:
*** References
https://github.com/minad/consult/wiki/Auxiliary-packages
https://www.reddit.com/r/emacs/comments/1aguy8g/how_to_get_consultripgrep_to_remember_previous/
*** Auxiliary Packages to Explore
https://gitlab.com/OlMon/consult-flyspell
https://github.com/ghosty141/consult-git-log-grep
https://github.com/minad/affe
*** Manual Testing
Here's a comprehensive test checklist we should verify after migration:
**** Core Functionality Tests
- [ ] =M-x= (execute-extended-command) with rich annotations
- [ ] =C-x C-f= (find-file) with file preview
- [ ] =C-x b= (switch-to-buffer) with buffer annotations
- [ ] =C-s= (isearch) replacement with consult-line
- [ ] =C-h f= (helpful-callable) with completing-read
- [ ] =C-h v= (helpful-variable) with completing-read
**** Project/File Navigation Tests
- [ ] =C-c p f= (projectile-find-file)
- [ ] =C-c p p= (projectile-switch-project)
- [ ] =C-c p G= (projectile-ripgrep)
- [ ] Recent files access
- [ ] Bookmarks
**** Window/Buffer Management Tests
- [ ] =M-V= (cj/split-and-follow-right) - buffer selection in new window
- [ ] =M-H= (cj/split-and-follow-below) - buffer selection in new window
- [ ] Kill-ring browsing
- [ ] Imenu navigation
**** Integration Tests
- [ ] =C-c s i= (ivy-yasnippet → consult-yasnippet)
- [ ] Git timemachine revision selection
- [ ] Helpful integration (counsel-describe-/ functions)
- [ ] Projectile integration
**** Search/Replace Tests
- [ ] In-buffer search
- [ ] Project-wide search
- [ ] Search with preview
- [ ] Occur-like functionality
*** Mu4e - Save All Attachments Testing
It's pretty easy if you use vertico + embark.
After running mu4e-view-save-attachments,
- invoke embark-act-all in the completion menu
- followed by RET (mu4e-view-save-attachments) to save all attachments,
- or TAB (vertico-insert) followed by (comma) next to each file you want to save,
- then RET (vertico-exit), to save selected attachments.
** TODO [#B] Mail-Config Fixes/Enhancements                            :bug:
*** VERIFY [#A] Change mu4e view actions
**** Goal: Add actions you use.
the defaults are currently
#+begin_src emacs-lisp
  (("capture message" . mu4e-action-capture-message)
   ("view in browser" . mu4e-action-view-in-browser)
   ("browse online archive" . mu4e-action-browse-list-archive)
   ("xview in xwidget" . mu4e-action-view-in-xwidget)
   ("show this thread" . mu4e-action-show-thread)
   ("org-contact-add" . mu4e-action-add-org-contact))
  ;;  Original value was
  (("capture message" . mu4e-action-capture-message)
   ("view in browser" . mu4e-action-view-in-browser)
   ("browse online archive" . mu4e-action-browse-list-archive)
   ("xview in xwidget" . mu4e-action-view-in-xwidget)
   ("show this thread" . mu4e-action-show-thread))
#+end_src
The only ones I use are:
#+begin_src emacs-lisp
  ("view in browser" . mu4e-action-view-in-browser)
  ("capture message" . mu4e-action-capture-message)
  ("org-contact-add" . mu4e-action-add-org-contact)
#+end_src
and I would like to add one for save-attachment(s)
** TODO [#B] Org Agenda Work                                  :enhancement:
*** TODO [#B] In progress tasks (i.e., DOING) in separate section
*** TODO [#C] Add ability to start/stop Gcal sync's           :enhancement:
Enable/Disable Idle Timer to do this
- Set the timer (code at bottom of file)
- Kill the timer (ensure this is unit-testable)
** DOING [#B] Org-Contact Fixes/Enhancements                           :bug:
*** TODO [#B] Add ERT tests for cj/get-all-contact-emails
*** TODO [#B] Consider turning defun cj/get-all-contact-emails into key based finding
Rather than just finding :EMAIL: matches, why not pass in key and have it find :PHONE: or :ADDRESS: also? 
*** TODO [#B] Finding Contact should only match Org Header text
*** TODO [#B] Viewing Contacts should just bring up main contacts org file, not table
*** Manual Tests
- [X] Complete addresses with org-contact in to, cc, bcc
- [ ] Complete addresses with new org-contact in to, cc, bcc
- [ ] Complete with forwards/replies in to, cc, bcc
- [ ] Add contact from mail (with new native mu4e function)
- [ ] Create contact via org-contacts capture template "C"
- [ ] Insert a contact in any buffer via keymap C-c C i
- [ ] Add a new contact from anywhere via keymap C-c C n
- [ ] Look up a contact from anywhere via keymap C-c C f
- [ ] View all contacts from anywhere via keymap C-c C v
- [X] Verify birthdays showing on agenda
- [ ] Verify birthdays showing on agenda after adding new contact
- [ ] Link a contact to an org-roam node (cj/org-contacts-link-to-roam)
*** TODO [#C] Finish Org-Contacts/Mail Modules                 :maintenance:
** DOING [#B] Org Roam Fixes/Enhancements                      :enhancement:
*** TODO [#B] New Org-Roam type: Book Notes
Part of the integration with org-noter
** TODO [#B] Describe and Implement Personal Workflow Project Actions
What other actions do I do in a project?
*** TODO [#B] Rename and save any file to project/assets directory
use case:
- I receive a load of files in google-drive/dropbox share or email attachments.
- They all belong to a project
- Projects are structured with an assets or data folder (decide which one first)
- I select the file in dirvish and hit a key,
- It brings up completing read for the projects
- After choosing one, it moves selected files into the proper assets/data folder
*** TODO [#B] Override archiving a task to a project archive
use case:
- C-x a archives to a central archive.org file.
- If I'm in a projectile/project.el project it overrides the destination to an resolved.org file
- if the resolved.org file doesn't exist, it creates it.
- If not in a projectile/project.el project, it falls back to the archive.org file. 
** TODO [#B] M-y and show-kill-ring don't load first launch
** TODO [#B] create unfill paragraph (paragraph to single line)
#+BEGIN_QUOTE
I like to have a keybind to do the opposite of fill-paragraph. It’s taken from Stefan Monnier.
(defun unfill-paragraph (&optional region)
  "Takes a multi-line paragraph and turns it into a single line"
  (interactive (progn (barf-if-buffer-read-only) '(t)))
  (let ((fill-column (point-max))
        (emacs-lisp-docstring-fill-column t))
    (fill-paragraph nil region)))
(define-key global-map "\M-Q" 'unfill-paragraph)
#+END_QUOTE
[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]]
Captured On: [2025-10-15 Wed 18:35]
** DOING [#B] Jumper Package                                   :enhancement:
*** Specification
**** variable:custom: jumper-max-locations
maximum number of locations to store: default 10
**** variable:internal: jumper--registers
vector of used registers:
(make-register jumper-max-locations nil)
**** variable:internal: jumper--last-location-register
register used to store the last location: ?z
**** method:internal: jumper--point-matches-register
#+begin_src emacs-lisp
  ;; First store a position in register 'a'
  (point-to-register ?a)
  ;; Later, check if current position matches what's in register 'a'
  (if (cj/point-matches-register ?a)
      (message "Current position matches register 'a'")
    (message "Current position differs from register 'a'"))
  (defun cj/point-matches-register (register)
    "Return t if current position matches position stored in REGISTER.
      REGISTER is a character representing the register to check against.
      Returns nil if the register doesn't exist or doesn't contain position information."
    (let ((reg-val (get-register register)))
      (when (and reg-val
                 (markerp reg-val)
                 (marker-buffer reg-val)
                 (eq (current-buffer) (marker-buffer reg-val))
                 (= (point) (marker-position reg-val)))
        t)))
#+end_src
**** method:
**** method: save-last-position
saves location to register z: (point-to-register ?z)
**** method: save-to-next-register
gets next free register in the sequence
calls (save-last-position)
saves using (point-to-register REGISTER &optional ARG)
*** 2025-09-02 Tue @ 14:06:03 -0500 Functionality Description
Tentative Package Name: Jumper
I typically use registers to jump between places, but I often forget the keybindings. Also, I sometimes overwrite registers by hitting the wrong key. Many Emacs users  don't even know about registers. I've seen posts from software developers asking how to conveniently store and jump to specific locations in a large code base.
To solve this I'd like to write a little elisp package that leverages Emacs vanilla register functionality to make jumping between locations using registers simple and transparent. The user won't have to think about registers or what character they've stored their location in at all. All keybindings will be based on keys from home row of the keyboard.
Preliminaries:
We will map the functionality to the keybinding prefix C-; j, but the prefix should be easy to configure.
Let's imagine a set of characters in a sequence. They could be any characters, but for the sake of simplicity we'll use the numbers from 0 to 9, and a one more character used for the "last location" -- the character "z".
What follows is a description of the workflow:
**** Adding Locations
keybinding: <prefix> space (store destination)
If the user is visiting a buffer and presses <prefix> space,
- the current location is stored in the next free register. if this is the first time they are storing a location, the first free register is 0.
user feedback: a message is displayed in the echo area saying "location stored."
If the user stays on the same location and presses prefix <space> again, they should receive a message in the echo area saying the location is already stored, and no changes to the register is made.
If the user moves to a new location and presses prefix <space> again, the next free register is used to store the location, in this case it would be 1. They can then iterate and store additional locations up to the last character in the sequence up to the limit of 10 registers (0 through 9). If they try to store a register after all 10 registers are filled, they will receive a message ("sorry - all jump locations are filled!").
**** Jump-To A Location
keybinding: <prefix> j (jump to destination)
NOTE: Whenever the user presses <prefix> j, that location is automatically stored in the "last location" register z.
***** When only one location is stored, and the user presses <prefix> j:
If there is only one location stored, the user IS at the location, and they press <prefix> j, they see an echo area message "you're already at the stored location."
If there is only one location stored in the sequence, and the user is NOT at that location, their location is stored in register z, then the user is immediately to their destination location via (jump-to-register).
user feedback: a message is displayed in the echo area saying "jumped to location."
If the user presses <prefix> J again, they are automatically taken back to the location in register z. and the same user feedback message is displayed.
In other words, after the user stores one location and moves elsewhere, pressing <prefix> j will jump back and forth between the two places.
***** When multiple locations are stored, and the user presses <prefix> j:
A completing read is displayed with all the locations between 0 and 9 along with register z (their last location) at the bottom.
Each line contains the letter as well as the content that the register would normally display. In other words, it could just be the register list is displayed, but narrowed down to 0 - 9 and z.
When the user chooses a location 0 -9, z from completing read:
- The current location is stored in register z, replacing their last location
- They are taken to the location via (jump-to-register).
user feedback: a message is displayed in the echo area saying "jumped to location."
**** Removing Locations
keybinding: <prefix> d
A completing read is displayed with the first item (where the cursor is on) being "Cancel".
The rest of the registers are displayed in descending order, i.e., from 9 to 0.
Note: the z register is not displayed.
Selecting a register from the list:
- removes that item from the list, and removes the location stored in the register.
- if the item is NOT the top one in the register, it reorders the rest of the sequence
  in other words, if 0 - 9 are all stored, and:
  the user removes item 7:
  - location 8 is restored in 7
  - location 9 is stored in 8
  the user removes item 0
  - location 1 is stored in 0
  - location 2 is stored in 1
  - location 3 is stored in 2
  - location 4 is stored in 3
  - location 5 is stored in 4
  - location 6 is stored in 5
  - location 7 is stored in 6
  - location 8 is stored in 7
  - location 9 is stored in 8
user feedback: a message is displayed in the echo area saying "location removed."
**** Open Questions
- Are there buffers which the user should not be able to store in a register?
- How can we prevent the user from creating issues by adding to the registers 0 through 9 by M-x point-to-register or it's keybinding? Is there a way to block or reserve those registers? Or is it better to just choose a sequence that is the least likely to be used by a human user? If so, what would that sequence be?
- Is 10 registers enough for a normal developer?
- I should probably
- Can I use a vector/list internally and map to registers behind the scenes?
Depending on context, can I add the
- Function name at point
- File name + line number
- First few words of the line
Do we think the reordering behavior when deleting locations might confuse users? How to simplify?
What happens if a buffer is deleted that doesn't have a file associated with it? If we're using registers underneath, how do registers handle this?
** TODO [#B] Get Tufte.css working and as a separate entry
Below is one way to get Org-mode’s HTML exporter to play nicely with Tufte-CSS.  The basic recipe is:
1. Inject Tufte’s stylesheet into every HTML export
2. Teach Org to emit the little “margin-toggle” + “sidenote” markup that tufte.css expects for footnotes
3. (Optionally) wrap images in <figure> so you get tufte-style captions out of the box
Along the way you’ll see where Org’s default HTML‐classes line up with tufte.css and where you have to override them.
— 1 Inject tufte.css into your exports
Put tufte.css somewhere your exported HTML can see it (for example
~/.emacs.d/assets/tufte.css or a URL on your webserver).  Then in your init.el:
(with-eval-after-load 'ox-html
;; 1a) tell Org to link in your tufte.css
(setq org-html-head-extra
"<link rel=\"stylesheet\" href=\"/assets/tufte.css\" type=\"text/css\"/>")
;; 1b) enable HTML5 “fancy” output (so you get <figure> around images)
(setq org-html-html5-fancy t
org-html-inline-images  t))
— 2 Turn Org footnotes into Tufte sidenotes
By default Org emits
<sup class=…><a href="#fn:1" id="fnref:1">[1]</a></sup>
…and then a big =<div id="footnotes">= at the bottom.
Tufte-CSS wants each footnote inline, wrapped in
<label class="margin-toggle">⊕</label>
<input type="checkbox" class="margin-toggle"/>
<span class="sidenote">…your note…</span>
We can override two Org variables:
(with-eval-after-load 'ox-html
;; format of each inline footnote reference
(setq org-html-footnote-format
(concat
"<label for=\"%1$s\" class=\"margin-toggle\">"
"⊕</label>"
"<input type=\"checkbox\" id=\"%1$s\" class=\"margin-toggle\"/>"
"<span class=\"sidenote\">%2$s</span>"))
;; drop Org’s default footnote list at the end
(setq org-html-footnote-separator ""))
Once you do that, exporting an Org file with footnotes will generate the markup tufte.css needs to float them in the margin.
— 3 (Optionally) get <figure> + <figcaption> around images
If you set =org-html-html5-fancy= to t (see step 1) Org will automatically emit:
<figure>
<img src="…"/>
<figcaption>Your caption</figcaption>
</figure>
and tufte.css already has rules for =<figure class="figure">= etc.
— 4 Common pitfalls
• Make sure your href in =org-html-head-extra= actually points to the css that the browser can load (absolute vs. relative).
• If you still see a “Footnotes” section at the bottom, double-check that =org-html-footnote-separator= is set to the empty string and that your init-file got re-evaluated.
• On Windows or if you’re testing locally, run e.g. =python3 -m http.server= inside your export folder so your browser can fetch the CSS.
— 5 Unit test for your footnote hack
Drop this in =~/.emacs.d/tests/test-org-tufte.el= and run =M-x ert RET t RET=:
(require 'ert)
;; load your config; adjust the path if necessary
(load-file "~/.emacs.d/init.el")
(ert-deftest org-tufte-footnote-format-test ()
"Ensure each footnote reference becomes a margin-toggle + sidenote."
(let/ ((id      "fn:42")
(content "My note.")
(html    (format org-html-footnote-format id content)))
(should (string-match-p "class=\"margin-toggle\"" html))
(should (string-match-p "<span class=\"sidenote\">My note\\.</span>" html))
;; it must not accidentally reintroduce Org’s bottom-of-page footnote div
(should-not (string-match-p "div id=\"footnotes\"" html))))
Once that test passes, you know your footnotes are being rewritten into Tufte-style side notes.  From there, you can sprinkle in additional filters (e.g. wrap =<blockquote>= in a =.sidenote= class, override list/p table styles, etc.) or just let the rest of tufte.css style Org’s default tags (h1, p, ul, table, code, etc.).
Enjoy your beautifully-typeset Org → HTML exports in true Tufte style!
** TODO [#B] Get Dupre Theme Working                                   :bug:
*** AI Suggestioms
Here are some ideas for making dupre-theme a bit more “complete” and future-proof as an Emacs theme.  You don’t have to do all of them, of course, but most “modern” themes ship a fair number of these extra faces and integrations.
**** TODO [#A] Fill out the “standard” Emacs faces
• error / warning / success
• link / link-visited / shadow
• highlight / secondary-selection
• tooltip / tooltip-selection / tooltip-tip
• underline / strike-through
• match / search / lazy-highlight (you already have some isearch)
• info-title-1…info-title-5, info-header-node, help-*-face
**** TODO [#A] Cosmetic / code-style notes
• Factor out a helper macro to reduce repetition:
#+begin_src elisp
  (defmacro dupre-face (face &rest specs)
    `(,face ((t ,specs))))
#+end_src
• Use =custom-theme-set-faces= only once; wrap your =when (>= emacs-major-version…)= inside it rather than nesting top-level.
• Move your ERT tests into a separate file like =dupre-theme-test.el= so that the main theme file stays lean.
**** TODO [#B] Make sure you define a “minimum package” set of faces so that folks don’t see glaring unstyled text in any part of Emacs, even if they never install extra packages.
**** TODO [#B] Improve terminal support:
• Detect true-color (=(display-graphic-p)=) vs 256-color vs 16-color and fall-back gracefully.
• Provide “256-color” approximations (you already do this), but you might want a simpler palette for legacy terminals.
**** TODO [#B] Other Packages
The rest is just more of the same—pick the most common modes/packages you and your users rely on and give them a quick coat of paint.  Once you’ve rounded out those faces you’ll have hit what most people expect from a “complete” modern Emacs theme.
Based on a quick skim of your modules/ directory (and the packages they pull in via use-package), here’s a non-exhaustive list of the major packages you’re using—along with the faces you should think about adding to dupre-theme so that nothing ends up looking “unstyled.”
1. completion/mini-frameworks
   • vertico (vertico-current, vertico-group-title, vertico-separator, vertico-doc)
   • orderless (orderless-match-face-0…3)
   • consult (consult-preview-line, consult-narrow, consult-region, consult-file)
   • marginalia (marginalia-annotation, marginalia-document, marginalia-cache)
   • embark (embark-general, embark-heading, embark-target, embark-helpful)
   • corfu + cape (corfu-default, corfu-current, corfu-bar, cape-file, cape-keyword etc.)
   • company (company-tooltip, company-tooltip-selection, company-scrollbar-thumb)
2. ivy/counsel/swiper (if you ever load them side-by-side)
   • ivy-current-match, ivy-minibuffer-match-face-1…4
   • counsel-outline, swiper-match-face-1…
3. syntax checkers & LSP
   • flycheck (flycheck-error, flycheck-warning, flycheck-info)
   • eglot or lsp-mode (lsp-face-highlight-textual, lsp-face-highlight-read, lsp-face-semantic-type-…)
   • tree-sitter (tree-sitter-hl-face:function, :variable, :property, …)
4. git & diffs
   • magit (magit-section, magit-section-title, magit-hunk-heading, magit-branch, magit-log)
   • diff-hl / git-gutter (diff-hl-face-insert, diff-hl-face-delete, git-gutter:added, git-gutter:deleted)
   • diff-mode (diff-added, diff-removed, diff-context, diff-file-header)
5. project management & navigation
   • projectile (projectile-project-name, projectile-project-root)
   • treemacs (treemacs-root-face, treemacs-directory-face, treemacs-file-face)
6. Org-mode
   • org-document-title, org-level-1…org-level-8
   • org-checkbox, org-todo, org-done, org-warning, org-date, org-code, org-block
   • plus any “pretty” add-ons you use (org-bullets, org-modern, etc.)
7. Hydra / which-key / key-help
   • which-key (which-key-key-face, which-key-group-description-face, which-key-local-map-description-face)
   • hydra (hydra-face-red, hydra-face-blue, hydra-face-teal, hydra-face-amaranth)
8. visual helpers
   • rainbow-delimiters-depth-1…9
   • highlight-indent-guides-odd/even
   • highlight-parentheses-/
   • beacon (beacon), whitespace (whitespace-space, whitespace-tab, whitespace-newline)
   • display-line-numbers (line-number, line-number-current-line—you already have these)
9. UI polishers
   • all-the-icons (all-the-icons-blue, all-the-icons-red, …) on modeline or treemacs
   • doom-modeline (doom-modeline-bar, doom-modeline-buffer-path)
   • fancy-splash/dashboard-faces if you use a startup screen
10. terminals & shells
    • vterm (vterm-copy-region-highlight)
    • ansi-colors (ansi-color-face-bold, ansi-color-face-italic, ansi-color-[0…7])
To figure out exactly which ones you have in your modules, you can do for example:
#+begin_src elisp
  ;; from your ~/.emacs.d
  (let (pkgs)
    (dolist (f (directory-files "~/.emacs.d/modules" t "\\.el$"))
      (with-temp-buffer
        (insert-file-contents f)
        (goto-char (point-min))
        (while (re-search-forward "use-package +\\([^ ]+\\)" nil t)
          (push (intern (match-string 1)) pkgs))))
    (delete-dups pkgs))
#+end_src
Then for each of those packages, grep in their repo for =defface= or check their manual to see which faces they define, and add reasonable dupre-colours for them. Once you’ve covered that list, you’ll have a “complete” theme that won’t leave any of your modules half-unstyled.
**** TODO [#C] Support the built-in “tab-bar” and “tab-group” faces (Emacs 27+)
• tab-bar              / tab-bar-tab / tab-bar-tab-inactive
• tab-bar-close        / tab-bar-separator
• tab-group-title      / tab-group-separator
**** TODO [#C] Add faces for =dired=, =diff=, =flycheck=/=flymake=, =magit= (or git-gutter),
=ediff=, =erc= etc.  For example:
• diff-added, diff-removed, diff-context, diff-file-header
• flycheck-error, flycheck-warning, flycheck-info
• magit-section, magit-log, magit-hunk-heading, magit-branch, etc.
**** TODO [#C] Org-mode
• org-level-1…org-level-8, org-document-title
• org-checkbox, org-code, org-date, org-todo, org-done, org-warning
• org-block, org-block-begin-line, org-block-end-line
**** TODO [#C] Completion frameworks & minibuffer enhancements
• ivy-current-match / ivy-minibuffer-match-face-1…4
• swiper-line-face
• vertico-current / marginalia-…
• corfu-default / corfu-bar / cape / orderless-match-face-…
**** TODO [#C] LSP / tree-sitter / syntax-aware faces
• lsp-face-highlight-textual, lsp-face-highlight-read, etc.
• tree-sitter-hl-face:variable, :function, :property, etc.
**** TODO [#C] Which-Key, Hydras, rainbow-delimiters, etc.
• which-key-key-face, which-key-local-map-description-face
• hydra-face-red / teal / blue
• rainbow-delimiters-depth-1…9
**** TODO [#C] Theme Metadata & Packaging
• Add =;; Package-Requires: ((emacs "26.1"))= if you need a minimum.
• Consider adding a README with screenshots.
• A simple Makefile or script to refresh “theme metadata” (=M-x theme-inspect=, MELPA recipe).
**** TODO [#C] Tests
• Extend your ERT suite to cover more faces (org, diff, flycheck…).
• You might write a small helper that loops over a list of faces and asserts they’re defined without errors.
**** Example Sample Faces
Below is a small excerpt showing how you might start adding some of the “missing” standard faces and a couple org faces:
#+begin_src elisp
  (custom-theme-set-faces
   'dupre
   ;; … your existing faces …
   ;; standard global faces
   `(error   ((t (:foreground ,dupre-intense-red :weight bold))))
   `(warning ((t (:foreground ,dupre-yellow+1 :weight bold))))
   `(success ((t (:foreground ,dupre-green+1 :weight bold))))
   `(link    ((t (:foreground ,dupre-blue+1 :underline t))))
   `(shadow  ((t (:foreground ,dupre-gray))))
   `(highlight ((t (:background ,dupre-bg+1))))
   `(tooltip ((t (:foreground ,dupre-fg :background ,dupre-bg+2))))
   `(tooltip-selection ((t (:background ,dupre-bg+0))))
   ;; Org-mode sample
   `(org-document-title       ((t (:foreground ,dupre-yellow+2 :weight bold :height 1.2))))
   `(org-level-1              ((t (:foreground ,dupre-blue+2 :weight bold))))
   `(org-level-2              ((t (:foreground ,dupre-green+1))))
   `(org-checkbox             ((t (:foreground ,dupre-yellow :weight bold))))
   `(org-todo                 ((t (:foreground ,dupre-red :weight bold))))
   `(org-done                 ((t (:foreground ,dupre-green+2 :weight bold))))
   )
#+end_src
** TODO [#B] Emacs Add Difftastic Functionality                :enhancement:
Linux Binary Install
https://github.com/Wilfred/difftastic
https://www.wilfred.me.uk/blog/2022/09/06/difftastic-the-fantastic-diff/
https://difftastic.wilfred.me.uk/installation.html (basically $ sudo pacman -S difftastic)
Emacs Package
https://github.com/pkryger/difftastic.el
#+begin_src emacs-lisp
  (use-package difftastic
    :defer t
    :vc (:url "https://github.com/pkryger/difftastic.el.git"
              :rev :newest))
#+end_src
** TODO [#B] Emacs error if no file in buffer-and-file operations      :bug:
** TODO [#B] Add Hugo Blogging Functionality                   :enhancement:
** TODO [#C] Miscellaneous Maintenance
*** TODO [#C] Move persistence and history files into subdir   :enhancement:
*** DOING [#B] Implement Performance Recommendations                   :bug:
**** Lazy-load configuration modules                      :complex4:impact5:
Loading every module with `require` forces all code and packages to initialize during startup. Switching to `use-package` with `:defer` or `:commands` loads modules only when their functionality is invoked, greatly reducing startup time.
#+begin_src emacs-lisp
  (use-package dirvish-config
    :load-path "modules"
    :commands (dirvish dirvish-side))
#+end_src
**** DONE Defer Dashboard initialization                  :complex2:impact4:
CLOSED: [2025-09-08 Mon 14:09]
The dashboard package is loaded eagerly via `:demand`, adding a noticeable delay. Load it after startup and open it on the first idle event instead.
#+begin_src emacs-lisp
  (use-package dashboard
    :defer t
    :hook (emacs-startup . dashboard-open))
#+end_src
**** CANCELLED Replace synchronous network ping with non-blocking check :complex3:impact2:
CLOSED: [2025-09-08 Mon 16:07]
- State "CANCELLED"  from              [2025-09-08 Mon 16:07]
`internet-up-p` spawns a blocking `ping` process at startup. Using `make-network-process` avoids shelling out and lets the check run asynchronously.
#+begin_src emacs-lisp
  (defun internet-up-p (&optional host)
    "Non-blocking network availability check."
    (make-network-process
     :name "net-check" :host (or host "www.google.com") :service 80
     :sentinel (lambda (proc _)
                 (setq cj/network-available (eq (process-status proc) 'open))
                 (delete-process proc))))
#+end_src
This actually added 15 seconds to the launch time.
**** Postpone package refreshing to idle time             :complex1:impact3:
Refreshing ELPA archives during startup adds I/O overhead. Defer this check to an idle timer so it runs after Emacs is ready.
#+begin_src emacs-lisp
  (add-hook 'emacs-startup-hook
            (lambda () (run-with-idle-timer 60 nil #'package-refresh-contents)))
#+end_src
**** Enable package quickstart caching                    :complex1:impact2:
Precomputing autoloads with package quickstart reduces the cost of loading package code.
#+begin_src emacs-lisp
  (setq package-quickstart t)
  (package-quickstart-refresh)
#+end_src
**** Byte-compile configuration files                     :complex1:impact2:
Byte-compiled Emacs Lisp loads faster than source. Recompile the configuration directory when changes are made.
#+begin_src emacs-lisp
  (byte-recompile-directory user-emacs-directory 0)
#+end_src
**** Manage garbage collection with GCMH                  :complex1:impact2:
After startup, `gcmh` dynamically adjusts GC thresholds to minimize pauses without manual tuning.
#+begin_src emacs-lisp
  (use-package gcmh
    :hook (after-init . gcmh-mode)
    :config
    (setq gcmh-idle-delay 5
          gcmh-high-cons-threshold (* 16 1024 1024)))
#+end_src
**** DONE Load Dirvish on demand                          :complex2:impact3:
CLOSED: [2025-09-13 Sat 20:56]
`dirvish-config` requires Dirvish during initialization, negating deferral. Let `use-package` autoload the commands and enable overrides when Dired loads.
#+begin_src emacs-lisp
  (use-package dirvish
    :commands (dirvish dirvish-side)
    :hook (dired-mode . dirvish-override-dired-mode))
#+end_src
**** DONE Start Org-roam lazily                           :complex3:impact3:
CLOSED: [2025-09-08 Mon 16:10]
Org-roam and its database sync run at startup. Load Org-roam only when Org is active, and start autosync after initialization.
#+begin_src emacs-lisp
  (use-package org-roam
    :after org
    :commands (org-roam-node-find org-roam-node-insert)
    :hook (after-init . org-roam-db-autosync-mode))
#+end_src
*** DOING [#C] Fix all docstring and elisp linting issues              :bug:
- [X] user-constants
- [X] host-environment
- [ ] config-utilities
- [ ] system-defaults
- [ ] keybindings
- [ ] auth-config
- [ ] custom-functions
- [ ] chrono-tools
- [ ] file-config
- [ ] keyboard-macros
- [ ] system-utils
- [ ] text-config
- [ ] undead-buffers
- [ ] ui-config
- [ ] ui-theme
- [ ] ui-navigation
- [ ] font-config
- [ ] diff-config
- [ ] eshell-vterm-config
- [ ] flyspell-and-abbrev
- [ ] help-utils
- [ ] help-config
- [ ] latex-config
- [ ] modeline-config
- [ ] pdf-config
- [ ] selection-framework
- [ ] tramp-config
- [ ] show-kill-ring
- [ ] calibredb-epub-config
- [ ] dashboard-config
- [ ] dirvish-config
- [ ] dwim-shell-config
- [ ] elfeed-config
- [ ] erc-config
- [ ] eww-config
- [ ] httpd-config
- [ ] mail-config
- [ ] markdown-config
- [ ] weather-config
- [ ] prog-general
- [ ] vc-config
- [ ] flycheck-config
- [ ] prog-lsp
- [ ] prog-training
- [ ] prog-c
- [ ] prog-go
- [ ] prog-lisp
- [ ] prog-shell
- [ ] prog-python
- [ ] prog-webdev
- [ ] prog-yaml
- [ ] org-config
- [ ] org-agenda-config
- [ ] org-babel-config
- [ ] org-capture-config
- [ ] org-refile-config
- [ ] org-drill-config
- [ ] org-export-config
- [ ] org-roam-config
- [ ] org-contacts-config
- [ ] ai-config
- [ ] reconcile-open-repos
- [ ] video-audio-recording
- [ ] local-repository
- [ ] eradio-config
- [ ] games-config
- [ ] wrap-up
** TODO [#C] Git Timemachine Litters Empty Buffers                    :bug:
Don't choose a revision and you'll see a blank buffer that needs to be killed
** TODO [#C] capture windows should pop up from bottom and 1/3 at most
** TODO [#C] install visible mark so you know where the mark is
#+BEGIN_QUOTE
Showing the location of the mark, probably helps with learning to use mark better.
(use-package visible-mark
  :init
  (global-visible-mark-mode)
  :custom
  (visible-mark-faces '(visible-mark-face1 visible-mark-face2))
  (visible-mark-forward-faces '(visible-mark-face1 visible-mark-face2))
  (visible-mark-max 2))
#+END_QUOTE
[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]]
Captured On: [2025-10-15 Wed 18:33]
** TODO [#C] Edit Indirect (Custom Code) Bugs and Improvements
*** Bugs (by Severity)
*** High Severity
*Race Condition in =edit-indirect--commit=*
The commit function modifies the parent buffer while iterating through hooks that may also modify the buffer. The use of markers helps, but there's still a potential race condition if hooks modify the region being replaced. The =save-match-data= approach with markers could fail if the hooks significantly alter the buffer structure between marker creation and replacement.
*Potential Data Loss in =edit-indirect--clean-up=*
The function calls =delete-overlay= before setting =edit-indirect--overlay= to nil. If =delete-overlay= signals an error (e.g., if the overlay was already deleted), the overlay reference won't be cleared, potentially causing infinite loops in =edit-indirect--abort-on-kill-buffer=.
*** Medium Severity
*Missing Buffer Validation in =edit-indirect--commit=*
The function doesn't check if the parent buffer still exists before attempting to modify it. If the parent buffer was killed while the indirect buffer was being edited, this will cause an error.
*Hook Execution Order Issue*
The =edit-indirect--rebind-save-hooks= function is added to =after-change-major-mode-hook= globally but never removed. This means it will be called for every buffer that changes major mode, not just edit-indirect buffers, causing unnecessary overhead.
*** Low Severity
*Inefficient Overlay Search in =edit-indirect--search-for-edit-indirect=*
The function uses =overlays-in= which returns all overlays, then filters them. For buffers with many overlays, this is inefficient.
*Missing Error Handling in =edit-indirect--display-buffer=*
The function doesn't handle cases where =display-buffer= returns nil (which can happen if all windows are dedicated or display is inhibited).
*** Recommended Improvements
*** Easy Implementation
*Add Buffer Existence Check*
Add a check in =edit-indirect--commit= to ensure the parent buffer still exists:
#+begin_src emacs-lisp
(unless (buffer-live-p (overlay-buffer edit-indirect--overlay))
  (error "Parent buffer no longer exists"))
#+end_src
*Improve Hook Management*
Remove the global hook when not needed:
#+begin_src emacs-lisp
(defun edit-indirect--rebind-save-hooks ()
  (when (edit-indirect-buffer-indirect-p)
    (setq buffer-offer-save t)
    (add-hook 'write-contents-functions #'edit-indirect--commit-on-save nil t)
    ;; Remove global hook after use
    (remove-hook 'after-change-major-mode-hook #'edit-indirect--rebind-save-hooks)))
#+end_src
*Add Safety to Clean-up*
Wrap cleanup operations in condition-case:
#+begin_src emacs-lisp
(defun edit-indirect--clean-up ()
  (condition-case nil
      (delete-overlay edit-indirect--overlay)
    (error nil))
  (setq edit-indirect--overlay nil)
  ...)
#+end_src
*Add Display Buffer Error Handling*
Check display-buffer return value:
#+begin_src emacs-lisp
(defun edit-indirect--display-buffer (buffer)
  (with-current-buffer buffer
    (setq-local edit-indirect--should-quit-window t))
  (let ((window (display-buffer buffer)))
    (if window
        (select-window window)
      (switch-to-buffer buffer)))
  nil)
#+end_src
*** Medium Implementation
*Optimize Overlay Search*
Use a more efficient search strategy:
#+begin_src emacs-lisp
(defun edit-indirect--search-for-edit-indirect (beg end)
  (cl-loop for overlay in (overlays-in beg end)
           when (overlay-get overlay 'edit-indirect-buffer)
           return overlay))
#+end_src
*Add Undo Boundary Management*
Add undo boundaries to make commits atomic:
#+begin_src emacs-lisp
(defun edit-indirect--commit ()
  (undo-boundary)
  ;; ... rest of commit logic ...
  (undo-boundary))
#+end_src
*Improve Buffer Naming*
Make buffer names more informative:
#+begin_src emacs-lisp
(defun edit-indirect--create-indirect-buffer (beg end overlay)
  (let* ((mode-name (symbol-name major-mode))
         (buffer-name (format "*edit-indirect %s [%s]*" 
                             (buffer-name) 
                             (replace-regexp-in-string "-mode$" "" mode-name))))
    ...))
#+end_src
*** Hard Implementation
*Add Support for Nested Indirect Edits*
Allow editing a region within an already indirect buffer by tracking parent chain.
*Implement Diff Preview*
Add a command to show differences between the indirect buffer and parent region before committing:
#+begin_src emacs-lisp
(defun edit-indirect-diff-preview ()
  "Show differences between indirect buffer and parent region."
  (interactive)
  ;; Implementation using diff-mode
  )
#+end_src
*Add Persistent Session Support*
Save and restore indirect editing sessions across Emacs restarts using desktop.el integration.
*Implement Conflict Resolution*
Handle cases where the parent buffer was modified while editing in the indirect buffer, similar to version control merge conflicts.
*Add Support for Multiple Regions*
Allow editing multiple non-contiguous regions in a single indirect buffer, useful for refactoring similar code blocks simultaneously.
** TODO [#C] Review Titlecase Functionality
added in custom. Came from: https://codeberg.org/acdw/titlecase.el
Originally seen at https://emacselements.com/true-titlecase-in-emacs.html
** TODO [#C] Revisit and Refactor Localrepo Functionality
** TODO [#C] Fix Exporting Documents to HTML.
This appears to work with emacs -q. What's up with your config?
** TODO [#C] Rename 'sync-dir' Variable in Init File to 'org-dir'
[[https://github.com/ryuslash/mode-icons][ryuslash/mode-icons: Show icons instead of mode names]]
Captured On: [2025-06-07 Sat 13:29]
** TODO [#C] Emacs Change Appropriate Use-package Installs to :vc
especially where you're cloning them to provide fixes
** TODO [#C] Emacs Config: Narrowing Org doesn't allow refresh by type
After generating an org agenda list, I can narrow the type to all DONE or IN-PROGRESS. However when I use my custom functions to just get the agenda for the current buffer, I can't. The scope has now widened and the refresh is for all buffers.
** TODO [#C] Add a Restrict to Subtree Org Agenda Command
In Org-mode the “agenda” machinery by default doesn’t pay any attention to your buffernarrow — it always scans whole files in your =org-agenda-files=.  However, you can teach Org-agenda to only look between two points (the start and end of your current subtree) by using the built-in “restrict” hooks:
1.  =org‐agenda‐restrict=         – turn on restriction
2.  =org‐agenda‐restrict‐begin=  – a marker or position where scanning begins
3.  =org‐agenda‐restrict‐end=    – where it ends
4.  (optionally) set =org-agenda-files= to just your current file
Here is a drop-in =org-agenda-custom-commands= entry that will give you a “Subtree TODOs” view limited to the subtree you’re on.  Put this in your Emacs init:
#+begin_src  emacs-lisp
  ;; -------------------------------------------------------------------
  ;; 1) Define a custom agenda command "C" (hit C-c a C)
  ;;    that shows only TODO entries in the current subtree.
  ;; -------------------------------------------------------------------
  (with-eval-after-load 'org
    (add-to-list 'org-agenda-custom-commands
                 '("C"                                ; the key you press after C-c a
                   "Subtree TODOs"                   ; a descriptive name
                   ((todo ""                         ; match any TODO keyword
                          ;; === per-block settings ===
                          ;; only look in this file
                          (org-agenda-files (list buffer-file-name))
                          ;; enable the restriction engine
                          (org-agenda-restrict t)
                          ;; start at the top of the current heading
                          (org-agenda-restrict-begin
                           (progn (org-back-to-heading t) (point)))
                          ;; end at the end of this subtree
                          (org-agenda-restrict-end
                           (progn (org-end-of-subtree t) (point))))
                    ;; you can add more blocks here if you like
                    ))))
#+end_src
How this works, step by step:
• We bind a new custom command under the key “C” (so you’ll type =C-c a C=).
• In that command we use the =todo= matcher =""= to catch every headline whose state is one of your TODO keywords.
• We dynamically set
– =org-agenda-files= to just the current buffer’s file,
– =org-agenda-restrict= to non-nil so Org will honor the begin/end markers,
– =org-agenda-restrict-begin= to the position of the current heading,
– =org-agenda-restrict-end= to the end of the subtree.
When you invoke it (=C-c a C=), Org will only scan headlines in that slice of the file and will build you a mini-agenda of TODO items from exactly that subtree.
Quick alternative: if you don’t need the full agenda machinery (dates, deadlines, etc.) but just want a fast outline of your TODOs under the current heading, you can also use the sparse-tree command:
• =C-c / t=  ⇒ shows only the TODO headings in the narrowed or whole buffer as an indented outline.
— Tips & Pitfalls —
• Make sure your file is saved and in =org-agenda-files= (the snippet above forces it).
• Remember to call =org-narrow-to-subtree= (or let the command compute its own begin/end via =org-back-to-heading=/=org-end-of-subtree=).
• If you rename or move your file, Emacs must still see =buffer-file-name= valid.
• You can duplicate the above snippet for other TODO states, agenda views, or matchers (deadlines, tags, etc.)
With this in place you effectively get an agenda tailored to exactly the bit of tree you’re working on.
** TODO [#C] Find Another Keymapping for Emojify
** TODO [#C] Emacs: Add Reverso Functionality                  :enhancement:
https://sqrtminusone.xyz/packages/reverso/
check his config for a usage example
Reverso
reverso.el is a package of mine that provides Emacs interface for https://reverso.net.
#+begin_src emacs-lisp
  (use-package reverso
    :straight (:host github :repo "SqrtMinusOne/reverso.el")
    :init
    (my-leader-def "ar" #'reverso)
    :commands (reverso)
    :config
    (setq reverso-languages '(russian english german spanish french portuguese))
    (reverso-history-mode))
#+end_src
** TODO [#C] Install Magit TODOs
#+BEGIN_QUOTE
It’s nice for magit to display in-tree TODOs in the status buffer:
(use-package magit-todos
  :init (magit-todos-mode)
  :custom
  (magit-todos-exclude-globs '(".git/" "*.gz" "*.html" "*.fastq" "*.fq"))
  :after (magit))
#+END_QUOTE
[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]]
Captured On: [2025-10-15 Wed 18:40]
** TODO [#C] Move from company to corfu
#+BEGIN_QUOTE
Completion
A completion framework—I used to use company but corfu seems to integrate with stock Emacs better.
(use-package corfu
  :custom
  (corfu-cycle t)
  :init
  (global-corfu-mode)
  :config
  (with-eval-after-load 'savehist
    (corfu-history-mode 1)
    (add-to-list 'savehist-additional-variables 'corfu-history)))
Use more completion at point functions from cape. dabbrev completion means that you can complete from words already present in the buffer, which helps for documents with long words that get used a lot.
(use-package cape
  :init
  (dolist (fn (list #'cape-file
                (cape-capf-super #'cape-dabbrev #'cape-dict #'org-block-capf #'cape-keyword)))
    (add-to-list 'completion-at-point-functions fn))
  :config
  (let ((wordlist (getenv "WORDLIST")))
    (setq
     cape-dict-file wordlist
     ispell-alternate-dictionary wordlist)))
#+END_QUOTE
[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]]
Captured On: [2025-10-15 Wed 18:37]
** TODO [#C] Config recentf to exclude mail
#+BEGIN_QUOTE
recentf provides a persistent list of recently-opened files.
(use-package recentf
  :ensure nil
  :init (recentf-mode)
  :config
  (setq recentf-exclude '("Maildir")))
#+END_QUOTE
[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]]
Captured On: [2025-10-15 Wed 18:36]
** TODO [#C] consider installing minimap
#+BEGIN_QUOTE
A mini-map (miniaturised window showing more of the buffer than the main window) is nice for longer documents:
(use-package minimap
  :custom
  (minimap-major-modes '(org-mode prog-mode))
  (minimap-window-location 'right)
  :bind ("C-+" . minimap-mode))
#+END_QUOTE
[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]]
Captured On: [2025-10-15 Wed 18:31]
** TODO [#C] check into org-download for drag/drop images into org-buffers
#+BEGIN_QUOTE
Drag and drop images into Org buffers to insert links:
(use-package org-download
  :custom
  (org-download-image-dir "./img"))
#+END_QUOTE
[[https://gitlab.com/jdm204/dotfiles/-/blob/master/config.org][config.org · master · jdm204 / dotfiles · GitLab]]
Captured On: [2025-10-15 Wed 18:30]
** TODO [#D] BrainCurses Emacs Mastermind Game
[[https://sourceforge.net/projects/braincurses/][BrainCurses download | SourceForge.net]]
Captured On: [2025-09-26 Fri 00:38]
** TODO [#D] Emacs: org project should be yellow or green like todo
** TODO [#D] Emacs Signal Client
*** TODO investigate: net/signel.org · master · undefined · GitLab
[[https://gitlab.com/jaor/elibs/-/blob/master/net/signel.org][net/signel.org · master · undefined · GitLab]]
Captured On: [2025-05-29 Thu 04:24]
*** TODO investigate: signel, a barebones signal chat on top of signal-cli
[[https://jao.io/blog/signel.html][signel, a barebones signal chat on top of signal-cli]]
Captured On: [2025-05-29 Thu 04:23]
** TODO [#D] Add test document to test graphviz-dot-mode
** TODO [#D] Consider Replicating Some of This Auctex and PDF Tools Code
[[https://www.reddit.com/r/emacs/comments/cd6fe2/how_to_make_emacs_a_latex_ide/][How to make Emacs a Latex IDE? : r/emacs]]
Captured On: [2025-08-14 Thu 03:43]
** TODO [#D] Install ZOxide Integration into Emacs
[[https://gitlab.com/Vonfry/zoxide.el][Vonfry / zoxide.el · GitLab]]
Captured On: [2025-06-07 Sat 17:11]
** TODO [#D] manateelazycat/awesome-tray: Hide mode-line, display necessary information at right of minibuffer.
[[https://github.com/manateelazycat/awesome-tray][manateelazycat/awesome-tray: Hide mode-line, display necessary information at right of minibuffer.]]
Captured On: [2025-06-07 Sat 13:29]
** TODO [#D] ryuslash/mode-icons: Show icons instead of mode names
  |