Content AND Presentation

Last major edit (later minor edits)

Summary: -CategoryEnglish

Changed:

< <journal 1 "^\d\d\d\d-\d\d-\d\d.*(en)$">

to

> <journal 1 "^\d\d\d\d-\d\d-\d\d.*(en)">


2026-11-02 Skill shed

A few weeks ago I mentioned how I started writing agent skills for use with my LLM-based coding assistant. It turned out that I encountered a few rough edges pretty quickly.

Being accustomed to “normal” programming, I naturally extended my normal approach to code to skills. Of course, being non-deterministic, they are not really “executable code” like Lisp, Python or JavaScript. Still, I figured that applying the usual practices to them is a good idea. This means (among others) keeping them in version control and commenting them.

Unfortunately, both are difficult with skills. First of all, they should reside in ~/.your_favorite_coding_agent/skills. Having a Git repo there is possible, but I don’t like the idea. For example, what if I have all my personal coding projects under one directory and my job projects under another? Thanks to such a structure I can use conditional includes and have my personal email address attached to commits in side projects, and my job email address in job projects. Then a place like ~/.agents/skills is yet another directory where some of my projects would reside.

Of course, I could probably symlink the directories where I develop my skills to the place the LLM sees them – but that would not help with another issue I have. When working on code, I can play around in my Git repo, not worrying about messing stuff up – I can always git reset --hard, and what happens in the working directory stays in the working directory and doesn’t influence the production environment. Having separate “development” and “production” environment for skills seems a natural thing to do – but that means that I have now two copies of each skill, and when I modify the “development” environment, I need to “deploy” any changes to “production”. If that seems like an overkill, it’s because it probably is – but consider this scenario. Imagine that several people work on a project, and they’d like to share the project skills. This is perfectly doable (and in fact, the several coding assistants I checked support that), but what if different team members use different coding agents? Most of them, but not all, look for skills in .agents/skills – so depending on what agent a person uses, they should copy the skill file(s) to a different place. And when there are two copies of a file, there is always a danger that someone will make some edits in on of them and other edits in the other one, leading to confusion (and data loss when the skill is blindly copied from “dev” to “prod”).

Another issue I have with skills is that they are supposed to be written in Markdown, which has serious problems. Of course, they are more or less free-form text, but Markdown is expected – if only because their entry point is always called SKILL.md. This means that if you want to write your skills in a superior format – for example, Org – you’re out of luck: SKILL.org will not be used by the agent (and writing a one-line SKILL.md telling to look in SKILL.org feels really stupid!).

All these problems are annoying, but they are not deal-breakers. The last one, however, is – at least for me. You just cannot comment skills! Markdown has no notion of “comments” (well, of course, there is no notion of “Markdown”, either – it is just a common name for a large group of similar but subtly different formats…). If you want to mark things like decisions made and their rationales, or decisions rejected, or “to-do” items mentioning stuff you’d like to add to your skills later – the only thing you can do is put them in a separate file (or live with the fact that the “comments” which were supposed to be only read by humans will be also ingested by the LLM, potentially confusing it and using up the precious context).

Of course, being a programmer, I decided to do something about these problems. Some time ago, I set out to write a set of tools for working with skills, which would help with these issues, and this way Skill shed was born.

It is a Node.js script whose main feature is the “deploy” command – copying the skill file(s) from their “source” directory to the “target”. Optionally, it can transform Markdown files and strip comments from them (comments use the well-known HTML syntax). The script takes care not to overwrite anything in the target directory not put there by itself, so if you accidentally edit your skill in the target directory, the deploy command will warn you about it and abort. Alongside SKILL.md and other files meant for the LLM, you need to create an .env file telling skill-shed where the target directory is – and that file may be git-ignored (and thus be different for each team member). The creation of this file can be automated. Also, you can lint skills, that is, check for a number of potential issues with them (like malformed frontmatter, too long description, unclosed Markdown fences or HTML comments, files in the skill’s directory not referenced in SKILL.md, dead URLs in the skill etc.) The plan is to also allow writing your skills in the SKILL.org file and have them converted to Markdown on deployment (that, however, is not yet implemented).

