Skip to main content

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:

FolderSizeUsed at runtime?
bin/120 KByes — every command
lib/72 KByes — sourced by bin
templates/12 KByes — welcome.txt + lovable/
scripts/20 KBno — internal docs generator
tests/108 KBno — contributor test suite
website/~9 MBno — docs site source, built in CI
.github/8 KBno — CI workflows
plans/ (under website/docs/)growingno — 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.sh logic changes. Adding a "convert to sparse" path to update.sh is unnecessary — re-running install.sh is 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 noclickops subcommand for sparse management. Users who want the full clone use git clone directly instead of install.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/ or tests/) clone the repo the normal way, not through install.sh.

Phases

Phase 1: Add sparse-checkout to the clone path — DONE 2026-05-29

Tasks

  • 1.1 Added slim_checkout() helper in install.sh; called after git clone succeeds.
  • 1.2 Prints ok "Slim layout applied (bin/ lib/ templates/ shell/ only)." on completion.
  • 1.3 Comment block at top of install.sh documents 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.sparseCheckout config; if not true, run slim_checkout().
  • 2.2 Same helper as Phase 1 — idempotent, safe to call on either path.
  • 2.3 info line printed: "Slimming install — restricting working tree to bin/ lib/ templates/ shell/ only." + a hint about git sparse-checkout disable to 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.sh noting that git pull --ff-only is sparse-aware and doesn't need adjustment, but pointing readers at install.sh if 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 should git clone the 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.txt1.5.1.
  • 4.2 bash tests/run-all.sh — 308 pass, 0 fail. Initial run found 6 failures in test-PLAN-002-installer.sh because shell/ was missing from the sparse set (v1.0.x shell-function dispatch). Added shell to 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 to completed/ 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 — title feat(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.sh content; 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.sh produces 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.sh on a pre-v1.5.1 full install slims it (with an info message 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 set is 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.txt ships 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 moves welcome.txt somewhere else (bin/?), update the sparse set in lockstep.
  • scripts/generate-docs.sh does NOT ship to users. Contributors who want to regenerate docs locally need a manual git clone of the repo (not install.sh). Document this in the relevant contributor section of project-noclickops.md if it isn't already.
  • The slim install still has .git/ with the full history, so contributors can convert their slim install to full with git sparse-checkout disable if they want, no re-clone needed.
  • The welcome.txt template printing in install.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.txt1.5.1.

Move:

  • website/docs/ai-developer/plans/backlog/PLAN-106-slim-install.mdplans/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

MetricPre-PLAN-106 (v1.4.x)Post-PLAN-106 (v1.5.4)Reduction
Working tree~10 MB216 KB~98%
.git/~1+ MB~556 KB~50%
Total install~10–11 MB772 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=1 opt-out env var — user explicitly cut it from the original draft. Contributors who need the full tree clone the repo directly instead of using install.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.