关注-up from #203. Not blocking it: #203 fixes the hardcoded call sites, this changes where the answer comes from.
Today
The package specifier a reader is told to run (@taskless/cli@latest, @taskless/cli-nightly@0.11.0-…x<sha>) is a build-time fact, baked into __TASKLESS_CLI__ by scripts/build-target.ts and applied by applyCliInvocation.
The problem with that
The nightly rename happens at pack time, in .github/scripts/nightly-pack.cjs, not at build time. So the specifier is computed and baked in one step and the package is actually named in another. The two agree today because the pipeline is careful, but nothing structurally enforces it: they are two derivations of one fact, and only one of them is what npm actually published.
A published package already carries its own identity. dist/index.js can read it:
readFileSync(new URL("../package.json", import.meta.url))
files: ["dist"] still ships package.json, since npm always includes it. No new dependency is needed; read-package-up solves a harder problem (walking up from an arbitrary path) than this one.
Resolved that way, the specifier cannot drift from what shipped, because it is what shipped.
Scope
The CLI's own surfaces only: the commands, and the migration that writes .taskless/README.md. These run in Node, where reading a file is free.
@taskless/cli/prompts is explicitly not in scope, and needs no change. It returns a <taskless-cli> marker for the caller to substitute, and that is the correct answer rather than a limitation. A consumer importing the render path directly has their own invocation, quite possibly an esoteric one, and we could never know what they wanted. A placeholder they fill is the best answer available on that surface, so there is nothing there to resolve at runtime and assertPromptsGraph is unaffected.
That also means this is not "two mechanisms." Prompts and the CLI are answering different questions: prompts is asked "what should the reader substitute," the CLI is asked "what am I."
Open questions for whoever takes this
- Does
__TASKLESS_CLI__ disappear entirely, or stay for self builds, whose invocation is a repo-relative path rather than a package specifier and therefore genuinely is a build-time fact?
- The launcher half (
npx versus pnpm dlx) is unchanged and stays a runtime detection in util/package-manager.ts. Note that module's warning: npm_config_user_agent is NOT a valid source, because pnpm run, pnpm exec, pnpm dlx, and every pnpm lifecycle script report identically. Reading it is what once told a developer running a package.json script to pnpm dlx @taskless/cli@latest auth login, reinstalling a CLI they already had pinned.
- Is there a test worth having that the resolved specifier matches the published
package.json name and version, given that the point is to remove a drift class?
Not urgent
Post-0.11. #203 makes the current mechanism correct at every call site; this makes the mechanism itself unable to go wrong.
关注-up from #203. Not blocking it: #203 fixes the hardcoded call sites, this changes where the answer comes from.
Today
The package specifier a reader is told to run (
@taskless/cli@latest,@taskless/cli-nightly@0.11.0-…x<sha>) is a build-time fact, baked into__TASKLESS_CLI__byscripts/build-target.tsand applied byapplyCliInvocation.The problem with that
The nightly rename happens at pack time, in
.github/scripts/nightly-pack.cjs, not at build time. So the specifier is computed and baked in one step and the package is actually named in another. The two agree today because the pipeline is careful, but nothing structurally enforces it: they are two derivations of one fact, and only one of them is what npm actually published.A published package already carries its own identity.
dist/index.jscan read it:files: ["dist"]still shipspackage.json, since npm always includes it. No new dependency is needed;read-package-upsolves a harder problem (walking up from an arbitrary path) than this one.Resolved that way, the specifier cannot drift from what shipped, because it is what shipped.
Scope
The CLI's own surfaces only: the commands, and the migration that writes
.taskless/README.md. These run in Node, where reading a file is free.@taskless/cli/promptsis explicitly not in scope, and needs no change. It returns a<taskless-cli>marker for the caller to substitute, and that is the correct answer rather than a limitation. A consumer importing the render path directly has their own invocation, quite possibly an esoteric one, and we could never know what they wanted. A placeholder they fill is the best answer available on that surface, so there is nothing there to resolve at runtime andassertPromptsGraphis unaffected.That also means this is not "two mechanisms." Prompts and the CLI are answering different questions: prompts is asked "what should the reader substitute," the CLI is asked "what am I."
Open questions for whoever takes this
__TASKLESS_CLI__disappear entirely, or stay forselfbuilds, whose invocation is a repo-relative path rather than a package specifier and therefore genuinely is a build-time fact?npxversuspnpm dlx) is unchanged and stays a runtime detection inutil/package-manager.ts. Note that module's warning:npm_config_user_agentis NOT a valid source, becausepnpm run,pnpm exec,pnpm dlx, and every pnpm lifecycle script report identically. Reading it is what once told a developer running apackage.jsonscript topnpm dlx @taskless/cli@latest auth login, reinstalling a CLI they already had pinned.package.jsonname and version, given that the point is to remove a drift class?Not urgent
Post-0.11. #203 makes the current mechanism correct at every call site; this makes the mechanism itself unable to go wrong.