AI productivity
You can push to GitHub from your phone, from bed, and it is built into iOS
A phone is not a terminal. It is a trigger. Every decision that followed came out of that one sentence.
This is a post about a small thing that changed my working day, and about the three bugs I hit building it, because the bugs are the useful part and almost no guide includes them. Everything here happened this afternoon, between half past four and half past five. I have the log.
The thing your phone can already do
The Shortcuts app on an iPhone has an action called Run Script Over SSH.
That is it. That is the whole surprise. There is a working SSH client inside the app Apple sells you for setting alarms and playing music, it handles keys properly, and you can point it at your own computer and run anything.
I have been an engineer for thirty years and I did not know. Nobody mentions it, because Shortcuts is marketed at people automating their morning routine, and the people who would want this are not reading that marketing.
So: you can push to GitHub from your phone, and you do not need to install anything.
Why I needed it, which is not convenience
I should be straight about where this came from, because it is not a productivity hack.
My dominant hand does not work. It has been injured for nearly two years and was completely paralysed for one of them. Typing on a phone is not mildly annoying for me. It is painful, and it is the reason things do not get done.
Which means the real cost of publishing was never the writing. It was that publishing required me to be upright, at a desk, with both hands, typing three git commands correctly. On a day when my back hurts or my hand is bad, that is the difference between a post going out and a post not going out.
So the brief was narrow and it was not about saving time. Get from lying down to published without touching a keyboard.
A phone is not a terminal
This is the sentence the whole build turned on, and I did not have it at the start. I arrived at it by getting things wrong.
A phone is not a small computer that you work on. It is a trigger. You are not going to type a command on it, you are not going to read a stack trace on it, and you are certainly not going to edit a command and try again on it. There is no second window. There is no scrollback.
Once you accept that, three design decisions make themselves.
The intelligence belongs on the machine with the keyboard. My first version put the whole command inside the shortcut. That is wrong, and I will come back to why.
The output matters more than usual, because it is the only thing you get. You cannot investigate. Whatever the tool says is the entire truth available to you.
And it must never ask you to decide anything. One tap, one sentence, done, or it has failed at the only job it had.
A phone is not a terminal. It is a trigger. Every decision that followed came out of that one sentence.
Inside your own network, which is where I would start
I built the version that works at home first, deliberately, because it is simple and because nothing is exposed to the internet. My Mac has a 192.168 address, which is not routable from outside. An attacker would need to be inside my flat and holding my unlocked phone, at which point my shell is not their most interesting option.
On the Mac, one switch. System Settings, General, Sharing, Remote Login. Set it to allow only your own account.
And one thing nobody mentions, which will cost you an hour. In that same panel there is Allow full disk access for remote users. Modern macOS blocks an SSH session from reading your Documents folder unless that is on. My projects live in Documents. Without it the shortcut connects perfectly and then cannot find anything, and the error does not tell you why.
Then the key. The Shortcuts action generates one for you, an ed25519 pair. The private half stays on the phone and never moves. You install the public half on the Mac by adding one line to ~/.ssh/authorized_keys, and the permissions are not decoration: the SSH server silently refuses to use that file if the folder or the file is writable by anyone else. That is the second most common reason this appears not to work.
mkdir -p ~/.ssh && chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys # paste the public key, one single line
chmod 600 ~/.ssh/authorized_keys
The first time it connects you get a prompt saying the host has not been seen before, with a fingerprint. That is correct behaviour and you should accept it once. If you ever see it again for the same machine, stop, because that means the host key changed.
Keep the phone stupid
My first version had the whole thing inside the shortcut. Change directory, add, commit, push, chained together in one long line in a text box on a phone.
It worked for about a day. Then it did not, and I was looking at a long command in a tiny field with no way to see what it had actually done.
So all of it moved into a file in the repository, deploy/publish.sh, and the shortcut now runs one short line. The difference is not elegance. It is that the script lives somewhere I can edit it on a real keyboard, it is in git with the rest of the site, and its comments explain themselves to whoever reads it next, including me in six months.
bash ~/Projects/your-site/deploy/publish.sh [Ask Each Time]
The shortcut became four fields and a sentence. Everything hard is on the Mac.
The first thing that broke was a quotation mark
I passed the commit message in quotes, the way you would in any terminal.
iOS replaces straight quotes with curly ones as you type. Bash does not treat a curly quote as a quote. So a perfectly ordinary message arrived at the script as three separate words with two strange characters attached, and the script took the first word and ignored the rest.
The fix is three lines: take everything that was passed rather than only the first argument, and strip the curly characters out. But the lesson is the one that generalises. The text field is not neutral. Something between your thumb and your program is editing what you typed, helpfully, and it does not tell you.
The second was a PATH with four directories in it
The script runs the site build before it commits. It failed immediately, saying npm was not installed.
npm is installed. It works perfectly in my terminal.
A command run over SSH is neither a login shell nor an interactive one, so none of your shell configuration runs. Not .zshrc, not .zprofile. Nothing that puts Homebrew on your PATH ever happens.
Here is the actual PATH my script was given, from the log:
PATH=/usr/bin:/bin:/usr/sbin:/sbin
Four directories. npm lives in /opt/homebrew/bin, which is not one of them. The script now goes looking in the usual places before deciding a tool is missing, and if it still cannot find it, it says so and carries on without the local build rather than refusing to work.
This one catches everybody once, and the reason it is so confusing is that the thing demonstrably works when you test it by hand. You are testing it in a different world.
The third one was mine, and it was the worst
I ran the shortcut. It showed me a box containing a single character.
0
That was all. Two of us looking at it, and neither could learn anything from it. We had no idea whether it had connected, found the folder, built, committed, refused, or exploded.
I eventually worked out what was happening by comparing two runs in the log. When the script finishes successfully, the phone shows everything it printed. When the script exits with an error code, the output is discarded and you get 0.
Read that again, because it is the whole lesson. The moment the tool had something important to tell me was precisely the moment it was guaranteed to say nothing.
The fix sounds wrong and is right. Every path through that script now exits successfully and carries the bad news in the words instead. Nothing automated reads that exit code. A person does, and the person is in bed. So instead of 0 you get a sentence beginning NOTHING DONE and then the reason.
And the thing I should have done from the first minute: every run writes a full trace to a log file, and every message the script prints ends by telling you where that log is. The two real bugs above took minutes to find once that existed. They had been invisible, not difficult.
The rule I would take from this
A tool that cannot explain its own failure is not finished, however well it works when it works.
I knew that. I have said it to other people. I still shipped a script whose entire failure vocabulary was one character, and I only noticed because the first thing I tried to do with it went wrong.
It is worth asking of anything you have built that somebody uses when you are not standing next to them. Not whether it works. Whether it can tell them what it did.
When it builds, and the question that improved it
The script originally built the whole site before every push, which took about twenty five seconds. Then I asked myself why, out loud, which is a question worth asking more often.
The honest answer is that it did not need to. The deployment pipeline builds it again anyway, runs its checks on the result, and publishes nothing if any of them fail. The live site cannot break either way.
So the local build buys two small things: finding out in twenty seconds instead of three minutes, and keeping a commit that does not build out of the history. And it costs twenty five seconds of every single push, which when the push is a tap from bed is most of the experience.
It now builds only when something that can actually break a build has changed. A post builds. An image, a card, a shell script or a document goes straight through in about two seconds. A rule instead of a habit, and the log says which one it applied so it can never be mysterious.
What it is actually like now
I tap an icon. A box asks what changed. I say a few words out loud. A few seconds later it tells me what it did.
Nothing here feeds the build, so straight through. Pushed, one file. Live in a few minutes.
That is from the kitchen, or from bed, with a laptop asleep in another room. And on a day when my hand is bad or my back is bad, that is the difference between the week's post going out and not going out.
It is a small piece of engineering. It is not a small change.
What is not finished
This works on my own network. Walk out of the front door and it stops.
The next piece is a private connection between the phone and the Mac that works over mobile data without exposing anything to the internet, so that I can take my dog out for a morning, leave the machine running at home, and still publish. What I will not do, and what you should not do either, is forward a port on your router. That puts your Mac on the open internet to save yourself twenty minutes.
When that half is built I will add it here rather than writing a second post, and you will be able to see both halves and judge the first one by how honestly I described the second.
If you want to try it
Everything you need is already on your two devices. Remote Login on the Mac, the Run Script Over SSH action on the phone, a key, and a short script in your repository rather than a long command in a text field.
And when it does not work, which it will not at first, the three things above are where I would look. The quotation mark. The PATH. And whether your tool is capable of telling you what it did.
Go and look in your Shortcuts app. The action is already in there, waiting.
MK!
Maria Catalina Kovacs
There is no tracking on this site. No analytics, no cookies, nothing recording where you went or how long you stayed.
But I would like to know if something here was worth your time. If it was, press the heart. It tells me nothing about you. It is a number going up, and it is what tells me to keep writing.
If you want more of this, I am on X. @Queendemetriak