ADR 0004: Distribution — npm, Scoped Packages, Changesets, Provenance from v1.0

Field Value
Status Accepted
Date 2026-05-12
Deciders Project maintainers
Supersedes
Superseded by
Related ADRs 0001 (Runtime + language), 0002 (Monorepo tooling), 0003 (License)

Summary

aidokit ships exclusively via npm. Libraries publish under the @aidokit/* scope with public access; the user-facing CLI publishes unscoped as aidokit. Pre-1.0 releases live under the alpha dist-tag; the latest tag is reserved for v1.0 GA and onward. Version coordination across packages uses Changesets, with pre-1.0 breaking changes permitted on minor bumps. npm provenance attestations are required for every published package from v1.0; pre-1.0 provenance is best-effort. Alternative distribution channels (single-binary, OS package managers, container images) are explicitly deferred — npx aidokit is the only supported entry point through v1.0.


Context

aidokit is a CLI plus a family of cooperating libraries. Distribution choices have to satisfy several constraints simultaneously:

The decision is constrained by three earlier ADRs:

This ADR fills the remaining gaps: registry, package naming, dist-tag policy, version-coordination tool, and provenance.


Decision

1. Registry: public npm

The canonical registry is registry.npmjs.org. No alternate registry, no private mirror, no GitHub Packages publishing pre-v1.0. The user's npx aidokit init resolves against npm with no additional configuration.

2. Package naming

Package Name Scope Access Purpose
User-facing CLI aidokit unscoped public npx aidokit init, npm i -g aidokit
Core SDK @aidokit/core @aidokit public Adapter + StackPack contracts, types, harness
Shared docs @aidokit/shared-docs @aidokit public docs/ and agent-artifacts/ templates
Stack detection @aidokit/stack-detect @aidokit public Per-stack detectors
Prereq check @aidokit/prereq-check @aidokit public Host environment checks
MCP catalog @aidokit/mcp-catalog @aidokit public MCP definitions + installers
Claude Code adapter @aidokit/adapter-claude-code @aidokit public First-party adapter
Node-TS stack pack @aidokit/stack-pack-node-ts @aidokit public First-party stack pack

The CLI is unscoped because users type its name into a terminal (npx aidokit); a scoped name (npx @aidokit/cli) is workable but more typing. Internal packages are scoped for namespace clarity, conflict prevention, and to keep the @aidokit/* namespace authoritatively under the maintainer organisation.

Third-party adapters and stack packs (post-v1.0) MUST publish under their own scope or unscoped — they may not use @aidokit/*. Conventions:

3. Dist-tag policy

Dist-tag Meaning Active during
alpha Pre-1.0 stream; breaking changes allowed on minor bumps v0.1 → v1.0
beta Reserved for v0.5 if we choose to widen the audience without going GA v0.5 (optional)
latest Default tag; stable v1.0+ releases v1.0 onward
next Optional opt-in to in-progress v1.x or v2.0 work post-v1.0

Pre-v1.0 publish commands:

pnpm changeset publish --tag alpha

latest is not updated pre-v1.0 — npx aidokit would otherwise grab a pre-alpha build for users who didn't request it. The marketing posture is "pre-alpha; use npx aidokit@alpha init".

v1.0 publish migrates latest to point at the GA release; alpha continues to track the active pre-release line for the next major (if any).

4. Version coordination: Changesets

Every PR that changes published behaviour MUST include a .changeset/*.md file generated via:

pnpm changeset

Changesets handles:

Pre-1.0 the policy is:

Post-1.0:

5. Provenance attestations from v1.0

From v1.0 GA, every @aidokit/* package and the aidokit CLI publish with npm provenance generated from GitHub Actions (or whatever CI we run at the time). Concretely:

npm publish --provenance --access public

Pre-1.0 provenance is best-effort: enable it if CI supports it; do not block releases on it. The reason: pre-1.0 we may publish from local machines during early alpha; provenance from local publishes is not meaningful.

Provenance is documented in the README's install section from v1.0; users can verify any release with:

npm audit signatures

6. Single-binary distribution is deferred to post-v1.0

Node SEA (Single Executable Application) and pkg-based binary builds are tracked as an open question in .docs/ARCHITECTURE.md §21 ("Whether to ship a standalone binary"). Until that decision is made, the only supported distribution is npm / npx. We will not maintain unofficial binary builds, snap/flatpak packages, or Homebrew formulae pre-v1.0.

7. No container distribution

aidokit is a scaffolder, not a service. A Docker image adds operational complexity (volume mounts to write into the user's project, OS-specific path translation) for no benefit. We do not publish container images. CI users can npx aidokit inside any image that has Node 20.

8. Engines and packageManager enforcement

Every published package.json declares:

{
  "engines": { "node": ">=20.0.0" },
  "packageManager": "pnpm@9.x.y"
}

The CLI prints a warning (not error) if the host Node is below the declared engines.node floor. The packageManager field is for contributor parity only — published consumers can install with npm, pnpm, or yarn.

9. Files included in published packages

Each package.json declares a files allowlist:

{
  "files": ["dist/**", "README.md", "LICENSE", "NOTICE", "CHANGELOG.md"]
}

Source files (src/**), tests (test/**, *.test.ts), TypeScript configs, and tooling configs are not published. The published surface is the compiled output plus the legal artefacts.

10. README and NOTICE per package

Every published package includes:

This is the standard pattern; pnpm + Changesets handle the copy automatically when files lists the artefacts.


Consequences

Positive

Negative

Neutral


Alternatives considered

Alternative 1: GitHub Packages (npm-format) as primary registry

Publish to GitHub Packages first; mirror to npmjs.org optionally.

Pros

Cons

Reason rejected: zero-config npx aidokit is a hard requirement.

Alternative 2: Curated marketplace pre-v1.0

A marketplace.aido.dev site curating verified adapters and stack packs, plus npm fallback.

Pros

Cons

Reason rejected: out of milestone. Reconsidered at v2.0.

Alternative 3: Dual publish to CJS + ESM

Ship every package as both CommonJS and ESM to widen compatibility.

Pros

Cons

Reason rejected: out of scope for this ADR; ADR-0001 is authoritative.

Alternative 4: Branch-based pre-releases (-rc, -pre, -next)

Use semver pre-release identifiers in version strings instead of dist-tags.

Pros

Cons

Reason rejected: not actually an alternative; we use both. Dist-tags govern audience opt-in; pre-release identifiers govern version ordering.

Alternative 5: Lerna instead of Changesets

Use Lerna for version management and publishing.

Pros

Cons

Reason rejected: Changesets is the better fit for our shape and the active community.

Alternative 6: Manual version bumps with no tooling

Each PR edits package.json versions by hand; maintainer publishes from local.

Pros

Cons

Reason rejected: the tooling cost of Changesets is low; the consistency benefit is high.


Implementation notes

Required package.json fields per published package

{
  "name": "@aidokit/<package-name>",
  "version": "0.1.0-alpha.0",
  "license": "Apache-2.0",
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js"
    }
  },
  "files": ["dist/**", "README.md", "LICENSE", "NOTICE", "CHANGELOG.md"],
  "engines": { "node": ">=20.0.0" },
  "publishConfig": { "access": "public" },
  "repository": { "type": "git", "url": "https://github.com/<org>/<repo>" }
}

The unscoped aidokit package adds a bin field:

{
  "bin": { "aidokit": "./dist/cli.js" }
}

Publish flow (post-Phase 0)

# Contributor PR
pnpm changeset                     # generates .changeset/*.md
git add .changeset/ && git commit
# ... PR merged

# Release (maintainer or CI)
pnpm changeset version             # consumes .changeset/*.md, updates package.json + CHANGELOG
pnpm install --frozen-lockfile=false
pnpm build
pnpm changeset publish --tag alpha # pre-1.0
# OR
pnpm changeset publish             # v1.0+ (defaults to 'latest')

CI publish job (GitHub Actions outline)

# .github/workflows/publish.yml
on:
  push:
    branches: [main]
permissions:
  contents: write
  id-token: write # required for npm provenance
  packages: write
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v3
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          registry-url: https://registry.npmjs.org
      - run: pnpm install --frozen-lockfile
      - run: pnpm build
      - run: pnpm test
      - uses: changesets/action@v1
        with:
          publish: pnpm changeset publish --tag alpha
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
          NPM_CONFIG_PROVENANCE: true

The exact workflow lands during workspace bootstrap (Phase 0b). This snippet is illustrative.

Pre-1.0 dist-tag pinning behaviour

npx aidokit init resolves aidokit@latest. Pre-1.0, latest is unset for the aidokit package, so npx aidokit init will print an error directing users to npx aidokit@alpha init. The README mirrors this guidance.


Validation

This decision is validated when:


References

Internal

External