From b296f5f805a6bab54c0c8cd4a6e3f7a25a7f159e Mon Sep 17 00:00:00 2001 From: "Ricardo Q. Bazan" Date: Sun, 18 Oct 2026 12:44:43 -0500 Subject: [PATCH 1/3] docs: point the CLAUDE.md files at the development guide and fix their stale claims Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01TzjvWWSt2KQNsVgAtswGTL --- CLAUDE.md | 36 +++++++++--------------------------- run-run/CLAUDE.md | 19 +++++++------------ run-run/cli/CLAUDE.md | 25 +++++++++++++------------ 3 files changed, 29 insertions(+), 51 deletions(-) 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/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