Originally published at harshit.cloud on 2026-05-20.
I kept opening a chat tab just to ask "what's the kubectl command for decoding a secret" or "convert 42 GiB to bytes". The context switch was costing more than the answer was worth.
Wrapped my AI CLI (pi here, but the same shape works for claude -p, llm, gh copilot, etc.) into a p function that always returns a single line.
the function
# p: one-shot AI query — `p whats 2 + 2`, `p kubectl secret decode grafana`
p() {
if [ $# -eq 0 ]; then
echo "usage: p <question or task>" >&2
return 1
fi
pi -p --no-session --append-system-prompt 'Answer in ONE line. No preamble, no explanation, no markdown, no code fences. For shell/kubectl/git/etc requests output only the command. For factual or math questions output only the answer.' "$*"
}
Three flags carry all the weight:
-
-p— non-interactive, print and exit -
--no-session— don't persist to session history, every call is ephemeral -
--append-system-prompt— force one-line, no markdown, no preamble
what it feels like
$ p whats 2 + 2
4
$ p kubectl secret decode grafana
kubectl get secret grafana -o go-template='{{range $k,$v := .data}}{{$k}}: {{$v | base64decode}}{{"\n"}}{{end}}'
$ p convert 42 GiB to bytes
45097156608
$ p git undo last commit but keep changes
git reset --soft HEAD~1
$ p regex for matching an email
[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}
No chat window, no scrolling, no "Certainly! Here's the command you asked for:" preamble. Just the answer.
why "$*" and not "$@"
"$*" joins all positional args into one string with spaces between them. "$@" would pass them as separate args, which most AI CLIs would then concatenate anyway — but some treat the first positional as the prompt and the rest as files. Joining explicitly avoids that ambiguity.
If your CLI of choice supports -- to end option parsing, prefer:
your-ai-cli -p ... -- "$*"
pi doesn't, hence the bare "$*".
the system-prompt nudge actually matters
Without --append-system-prompt, even with -p, the default coding-assistant prompt loves to wrap shell commands in code fences and add a one-sentence intro. That breaks copy-paste and clutters the terminal.
The phrasing that worked best in testing:
Answer in ONE line. No preamble, no explanation, no markdown, no code fences. For shell/kubectl/git/etc requests output only the command. For factual or math questions output only the answer.
The "No markdown, no code fences" line is doing most of the work. Without it you get backtick-wrapped output that won't pipe.
variations worth knowing
Pipe directly into pbcopy for instant copy:
pc() { p "$@" | tee /dev/tty | pbcopy }
Now pc git squash last 3 commits prints the command and copies it.
Pipe straight into eval if you trust it (don't):
pe() { eval "$(p "$@")" }
I have pc but not pe. Auto-executing model output is a bad habit even when it's almost always right.
why this beats the chat UI for short questions
| Action | Chat UI | p |
|---|---|---|
| Switch context | yes | no |
| Round-trip latency | ~3-5s + UI | ~1-2s |
| Output format | markdown, prose | bare line |
| Copy command | select + copy | already in scrollback |
| Session pollution | yes | no (--no-session) |
For anything longer than a paragraph the chat UI is still better. For "what's the syntax for X", terminal wins every time.
the meta-lesson
The friction between "I have a question" and "I have an answer" is mostly UI, not model latency. A 7-line shell function removed almost all of it.






















