diff --git a/.changeset/stale-texts.md b/.changeset/stale-texts.md new file mode 100644 index 0000000..dca759a --- /dev/null +++ b/.changeset/stale-texts.md @@ -0,0 +1,14 @@ +--- +"@rrlab/cli": patch +"@rrlab/astro-plugin": patch +"@rrlab/biome-plugin": patch +"@rrlab/oxc-plugin": patch +"@rrlab/ts-plugin": patch +"@rrlab/tsdown-plugin": patch +"@rrlab/vitest-plugin": patch +"@variablelab/vland": patch +"@variablelab/clibuddy": patch +"@variablelab/loggy": patch +--- + +The npm description, and the README where it changed, say what the code does. diff --git a/CLAUDE.md b/CLAUDE.md index 38a9304..44b4b5b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,39 +4,21 @@ You are an agent maintaining this monorepo. The microkernel refactor that produc ## Layout -``` -dx/ -├── shared/ # @variablelab/* — shared utilities (clibuddy, loggy) -├── run-run/ # @rrlab/* — the rrlab product -│ ├── cli/ # @rrlab/cli — the kernel -│ ├── *-plugin/ # @rrlab/-plugin — the official plugins, one per wrapped tool -│ └── *-config/ # @rrlab/-config — shared presets -└── vland/ # @variablelab/vland CLI + the scaffolds it emits - ├── cli/ # @variablelab/vland — the `vland init` CLI - └── templates/ # scaffolds emitted by `vland init` -``` +The folders and what each holds: [docs/development.md](docs/development.md#layout). This repo hosts **two npm orgs** intentionally: `@variablelab/*` (Variableland's utilities + the `vland` CLI) and `@rrlab/*` (the `rr` CLI product). Cross-pollination is fine — clibuddy and loggy are shared utilities both stacks depend on. -Stack: pnpm workspaces, Turborepo, Changesets, `tsdown` for builds, vitest for tests, Biome for lint/format, TypeScript strict, Node 24 for development (pinned in `mise.toml`). Published packages declare `engines.node` as `>=22.18.0`, the first Node 22 that strips types by default, which `rr` needs to load `run-run.config.{ts,mts}`. CI runs the full check on both that floor (`mise.floor.toml`; run it locally with `MISE_ENV=floor`) and the `mise.toml` version. Bump the floor in `mise.floor.toml` and every `engines.node` together. +Stack: pnpm workspaces, Turborepo, Changesets, `tsdown` for builds, vitest for tests, Biome for lint/format, TypeScript strict. ## Build & verify -```bash -pnpm --filter @rrlab/cli build # build a single package -pnpm --filter @rrlab/cli test # test a single package -pnpm test # all (turbo handles affected detection) -pnpm rr check # dogfooded lint + format + tsc -pnpm build && pnpm test && pnpm rr check # full check -``` +Setup, the Node floor, the everyday commands and CI are in [docs/development.md](docs/development.md). `pnpm rr check` must be green before any commit. If `pnpm rr lint --fix` or `pnpm rr format --fix` applies safe fixes, run them and re-verify. ## Working principles (non-negotiable) -1. **Strict test discipline.** Every feature + bug fix lands with tests: - - Features: a happy-path test and at least one failure case (in `test/integration/` or `src/**/__tests__/`). - - Fixes: a regression test that would have caught the bug. +1. **Strict test discipline.** Every feature and fix lands with the tests [docs/contributing.md](docs/contributing.md#pull-requests) asks for, in the folders [docs/development.md](docs/development.md#tasks) names. - Refactors with no behaviour change: existing tests must still pass; remove tests only when they were asserting implementation details that no longer exist. - If you can't write a meaningful test, the design is probably wrong — stop and report. @@ -46,7 +28,7 @@ pnpm build && pnpm test && pnpm rr check # full check - Pattern drift (e.g., one file uses `for...of`, the neighbour uses `.forEach()`) → align with the closer neighbour's pattern. - Don't go on cleanup spelunking expeditions for files unrelated to your task. Stay in the blast radius of what you're already touching. -3. **Atomic commits**, imperative present-tense English: `add PluginRegistry`, `fix tsc cwd handling on monorepos`, `refactor edit-json idempotency`. Use Changesets (`pnpm changeset`) for any change to a publishable package. +3. **Atomic commits** as [Conventional Commits](docs/contributing.md#pull-requests), such as `feat(cli): add PluginRegistry` or `fix(ts-plugin): handle tsc cwd on monorepos`. A change to what a package ships to npm needs a changeset (`pnpm changeset`). See [docs/contributing.md](docs/contributing.md#changesets) for the bump. 4. **Never publish to npm.** No `pnpm publish`, no `changeset publish`, no `npm publish`. The human handles publication. @@ -54,13 +36,12 @@ pnpm build && pnpm test && pnpm rr check # full check ## Coding conventions -- **Imports**: `#src/...` aliases inside a package; `@rrlab/...` / `@variablelab/...` across packages. +- **Imports**: `#src/...` inside `@rrlab/cli` and `@variablelab/vland`, which map it in `imports`. Relative paths in the other packages. Package names (`@rrlab/...`, `@variablelab/...`) across packages. - **Exports**: named exports preferred; default exports only when the consumer needs them (e.g. plugin factories). - **Services**: classes ending in `Service`, dependencies via constructor. Only `logger` is a singleton. -- **Commands**: factory functions named `createCommand(ctx)` returning a configured commander `Command`. A command is a thin wrapper: it resolves its provider (throwing `MissingPluginError` with its `providers` list when absent) and delegates to exactly one action. See `decisions/015-command-action-service-layering.md`. -- **Actions**: free functions in `src/actions/` taking a **single options object** named `ActionConfig` — never positional params. Dependencies (`ctx`, the resolved provider) sit at the top level; parsed flags/positional args nest under `options` / `args` (reuse the verb-option types from `src/types/tool.ts`). Actions import only `services`/`render`/`lib` — never the CLI framework. +- **Commands and actions** (in `@rrlab/cli`): see "Commands are thin wrappers over actions" in [run-run/cli/CLAUDE.md](run-run/cli/CLAUDE.md#commands-are-thin-wrappers-over-actions) and [decisions/015](decisions/015-command-action-service-layering.md). - **Errors**: domain errors are classes — kernel-internal ones in `src/errors/` (e.g. `MissingPluginError`), SDK ones in `src/lib/plugin/errors.ts` (`MultipleProvidersError`). Each builds its user-facing message from structured constructor args. Do NOT set `this.name` — keep the plain `Error:` prefix, so output stays clean. -- **Tests**: vitest. Unit tests in `src/**/__tests__/*.test.ts`. Integration tests in `test/integration/*.test.ts`. Inline snapshots for CLI output assertions. +- **Tests**: vitest, in the folders [docs/development.md](docs/development.md#tasks) names. Inline snapshots for CLI output assertions. - **No `any`.** If TypeScript pushes you there, stop and report — it usually means a design problem. - **Match neighbours.** Before introducing a new pattern, scan 2-3 nearby files. If they use a different style, follow theirs. @@ -72,6 +53,7 @@ pnpm build && pnpm test && pnpm rr check # full check ## Going deeper +- **[`docs/contributing.md`](docs/contributing.md)** — branches, commits, tests, changesets and releases. - **`run-run/CLAUDE.md`** — orientation for working on the `@rrlab/*` ecosystem (plugin contract, conventions for adding/modifying plugins). - **`run-run/cli/CLAUDE.md`** — kernel internals (commander patterns, context lifecycle, edit-json DSL, KDL spec emission). - **`decisions/`** — one file per architectural decision. Read before designing anything that touches plugin lifecycle, dependency model, or templating. Format / template in `decisions/README.md`. diff --git a/README.md b/README.md index e06b527..269d809 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ The monorepo of run-run and vland. - [`run-run/`](run-run): run-run. The CLI is in `cli/`, a plugin per tool in `*-plugin/` and the presets in `*-config/`. The [run-run README](run-run/cli/README.md#plugins) lists the plugins. - [`vland/`](vland): vland. The CLI is in `cli/`, and the templates it starts a project from are in `templates/`. -- [`shared/`](shared): `@variablelab/clibuddy` and `@variablelab/loggy`, internal to the two CLIs. +- [`shared/`](shared): the internal packages the CLIs depend on. ## Contributing diff --git a/docs/contributing.md b/docs/contributing.md index 0f354a4..cf377fe 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -9,7 +9,7 @@ dx takes bug reports, fixes and small features for run-run, vland and their pack ## Pull requests -1. Branch from `main` with a prefix: `feat/`, `fix/`, `docs/`, `chore/` or `test/`. +1. Branch from `main` with a prefix: `feat/`, `fix/`, `docs/`, `ci/`, `chore/` or `test/`. 2. Write commits as [Conventional Commits](https://www.conventionalcommits.org), such as `fix(oxc-plugin): stop installing a flaky oxlint-tsgolint`. The pull request title takes the same form, because it becomes the squash commit. 3. A feature lands with a test of the happy path and at least one failure case, and a fix with a regression test. 4. Run `pnpm rr check` and `pnpm test` before you push. @@ -29,4 +29,4 @@ Nobody edits versions or `CHANGELOG.md` by hand, and nobody publishes from a clo When changesets land on `main`, the Release job opens or updates a "chore: update versions" pull request as `vland-bot`. A maintainer merges it, and the next run on `main` publishes each new version to npm, with npm trusted publishing, and tags it. -A new tag of `@rrlab/cli` or `@variablelab/vland` regenerates the `CLI.md` of both CLIs in a "chore: regenerate CLI docs" pull request, which a maintainer merges too. +A new tag of a CLI regenerates the `CLI.md` of every CLI in the cli docs workflow, in a "chore: regenerate CLI docs" pull request, which a maintainer merges too. diff --git a/docs/development.md b/docs/development.md index b42afb4..d36c852 100644 --- a/docs/development.md +++ b/docs/development.md @@ -6,7 +6,7 @@ How to work on dx in a clone. The [contributing guide](contributing.md) covers h - [`run-run/`](../run-run): run-run. `cli/` is `@rrlab/cli`, the kernel and the `rr` command. Each `*-plugin/` wraps one tool, and each `*-config/` is a preset that a plugin installs. [`run-run/CLAUDE.md`](../run-run/CLAUDE.md) covers the architecture: the plugin contract and how to add or change a plugin. - [`vland/`](../vland): vland. `cli/` is `@variablelab/vland`, and `templates/` holds the projects `vland init` starts from. -- [`shared/`](../shared): `@variablelab/clibuddy` and `@variablelab/loggy`, the internal packages both CLIs depend on. +- [`shared/`](../shared): `@variablelab/clibuddy` and `@variablelab/loggy`, the internal packages the CLIs depend on. - [`decisions/`](../decisions): the architecture decisions, one file each. - `docs/`: this guide and the [contributing guide](contributing.md). diff --git a/run-run/CLAUDE.md b/run-run/CLAUDE.md index d49c64f..fd313e6 100644 --- a/run-run/CLAUDE.md +++ b/run-run/CLAUDE.md @@ -1,6 +1,6 @@ # CLAUDE.md — `run-run/` (the `@rrlab/*` ecosystem) -Orientation for working on the `@rrlab/*` packages. Prerequisite reading: the repo-root `CLAUDE.md` (working principles, build commands, coding conventions). +Orientation for working on the `@rrlab/*` packages. Prerequisite reading: the repo-root `CLAUDE.md` (working principles, coding conventions). ## Why `rr` exists — and how it's meant to be invoked @@ -10,7 +10,7 @@ This means: - **Do not wrap `rr` in `package.json` scripts.** Aliases like `"lint": "rr lint"`, `"format": "rr format --fix"`, `"tscheck": "rr tsc"`, `"check": "rr check"` re-introduce the very indirection rr was created to remove (`pnpm lint` → `rr lint` → `biome check` is three layers for what should be one). The user types `rr lint`, not `pnpm lint`. - **The expected setup**: `node_modules/.bin` on `PATH` — typically via mise (`[env] _.path = ["{{config_root}}/node_modules/.bin"]`) — so `rr` is directly invokable from the project root without a package-manager hop. Templates ship a `mise.toml` that wires this for new projects. -- **`package.json scripts` may still exist** — but only for things `rr` does NOT cover (`vitest run`, `tsx watch`, `node dist/...`, `vite dev`, `turbo run dev`, `changeset publish`). These are not single-command aliases; they're either different tools or multi-step compositions. +- **`package.json scripts` may still exist** — but only for things `rr` does NOT cover (`tsx watch`, `node dist/...`, `vite dev`, `turbo run dev`, `changeset publish`). These are not single-command aliases; they're either different tools or multi-step compositions. - **Lifecycle / orchestration hooks MAY call `rr` directly.** lefthook git hooks, `prepublishOnly`, GitHub Actions workflows — those are triggered by other systems, not typed by the developer, so they don't fight the "one entry point" goal. A `pre-commit: rr jsc --fix-staged` lefthook job is exactly right. - **READMEs teach `rr `, not `pnpm `.** Mention the mise PATH note once; from then on every developer-facing instruction is `rr `. @@ -27,7 +27,7 @@ If you find yourself tempted to add a `pnpm