The project itself is an experiment – it is one of my first projects written with an LLM (although I would not call it “vibe-coded” – I carefully reviewed every single commit!). One of the things I usually do when I delegate coding to an LLM is an extensive test suite. In fact, in LLM-assisted projects I often insist on 100% test coverage, which sounds crazy but actually makes sense. Also, I don’t use things like Lodash or Ramda – coding in vanilla JS is less convenient for a human but doesn’t make that much difference for an LLM, and thus I can afford the luxury of having as few dependencies as possible. (That is way less important in Python, which adheres to the “batteries included” philosophy and has a really extensive standard library; using JS is more like buying a piece of furniture but without the screws and glue necessary for assembling it…)

Anyway, if you find this project useful, use it, star it on GitHub, fork it, open a PR or issue – I really hope it’s useful not only for me! I cannot promise I will review your PRs etc., but I’m definitely willing to make this tool more useful.

See you next week!

CategoryEnglish, CategoryBlog, CategoryLLM

Comments on this page

2026-10-26 Unicode punctuation in Emacs

I have a rather complicated relationship with non-ASCII punctuation. On the one hand, I like to keep my files (for example Org mode files) clean of Unicode stuff – especially emojis – with the obvious exception of Polish diacritical characters (“ąćęłńóśźżĄĆĘŁŃÓŚŹŻ”, if you are curious). On the other hand, I am a big fan of long dashes (though I very much prefer en-dashes to em-dashes); I also like using “proper” quotation marks.

I used to use Typopunct to enter these characters easily, but recently I tried to reduce my need for external packages. It turns out that Emacs already has support for half of what I need in the form of the Electric Quote mode.

