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:
- Reachability. The target audience already has Node and uses
npx. Any other channel adds installation overhead before the user can run a single command. - Coherent versioning.
@aidokit/clidepends on@aidokit/coredepending on@aidokit/adapter-claude-code. Releasing one package without the others creates broken combinations users will hit atpnpm installtime. - Trust signals. Third-party adapter authors (post-v1.0) and security-conscious users want to verify that a published package was built from the corresponding git ref. Provenance attestations are the standard answer.
- Pre-1.0 fluidity vs. post-1.0 stability. Pre-1.0, we will ship breaking changes between minor versions. Users who pin to
aidokitonlatestwould break unexpectedly. The dist-tag mechanism solves this:npx aidokit@alpha initopts in to the pre-1.0 stream. - License surface. The Apache-2.0 declaration (ADR-0003) must be carried correctly in every published
package.json. A misconfigured publish breaks legal compliance for downstream consumers.
The decision is constrained by three earlier ADRs:
- ADR-0001 fixes Node.js + TypeScript + ESM-only modules. No CommonJS dual publish.
- ADR-0002 fixes pnpm workspaces + Turborepo for monorepo coordination.
- ADR-0003 fixes Apache-2.0 as the published licence.
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:
- First-party:
@aidokit/adapter-<name>,@aidokit/stack-pack-<name>. - Third-party scoped:
@acme/aidokit-adapter-<name>,@acme/aidokit-stack-pack-<name>. - Third-party unscoped:
aidokit-adapter-<name>,aidokit-stack-pack-<name>.
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:
- Per-package version bumps (patch / minor / major).
- Dependency-version updates inside the workspace (when
@aidokit/coreminor-bumps, dependents that ranged on it minor-bump too). - CHANGELOG.md generation per package.
- A "release PR" that aggregates pending changesets into the actual
npm publishinvocation.
Pre-1.0 the policy is:
- Patch — internal refactor, doc fixes, bug fixes that don't change the API or emitted output.
- Minor — new features, and breaking changes (allowed pre-1.0 per
.docs/ROADMAP.md§Release principles). - Major — reserved; pre-v1.0 we do not version-major. v1.0.0 is the first major.
Post-1.0:
- Strict semver. Breaking changes require major bumps and ship with
aidokit migratescripts.
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:
- Its own
README.md(a short package-level overview pointing at the monorepo docs). - The repository-root
LICENSEandNOTICEfiles, copied into the package at publish time by the build step.
This is the standard pattern; pnpm + Changesets handle the copy automatically when files lists the artefacts.
Consequences
Positive
- One install path. Users learn
npx aidokit initand never need to know about alternate channels. - Coherent versioning. Changesets prevents the "broken workspace combination" failure mode; releases always include consistent peer dependencies.
- Pre-1.0 turbulence is opt-in.
latestusers (post-v1.0) never see pre-release builds;alphausers explicitly chose to. - Trust signals from v1.0. Provenance attestations let security-conscious users verify what they install.
- Namespace clarity.
@aidokit/*is unambiguously first-party;aidokit-*packages are third-party convention. - Legal compliance is mechanical. Every published package carries
LICENSE+NOTICE+ the correct SPDX identifier.
Negative
- npm is the single point of distribution. An npm outage blocks
npx aidokituntil it recovers. We accept this; alternatives have larger downsides. - No offline install story pre-v1.0. Users without network access cannot bootstrap. Mitigation:
npm packproduces a.tgzthat can be transported; this is a workaround we document only on request. - Provenance setup costs CI time. GitHub Actions publish jobs need OIDC permissions; the workflow file is a few extra lines. Negligible cost.
- Single-binary distribution might frustrate Windows users who don't have Node. This is a real audience gap; v1.0 will reconsider the open question.
Neutral
- Changesets generates per-package CHANGELOGs. Plus the repository-root
CHANGELOG.mdfor narrative pre-v0.1 entries. From v0.1 release onward, the root CHANGELOG can either be removed or kept as a curated overview pointing at per-package CHANGELOGs — to be decided at v0.1 release. - Pre-1.0
latestis empty. Users who runnpm view aidokitwill see versions but nolatesttag until v1.0. This is intentional and documented in the README.
Alternatives considered
Alternative 1: GitHub Packages (npm-format) as primary registry
Publish to GitHub Packages first; mirror to npmjs.org optionally.
Pros
- Tighter integration with GitHub Actions and OIDC.
- Per-org access control.
Cons
- Users have to configure
.npmrcwith a GitHub Packages registry override to install.npx aidokitno longer "just works". - Reduces reach for users who don't have GitHub accounts (unlikely audience but possible).
- Provenance attestations are achievable on npmjs.org as well; no unique advantage.
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
- Discovery surface beyond
npm search. - Verification badge programme.
Cons
- Marketplace is explicit v2.0 scope per
.docs/ROADMAP.md. - Pre-1.0 we don't have enough ecosystem to justify the curation infrastructure.
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
- Older toolchains that lack ESM support can consume
aidokitpackages.
Cons
- ADR-0001 already decided ESM-only. The justification (TS
nodenextresolution, ecosystem convergence, build complexity) stands. - Reopening that decision belongs in a superseding ADR to 0001, not in this one.
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
- Pre-release identifiers are semver-standard.
- Tooling treats
0.5.0-rc.1as less than0.5.0automatically.
Cons
- npm dist-tags are the idiomatic way to opt into pre-release streams without per-version pinning.
- Combining pre-release identifiers with dist-tags is allowed and useful (e.g., publish
0.5.0-rc.1under thebetatag) — we can use both. This is what we'll do at v0.5 forbeta-tagged candidates.
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
- Long history; well-known in the monorepo space.
Cons
- Lerna's "fixed mode" (all packages share one version) is too coarse for
aidokit's independent package versioning. - Lerna's "independent mode" requires per-package commit-prefix conventions that contributors will forget.
- Maintenance attention on Lerna has decreased; Changesets is the current de-facto standard for pnpm workspaces.
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
- No tooling to learn.
Cons
- Cross-package dependency-version coordination becomes the maintainer's problem.
- CHANGELOG fidelity drops.
- Contributors don't know whether their change is patch / minor / major; the choice is made post-hoc.
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:
- [ ] First
pnpm changeset publish --tag alphasucceeds in CI without manual intervention. - [ ]
npx aidokit@alpha --versionresolves correctly against the published registry. - [ ]
npx aidokit init(no tag) prints a clear message directing pre-1.0 users to@alphauntil v1.0. - [ ] Every published
package.jsoncarries"license": "Apache-2.0"and the correctengines.nodefloor. - [ ]
npm audit signaturesagainst v1.0 packages reports verified provenance. - [ ] No published package contains source files, test files, or
tsconfig.json.
References
Internal
- ADR 0001 — Runtime + language (ESM, Node 20+)
- ADR 0002 — Monorepo tooling (pnpm + Turborepo)
- ADR 0003 — License (Apache-2.0)
.docs/ARCHITECTURE.md§17 — Versioning strategy.docs/ARCHITECTURE.md§21 — Open question on single-binary distribution.docs/ROADMAP.md— Pre-1.0 breaking-change policy; v1.0 GA exit criteria
External
- npm provenance — https://docs.npmjs.com/generating-provenance-statements
- Changesets — https://github.com/changesets/changesets
- Semantic Versioning 2.0.0 — https://semver.org/
- pnpm publishing — https://pnpm.io/cli/publish
- "Modern npm dist-tag conventions" — https://docs.npmjs.com/cli/v10/commands/npm-dist-tag