PLAN-106: Slim install via git sparse-checkout
IMPLEMENTATION RULES: Before implementing this plan, read and follow:
Status: Completed 2026-05-29
Shipped across four releases (v1.5.1, v1.5.2, v1.5.3, v1.5.4) — each refinement was small and isolated. Final install footprint: 772 KB total (216 KB working tree + 556 KB shallow .git/), down from the pre-PLAN-106 ~10 MB. Working tree contains only bin/ lib/ templates/ shell/ version.txt — nothing else. See "Completion notes" at the bottom.
Goal: Cut a fresh noclickops install from ~10 MB to ~250 KB by sparse-checking out only the folders users actually run from (bin/, lib/, templates/, shell/). website/, tests/, .github/, and the plans tree stay in the repo but never reach user disks.
Last Updated: 2026-05-29
Investigation: INVESTIGATE-plans-and-scaffolding.md (this PLAN is the "PLAN-A — Sparse-checkout slim install" entry)
Prerequisites: none.
Blocks: nothing — this PLAN is standalone. PLAN-107 / 108 / 109 (the scaffolding trio) ship later in their own PR.
Priority: Medium — install bloat is real but not painful enough to block work. Shipping this first because it's small, isolated, and reversible.
Branch: feat/v1.5.1-slim-install.
Problem
install.sh does a plain git clone of the whole repo. As of v1.5.0:
| Folder | Size | Used at runtime? |
|---|---|---|
bin/ | 120 KB | yes — every command |
lib/ | 72 KB | yes — sourced by bin |
templates/ | 12 KB | yes — welcome.txt + lovable/ |
scripts/ | 20 KB | no — internal docs generator |
tests/ | 108 KB | no — contributor test suite |
website/ | ~9 MB | no — docs site source, built in CI |
.github/ | 8 KB | no — CI workflows |
plans/ (under website/docs/) | growing | no — project management history |
~95 % of every user's ~/.noclickops is content they never touch, and the website/docs/ai-developer/plans/completed/ tree grows monotonically with each shipped PLAN.
Sparse-checkout fixes this without moving any files — git materialises only the configured paths in the working tree. The full repo history stays in .git/ (so git log etc. still work) but the working tree shrinks to what's actually run.
What it delivers
install.sh — sparse-checkout on clone
After git clone, the installer initialises cone-mode sparse-checkout with the runtime-only path set:
git -C "$NOCLICKOPS_DIR" sparse-checkout init --cone
git -C "$NOCLICKOPS_DIR" sparse-checkout set bin lib templates shell
Cone mode requires git 2.25+ (January 2020); already universal on supported macOS / Linux / WSL / Git Bash.
install.sh — slim existing installs on re-run
After the existing git pull --ff-only for already-installed users, the installer checks whether sparse-checkout is enabled and turns it on if not — so anyone who re-runs install.sh to upgrade from v1.5.0 gets the slim layout. No surprises: the script prints what it's about to do.
bin/update.sh — no code change, header note only
git pull --ff-only is sparse-aware (git won't materialise excluded paths). Update behaviour is unchanged. Add a comment in the header noting this so the next reader knows the interaction was considered.
version.txt → 1.5.1
Patch bump — install behaviour change only; no new commands, no CLI surface change.
README.md — short note in the install section
One paragraph after the install one-liner explaining what gets checked out (and that contributors who want the full tree can git clone the repo directly instead of going through the installer).
What this PLAN does NOT do
- No content moves.
website/,plans/,tests/all stay exactly where they are in the repo. Only the user-side checkout changes. - No
bin/update.shlogic changes. Adding a "convert to sparse" path toupdate.shis unnecessary — re-runninginstall.shis the documented way to change the install layout. - No partial clone (
--filter=blob:none). That would shrink.git/itself too, but introduces lazy blob fetching at runtime. Not worth the operational complexity for the ~2-3 MB it'd save on top of sparse-checkout. - No
noclickopssubcommand for sparse management. Users who want the full clone usegit clonedirectly instead ofinstall.sh. - No opt-out env var. The installer's job is to set users up to run commands — the slim tree is the right layout for that audience. Contributors who need the full tree (to edit
website/ortests/) clone the repo the normal way, not throughinstall.sh.
Phases
Phase 1: Add sparse-checkout to the clone path — DONE 2026-05-29
Tasks
- 1.1 Added
slim_checkout()helper ininstall.sh; called aftergit clonesucceeds. - 1.2 Prints
ok "Slim layout applied (bin/ lib/ templates/ shell/ only)."on completion. - 1.3 Comment block at top of
install.shdocuments the slim-by-default behaviour. Validation passed: fresh install via mktemp produces a 240 KB working tree (3 MB.git/).
Validation
# Manual test, fresh install:
rm -rf /tmp/noclickops-test
NOCLICKOPS_DIR=/tmp/noclickops-test \
NOCLICKOPS_REPO_URL=$(pwd) \
bash install.sh < /dev/null
du -sh /tmp/noclickops-test # expect < 500 KB working tree
ls /tmp/noclickops-test # expect only bin/ lib/ templates/ shell/ + LICENSE README.md install.sh install.ps1 version.txt
Phase 2: Slim existing installs on re-run — DONE 2026-05-29
Tasks
- 2.1 In the "already installed" branch, check
core.sparseCheckoutconfig; if nottrue, runslim_checkout(). - 2.2 Same helper as Phase 1 — idempotent, safe to call on either path.
- 2.3
infoline printed: "Slimming install — restricting working tree to bin/ lib/ templates/ shell/ only." + a hint aboutgit sparse-checkout disableto restore. Validation passed: upgrade flow (full clone → re-run installer) went from 1.6 MB → 240 KB working tree.
Validation
# Manual test, upgrade flow:
rm -rf /tmp/noclickops-test
git clone . /tmp/noclickops-test # simulates an existing v1.5.0 full install
du -sh /tmp/noclickops-test # baseline ~10 MB
NOCLICKOPS_DIR=/tmp/noclickops-test \
NOCLICKOPS_REPO_URL=$(pwd) \
bash install.sh < /dev/null
du -sh /tmp/noclickops-test # should now be < 500 KB
Phase 3: bin/update.sh header note + README
Tasks
-
3.1 Add a short comment block at the top of
bin/update.shnoting thatgit pull --ff-onlyis sparse-aware and doesn't need adjustment, but pointing readers atinstall.shif they want to change the sparse set. -
3.2 Add a paragraph after the install one-liner in
README.md:Slim by default. The installer sparse-checks-out only the folders users run from (
bin/,lib/,templates/) — around 250 KB instead of the full ~10 MB. Contributors who want the full tree shouldgit clonethe repo directly instead of using the installer.
Validation
User confirms README reads clearly.
Phase 4: Version bump + tests + commit — DONE 2026-05-29
Tasks
- 4.1
version.txt→1.5.1. - 4.2
bash tests/run-all.sh— 308 pass, 0 fail. Initial run found 6 failures intest-PLAN-002-installer.shbecauseshell/was missing from the sparse set (v1.0.x shell-function dispatch). Addedshellto the sparse set; tests now green. - 4.3 Commit on
feat/v1.5.1-slim-install. - 4.4 PLAN moved to
active/at start (per convention); moves tocompleted/in Phase 5's final commit.
Validation
git log --oneline -1
cat version.txt # 1.5.1
Phase 5: Push, open PR, merge
Tasks
- 5.1
git push -u origin feat/v1.5.1-slim-install. - 5.2
gh pr create— titlefeat(v1.5.1): slim install via git sparse-checkout; body summarises the size cut. - 5.3
gh pr merge --merge --delete-branch. - 5.4
git checkout main && git pull --ff-only. - 5.5 Watch the docs deploy run (the only consumer of this branch's changes is
install.shcontent; docs build is unaffected, but a green deploy confirms no incidental regression).
Validation
gh pr view --json state --jq '.state' # MERGED
git log --oneline -3
Acceptance criteria
- Fresh install via
install.shproduces a working tree under 500 KB. -
noclickops,noclickops update, and at least one inspect command (noclickops info --help) work on the slim install. - Re-running
install.shon a pre-v1.5.1 full install slims it (with aninfomessage explaining the change). -
bash tests/run-all.sh→ 308 pass, 0 fail (no command logic changed). -
version.txt=1.5.1. - PR opened, reviewed, merged; deploy still green.
Implementation notes for whoever picks this up
- Cone-mode is faster + safer than non-cone. Non-cone sparse-checkout has known performance cliffs on big repos and supports gitignore-style patterns that overlap in surprising ways. Cone-mode is path-prefix-only, but that's exactly what we want (
bin lib templates shell). Stick with--cone. git sparse-checkout setis idempotent. Running it twice with the same args is a no-op. Safe to call on every install.sh run if cleaner than the "is it enabled?" check — but the explicit gate is more readable for the slim-existing-install message.templates/welcome.txtships in the slim install.templates/is already in the included set; this is by design (the installer prints it on first run). If the future moveswelcome.txtsomewhere else (bin/?), update the sparse set in lockstep.scripts/generate-docs.shdoes NOT ship to users. Contributors who want to regenerate docs locally need a manualgit cloneof the repo (notinstall.sh). Document this in the relevant contributor section ofproject-noclickops.mdif it isn't already.- The slim install still has
.git/with the full history, so contributors can convert their slim install to full withgit sparse-checkout disableif they want, no re-clone needed. - The
welcome.txttemplate printing ininstall.sh(line 146) reads$NOCLICKOPS_DIR/templates/welcome.txt. That path is still present in a slim install. Good.
Files to modify / create
Modify:
install.sh— Phase 1 (sparse-checkout on clone), Phase 2 (slim on re-run).bin/update.sh— Phase 3.1 (header comment about sparse-awareness).README.md— Phase 3.2 (one paragraph after install one-liner).version.txt—1.5.1.
Move:
website/docs/ai-developer/plans/backlog/PLAN-106-slim-install.md→plans/active/→plans/completed/per the convention.
No new files.
Completion notes (2026-05-29)
Shipped over four releases as user feedback surfaced refinements:
v1.5.1 — initial slim via cone-mode sparse-checkout
PR #14. install.sh added git sparse-checkout init --cone + set bin lib templates. After-clone hook runs on fresh installs; after-pull hook slims existing pre-v1.5.1 full installs in place. bin/update.sh got a header comment noting git pull --ff-only is sparse-aware.
Smoke-test gap surfaced: 6 tests in test-PLAN-002-installer.sh failed because shell/ (the v1.0.x dispatcher) wasn't in the sparse set. Added shell to the set; tests green.
Result: working tree dropped from ~10 MB → 248 KB. .git/ unchanged (~3 MB local; ~1.1 MB on real installs because the clone is from a remote with full history).
Hotfix PR #15: PLAN-106's Investigation: link assumed sibling-folder resolution; after the move to active/ it needed ../backlog/INVESTIGATE-plans-and-scaffolding.md. One-line fix.
v1.5.2 — shallow clone
PR #16. User feedback: .git/ itself was still 1+ MB on real installs. Added --depth=1 to the clone. git pull --ff-only (already in update.sh) maintains the shallow boundary over time, so .git/ stays small forever.
Result: .git/ dropped from ~1 MB → ~544 KB on real installs. Total install ~800 KB.
v1.5.3 — unrelated, shipped on a separate branch
PR #17. Rich --help output + getting-started doc + v2 INVESTIGATE. Not part of PLAN-106; mentioned only for sequencing context.
v1.5.4 — strict slim via non-cone sparse-checkout
PR #18. User feedback: v1.5.3's slim install STILL kept root-level files (README.md, LICENSE, AGENTS.md, CLAUDE.md, install.sh, install.ps1) because cone-mode always preserves the repo root. Switched to non-cone sparse-checkout with an explicit allow-list pattern:
/bin/ /lib/ /templates/ /shell/ /version.txt
Trade-off: non-cone has known performance cliffs on large repos. noclickops is ~10 MB tracked; not a concern.
Documentation update: README's slim paragraph now notes that install.sh itself is also dropped from disk — re-running requires curl-piping the installer again. Acceptable: the installer is documented in install instructions; rare flow.
Result: working tree 216 KB (-40 KB). Total install 772 KB. Working tree contents are exactly bin lib shell templates version.txt.
Net change vs pre-PLAN-106
| Metric | Pre-PLAN-106 (v1.4.x) | Post-PLAN-106 (v1.5.4) | Reduction |
|---|---|---|---|
| Working tree | ~10 MB | 216 KB | ~98% |
.git/ | ~1+ MB | ~556 KB | ~50% |
| Total install | ~10–11 MB | 772 KB | ~93% |
Things that did NOT happen
- No partial clone (
--filter=blob:none) — considered, dropped (added complexity for marginal saving on top of sparse + shallow). - No
NOCLICKOPS_FULL_CLONE=1opt-out env var — user explicitly cut it from the original draft. Contributors who need the full tree clone the repo directly instead of usinginstall.sh.
Follow-ups
None — PLAN-106 is complete. The "strict slim" principle is now memorialised in install.sh's top comment block: only what's needed at runtime ships to user disks.