Skip to content

Fix empty scaffold, add agent skills and non-interactive flags - #1

Merged
adesege merged 4 commits into
mainfrom
worktree-fix-template-source
Sep 20, 2026
Merged

adesege merged 4 commits into
mainfrom
worktree-fix-template-source

Conversation

@adesege

@adesege adesege commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

The bug

npm create stratal@latest my-app created an empty folder and exited successfully.

The examples moved out of the monorepo into strataljs/examples, where each one sits at the repo root rather than under examples/. The CLI still pointed at github:strataljs/stratal/examples/<dir>, and giget resolves a missing subdirectory to an empty extraction that exits 0 — so there was a folder, no files, and no error.

Repointed the source, and added a guard that fails loudly when nothing is extracted. Also added the two examples that landed since: commands and inertia.

Agent skills by default

Each new project now gets the Stratal agent skills.agents/skills/stratal, the .claude/skills/stratal symlink and skills-lock.json. They are fetched at scaffold time rather than vendored into the package, the same way templates are, so they cannot go stale against the framework. --no-skills opts out, and a failure there never fails the scaffold.

Non-interactive use

The package manager choice and the overwrite confirmation had no flags, so the CLI could not run unattended. Added --package-manager, --no-install, --force and --yes.

More importantly it now refuses to prompt at all when stdin is not a tty: anything still unanswered is reported as an error naming the flag to pass, so a run either completes or exits non-zero instead of hanging. Unknown options and missing flag values are rejected rather than silently ignored.

--force replaces

giget merges into the target, so scaffolding one template over another left the previous project's files alongside the new ones even though the prompt had offered to overwrite. The directory is now emptied first.

The template is staged in a sibling directory and moved into place only once it has downloaded, so a failed fetch no longer leaves the target emptied. A project name is also rejected unless it resolves inside the current directory — app/.. passes the character check but points at the directory the command was run in, which this would otherwise clear.

Dependencies

@clack/prompts 1.8.1, giget 3.3.1, nypm 0.6.10, @types/node 26.6.2, typescript 7.0.2.

Two upgrades needed follow-ups:

  • TypeScript 6+ defaults types to [], so tsconfig.json now names node explicitly. Without it the build produced 21 errors.
  • Yarn 4.12 could not fetch TypeScript 7 at all — its builtin compat patch expects lib/_tsc.js, which the Go rewrite does not ship. Hence yarn 4.18. Script execution stays disabled; esbuild's binary arrives through its optional platform dependency, verified from a clean install.

Publishing

Moved to npm trusted publishing, which authenticates over OIDC and attaches provenance on its own — no token and no --provenance flag. The job also typechecks now, and refuses to publish when the tag and the package version disagree.

Verification

  • Scaffolded hello-world, crud-api and inertia; all 18 declared templates confirmed to exist upstream.
  • A scaffolded project installs (164 packages), typechecks, boots under wrangler dev, and serves {"message":"Hello World"} / HTTP 200.
  • Ran the packed tarball end to end via npm pack, so the published artifact is exercised, including the bin wiring.
  • Non-interactive paths: missing name, missing template and non-empty directory each exit 1 with an actionable message; fully specified runs exit 0 with no prompts.
  • Interactive mode verified under a real pty.
  • --force removes files from a previous template; a failed download leaves the target untouched and no staging directory behind.

One behaviour change worth noting for Windows: installSkills spawns npx through a shell there, because npx is a .cmd that Node has refused to spawn from execFile since the CVE-2024-27980 fix. Previously this failed silently and Windows users got no skills. That fix is reasoned from the Node behaviour and has not been executed on Windows.

Templates moved out of the monorepo into strataljs/examples, where each
one sits at the repo root instead of under examples/. giget resolves a
missing subdirectory to an empty extraction rather than an error, so
every scaffold silently produced an empty folder. Repoint the source and
fail loudly when nothing is extracted.

Also add the two examples that landed since (commands, inertia), install
the Stratal agent skills into each new project by default with a
--no-skills opt-out, and upgrade dependencies to current.

TypeScript 7 no longer auto-includes @types packages, so tsconfig now
names node explicitly. Yarn 4.12 could not fetch TypeScript 7 (its
builtin compat patch expects lib/_tsc.js, which the Go rewrite does not
ship), hence the yarn bump to 4.18.
npx is a .cmd there, which Node refuses to spawn from execFile without
a shell since the CVE-2024-27980 fix. The failure was swallowed by the
surrounding try/catch, so Windows users silently got no agent skills.
The package manager choice and the overwrite confirmation had no flags,
so the CLI could not run unattended. Add --package-manager, --no-install,
--force and --yes, and refuse to prompt at all when stdin is not a tty:
anything still unanswered is reported as an error naming the flag to
pass, so a run either completes or exits non-zero instead of hanging.
Unknown options and missing flag values are now rejected too, rather
than being silently ignored.

Publishing moves to npm trusted publishing, which authenticates over
OIDC and attaches provenance on its own, so the workflow needs no token
and no --provenance flag. It also now typechecks and refuses to publish
when the tag and the package version disagree.
giget merges into the target, so scaffolding one template over another
left the previous project's files sitting alongside the new ones even
though the prompt had offered to overwrite. Empty the directory first so
--force means what it says.

The template is staged in a sibling directory and moved into place only
once it has downloaded, so a failed fetch no longer leaves the target
emptied. A project name is now also rejected unless it resolves inside
the current directory: "app/.." passes the character check but points at
the directory the command was run in, which this would otherwise clear.
@adesege
adesege merged commit 044c197 into main Sep 20, 2026
1 check passed
@adesege
adesege deleted the worktree-fix-template-source branch September 20, 2026 22:24
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