> For the complete documentation index, see [llms.txt](https://gotts.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://gotts.gitbook.io/docs/monorepo-infrastructure/monorepo/06-publishing.md).

# Publishing

> **Part of**: [Monorepo Infrastructure PRD](/docs/monorepo-infrastructure/monorepo.md) | **Last Updated**: 2026-02-19

***

## npm Scope: `@gotts.ai`

All published packages use the `@gotts.ai` npm scope:

| Package                      | npm Name                      | Published     |
| ---------------------------- | ----------------------------- | ------------- |
| `packages/safe`              | `@gotts.ai/safe`              | Yes           |
| `packages/vault`             | `@gotts.ai/vault`             | Yes           |
| `packages/agent-proxy`       | `@gotts.ai/agent-proxy`       | Yes           |
| `packages/typescript-config` | `@gotts.ai/typescript-config` | No (internal) |
| `packages/eslint-config`     | `@gotts.ai/eslint-config`     | No (internal) |
| `packages/ui`                | `@gotts.ai/ui`                | No (internal) |

Internal packages are marked `"private": true` in their `package.json` and are never published.

## Changesets

[Changesets](https://github.com/changesets/changesets) manages versioning and changelogs across the monorepo.

### `.changeset/config.json` (normative)

```jsonc
{
  "$schema": "https://unpkg.com/@changesets/config@3/schema.json",
  "changelog": "@changesets/cli/changelog",
  "commit": false,
  "fixed": [],
  "linked": [],
  "access": "public",
  "baseBranch": "main",
  "updateInternalDependencies": "patch",
  "ignore": [],
  "privatePackages": {
    "version": false,
    "tag": false,
  },
}
```

**Key settings:**

| Setting                      | Value            | Purpose                                                                   |
| ---------------------------- | ---------------- | ------------------------------------------------------------------------- |
| `access`                     | `"public"`       | All published packages are public (scoped packages default to restricted) |
| `commit`                     | `false`          | Changesets does not auto-commit; CI handles it                            |
| `baseBranch`                 | `"main"`         | Version bumps compare against `main`                                      |
| `privatePackages`            | `version: false` | Private packages are not versioned or tagged                              |
| `updateInternalDependencies` | `"patch"`        | When a dependency bumps, dependents get a patch bump                      |

### Workflow

1. **Developer creates a changeset** when making a user-facing change:

```bash
pnpm changeset
```

This prompts for:

* Which packages changed
* Semver bump type (patch / minor / major)
* Summary of the change

A markdown file is created in `.changeset/` (e.g., `.changeset/cool-dogs-dance.md`).

2. **PR includes the changeset file.** CI checks that PRs touching published packages include a changeset.
3. **Release workflow** (on merge to `main`):

```bash
# Bump versions and update changelogs
pnpm changeset version

# Build and publish
pnpm release
```

See [08-dx-ci.md](/docs/monorepo-infrastructure/monorepo/08-dx-ci.md) for the GitHub Actions release workflow.

## package.json Exports

Every published package uses the `exports` field for clean entry points. This replaces the legacy `main`/`module`/`types` fields.

### Standard Published Package (normative)

```jsonc
{
  "name": "@gotts.ai/safe",
  "version": "0.1.0",
  "type": "module",
  "exports": {
    ".": {
      "import": {
        "types": "./dist/index.d.ts",
        "default": "./dist/index.js",
      },
      "require": {
        "types": "./dist/index.d.cts",
        "default": "./dist/index.cjs",
      },
    },
  },
  "files": ["dist", "README.md", "LICENSE"],
  "publishConfig": {
    "access": "public",
    "provenance": true,
  },
  "engines": {
    "node": ">=20.0.0",
  },
}
```

**Key fields:**

| Field                      | Purpose                                                    |
| -------------------------- | ---------------------------------------------------------- |
| `type: "module"`           | Package is ESM by default                                  |
| `exports`                  | Conditional exports for ESM (`import`) and CJS (`require`) |
| `exports.*.types`          | TypeScript declarations (must be first in condition order) |
| `files`                    | Whitelist of files included in the npm tarball             |
| `publishConfig.access`     | Public access for scoped packages                          |
| `publishConfig.provenance` | npm provenance attestation (see below)                     |
| `engines.node`             | Minimum Node.js version                                    |

### Multi-Entry Published Package

For packages with sub-path exports:

```jsonc
{
  "name": "@gotts.ai/vault",
  "version": "0.1.0",
  "type": "module",
  "exports": {
    ".": {
      "import": {
        "types": "./dist/index.d.ts",
        "default": "./dist/index.js",
      },
      "require": {
        "types": "./dist/index.d.cts",
        "default": "./dist/index.cjs",
      },
    },
    "./tools": {
      "import": {
        "types": "./dist/tools.d.ts",
        "default": "./dist/tools.js",
      },
      "require": {
        "types": "./dist/tools.d.cts",
        "default": "./dist/tools.cjs",
      },
    },
    "./client": {
      "import": {
        "types": "./dist/client.d.ts",
        "default": "./dist/client.js",
      },
      "require": {
        "types": "./dist/client.d.cts",
        "default": "./dist/client.cjs",
      },
    },
  },
  "files": ["dist", "README.md", "LICENSE"],
}
```

### Internal Package

```jsonc
{
  "name": "@gotts.ai/ui",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "exports": {
    ".": {
      "types": "./src/index.ts",
      "default": "./src/index.ts",
    },
    "./*": {
      "types": "./src/*.ts",
      "default": "./src/*.ts",
    },
  },
}
```

## npm Provenance

All published packages include [npm provenance](https://docs.npmjs.com/generating-provenance-statements) attestations. This provides a cryptographic link between the published package and its source commit.

**Requirements:**

* Publishing happens in GitHub Actions (not locally)
* The workflow has `id-token: write` permission
* `publishConfig.provenance: true` is set in `package.json`

Users can verify provenance on the npm package page or via:

```bash
npm audit signatures
```

## publint

[publint](https://publint.dev) validates that `package.json` is correctly configured for publishing. It checks:

* `exports` field correctness
* `files` whitelist includes all referenced files
* `type` field matches file extensions
* No missing `types` conditions

Run manually:

```bash
pnpm dlx publint packages/safe
```

Or as a CI check (see [08-dx-ci.md](/docs/monorepo-infrastructure/monorepo/08-dx-ci.md)).

## arethetypeswrong

[arethetypeswrong](https://arethetypeswrong.github.io/) validates TypeScript resolution for published packages. It catches issues where types resolve correctly in the monorepo but fail for external consumers.

Run after building:

```bash
pnpm dlx @arethetypeswrong/cli packages/safe
```

## Versioning Strategy

| Change Type                        | Semver Bump | Example                         |
| ---------------------------------- | ----------- | ------------------------------- |
| Breaking API change                | Major       | Rename tool, remove parameter   |
| New feature (backwards compatible) | Minor       | New MCP tool, new SDK method    |
| Bug fix, perf improvement          | Patch       | Fix tool response, faster query |
| Internal refactor (no API change)  | Patch       | Restructure internals           |

**Pre-1.0 rule:** During initial development (0.x.y), minor bumps may include breaking changes. The API is not considered stable until 1.0.0.
