lazy-alias 以本機 CPU 上的 1.5B 模型,將重複 shell 命令序列轉成函數
lazy-alias: my shell history writes my aliases, with a 1.5B model on my CPU
lazy-alias 讀取 Bash 指令歷史記錄,找出反覆輸入的 2-3 步命令序列,透過本機 CPU 運行的 Qwen2.5-1.5B-Instruct 將其封裝為 shell 函數,待用戶批准後才寫入。
This is a submission for the Hacktoberfest Open-Source AI Challenge, Week 3: Slow and Steady.
What I Built
lazy-alias reads your bash history, finds the 2-3 step command sequences you keep
retyping, and asks a small open-weight model running on your CPU to name each one as a shell
function. Nothing is written until you approve it. Weeks later, when you've forgotten what you
called it, lazy-alias find "restart the containers" gets it back by meaning, not just by keyword.
It's built for the laziest possible workflow: you don't have to notice your own habits. Your
history already did.
Demo
Demo transcript (no video): real output of ./demo.sh, shown below.
Real output from ./demo.sh (synthetic seeded history, see The Lazy Payoff):
== lazy-alias ingest
ingested 932 commands into TimescaleDB
== lazy-alias suggest --yes
# typed 38 times on 21 days: git add -A && git commit -m wip && git push
# Pushes changes to the remote repository after adding and committing them.
git_commit_push() {
git add -A && \
git commit -m wip && \
git push
}
...
== lazy-alias find 'restart the containers'
keyword: []
vector: ['compose_operations', 'use_context_web', 'api_test_setup']
best (RRF):
compose_operations() {
docker compose down && \
docker compose up -d --build && \
docker compose logs -f api
}
Code
https://github.com/CyberScryb/lazy-alias
Python, about 220 lines: history.py (parse), db.py (TimescaleDB + pgvector SQL),
naming.py (Qwen via llama.cpp), search.py (rank fusion), cli.py. MIT licensed, 3 unit tests.
How I Built It
Storage: TimescaleDB. History is loaded into a commands hypertable. Sessions split on
30-minute gaps. demo.sh starts a throwaway, user-owned Postgres cluster in ./.pgdata on a
private socket, so it never touches the system Postgres config.
Mining habits in SQL. Window functions (lead() over each session) pair every command with
the next one or two, as long as they're at most 5 minutes apart. Grouping by the sequence gives a
count, and count(DISTINCT time_bucket('1 day', ts)) tells a daily habit apart from one
afternoon of repetition. Two-step sequences already contained in a three-step one are dropped.
Naming with an open model. Qwen2.5-1.5B-Instruct (q4_k_m GGUF, about 1 GB) runs through
llama.cpp on CPU at temperature 0. It returns JSON with a name and a one-line description; the
name is sanitized into a valid, unique shell identifier. You approve each function with y.
I could swap models in one line (LAZY_ALIAS_MODEL).
Finding forgotten functions: hybrid search. Each approved function is stored with a
tsvector and a 384-dim all-MiniLM-L6-v2 embedding in a pgvector column. find runs a keyword
query (to_tsquery, ranked by ts_rank) and a cosine-distance query (<=>), then merges them
with reciprocal-rank fusion. "docker logs" hits by keyword; "restart the containers" shares no
words with the function, so the vector side finds it.
Why Does Open Innovation Matter?
Your shell history is one of the most private files on your machine: hostnames, paths,
sometimes tokens pasted by mistake. A tool that reads it should never ship it to an API.
Open-weight models small enough for a laptop CPU, plus an open-source database with vector
search built in, make that possible. Everything here is inspectable, runs offline once the models
are downloaded, and costs nothing per call. Open licenses (Apache-2.0 models, open-source
Postgres extensions, MIT code) mean anyone can fork it for zsh, fish, or their own team.
The Lazy Payoff
On the demo history, lazy-alias found four habits:
-
cd ~/proj/api && source .venv/bin/activate && pytest -q: 44 times on 19 days -
git add -A && git commit -m wip && git push: 38 times on 21 days -
kubectl config use-context staging && kubectl get pods -n web: 36 times on 19 days -
docker compose down && up -d --build && logs -f api: 29 times on 17 days
That's 147 multi-step retypes that become one word each.
Disclaimer: these numbers come from a synthetic, seeded 30-day history generated by
seed_history.py, not from anyone's real shell. They show what the tool does, not a measured
productivity gain. Run it on your own history to see yours.
Prize Categories
- Overall
-
Best Use of Tiger Data: TimescaleDB stores the history as a hypertable and
time_bucketseparates daily habits from one-off bursts. pgvector stores the embeddings, andfindruns hybrid keyword + vector search (tsvector + pgvector, fused with RRF), which is exactly what the category prompt asks for.
My Agent Session
An AI coding agent built this with me. It wrote the code, set up the local TimescaleDB +
pgvector cluster, ran the tests and the demo, and drafted this post. The demo output above is
pasted from its real run (demo_run.txt in the repo). I don't have a session-replay embed to share.
AI Assistance
I built this with help from an AI coding agent, which wrote much of the code and this draft.
An AI agent ran everything on its machine and drafted this post; I approved it for publishing.
Every output shown above comes from a real run of ./demo.sh.
Credits
- TimescaleDB (Tiger Data) and pgvector
- Qwen2.5-1.5B-Instruct (Apache-2.0), Qwen team
- all-MiniLM-L6-v2 (Apache-2.0), via fastembed
- llama.cpp and llama-cpp-python; psycopg
來源:DEV Community · dev.to