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)">
As I’ve mentioned some time ago, I’ve been using Claude Code for some time now.
Now, a disclaimer is in place. I’m still a bit skeptical about the current “AI” hype – in particular, I really avoid using the term “AI”. I consider it a very misleading marketing gimmick. When non-technical people hear the phrase “artificial intelligence”, a computational model capable of generating more or less random sequence of letters which sometimes create a semblance of “understanding” or “intelligence” is probably not what they think. Well, I am really a layman, too – I have some extremely vague understanding what happens inside these “chatbots”, but other than that they seem a bit magical to me as well – but my point is, they are really closer to a giant spreadsheet with billions (or even more) of cells than to Skynet.
Anyway – even I have to admit that what Claude Code is able to do is pretty impressive. However, I quickly discovered that the OOTB experience is less than ideal. One of the easier ways to enhance Claude (and other LLMs, apparently) is by writing skills, which are basically reusable prompts or procedures for performing some tasks, written in natural language.
Compared to classic programming, writing skills is, well, a slightly different skill. Unlike hooks, they are not deterministic, and even though Claude has a way to “invoke” a skill directly, by typing a slash followed by the skill’s name, they can be invoked by a prompt which Claude interprets as suggesting using a skill, which is cool but much less certain.
From what I have learned so far, one of the biggest challenges when using LLMs in general and agentic coding in particular is “context management”. LLMs have very limited “attention span”, and giving them too much data makes them work worse. From skills perspective, this means that they should be as concise as possible. Two nice tricks that can help with that are: skill descriptions and dividing skills into several files. First of all, every skill file has a “description” field. All installed skills’ descriptions are preloaded at the beginning of the session, so providing concise descriptions clearly describing when Claude should use a skill is a crucial step. Besides, if a skill is really large – or has substantial parts which are needed really seldom – it may make sense to move these parts to separate files, and link to them from the main skill file. (Apparently, it is enough to tell Claude something along the lines of “When the user requests …, read the … file”.)
One problem with guidelines like this is that it is very difficult to know whether they are genuinely useful tips or just superstitions. The fact they are officially recommended gives some hope. Interestingly, Anthropic released a tool which helps with developing and evaluating skills. Reading through the files in its repo is a mind-blowing experience: the tool is itself a skill, and I have to say that the fact that it works (and I tried it out!) seems magical.
After all that intro, let me share one of the first skills I created (with help from Claude itself, of course). It is very simple in the sense that it only consists of one SKILL.md file, but it already proved itself to be very useful a few times on the first day I installed it!
It is often said that LLMs are generally very agreeable – they tend to flatter the user more than criticize them. Connected with the fact that the normal workflow is to tell the “agent” what you want done and expect obedience, there is a real danger that the only thing your coding agent will help you achieve is writing bad code faster. On the other hand, since LLMs were trained on a lot of written material, in particular describing good and bad practices, gotchas and pitfalls, it seems worthwhile to ask them if your idea is any good before implementing it and learning that it’s not the hard way.
So, here is the complete SKILL.md file for my assess skill.
--- name: assess description: > Assesses pros and cons of a user's idea, with a focus on weaknesses. Use when user asks to assess an idea. --- # Idea Critique Skill Before applying this framework, say: "Applying /assess framework". When asked to assess, evaluate, or critique an idea, apply this framework: ## Stance Default to skeptical. Assume the idea may have a fatal flaw and actively hunt for it before concluding it doesn't exist. This is not about being negative — it's about stress-testing so weak points surface early. ## Process 1. **Steelman first** — articulate the idea's best possible form before critiquing it 2. **Pros** — list genuine strengths with brief reasoning 3. **Cons** — list weaknesses; spend more effort here than on pros 4. **Weak points deep-dive** — pick the 1–3 most dangerous weaknesses and stress-test them hard. Ask: what would have to be true for this to fail? 5. **Verdict** — a honest one-line summary of where the idea stands 6. **Suggested pivots** — if flaws exist, propose concrete ways to address them ## Weighting Score each pro/con 1–3 for significance. Call out any single con scored 3 as a potential deal-breaker. Mark the scores as `[★☆☆]`, `[★★☆]` and `[★★★]`. ## Tone Direct and honest. Avoid hedging language that softens real concerns. The user wants signal, not comfort. --- Now apply this framework to the following idea: $ARGUMENTS
Note the use of $ARGUMENTS at the end – this ensures that I can tell Claude to invoke this explicitly, by saying /assess <my idea>.
I think the most astonishing thing about this skill is that it works really well despite being so short. At 220 words it takes up about 300 tokens, which is way smaller than, say, the aforementioned skill-creator (roughly 20k tokens!) – but is really useful.
Also, this is just the beginning. As of now, I have quite a few custom skills, and this one is one of the smallest (in terms of the word count). And despite the problems with the fact that they are, well, more what you’d call “guidelines” than actual rules, they are genuinely useful.
Recently I found myself killing and yanking more often than ever.
One problem with that is that the capitalization and punctuation of what I have yanked is often wrong. As a (pretty stupid) example, I might see the sentence “This is bad.”, and I might want to write another one: “He thinks that this is bad, and he’s right.” If I use kill-sentence (M-k) or backward-kill-sentence (C-x DEL), I have the whole This is bad. in the kill ring, but what I need to yank is this is bad (with lower-case “t” and without the period), so I need to make both changes after yanking. After several dozen such cases I thought, this is Emacs, I shouldn’t be doing this manually!
As is often the case, coding a feature where a text is transformed in a given way is pretty easy, but finding a good UI is not. I figured, however, that I could make it so pressing C-y for the second time could trigger the transformation. Yanking the same thing twice in a row does not happen very often, and if I really need to do it, I can just press C-y C-g C-y.
First, let’s enumerate the possible transformations. Making the first letter lower- or upper-case is the obvious one. Putting the whole yanked text in quotation marks is another. Stripping punctuation from its end is the last one I can think of. For now, I’ll just implement lower-casing the first letter and stripping punctuation.
If I were to code that several years ago, I’d use a hydra. Now that Emacs has transient built-in, I’ll try to use it – these days I tend to prefer built-in facilities over external packages. But first things first – let’s start with making C-y do something else than usual when pressed twice in a row. I could just advise yank (and if you read my blog regularly, you know I like advice!), but in this case, I don’t think this is the best idea. I have no idea how often yank is called from Elisp, and whenever it is, I don’t want to risk my code firing in such situations. So, I’ll choose the boring route and define my own command which I will then bind to C-y. This has one problem, though: in Org mode, where I might need this whole feature, C-y is bound to org-yank instead. For now, I’ll just ignore it and see whether I actually need my feature in Org mode, too; if so, I’ll just write an Org version of my enhanced yank and that’s it.
(defun mbork-yank-or-transform (&optional arg)
"Call `yank' or `mbork-transform-region-transient'.
The decision depends on whether `last-command' was `yank'."
(interactive "*P")
(if (eq last-command 'yank)
(mbork-transform-region-transient)
(yank arg)))
Now comes a harder part – defining the transient. I had a few issues with that. One was that I’ve never defined transients before, and that it was quite difficult for me to wrap my head around its manual. Luckily, by combining the manual, the Transient showcase, interrogating an LLM and trial-and-error, I was able to come up with a solution which actually worked:
(defvar mbork-transform-region-overlay nil
"An overlay used by `mbork-transform-region-transient'.")
(defun mbork-transform-region-make-overlay ()
"Highlight the region using `mbork-transform-region-overlay'."
(setq mbork-transform-region-overlay
(make-overlay (region-beginning) (region-end) nil nil t))
(overlay-put mbork-transform-region-overlay
'face
'secondary-selection))
(defun mbork-transform-region-delete-overlay ()
"Delete `mbork-transform-region-overlay'."
(remove-hook 'transient-exit-hook
#'mbork-transform-region-delete-overlay)
(delete-overlay mbork-transform-region-overlay))
(transient-define-prefix mbork-transform-region-transient ()
"Apply one or more transformations to the region."
[[("." "Remove punctuation"
mbork-transform-region-remove-punctuation
:transient t)
("l" "Lower-case the first letter"
mbork-transform-region-lowercase-first
:transient t)]
[("RET" "Quit"
ignore
:transient nil)]]
(interactive "*")
(add-hook 'transient-exit-hook
#'mbork-transform-region-delete-overlay)
(mbork-transform-region-make-overlay)
(transient-setup 'mbork-transform-region-transient))
I have a feeling that it’s not ideal – for example, exiting via ignore seems hackish, not to mention the hook stuff (which I needed to make sure the overlay is properly deleted, no matter whether the user exists via RET or C-g), but it works. The only thing remaining is to define the actual transformations – and that’s (of course) the easiest part, it’s just normal Elisp:
(defun mbork-transform-region-remove-punctuation ()
"Remove punctuation from the end of the region."
(interactive "*")
(save-excursion
(goto-char (1- (region-end)))
(when (looking-at-p "[.,!?]")
(delete-char 1))))
(defun mbork-transform-region-lowercase-first ()
"Make the first character of the region lowercase."
(interactive "*")
(downcase-region (region-beginning) (1+ (region-beginning))))
(By the way, the obligatory plug is that if “just normal Elisp” sounds like magic to you, and if you’d like to learn how you can easily modify Emacs to suit your needs like what I’m doing in this post, the best go-to source is the late Robert J. Chassell’s Introduction to Programming in Emacs Lisp – and if that is not enough for you, then my book about Emacs Lisp is envisioned as a next step after that. While it doesn’t cover transients, it consists of three progressively more complex features coded from scratch to production quality, with detailed explanations. Check it out!)
Now let’s just bind C-y to my command and I’m done!
# Don't do it like this. (bind-key "C-y" #'mbork-yank-or-transform text-mode-map)
Well, almost done. It turns out (rather expectedly) that Org mode is also derived from Text mode, so this overrides its normal org-yank binding, too. (Of course, if I ever need my command in Org mode, too, I’ll just write a similar mbork-org-yank-or-transform and bind it there.) Happily, there is a simple solution:
(bind-key [remap yank] #'mbork-yank-or-transform text-mode-map)
This way my command replaces any keybinding normally mapped to yank, including, for instance, S-<insert> etc., but leaves org-yank alone.
And that’s all there is to it. The only thing that’s left is that I’ll probably extend the transformation set over time – but that is another story.
Like many Emacs users, I’m a heavy Org mode user. Some time ago I decided that I need a way to access at least some of my Org files on my (Android) phone. I started with Organice. I quite liked it, but apparently it didn’t like a spotty internet connection, which is a showstopper for me. (Note: this might have been a misconfiguration problem on my side, the Organice website claims that it does work offline!) An important use-case for me is making notes during mountain hikes with spotty or non-existent connectivity, so editing files while offline is a must. I then took a look at Orgzly Revived, and I stayed with it. It isn’t ideal – for example, its support for clocking is not very convenient (it takes four taps to start clocking on an item!) – but it does its job well enough for me. I sync my Orgzly Revived notebooks to a directory on my phone, and sync that directory with my main machine using Syncthing, and this setup works for me quite well.
There was one thing, however, which really bothered me. Orgzly Revived supports Org mode syntax really well – including checkboxes – but creating a checkbox is a nightmare. Note: this is not Orgzly Revived’s fault, it’s just that entering square brackets on my phone’s keyboard is extremely inconvenient. I could of course start using another keyboard, and that’s an option I also consider, but for now I don’t want to switch. Also, after creating the first checkboxed item, Orgzly Revived makes sure the following ones are created automatically, so only creating the first checkbox is problematic.
I looked around and decided to check out Expandroid. After installing it and giving it the suitable permissions I created a Ck keyword and told Expandroid to convert it to - [ ] – and it just works! From now on I will look out for other shortcuts I might need.
One final note is that Expandroid is config-compatible with Espanso, which I happen to use on my Linux box. Espanso is really great for when I need to easily type things like my name or bank account number in places other than Emacs (which has abbrevs for that). It is even possible to share the config between Espanso and Expandroid using Syncthing – I decided against it in my case, since I expect the sets of shortcuts I tend to need on my phone and on my computer to be very different.
That’s it for today, see you next time!
CategoryEnglish, CategoryBlog, CategoryEmacs, CategoryOrgMode