Migrate npm packages from "logging in every time or maintaining a long-lived token" to an auditable automated publishing chain. Two peer authentication methods are supported:
- oidc — GitHub Actions trusted publishing, CI without long-lived tokens (recommended for continuous releases of GitHub repositories);
- bypass — local granular NPM_TOKEN, directly
npm publish(suitable for quick local publishing and packages without CI).
Output must distinguish first-time bootstrap, authentication configuration, publish execution, and registry read-back; workflow generation must not be equated with having actually published.
Triggers
Activate when
- The user says "automatically publish an npm package", "publish a new npm package but don't make me log in every time", "switch npm releases to GitHub Actions".
- The user says "use NPM_TOKEN to publish this package locally", "publish this npm package locally without login".
- User says “publish npm without login”, “automate npm releases”, or “set up tokenless npm publishing”.
- The user needs to determine in bulk whether packages are being published for the first time, choose between OIDC and NPM_TOKEN, or verify whether a release is actually live.
Do not activate when
- What is being published is an Agent Skill rather than an npm package; use
lov-skill-publisher。 - for generic GitHub Release, Tauri signing, or multi-platform CI/CD only; use
lov-release-via-cicd。 - The user only asks about npm authentication principles without requesting configuration or publishing; explain directly without modifying the repository.
User Profile (cross-session)
Read skill.yaml on every invocation. Resolve the current request, project
context, Skill records, shared preferences, and the shared user/brand Profile in
that order. Keep repository names, workflow choices, and credentials out of the
portable source unless the user explicitly asks to persist a non-secret default.
Persist only direct durable statements through scripts/profile_store.py and
report the canonical Profile path. Never persist npm tokens, OTP values, cookies,
OIDC assertions, or secret-like environment values. See
references/user-profile.md for the full contract.
Skill Group Composition
Read references/skill-composition.md before invoking an adjacent Skill. Sibling
Skills are optional artifact-level handoffs, not hidden runtime dependencies.
Workflow (MANDATORY)
Follow these steps in order.
Step 0: Resolve runtime and current npm rules
- Resolve this source as
SKILL_DIRand the target package directory from the request or current working directory. - Read
references/npm-publishing-contract.mdand recheck the linked npm documentation when authentication behavior, supported runners, or deprecation dates could have changed. - Read
references/skill-composition.mdbefore choosing an optional handoff. - Verify
package.json, git, npm, andscripts/publish.pyexist. Do not request or print credentials during this read-only phase.
Step 1: Audit without changing external state
Run the planner first:
python3 "$SKILL_DIR/scripts/publish.py" PACKAGE_DIR --dry-run --json
The audit must establish:
- package name, version, visibility,
privateflag, registry, and repository URL; - GitHub owner/repository and the exact workflow filename;
- whether the package exists on the npm registry and whether this exact version already exists;
- whether an existing workflow contains
id-token: write,npm publish, and no long-lived publish secret; - whether the state is
bootstrap_required,package_exists, orregistry_unknown.
Do not turn a timeout, authorization error, or registry outage into “package does not exist”. Stop before publication when registry state is unknown.
Step 2: Select the authentication path
The planner emits auth.mode as oidc or bypass. With --auth-mode auto
(the default), it resolves to oidc when a GitHub repository resolves and
bypass otherwise; pass --auth-mode oidc|bypass to override inference. Both
modes are first-class and equal; prefer oidc for GitHub-repository continuous
releases and bypass for local, CI-less, or new-package publication.
oidc — GitHub trusted publishing
Trusted publishing cannot be registered for a name that does not yet exist. For
bootstrap_required, explain that one interactive identity proof is unavoidable,
then perform it only when the request authorizes publication:
npm login
npm publish --access public
Re-read the exact package and version from the npm registry. A successful command without registry visibility is not completion.
For package_exists, configure or verify the exact repository/workflow binding.
With a current npm CLI, the plan emits a command shaped like:
npm trust github PACKAGE --repo OWNER/REPO --file publish.yml --allow-publish
This management action requires interactive identity proof. If it was already configured, verify the existing binding rather than replacing it blindly.
bypass — local granular NPM_TOKEN
A granular (Bypass-2FA) npm_ token publishes directly, including new packages.
Detect the token without reading its value; auth.token reports whether
NPM_TOKEN or a ~/.npmrc _authToken is present. When it is missing, warn and
request a granular token before publishing. auth.publish_command carries the
exact command; never echo the token value.
Step 3: Generate the workflow or the publish command
After resolving blocking audit errors, materialize the mode-specific artifact:
python3 "$SKILL_DIR/scripts/publish.py" PACKAGE_DIR --write --json
For oidc, the writer emits the tokenless workflow. It is conservative:
- it preserves an equivalent OIDC workflow;
- it refuses to replace an unrelated or token-based workflow unless
--forceis explicitly justified; - it never writes
NPM_TOKEN, an npm access token, or an OTP; - it grants only
contents: readandid-token: write; - it uses a hosted runner and lockfile-aware install command;
- it runs build and test scripts when present before
npm publish.
Review the diff. The package's repository.url must match the GitHub repository
used by the trusted publisher; fix the package metadata before publishing when it
does not match.
For bypass, the writer sets write_state to not-applicable — no workflow is
generated. Use the auth.publish_command from the plan as the release command.
Step 4: Run the release gate
Before triggering the workflow:
- Confirm the requested version is not already present. npm versions are immutable; never retry by overwriting the same version.
- Run the package's existing test and build gates.
- Inspect the payload with
npm pack --dry-run --json; reject secrets, unintended source files, private paths, or missing build output. - Confirm the release commit is clean and contains the workflow and package version intended for the tag.
- Do not expose credentials in command output or logs.
Step 5: Trigger and verify the release
Only when the user asked to publish, act by mode:
oidc— create/push the intended version tag or run the configured manual workflow, then follow the GitHub Actions run to a terminal state.bypass— runauth.publish_commandfrom the plan against the granularNPM_TOKEN.
Then verify independently:
npm view PACKAGE@VERSION version --registry https://registry.npmjs.org
For public repositories, also check the npm provenance record when supported.
Report the exact version, workflow run (or local publish), registry result, and
provenance state. queued, in_progress, a generated YAML file, or an accepted
npm publish request is not equivalent to a live package version. A root
endpoint 404 right after publish can be CDN lag; re-read the specific version
endpoint before concluding failure.
Step 6: Report the state precisely
Use these states:
audited— repository and registry state classified;bootstrap-required— package name does not yet exist;workflow-written— tokenless OIDC workflow exists locally;trusted-publisher-configured— npm binding was verified;publish-command-ready— bypass mode resolved a publish command and token;publish-triggered— CI accepted the release request;published— publish (CI or local) finished successfully;verified-live— exact package and version were read back from npm;blocked— name the missing identity proof, registry evidence, or failed gate.
Validation
Exercise both registry branches and both auth modes without external writes:
python3 scripts/publish.py CASE_DIR --registry-state missing --dry-run --json
python3 scripts/publish.py CASE_DIR --registry-state exists --dry-run --json
python3 scripts/publish.py CASE_DIR --auth-mode oidc --dry-run --json
python3 scripts/publish.py CASE_DIR --auth-mode bypass --dry-run --json
python3 scripts/validate_skill.py .
An activation check should route "help me automatically publish this new npm package, no login from now on"
and "use NPM_TOKEN to publish this package locally" here. "List this Agent Skill on
LovStudio" must stay with lov-skill-publisher.
Dependencies
- Python 3.9+ standard library for the planner/writer.
- Git and a valid GitHub repository for automatic repository inference.
- Node/npm versions currently supported by npm Trusted Publishing.
- PyYAML only for local Skill source validation.
- npm/GitHub authentication is required only for the authorized external setup and publish steps; no credential is stored by this Skill.
Generic feedback loop
When the user raises revision feedback during a Skill-driven task, before continuing with the current artifact you must perform:
- First determine whether the feedback is
task-specific(this time only) orreusable(reusable across tasks). task-specificOnly modify the current task; do not change the Skill.reusableFirst determine the scope: for domain rules, first update the corresponding canonical Skill; for rules applicable to all Skills, first update the shared specification.- After completing rule updates, versioning, lint, and distribution verification, then apply the changes to the current task.
reusableThe change invalidates previous "confirm", "continue", and "go ahead" responses; after completing the current artifact modification and read-back, you must stop and wait for the user's next instruction, without automatically proceeding to publishing, committing, or other external writes.