<?xml version="1.0" encoding="UTF-8"?>
<spec xmlns="https://vibevm.org/spec/1">
  <title id="root">Publish a package</title>
  <status stage="doc" state="work" audience="user,author"/>
  <p p="1">You wrote a package and want others to install it. This page publishes it to the place your projects install from, as its own repository, tags the version, and checks that a fresh project can install it.</p>
  <prompt id="publish-a-package" p="2">
    Publish the in-tree package org.acme/notes of this project, the slot vibevm/vibepacks/org.acme/notes/v0.1.0, to the first registry of this project, using the publish token already in my environment, then create a scratch project elsewhere and install the published package into it to prove it works. Ask me before the actual push.
    <needs>the vibevm skill installed for your agent; a publish token for the registry's host in the environment or under `~/.vibe/`; the package's `vibe.toml` complete, with a version that has not been published before</needs>
    <outcome>a repository named after the package's coordinate exists in the registry organisation with a tag for the version; a scratch project installs it and `vibe list` shows the version</outcome>
    <assert>vibe registry publish vibevm/vibepacks/org.acme/notes/v0.1.0 --dry-run</assert>
  </prompt>
  <section id="what-happens" title="What happens">
    <p p="3">The agent runs `vibe registry publish vibevm/vibepacks/org.acme/notes/v0.1.0 --dry-run` first and shows you what would happen: the [registry](../glossary/index.xml#registry), the repository name derived from the [coordinate](../glossary/index.xml#coordinate), the version tag. After your yes it runs the real command: vibe creates the repository in the registry organisation through the host's API if it does not exist, pushes the package's shippable tree, and tags the version. The package is then one more repository the registry's [index](../glossary/index.xml#index-registry) will pick up. To prove it, the agent creates a scratch project and installs the package by coordinate.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#SHAPE-OWN-REPO" p="4"/>
    <p p="5">The publisher is a mechanical tool: it creates the repository, pushes the content and tags the version, and nothing more. The host of the registry's address picks the adapter that creates repositories; GitHub and GitVerse are known, and an unknown host is a clear error rather than a guess. In the repository the package content lies flat at the root, and the version is the tag `v` followed by the version number.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#PUBLISH-UTILITY" p="6"/>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#PUB-ADAPTER-SELECTION" p="7"/>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#FLAT-LAYOUT" p="8"/>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#LAYOUT-TAG-VERSION" p="9"/>
  </section>
  <section id="by-hand" title="By hand">
    <p p="10">1. Put a publish token where vibe reads it. That is the environment variable `VIBEVM_PUBLISH_TOKEN`, or a file `~/.vibe/&lt;host&gt;.publish.token` such as `~/.vibe/github.publish.token`, readable by you alone. The token needs the right to create repositories in the organisation.</p>
    <rule ref="spec://org.vibevm.core/vibevm/common/PROP-000#token-secrecy" p="11"/>
    <p p="12">vibe looks for the token in a fixed order and takes the first it finds. First comes `VIBEVM_PUBLISH_TOKEN_&lt;HOST&gt;` for the registry's host, such as `VIBEVM_PUBLISH_TOKEN_GITHUB`. Then `VIBEVM_PUBLISH_TOKEN`, then the per-host file, then the older `~/.vibe/git.publish.token`. The token is a surface secret: never printed, never logged, never written to a file vibe writes. It leaves the process only in the request to the host, over an encrypted connection.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#PUB-TOKEN-LOADING" p="13"/>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#TOK-HOST-ENV-VAR" p="14"/>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#TOK-PER-HOST-FILE" p="15"/>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#TOKEN-SECRECY-INVARIANT" p="16"/>
    <p p="17">2. Rehearse:</p>
    <example id="publish-dry-run" fixture="package-notes" p="18">
      <run>vibe registry publish vibevm/vibepacks/org.acme/notes/v0.1.0 --dry-run</run>
      <expect></expect>
    </example>
    <p p="19">3. Publish. `--registry` picks a registry by name; without it the first one in the [manifest](../glossary/index.xml#manifest) is used:</p>
    <example id="publish" fixture="package-notes" p="20">
      <run>vibe registry publish vibevm/vibepacks/org.acme/notes/v0.1.0 --registry local --dry-run</run>
      <expect></expect>
    </example>
    <p p="21">4. Install it from a fresh project to be sure: `vibe init scratch` and `vibe install &lt;group&gt;/notes --path scratch`.</p>
  </section>
  <section id="versions" title="Publishing a version again">
    <p p="22">Publishing a version that already exists replaces it: vibe appends a commit with the new content and moves the version tag to it. The version number changes only when you change it in the manifest. A consumer who already resolved that version keeps the exact bytes the [lock file](../glossary/index.xml#lock-file) recorded, and verifies the content [fingerprint](../glossary/index.xml#fingerprint) on every install, so the moved tag is noticed rather than silently accepted; a fresh install gets the new content. When the change matters to consumers, bump the version.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#PUBLISH-MUTABLE-VERSIONS" p="23"/>
    <rule ref="spec://org.vibevm.core/vibevm/common/PROP-044#THE-FREEZE-MODEL" p="24"/>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#EFF-FORCE-PUSH-CAUGHT" p="25"/>
    <p p="26">The publisher never rewrites the registry's history: every publish commit is a child of the head it observed, the tag moves only inside one atomic push guarded by the expected state of both refs, older tags are never touched, and the run stays inside the organisation the manifest names.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#PUBLISH-NEVER-RULES" p="27"/>
  </section>
  <section id="workspaces" title="Several packages at once">
    <p p="28">A repository that develops several packages publishes them with `vibe workspace publish`. It orders the members by their dependencies on each other and publishes each as its own repository. It stops at the first failure with a report of what was published and what remains. Each published copy carries an `[origin]` table naming the repository and the commit it came from, so a copy can always be traced to its source. `--member` restricts the run to one node; `--dry-run` shows the selection and the order without pushing.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-009#PUBLISH-REGEN" p="29"/>
  </section>
  <section id="edge-cases" title="Edge cases and rules">
    <p p="30">`--repo-url` pushes straight to an existing git repository with your local git credentials and loads no publish token; use it for hosts without an API adapter.</p>
    <p p="31">The token is never sent to a package's install script and never printed, even in machine-readable output; if a command prints it, that is a bug to report.</p>
    <rule ref="spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-020#TOKEN-NEVER-IN-ENV" p="32"/>
    <p p="33">A documentation package is published the same way; what differs is how it is consumed, by `vibe cache add` and the site rather than by `vibe install`.</p>
  </section>
</spec>
