From 7afeba7084155624e3d0f096afb18628cb7239e8 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Wed, 22 Jul 2026 23:35:48 -0400 Subject: [PATCH 1/2] docs(skills): align lint/build/lifecycle skills with current bsh behavior - lint skill: drop false publint-in-lint claim, document --strict/ --warnings/--format json, add remedies table, surface scoping notes - build skill: dts default, publint publish gate, entry error message - lifecycle skill: strict lint pass moves to PR handoff step --- .changeset/skills-updates.md | 7 +++ README.md | 2 +- skills/build/SKILL.md | 25 +++++++-- skills/lifecycle/SKILL.md | 15 +++++- skills/lint/SKILL.md | 79 +++++++++++++++++++++------- skills/lint/references/lint-rules.md | 34 ++++++++---- 6 files changed, 125 insertions(+), 37 deletions(-) create mode 100644 .changeset/skills-updates.md diff --git a/.changeset/skills-updates.md b/.changeset/skills-updates.md new file mode 100644 index 0000000..dd923bc --- /dev/null +++ b/.changeset/skills-updates.md @@ -0,0 +1,7 @@ +--- +'@bomb.sh/tools': patch +--- + +Updates the `lint`, `build`, and `lifecycle` skills to match current `bsh` behavior + +The lint skill no longer claims publint runs in `pnpm run lint` (it gates `pnpm run build`), documents `--strict`, `--warnings`, and `--format json`, and adds a remedies table mapping common violations to their sanctioned fixes. The lifecycle skill moves the knip dead-code gate (`lint --strict`) to the PR handoff step. diff --git a/README.md b/README.md index 4096fd2..432536b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ If you'd like to use this package for your own projects, please consider forking - `bsh test` command, using [`vitest`](https://vitest.dev/) - `bsh format` command, using [`oxfmt`](https://oxc.rs/docs/guide/usage/formatter) - `bsh lint` command, using [`oxlint`](https://oxc.rs/docs/guide/usage/linter), [`knip`](https://knip.dev), [`tsgo`](https://npmx.dev/@typescript/native-preview) -- `bsh publint` command, using [`publint`](https://publint.dev/) +- `bsh publint` command, using [`publint`](https://publint.dev/) (also runs automatically as a publish gate in `bsh build`) - `bsh sync` command, which links our shared [agent skills](#agent-skills) into your project - shared `tsconfig.json` file diff --git a/skills/build/SKILL.md b/skills/build/SKILL.md index b3cd966..969df3d 100644 --- a/skills/build/SKILL.md +++ b/skills/build/SKILL.md @@ -2,8 +2,9 @@ name: build description: > TypeScript to ESM compilation via tsdown with opinionated defaults. Sourcemaps, - clean dist/, unbundled output. Flags: --dts, --bundle, --minify. Default entry - src/**/*.ts. Use when building a package for publish or generating declarations. + clean dist/, unbundled output, .d.mts types by default, publint publish gate. + Flags: --no-dts, --bundle, --minify. Default entry src/**/*.ts. Use when + building a package for publish or generating declarations. metadata: type: core library: '@bomb.sh/tools' @@ -31,28 +32,42 @@ pnpm run build - **Sourcemaps:** enabled - **Clean:** removes dist/ before building - **Unbundled** by default (each source file produces one output file) +- **Types:** `.d.mts` declarations generated by default (via tsgo) - **Entry:** defaults to `src/**/*.ts` - **Config:** explicitly disabled (`config: false`) — tsdown.config.ts is ignored +## Publish gate + +After a successful build, `bsh build` runs **publint** (strict mode) against +the emitted `dist/` and fails the build on errors — broken `exports` targets, +missing declaration files, and invalid `package.json` fields are caught here, +not at publish time. Warnings and suggestions are advisory. + +A failed gate after `--no-dts` almost always means `package.json` declares +types that weren't emitted — either rebuild with types (the default) or fix +the `exports` map. + ## Flags Pass flags after `--` in pnpm: | Flag | Effect | |------|--------| -| `--dts` | Generate `.d.mts` declaration files | +| `--no-dts` | Skip `.d.mts` declaration generation (on by default) | | `--bundle` | Bundle into single output (disables unbundled mode) | | `--minify` | Minify output | | positional args | Custom entry points (replaces default `src/**/*.ts`) | ```sh -pnpm run build -- --dts +pnpm run build -- --no-dts pnpm run build -- --bundle pnpm run build -- --minify pnpm run build -- src/index.ts -pnpm run build -- --dts --minify ``` +If no entry files match, the build fails with `No entry files matched: …` — +pass explicit entries or check that `src/` exists. + ## Common Mistakes ### MEDIUM: Passing unnecessary flags diff --git a/skills/lifecycle/SKILL.md b/skills/lifecycle/SKILL.md index 8e52f99..c8372eb 100644 --- a/skills/lifecycle/SKILL.md +++ b/skills/lifecycle/SKILL.md @@ -27,7 +27,7 @@ All scripts proxy to `bsh ` via `package.json`. Never bypass them. | `pnpm run build` | `bsh build` | Build with tsdown (ESM, sourcemaps, clean) | | `pnpm run dev` | `bsh dev` | Watch mode with native Node TS transforms | | `pnpm run format` | `bsh format` | Format with oxfmt | -| `pnpm run lint` | `bsh lint` | Parallel: oxlint + publint + knip + tsgo | +| `pnpm run lint` | `bsh lint` | Parallel: oxlint + knip + tsgo | | `pnpm run test` | `bsh test` | Run tests with vitest (single run) | | `pnpm run init` | `bsh init` | Scaffold new project from template | @@ -101,6 +101,9 @@ pnpm run lint pnpm run lint -- --fix ``` +- [ ] Warnings are collapsed by default (they never fail the run). Expand them + with `--warnings` when triaging. + ### 4. Format - [ ] Normalize code style: @@ -111,7 +114,8 @@ pnpm run format ### 5. Build -- [ ] Verify the package compiles: +- [ ] Verify the package compiles (types are generated by default) and passes + the publint publish gate: ```sh pnpm run build @@ -127,6 +131,13 @@ pnpm run test ### 7. PR Handoff +- [ ] Run the strict lint pass — this is the dead-code gate (knip unused + exports/types/files) that doesn't run during the TDD loop: + +```sh +pnpm run lint -- --strict +``` + - [ ] Present a summary of changes to the human - [ ] **Human creates the PR** — agents do not create or merge PRs autonomously diff --git a/skills/lint/SKILL.md b/skills/lint/SKILL.md index 9f78cb2..6f997cb 100644 --- a/skills/lint/SKILL.md +++ b/skills/lint/SKILL.md @@ -1,11 +1,12 @@ --- name: lint description: > - Multi-tool linting pipeline: oxlint, publint, knip, tsgo run in parallel via - pnpm run lint. Covers Bombshell conventions: URL over node:path, max 2 params - with options bag, named exports only, import type, prefer node builtins, no - console.log, no generic Error. Use when checking code quality or understanding - lint violations. + Multi-tool linting pipeline: oxlint, knip, and tsgo run in parallel via + pnpm run lint (publint gates pnpm run build instead). Covers Bombshell + conventions: URL over node:path, max 2 params with options bag, named + exports only, import type, prefer node builtins, no console.log, no + generic Error. Use when checking code quality or understanding lint + violations. metadata: type: core library: '@bomb.sh/tools' @@ -20,32 +21,63 @@ metadata: # Lint -Multi-tool linting pipeline. All four tools run in parallel with unified output. +Multi-tool linting pipeline. oxlint, knip, and tsgo run in parallel with unified output. ## Setup ```sh -pnpm run lint # Report violations -pnpm run lint -- --fix # Auto-fix (oxlint only), then report remaining -pnpm run lint -- src/foo.ts # Target specific files (default: ./src) +pnpm run lint # Report errors (warnings collapsed to counts) +pnpm run lint -- --warnings # Show warnings in full +pnpm run lint -- --strict # Include knip dead-code checks (pre-commit gate) +pnpm run lint -- --fix # Auto-fix (oxlint only), then report remaining +pnpm run lint -- src/foo.ts # Target specific files (default: whole project) +pnpm -s run lint -- --format json # Machine-readable report for agents/CI ``` -Do not run oxlint, publint, knip, or tsgo directly. Always use `pnpm run lint`. +Do not run oxlint, knip, or tsgo directly. Always use `pnpm run lint`. +(`--format json` needs `pnpm -s` to keep pnpm's banner out of stdout.) -See [lifecycle/SKILL.md](../lifecycle/SKILL.md) for where lint fits in the development workflow (step 5: after tests pass, before format). +See [lifecycle/SKILL.md](../lifecycle/SKILL.md) for where lint fits in the development workflow (step 3: after tests pass, before format). Run `--strict` at PR handoff, not during the TDD loop. ## How It Works -`pnpm run lint` runs `bsh lint`, which executes four tools via `Promise.allSettled`: +`pnpm run lint` runs `bsh lint`, which executes three tools via `Promise.allSettled`: | Tool | What It Checks | Fix Support | |------|---------------|-------------| | **oxlint** | JS/TS linting via Rust-based engine with Bombshell config | Yes (`--fix`) | -| **publint** | `package.json` exports, types, and field correctness (strict mode) | No | -| **knip** | Unused dependencies, devDependencies, exports, types, files | No | -| **tsgo** | TypeScript type errors (`--noEmit`, native Go compiler) | No | +| **knip** | Unused dependencies/devDependencies (always); unused exports, types, files (`--strict` only) | No | +| **tsgo** | TypeScript type errors (`--noEmit`, project mode) | No | -Results merge into a single report grouped by file. Exit code 1 if any errors exist. +Results merge into a single report grouped by file. **Errors print in full; +warnings collapse to a per-rule count** (they never affect the exit code — +pass `--warnings` to expand them). Exit code 1 if any errors exist. + +publint is deliberately not part of lint: its file-existence checks need +`dist/`, so it runs as a publish gate in `pnpm run build` (see +[build/SKILL.md](../build/SKILL.md)) and standalone via `pnpm run publint`. + +## Remedies + +The sanctioned fix for the most common violations. Do not improvise +alternatives (e.g. sprinkling disable comments). + +| Violation | Fix | +|-----------|-----| +| `tsc/TS2688` — Cannot find type definition file for 'node' | `pnpm add -D @types/node` | +| `tsc/TS2591` — Cannot find name 'process'/'Buffer' | `pnpm add -D @types/node` | +| `oxlint/eslint(no-restricted-imports)` | Use the `URL` API; `fileURLToPath()` only at third-party boundaries | +| `oxlint/eslint(max-params)` / `bombshell-dev(max-params)` | Options bag with a typed interface. If conforming to a platform interface, say so with `override`/`implements` — the rule exempts those | +| `oxlint/import(no-default-export)` | Use a named export | +| `oxlint/bombshell-dev(no-generic-error)` | Define a project error class with a `code`; throw that | +| `oxlint/bombshell-dev(exported-function-async)` | Only fires on public entry files: make it `async`, or move the export out of the public surface | +| `oxlint/bombshell-dev(require-export-jsdoc)` | Only fires on public entry files: add a one-line `/** ... */` | +| `knip/unused-dependency` | Remove it from `package.json` | +| `knip/unused-export` (`--strict`) | Un-export it, or delete it if nothing consumes it | + +Inline `// oxlint-disable` comments are a last resort for genuinely +exceptional cases, never a workflow. If a rule is wrong for your project +shape, raise it — the fix belongs upstream in `oxlintrc.json`. ## Core Patterns @@ -68,7 +100,12 @@ await thirdPartyLib(configPath); ### Options bag for >2 parameters -Functions must have at most 2 parameters. Use an options object for anything beyond that. +Functions we author must have at most 2 parameters. Use an options object for anything beyond that. + +Signatures conforming to an API you don't control are exempt — `override` +methods, members of classes that `extends` or `implements`, and inline +callbacks. If a platform interface forces more parameters on you, express the +conformance (`override`, `implements`) and the rule stays silent. ```ts // Correct @@ -144,7 +181,11 @@ Use `const` by default. Use `let` only when reassignment is necessary. `var` is ### Exported functions: `async`, explicit return types, JSDoc -All three are enforced (as warnings) on exported functions: +All three are enforced (as warnings) on exported functions — but `async` and +JSDoc only apply to the **public API surface**: files consumers can import, +derived from `package.json` `exports`/`bin` (mapping `dist/` paths back to +`src/`). Internal modules are exempt. Packages with no publish surface (apps, +examples) are exempt entirely. ```ts // Correct @@ -274,7 +315,7 @@ Builtin error constructors (`Error`, `TypeError`, `RangeError`, `ReferenceError` ## Tensions -- **Prototyping speed vs lint strictness**: During early prototyping (see [lifecycle](../lifecycle/SKILL.md)), you may want to move fast. The lint rules still apply -- write correct code from the start rather than fixing lint after the fact. +- **Prototyping speed vs lint strictness**: During early prototyping (see [lifecycle](../lifecycle/SKILL.md)), you may want to move fast. The lint rules still apply -- write correct code from the start rather than fixing lint after the fact. Dead-code checks (knip unused exports/types/files) are deferred to `--strict` at PR handoff precisely so they don't fire mid-implementation. - **Opinionated defaults vs explicit config**: The lint config is intentionally strict and not configurable per-project. If a rule is wrong, change it in `oxlintrc.json` upstream. ## Reference diff --git a/skills/lint/references/lint-rules.md b/skills/lint/references/lint-rules.md index 6d8a1d9..8b5465a 100644 --- a/skills/lint/references/lint-rules.md +++ b/skills/lint/references/lint-rules.md @@ -14,10 +14,11 @@ Complete rule table for `oxlintrc.json` and custom Bombshell plugin rules. | `no-console` | warn | oxlint | Bans `console.log`; allows `console.info`, `warn`, `error`, `debug` | | `prefer-const` | error | oxlint | Use `const` when variable is never reassigned | | `no-var` | error | oxlint | No `var` declarations | -| `max-params` | error | oxlint | Max 2 parameters per function | | `typescript/explicit-function-return-type` | warn | oxlint/typescript | Function declarations need return types (expressions exempt) | | `node/no-path-concat` | error | oxlint/node | No string concatenation with `__dirname`/`__filename` | +Deliberately disabled category defaults: `unicorn/consistent-function-scoping`, `no-underscore-dangle`. + ## Bombshell Custom Plugin Rules Defined in `rules/plugin.js`, registered as the `bombshell-dev` plugin. @@ -25,8 +26,20 @@ Defined in `rules/plugin.js`, registered as the `bombshell-dev` plugin. | Rule | Severity | Description | |------|----------|-------------| | `bombshell-dev/no-generic-error` | error | No `throw new Error(...)` or other builtin error constructors. Use project-specific error classes. | -| `bombshell-dev/require-export-jsdoc` | warn | Exported functions and classes must have a `/** ... */` JSDoc comment. | -| `bombshell-dev/exported-function-async` | warn | Exported functions must use `async`. Adding async later is a breaking change. | +| `bombshell-dev/max-params` | error | Max 2 parameters in authored signatures — use an options bag. Exempt: `override` methods, members of classes that `extends`/`implements`, inline callbacks. | +| `bombshell-dev/require-export-jsdoc` | warn | Exported functions and classes must have a `/** ... */` JSDoc comment. **Public API surface only** (see below). | +| `bombshell-dev/exported-function-async` | warn | Exported functions must use `async`. Adding async later is a breaking change. **Public API surface only** (see below). | + +## Public API Surface Scoping + +`require-export-jsdoc` and `exported-function-async` encode conventions that +exist to prevent breaking changes for consumers — so they only apply to files +consumers can import. `bsh lint` derives the public surface from +`package.json` (`exports`, `bin`, `main`/`module`, mapping `dist/` paths back +to `src/`), including conventional `packages/*/` workspace members, and scopes +these rules to those files via oxlint `overrides`. Internal modules, packages +without a publish surface (apps, examples), and wildcard passthrough exports +(`"./*": "./dist/*"`) are exempt. ## oxlint Categories @@ -41,12 +54,13 @@ These apply in addition to individual rules: `unicorn`, `typescript`, `oxc`, `import`, `node`, plus the custom `bombshell-dev` JS plugin. -## Other Tools in the Pipeline +## Other Tools -| Tool | What It Catches | -|------|----------------| -| **publint** | Incorrect `package.json` exports, missing types, invalid fields | -| **knip** | Unused dependencies, devDependencies, exports, types, files | -| **tsgo** | TypeScript type errors (`--noEmit`) | +| Tool | Where it runs | What It Catches | +|------|---------------|-----------------| +| **knip** | `bsh lint` | Unused dependencies/devDependencies (always); unused exports/types/files (`--strict` only) | +| **tsgo** | `bsh lint` | TypeScript type errors (`--noEmit`, project mode) | +| **publint** | `bsh build` (publish gate) and `bsh publint` | Incorrect `package.json` exports, missing types, invalid fields (strict mode) | -These tools have no configurable rules in this project. They run with defaults (publint in strict mode, knip configured in `package.json`). +publint does not run in `bsh lint` — its file-existence checks need `dist/`, +so it gates the build instead. From 1a5d536a4990aca1d85a611f03dfc6e939908891 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Thu, 23 Jul 2026 00:09:00 -0400 Subject: [PATCH 2/2] docs(skills): bump stale library_version metadata to 0.6.0 All skills claimed 0.3.1 while the package is at 0.5.6 and the pending changesets ship in 0.6.0. --- skills/build/SKILL.md | 2 +- skills/dev/SKILL.md | 2 +- skills/format/SKILL.md | 2 +- skills/init/SKILL.md | 2 +- skills/lifecycle/SKILL.md | 2 +- skills/lint/SKILL.md | 2 +- skills/migrate/SKILL.md | 2 +- skills/test/SKILL.md | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/skills/build/SKILL.md b/skills/build/SKILL.md index 969df3d..872324a 100644 --- a/skills/build/SKILL.md +++ b/skills/build/SKILL.md @@ -8,7 +8,7 @@ description: > metadata: type: core library: '@bomb.sh/tools' - library_version: '0.3.1' + library_version: '0.6.0' requires: - lifecycle sources: diff --git a/skills/dev/SKILL.md b/skills/dev/SKILL.md index 084876f..f1910c0 100644 --- a/skills/dev/SKILL.md +++ b/skills/dev/SKILL.md @@ -7,7 +7,7 @@ description: > metadata: type: core library: '@bomb.sh/tools' - library_version: '0.3.1' + library_version: '0.6.0' requires: - lifecycle sources: diff --git a/skills/format/SKILL.md b/skills/format/SKILL.md index 0372ae0..ea9a967 100644 --- a/skills/format/SKILL.md +++ b/skills/format/SKILL.md @@ -7,7 +7,7 @@ description: > metadata: type: core library: '@bomb.sh/tools' - library_version: '0.3.1' + library_version: '0.6.0' requires: - lifecycle sources: diff --git a/skills/init/SKILL.md b/skills/init/SKILL.md index cf93cbe..7cf44db 100644 --- a/skills/init/SKILL.md +++ b/skills/init/SKILL.md @@ -7,7 +7,7 @@ description: > metadata: type: core library: '@bomb.sh/tools' - library_version: '0.3.1' + library_version: '0.6.0' sources: - 'bombshell-dev/tools:src/commands/init.ts' --- diff --git a/skills/lifecycle/SKILL.md b/skills/lifecycle/SKILL.md index c8372eb..c4eaaad 100644 --- a/skills/lifecycle/SKILL.md +++ b/skills/lifecycle/SKILL.md @@ -8,7 +8,7 @@ description: > metadata: type: lifecycle library: '@bomb.sh/tools' - library_version: '0.3.1' + library_version: '0.6.0' sources: - 'bombshell-dev/tools:skills/lifecycle/SKILL.md' - 'bombshell-dev/tools:src/bin.ts' diff --git a/skills/lint/SKILL.md b/skills/lint/SKILL.md index 6f997cb..ab73eb0 100644 --- a/skills/lint/SKILL.md +++ b/skills/lint/SKILL.md @@ -10,7 +10,7 @@ description: > metadata: type: core library: '@bomb.sh/tools' - library_version: '0.3.1' + library_version: '0.6.0' requires: - lifecycle sources: diff --git a/skills/migrate/SKILL.md b/skills/migrate/SKILL.md index 4318109..a1b9284 100644 --- a/skills/migrate/SKILL.md +++ b/skills/migrate/SKILL.md @@ -10,7 +10,7 @@ description: > metadata: type: lifecycle library: "@bomb.sh/tools" - library_version: "0.3.1" + library_version: "0.6.0" requires: - lifecycle sources: diff --git a/skills/test/SKILL.md b/skills/test/SKILL.md index 47a9d14..244bee4 100644 --- a/skills/test/SKILL.md +++ b/skills/test/SKILL.md @@ -9,7 +9,7 @@ description: > metadata: type: core library: '@bomb.sh/tools' - library_version: '0.3.1' + library_version: '0.6.0' requires: - lifecycle sources: