跳到正文
DEV Community· Nathaniel Ady·· 3 小時前AI 評分53

lazy-alias 以本機 CPU 上的 1.5B 模型,將重複 shell 命令序列轉成函數

lazy-alias: my shell history writes my aliases, with a 1.5B model on my CPU

AI 導讀

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_bucket separates daily habits from one-off bursts. pgvector stores the embeddings, and find runs 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