The Electric Quote mode works somewhat different than Typopunct – the default way of entering the left and right quotes is by using ` and ' for single quotes and the same characters doubled for double ones. This is somewhat reminiscent of “classical” (pre-UTF-8) TeX, but it’s not something I’m accustomed to. I prefer to just type " and have Emacs replace it by the “correct” double quote. That’s easy enough:

(setopt electric-quote-replace-double t)
(setopt electric-quote-context-sensitive t) ; for single quotes

Since I don’t use single quotes very often, I don’t care about them as much, but making them work doesn’t cost much. (By the way, I can always say C-x 8 [, C-x 8 ], C-x 8 { and C-x 8 } to insert Unicode quotes, whether Electric Quote mode is on or not. Polish opening quotes are trickier – the Emacs manual doesn’t mention it, but they can be inserted using C-x 8 , ".) So, the only remaining problem is switching between Polish and English quotes. Like I mentioned a few months ago, my Org files sometimes contain both languages, so setting a language per file is not practical for me. When I used Typopunct, I had a simple function to switch languages, and it was bound to the same keybinding I used to toggle Typopunct, but with a prefix argument. That was a bit fiddly – with a prefix argument, it turned Typopunct on and switched the language, without it, it toggled the mode, and I never remembered the exact behavior. Let’s solve my problem with Electric Quote mode, but with a simpler semantic – instead of using a prefix argument, I’ll just make it a three-way toggle.

(require 'cl-lib)

(defvar mbork-electric-quote-language 'off
  "The language of electric quotes - `off', `en' or `pl'.")

(defun mbork-electric-quote-toggle ()
  "Toggle Electric Quote mode behavior.
This is a three-way toggle – the possible states are \"off\",
\"English\" and \"Polish\".  Note: only double quotes are influenced by
the language setting."
  (interactive)
  (cl-ecase mbork-electric-quote-language
    (off (electric-quote-mode 1)
         (setq electric-quote-chars '(?‘ ?’ ?“ ?”))
         (setq mbork-electric-quote-language 'en))
    (en (electric-quote-mode 1)
         (setq electric-quote-chars '(?‘ ?’ ?„ ?”))
         (setq mbork-electric-quote-language 'pl))
    (pl (electric-quote-mode -1)
        (setq mbork-electric-quote-language 'off)))
  (message "Electric quote mode set to %s"
           (cl-ecase mbork-electric-quote-language
             (off "off")
             (en "English")
             (pl "Polish"))))

One problem with this code is that the Polish opening quote does not work with the Electric Pair mode. This is easy to fix, even if that fix is not particularly elegant:

(push '(?„ . ?”) electric-pair-pairs)

The last thing I need to do is to bind mbork-electric-quote-toggle to some key. Since f8 is my prefix to various toggles, I figured that f8 " is the right choice:

(bind-key "<f8> \"" #'mbork-electric-quote-toggle)

(Actually, in my config it looks a bit different, since I have a transient installed on f8, but that is another story. That also allows me to make it repeating (via :transient t), so that f8 " " toggles twice etc. As I’ve written two weeks ago, it’s possible to do that without transient.)

Of course, that leaves the issue of en-dashes. This, however, is a non-issue for me, since my keyboard layout binds them on AltGr+- (that is, right alt and the hyphen).

And that’s it for today!

CategoryEnglish, CategoryBlog, CategoryEmacs, CategoryOrgMode

Comments on this page

2026-10-17 Using timers to perform weekly tasks

There are some things I do on a regular basis, and of course I try to automate as much as possible so that I don’t need to remember them. One of these things is a weekly review. A part of my weekly review is checking how much time I’d spent on various things during the past week. Of course, I use Org-mode clock tables for that. I have this piece of Elisp

(org-dblock-update '(4))

which updates all clock tables in the current buffer, and I run it in the Org file before I start my review.

However, this takes some time (slightly less than a minute, but still). My usual procedure is that I evaluate that form some time after I get up on Sundays (I do my review on Sunday morning), then I go to the bathroom or something, and when I’m back, my clock tables are updated.

I thought that it would be cool if Emacs did it itself when I open my laptop on Sunday morning. Emacs has the concept of idle timers which run after it is “idle” for some time (meaning the user didn’t do anything). My idea is that if Emacs is idle for, say, a minute, it’s probable that I am away from keyboard, so I may not come back within another minute. It is then relatively safe to run something which could take a bit longer.

Of course, I definitely do not want Emacs to update my clock tables every time I don’t do anything in it for one (or even ten) minutes – that would be wasteful and potentially annoying. I only want to do this on Sundays, and only once per Sunday.

This means that I need my idle timer to be set up only on Sunday mornings. This means that I need a regular timer to set up an idle timer.

Now, I can just set up a repeating timer with a period of one week, aimed at, say, 3am on Sundays. I don’t turn my laptop off (usually), just put it to sleep, so Emacs is basically open all the time. This means that my “regular” timer would run as soon as I open my laptop, say on Sunday at about 5:30am (which is when I usually sit down to my weekly review). This would break if I actually turned my laptop off on Saturday evening and turned it back on on Sunday, since the timer set up for 3am would be skipped. If I were worried about such a case, I could add a check in init.el to make sure that the idle timer is set up when I turn my laptop on on Sunday morning. (In fact, I could even update the clock tables in my init.el – since I start Emacs once every few days at most, I don’t really care about longer startup times.) I don’t expect to turn my laptop off on Saturday evening, though, so let’s concentrate on the initial idea of an idle timer.

Note that I don’t need to write any code to find when 3am next Sunday falls – Emacs can already do that using the excellent org-read-date function: (org-read-date t t "Sun 3:00"). Of course, in order to be able to do that, I need to (require 'org) first.

(require 'org)

(defun mbork/update-clock-tables-for-weekly-review ()
  "Update clock tables for the weekly review."
  (message "Updating clock tables in personal.org...")
  (let ((inhibit-message t))
    (with-current-buffer "personal.org"
      (org-dblock-update '(4))))
  (message "Updating clock tables in personal.org...done"))

(defun mbork/set-up-clock-tables-update ()
  "Set up updating clock tables with an idle timer."
  (run-with-idle-timer
   60 nil
   #'mbork/update-clock-tables-for-weekly-review))

(run-at-time
 (org-read-date t t "Sun 3:00")
 (* 60 60 24 7)
 #'mbork/set-up-clock-tables-update)

As is often the case, after writing this code I learned that it doesn’t work as I expected. The problem is simple. When mbork/set-up-clock-tables-update is run after I open my laptop, the time it was asleep is also counted as idle time, so the clock tables are updated as soon as I open my laptop – definitely not what I want.

At first I thought that I could attach mbork/set-up-clock-tables-update to post-command-hook in my run-with-timer invocation, and make that function remove itself from that hook. It is definitely not an elegant way, but at least it could work. However, it dawned on me that there might be a better way. If only I could find a way to reset the idle timer (without the user doing anything), I could add such a reset to mbork/set-up-clock-tables-update. I couldn’t find anything about resetting the idle timer in the Elisp manual, and grepping the sources didn’t give any obvious hints. I asked an LLM how to do that, and it suggested (setq unread-command-events '(t)) – however, it was not able to explain why I should use (t). I tried reading the docs, and it seemed to me that the (t) was a typical hallucination. (I also asked another LLM, and the answer was a hilarious back-and-forth of “do it this way… no, wait, that doesn’t exist, this way is the right one… no, it doesn’t exist, either”, ended by a suggestion of using (discard-input). After some experiments, I arrived at something that seemed better to me, that is, (setq unread-command-events nil).

I ended up asking on the Emacs mailing list, and after a false start due to my poor wording of the question, I’ve received a few very interesting answers. (Protip: if you want to ask a question on one of the GNU mailing lists, and you expect a need to link to the ensuing thread, don’t ask near the end of a month, since apparently there is no way to have a single link to a thread spanning more than one month.) First of all, Eli Zaretskii told me that the (setq unread-command-events nil) is rather fragile. I didn’t understand why, but it’s Eli, so it’s safe to assume that he does know what he’s talking about. The solution he suggested was very clever – a bit tricky but clearly robust. His idea was to set up a “normal” timer which would then set up an idle timer for 60 seconds. This means that when I wake up my laptop on Sunday morning, this is what will happen. The “Sunday 3:00am” timer will fire and set up another timer. That timer will fire in 60 seconds and set up yet another timer, set up to fire after 60 seconds of idleness. So, if I just open my laptop and do nothing else, the idle time from the whole night will accumulate (so to speak) and the idle timer will run as soon as it is set up, that is, a minute after the laptop was opened. On the other hand, if I do anything in Emacs during that 60 seconds after opening my laptop, my action will reset the idle time, and the idle timer will properly wait until 60 seconds of idleness. Phew! I admit that stacking three timers is a bit convoluted (as you can see from my slightly awkward attempts to explain the whole idea in English), but I can see no reason for this contraption to fail. So, let’s get coding!

(require 'org)

(defun mbork/update-clock-tables-for-weekly-review ()
  "Update clock tables for the weekly review."
  (message "Updating clock tables in personal.org...")
  (let ((inhibit-message t))
    (with-current-buffer "personal.org"
      (org-dblock-update '(4))))
  (message "Updating clock tables in personal.org...done"))

(defun mbork/set-up-clock-tables-update-after-60-seconds-idleness ()
  "Set up an idle timer to update clock tables."
  (run-with-idle-timer
   60 nil
   #'mbork/update-clock-tables-for-weekly-review))

(defun mbork/set-up-clock-tables-update-in-60-seconds ()
  "In 60 seconds, set up an idle timer to update clock tables."
  (run-with-timer
   60 nil
   #'mbork/set-up-clock-tables-update-after-60-seconds-idleness))

(run-at-time
 (- (org-read-date t t "Sun 3:00")
    (float-time))
 (* 60 60 24 7)
 #'mbork/set-up-clock-tables-update-in-60-seconds)

And from now on, every Sunday on 3am (in reality, as soon as I open my laptop) Emacs will update the clock tables when I leave it for a minute or more. This way, I can open my laptop on Sunday morning, do things like capture my weight, then go to the bathroom – and assuming I spend at least about two minutes there, come back to updated clock tables. Yay for automation!

One final remark is that one of the answers on the mailing list prompted me to update my Emacs. It turned out that after I blogged about some cool new things on the master branch last December, some new cool things landed! One of them is a new built-in package called system-sleep. One of its features is that it allows to attach Elisp functions to pre-sleep and post-wake events. Note that the pre-sleep event is potentially fragile because of reasons outside Emacs – this is what the manual says:
Handling ‘pre-sleep’ events should be done as fast as possible and avoid user prompting. Systems often grant a very short pre-sleep processing interval, typically ranging between 2 and 5 seconds. The system may sleep even if your processing is not complete, so be sure you do as little as possible. For example, your function could close active connections or serial ports.

I don’t think these will be useful in my case – checking if it’s Sunday morning and whether the clock tables were updated today or not every time I open my laptop is certainly possible but even more complex than what I have above. Still, it’s good to know that system-sleep exists, since it might come in handy some day. As you can see, contrary to some opinions, Emacs is far from dead and new useful things are being implemented all the time!

CategoryEnglish, CategoryBlog, CategoryEmacs

Comments on this page

More...

CategoryBlog