There was a hornets nest hinged into the window frame of my apartment. Not a metaphor. Actual hornets, actually at the window, doing hornet things in the hinge.
I did not want to deal with this. Not in the sense of “I don’t want to call pest control.” In the sense of “I don’t want to log into the resident portal, click through the cascading dropdowns, remember whether it’s General > Pest Control > Outdoor Treatment or General > Pest Control > Other, fill the box that asks if this is an emergency, and then hunt for the Submit button that is not the first Submit button on the page.”
So I typed four words to my terminal.
/maintenance hornets at window
And walked away.
Ninety minutes later the property emailed me: “Service request is In Progress.” I had not opened a browser tab. I had not touched the portal. I had not confirmed anything. The skill did the whole dance.
What the skill actually does
The skill is a markdown file at ~/.claude/commands/maintenance.md. It encodes two things:
-
Who I am as a resident. Unit number, email, phone, the fact that I have no pets, the fact that maintenance can enter whenever. These never change, so the skill defaults them. It never asks.
-
How to drive the portal. Which URL to hit, how the login splits across two domains, which button to click, which textarea breaks submit if left empty, how to disambiguate the fake Submit button from the real one.
The whole thing runs through a Chrome browser tool. Nothing fancy. What’s interesting is everything the skill had to learn to not stall.
The ways the portal fights back
Nothing about this portal was built to be automated. That’s the point. The ugliness in the skill file is what makes it work.

Then the ”+ Add a new request” button. find won’t click it reliably, because the agent gets back a reference that goes stale when the modal opens. So the skill clicks it via raw JavaScript inside the page:
[...document.querySelectorAll('button')]
.find(b => /add a new request/i.test(b.textContent))
.click()
The form is a stack of cascading comboboxes. Category appears first. Pick “General” and a sub-category combobox materializes. Pick “Pest Control” and a sub-sub-category combobox appears. So the skill fills them in order, waits for each to render, then moves on.
Then the trap. There’s a required textarea at the bottom labeled something like “If this is a maintenance emergency, have you also called the emergency maintenance phone number…”. If you leave it blank, the submit silently fails with “Additional question 1 - Cannot be empty.” You’d never notice it was the one that broke you. The skill types “Not an emergency.” into it every single time.
And the Submit button. There are two of them in the DOM. One is visible to the user. The other is a hidden duplicate from the form’s internal wiring. If you grab querySelector('button') with any sensible filter, you get the hidden one and nothing happens. You click, the form sits there, the agent reports success, and your hornets are still at the window. The skill picks the right one by filtering on offsetParent:

If offsetParent is null, the element isn’t rendered. That’s the hidden one. The visible Submit survives the filter, gets clicked, and the request goes in.
The timeline
- 11:52 AMI noticed the hornetsLooked at the window. Nope.
- 11:54 AMTyped /maintenance hornets at windowWalked away to make lunch.
- 11:56 AMSkill logged inEmail + redirect to realpage + password check.
- 11:57 AMForm submittedRight category, right sub, 'Not an emergency.' typed into the trap field.
- 1:23 PMProperty emailed: 'In Progress'I was eating a sandwich.
That’s the whole story. Four words of input, one sandwich of waiting, one ticket on file.
The point of encoding this
A maintenance request once a quarter is not a thing I need to automate for time savings. If I did the math it would be hours-negative for the rest of my life.
The point is different. The point is that writing the skill forced me to notice the twelve small things that make filing a request annoying — the login split, the stale refs, the trap textarea, the two Submit buttons. All twelve are now written down in a single markdown file. I will never re-learn them. Next time there is a thing wrong with my apartment I will type one line and go do something else, and the knowledge of how to drive this portal lives somewhere I can see it and edit it, instead of in the part of my brain that gets reused for other annoyances and forgets.
That’s what a Claude Code skill is to me now. Not a time-saver. A place to put the fight so you only have to have it once.
Hornets are gone, by the way.