This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass
What I Built
TouchGrass is an offline garden planner. You tell it your USDA hardiness zone and today's date; it tells you which crops will thrive if you sow them this week — and then you close the laptop and go outside.
The entire decision core is TabPFN, Prior Labs' open-weight tabular foundation model, running on my laptop's CPU with the Wi-Fi off. No API keys, no cloud endpoint, no per-request cost.
🌱 TouchGrass — zone 5, week of 2026-04-20
last spring frost Apr 15 · first fall frost Oct 15
this week: 25.4 weeks before first fall frost
✅ spinach thrive probability 84%
✅ radish thrive probability 84%
✅ lettuce thrive probability 83%
✅ zucchini thrive probability 83%
✅ peas thrive probability 83%
Computed locally with TabPFN. Now go outside. 🍂
And in the fall, asked five weeks before first frost, it correctly gets cautious:
🌱 TouchGrass — zone 7, week of 2026-10-06
this week: 5.0 weeks before first fall frost
🤔 spinach thrive probability 49%
🤔 peas thrive probability 49%
...
Who is it for? Anyone with a garden bed and a terminal — the goal was to make the screen the shortest part of the experience: one command, five crops, go plant.
Demo
uv sync
uv run touchgrass.py --zone 7 --date 2026-10-06
That's the whole UX. The heavier demo is turning off networking first and watching it still answer — that's the point.
Code
Repo: https://github.com/linhlban150612/touchgrass-planner (MIT).
How I Built It
Three small pieces:
-
data/frost_dates.csv— median last-spring / first-fall frost dates per hardiness zone (zones 3–10). -
data/plantings.csv— a training set of historical planting outcomes: crop, weeks from last spring frost, weeks before first fall frost, soil temperature, daylight hours → outcome class (thrived/stunted/failed). - TabPFN as the whole model. For each candidate crop I build one probe row for this week's frost window and ask TabPFN to classify the outcome. There is no training loop, no hyperparameter tuning, no gradient descent — a tabular foundation model doing few-shot classification over 55 rows and just working.
clf = TabPFNClassifier(device="cpu",
categorical_features_indices=[0]) # crop
clf.fit(X_train, y_train)
proba = clf.predict_proba(X_probe) # one row per candidate crop
My first run had a fun bug: I forgot to include the crop as a feature, so every crop scored an identical 51% — TabPFN was correctly telling me all my probe rows were the same row. Adding the crop as a categorical feature gave it the signal it needed.
Why Does Open Innovation Matter?
- It runs on a laptop with no internet. TabPFN's weights are downloaded once and run locally. A gardener standing in a field with zero signal gets the exact same answer as one on fibre. A closed API returns a DNS error.
- It keeps your data off servers you don't control. Your zone, your dates, your planting history never leave the machine.
- It costs nothing to run. Every planting plan is $0.00. No key to rotate, no quota to watch, no endpoint to be deprecated under you.
- You can swap it. Because the model is a local artifact, swapping TabPFN for another open tabular model is a one-line change — the tool's value lives in the data and the idea, not in a vendor lock-in.
Where the open approach worked better than a closed one: latency and availability. Local inference returns in well under a second on CPU with zero network round-trips, and the failure mode of "airplane mode" simply doesn't exist. For a tool whose explicit goal is to get you away from connectivity, closed inference would be self-defeating.
My Agent Session
The whole project — code, data, debugging the identical-probabilities bug, git history — was built in a single agent session. The commit history in the repo (including the "crop as categorical feature" fix above) is the honest trace of that session.
Prize Categories
- Prior Labs — Best Use of TabPFN: TabPFN isn't bolted on; it is the product. A tabular foundation model forecasting planting success from historical data, fully local, inside a CLI.
Top comments (0)