Skip to content

Resolve the CLI's own package specifier at runtime instead of baking it at build time #207

Description

@thecodedrift

关注-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.

Metadata

Metadata

Assignees

No one assigned

    标签

    CLIRelated to the taskless CLI

    Type

    No type

    项目

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions