Skip to content

Create the repo, and scaffold into non-empty directories#12

Merged
DanMat merged 2 commits into
mainfrom
feat/repo-provisioning-merge
Jul 21, 2026
Merged

Create the repo, and scaffold into non-empty directories#12
DanMat merged 2 commits into
mainfrom
feat/repo-provisioning-merge

Conversation

@DanMat

@DanMat DanMat commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Implements the two items at the top of Next up. They're two halves of one gap: Packkit assumed a greenfield empty directory, but the real workflow is "the repo already exists" — either you created it, or you cloned it.

Create the repo

npx create-packkit ts-lib my-lib --github            # private, pushed
npx create-packkit ts-lib my-lib --github --public
npx create-packkit ts-lib my-lib --git-remote git@bitbucket.org:me/my-lib.git

--github shells out to gh rather than calling the REST API, so Packkit never reads, prompts for, or stores a tokengh already owns those credentials. Opt-in, and private unless --public, since a wrong default there leaks source.

The remote is resolved before generating. The repository URL is baked into package.json links and README CI badges at generate time, so creating the repo afterwards would ship a first commit pointing at nothing. Verified:

$ npx create-packkit ts-lib t5 --git-remote https://github.com/DanMat/t5.git
package.json → {"type":"git","url":"git+https://github.com/DanMat/t5.git"}
README.md    → https://github.com/DanMat/t5/actions/workflows/ci.yml/badge.svg

Creation runs last, so a failure leaves a complete local project plus a printed retry command rather than a half-state.

Scaffold into a non-empty directory

--merge writes what's absent and never overwrites; collisions are kept and reported.

Separately — and this is the more consequential fix — a directory holding only .git now counts as empty. git clone of a fresh repo leaves .git behind, and dirIsEmptyOrMissing counted dotfiles, so the entire create → clone → scaffold flow aborted on the clone. That abort is what forced the "backup, move, restore" workaround in the field review; it was never an overwrite risk, it was a refusal to run.

MCP

merge, github, and public are exposed on packkit_scaffold, moving packkit-mcp to 0.2.0. It now imports the shared side-effect layer through a new create-packkit/scaffold export instead of keeping its own copy, so "never overwrite" has exactly one implementation.

That import means packkit-mcp requires create-packkit >= 2.8.0. sync-mcp-version.mjs now fails the release if the version being cut falls outside the range mcp declares — a patch bump would otherwise publish an MCP server that cannot install. Confirmed green at 2.8.0, red at 2.7.3.

Testing

40 tests pass, including 5 new ones covering the .git-only case, merge preservation (asserts original bytes survive), and nested writes. Manually verified: clean scaffold unchanged, .git-only dir scaffolds, non-empty aborts with exit 1, --merge preserves modified files, push lands in a local bare repo, --github --no-git rejected, MCP server starts and lists the new options.

Not exercised: creating a real GitHub repo — that needs your account.

🤖 Generated with Claude Code

DanMat and others added 2 commits July 21, 2026 16:06
Packkit assumed a greenfield empty directory, but the real workflow is
"the repo already exists" — either you made it, or you cloned it. Two
halves of the same gap.

Create the repo
  --github creates the remote and pushes; --git-remote <url> does the
  same for GitLab/Bitbucket/Gitea/self-hosted. Shells out to `gh` rather
  than calling the API, so Packkit never reads, prompts for, or stores a
  token. Opt-in, and private unless --public — a wrong default here
  leaks source.

  The remote is resolved *before* generating, because the repository URL
  is baked into package.json links and README badges at generate time.
  Deriving it up front is what makes those badges point somewhere real
  in the first commit.

  Creation runs last, so a failure leaves a complete local project and a
  printed retry command rather than a half-state.

Scaffold into a non-empty directory
  --merge writes what's absent and never overwrites; collisions are kept
  and reported. Separately, a directory holding only .git now counts as
  empty: `git clone` of a fresh repo left .git behind and the old check
  aborted on it, breaking create -> clone -> scaffold outright. That was
  the abort behind the reported "backup, move, restore" workaround.

Both surfaces are exposed over MCP (merge/github/public), which is what
moves packkit-mcp to 0.2.0. It now imports the shared side-effect layer
via a new create-packkit/scaffold export instead of duplicating it, so
"never overwrite" has one implementation.

That import means packkit-mcp needs create-packkit >= 2.8.0, so
sync-mcp-version.mjs now fails the release if the version being cut
falls outside the range mcp declares — a patch bump would otherwise
publish an MCP server that cannot install.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Found by creating a real repo: --github with --no-install pushed a
project with no lockfile, and actions/setup-node's `cache: npm` errors
outright when it can't find one. Every repo Packkit created that way was
red on its very first CI run, which rather defeats the point of creating
the repo for you.

When a remote is being created and the install was skipped, write the
lockfile without installing (~15s vs a full install). Yarn has no mode
that works across its v1/v2+ split, so it warns instead of failing
silently.

Verified end to end: a freshly created private repo now goes green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@DanMat

DanMat commented Jul 21, 2026

Copy link
Copy Markdown
Owner Author

Verified against real repos before merge.

Created DanMat/packkit-smoke-test with --github — private by default, pushed, and the derived URL landed correctly in the pushed copy:

package.json → git+https://github.com/DanMat/packkit-smoke-test.git
README.md    → .../actions/workflows/ci.yml/badge.svg

That test also caught a real bug. CI on the new repo failed immediately: --no-install meant no lockfile, and actions/setup-node's cache: npm errors outright when it can't find one. So every repo created this way was red on its first CI run — which defeats the point of creating the repo for you.

Fixed by writing the lockfile without a full install (~15s) whenever a remote is being created and the install was skipped. Yarn has no mode that works across its v1/v2+ split, so it warns rather than pushing a repo that will fail.

Re-verified on DanMat/packkit-smoke-2: CI passes. The Release workflow still fails there with ENEEDAUTH, which is expected — a brand-new repo has no npm credentials, and that's pre-existing behavior unrelated to this PR (filed separately).

41 tests pass.

@DanMat
DanMat merged commit 636499f into main Jul 21, 2026
4 checks passed
@DanMat
DanMat deleted the feat/repo-provisioning-merge branch July 21, 2026 20:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant