# Development Guide

Scoped package: **`@carllee1983/dbcli`**. All `npm` / `npx` examples below use this name.

## npm Publishing Process

### Pre-Publication Checklist

Before running `npm publish`:

1. **Verify build is clean:**
   ```bash
   bun run build
   ls -lh dist/cli.mjs   # expect a few MB (bundled CLI + dependencies)
   ```

2. **Verify tests pass (pick one):**
   ```bash
   # Fast path: unit + core only (matches CI-style smoke)
   bun run test:unit

   # Full suite (Bun test runner)
   bun test
   ```

   For live database integration tests, set an explicit config if needed:
   ```bash
   LIVE_DB_CONFIG_PATH=/path/to/.dbcli bun test tests/integration/live-db.test.ts
   ```

   If no live config is available, `tests/integration/live-db.test.ts` skips
   instead of falling back to the default PostgreSQL configuration. Use
   `SKIP_INTEGRATION_TESTS=true` to skip integration tests when running a broad `bun test`.

3. **Update version in package.json:**
   ```bash
   npm version minor   # bumps version + creates git tag (in this repo)
   # OR edit the "version" field in package.json by hand
   ```

4. **Verify package contents (dry run):**
   ```bash
   npm pack --dry-run
   ```
   Expect **`dist/`** (including `cli.mjs`, `core.mjs` / `core.d.ts`, and `agent-core.mjs` / `agent-core.d.ts`), **`assets/`** (e.g. `SKILL.md`, `reference.md` for `dbcli skill`), **`README.md`**, **`CHANGELOG.md`**, **`LICENSE`**, and **`package.json`**. There must be **no** `src/`, `tests/`, or `node_modules/`. The listing may also include other root `README*.md` files (npm can still pack them even when `files` is set); dev-only readmes are listed in **`.npmignore`** — re-check with dry-run if you add or remove docs.

5. **Check package size:**
   ```bash
   npm pack
   ls -lh carllee1983-dbcli-*.tgz   # compressed tarball (typically well under 5MB)
   rm carllee1983-dbcli-*.tgz       # cleanup
   ```

### Publication

Publication uses the `prepublishOnly` script in `package.json`:

```bash
npm publish
```

What runs (conceptually):

1. **`prepublishOnly`:** `bun run build` — rebuilds `dist/cli.mjs` from `src/cli.ts` via `scripts/build.ts`.
2. **Tarball** — paths from the **`files`** field in `package.json`, further filtered by **`.npmignore`**. (Some npm versions also merge in extra root `README*` files; use dry-run to see exactly what will ship.)
3. **Registry** — with `"publishConfig": { "access": "public" }`, the scoped package is published as **public**.

A failed `bun run build` will fail the publish, so you should not ship a stale `dist/` from a previous local build.

### Verification (Post-Publication)

After publishing:

1. **Global install:**
   ```bash
   bun install -g @carllee1983/dbcli
   which dbcli
   dbcli --version
   ```
   `npm install -g` also works, but the installed executable still runs under Bun via its
   `#!/usr/bin/env bun` shebang — verify on a machine that has Bun on `PATH`.

2. **Zero-install (bunx / npx):**
   ```bash
   cd /tmp && mkdir -p test-dbcli && cd test-dbcli
   bunx @carllee1983/dbcli --help
   bunx @carllee1983/dbcli --version
   # or: npx @carllee1983/dbcli --help
   ```

3. **Windows (if available):** `npm install -g @carllee1983/dbcli`, then `dbcli --help`. npm creates the `.cmd` stub for the `bin` entry; no hand-written `.cmd` in the repo. Bun must be on `PATH` there too.

### Rollback (if needed)

If a bad release must be mitigated:

```bash
npm unpublish @carllee1983/dbcli@<VERSION>
# and/or
npm deprecate @carllee1983/dbcli@<VERSION> "Reason; use <SAFE_VERSION> instead"
```

Then ship a patch version with the fix. Prefer **deprecate** over **unpublish** when consumers may already depend on the version.

### Configuration Details

- **`files` (in `package.json`):** Publishes `dist/`, `assets/`, `README.md`, `CHANGELOG.md`, `LICENSE`. The `assets/` tree is required for `dbcli skill` to copy bundled `SKILL.md` / `reference.md` from the installed package.
- **`prepublishOnly`:** `bun run build` so `dist/cli.mjs` matches current source.
- **`engines`:** Declares `bun >= 1.3.3` only. `node` was removed because the published bundles cannot run on Node: `dist/cli.mjs` dynamic-imports the extensionless `./cli-runtime` (Bun's resolver appends `.mjs`, Node's does not) and `dist/core.mjs` calls Bun globals. `tests/integration/runtime-contract.test.ts` fails if `engines.node` comes back without the bundles being fixed to match, and if `dist/agent-core.mjs` — the one entry point that is Node-importable — regresses. See `docs/adr/0008-dbcli-is-a-bun-program-and-engines-says-so.md`.
- **Shebang:** `scripts/build.ts` prepends `#!/usr/bin/env bun` to `dist/cli.mjs`; the `bin` field in `package.json` points at that file.
- **`exports`:** `./core` and `./agent-core` only — there is deliberately no `.` entry. It used to point at `dist/cli.mjs`, and `src/cli-runtime.ts` calls `outputHelp()` and `parseAsync(process.argv)` at module top level, so importing the package ran the CLI against the host's argv and never returned. `bin` does not resolve through `exports`, so the executable is unaffected. `tests/integration/runtime-contract.test.ts` pins both the absence of `.` and the side effect that justifies it.
- **`postinstall`:** `scripts/postinstall-check-bun.mjs` reports a missing Bun — npm ignores `engines.bun`, so this is the only signal an npm-only machine gets before `dbcli` refuses to start. The command is `bun … || node …` so it runs under whichever runtime is present: Bun-only machines have no `node` to invoke it with, and a hard `node` dependency there would break the primary install path. It must **not** end in `|| exit 0`; v1.55.0 shipped with that mask and npm, which hides lifecycle output unless a script fails, therefore showed nothing at all. On a global install (`npm_config_global === 'true'`) a missing Bun exits 1, so the reason is printed and npm rolls the `bin` back; a dependency install only warns, because that is the `./agent-core` consumer. Both outcomes are pinned in `tests/integration/runtime-contract.test.ts`.
- **Live DB tests:** `tests/integration/live-db.test.ts` uses project `.dbcli` by default or `LIVE_DB_CONFIG_PATH` when you point at another config directory. Set `SKIP_INTEGRATION_TESTS=true` to skip all integration tests.

For contributor workflow and release process, see **[CONTRIBUTING.md](./CONTRIBUTING.md)** and the main **[README.md](./README.md)** Development section.

### Troubleshooting

| Issue | What to do |
|-------|------------|
| `prepublishOnly` / build fails | Fix TypeScript or build errors, run `bun run test:unit`, then `bun run build` again. |
| Tarball unexpectedly large or bloated | Inspect the bundle: e.g. `bun build ./src/cli.ts --outfile=dist/cli.mjs --target=bun --metafile=meta.json` and review the metafile; trim dependencies or dev-only code paths. |
| Windows: `dbcli` not found after global install | Confirm PATH includes npm’s global `bin`; reinstall `npm i -g @carllee1983/dbcli`. |
| `npx` download slow or cache weird | `npm cache clean --force` and retry `npx @carllee1983/dbcli --version`. |
