@duyquangnvx/harness

中文文档
0.6.3发布于 2026/11/08MITES Module零依赖近 30 天下载 3921

Shared repo config: Biome, tsconfig, lefthook and Ruff presets and a lint-on-edit hook for Claude Code

原始使用文档
文档目录4 节
  1. The harness command
  2. Set up a repo
  3. Git hooks under npm 12 and pnpm 11
  4. Release this package

@duyquangnvx/harness

Shared repo configuration: Biome, tsconfig, lefthook and Ruff presets and a lint-on-edit hook for Claude Code. A repo extends the presets and keeps its own overrides in its own files; the package's harness command sets it up, checks it and upgrades it. A repo that publishes a package releases it with @duyquangnvx/release.

FileWhat it isA repo uses it as
biome.preset.jsonFormatter and the mechanical rules: any, !, enum, console, nested ternaries, complexity, file length, exhaustive switches"extends": ["@duyquangnvx/harness/biome"]
tsconfig.strict.jsonstrict plus the checks it leaves out"extends": ["@duyquangnvx/harness/tsconfig.strict.json"]
lefthook.ymlPre-commit: Biome on staged files, then tsc --noEmit, both from node_modules/.bin under any package managerextends: [node_modules/@duyquangnvx/harness/lefthook.yml]
ruff.tomlPython line length and rule selectionextend = "node_modules/@duyquangnvx/harness/ruff.toml"
lint-on-edit.mjsClaude Code PostToolUse hook: formats and lints each edited file, exit 2 hands what is left back to Claude. Runs Biome only when it is installed in the repo, Ruff only when a Ruff config existsthe command below

The Biome preset is flat, because a file Biome extends cannot extend another. Biome takes the specifier without .json.

The harness command

CommandWhat it does
initSets the repo up: below
doctor [--json]Says, changing nothing, what the git repo it runs in and this machine lack or have behind, each with the command or line that fixes it. The repo: the package against the latest on npmjs, Biome at the version the package pins, each config extending its preset, the lint hook, lefthook's git hook, an AGENTS.md and a CLAUDE.md importing it, and what the old copy-everything harness left. The machine: the rules link at ~/.claude/rules/harness, the duyquangnvx plugin, and Matt's skills at the tag this package names
context [--json]Says, changing nothing, what Claude Code loads at launch in the folder it runs in, with a rough token count each: instruction files and their imports, rules (those with paths: as on demand), and skill and subagent descriptions from ~/.claude, .claude and enabled plugins. It names the lines two loaded files both hold and any file loaded from above the repo root (ADR 0010)
upgradeAdds @duyquangnvx/harness@latest with the repo's package manager, sets Biome to the version it pins and prints what changed. A Python-only repo runs npx @duyquangnvx/harness@latest upgrade, which copies the hook and Ruff config again; it refuses when the package running is behind the latest on npmjs

Run it as npx @duyquangnvx/harness <command> before the package is installed, and through the package manager after (pnpm exec harness doctor). init and upgrade refuse to write over uncommitted changes. harness <command> --help has the details.

ExitMeaning
0nothing to fix
1a usage error
2something to fix in the repo or on the machine, which the output names
70a bug

Set up a repo

npx @duyquangnvx/harness init

In a repo with package.json and a lockfile, init adds the package and the tools its presets run to devDependencies with the package manager the lockfile names, makes lefthook install the prepare script when there is none, denies lefthook's install script as below, writes the configs below when they are missing, and adds the hook to .claude/settings.json, pointing a hook that runs the old copy .claude/hooks/lint-on-edit.mjs at the package instead. A repo with no TypeScript (no tsconfig.json, no typescript package and no .ts file git sees) gets neither typescript nor tsconfig.json, and doctor does not ask for a tsconfig.json. It never rewrites a file the repo already has: it prints the line that file needs and exits 2.

In every repo, Python-only too, init writes an AGENTS.md holding ## Project status (the greenfield default) and placeholder ## Commands, ## Coding standards and ## Agent skills sections, each only a comment saying what belongs there, and a CLAUDE.md holding only @AGENTS.md, so Claude Code and other agents read one file. A CLAUDE.md with content of its own gets how to move that into AGENTS.md. doctor checks that AGENTS.md exists, never what is in it: it holds only what an agent cannot find on its own, so commands that package.json or the README already give stay out.

By hand, the same setup is:

pnpm add -D @duyquangnvx/harness lefthook typescript
pnpm add -D --save-exact @biomejs/[email protected]

biome.json, adding the repo's own exceptions next to extends:

{
  "$schema": "https://biomejs.dev/schemas/2.6.2/schema.json",
  "extends": ["@duyquangnvx/harness/biome"]
}

tsconfig.json, in a repo with TypeScript: add "extends": ["@duyquangnvx/harness/tsconfig.strict.json"].

lefthook.yml. Jobs merge by name, and where both files set a key the package's value wins, so a repo adds its own jobs, or keys such as root, beside the package's:

extends:
  - node_modules/@duyquangnvx/harness/lefthook.yml

The jobs run the tools from the repo's node_modules/.bin, so the preset works under npm and pnpm alike and never fetches a tool that is missing. pnpm exec fails in an npm repo, and npx adds about half a second a job and resolves a missing name on the registry.

.claude/settings.json; before the first install the package is missing, and the hook then exits 0:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "f=\"$CLAUDE_PROJECT_DIR/node_modules/@duyquangnvx/harness/lint-on-edit.mjs\"; [ -f \"$f\" ] || exit 0; node \"$f\""
          }
        ]
      }
    ]
  }
}

A repo with Python next to package.json: ruff.toml holding the extend line above. A Python-only repo, one without package.json, has no node_modules: there init copies the hook to .claude/hooks/lint-on-edit.mjs and this config to .claude/harness.ruff.toml, adds node "$CLAUDE_PROJECT_DIR/.claude/hooks/lint-on-edit.mjs" as the hook in .claude/settings.json, and writes a ruff.toml holding extend = ".claude/harness.ruff.toml" when the repo has no Ruff config; a ruff.toml, .ruff.toml or pyproject.toml [tool.ruff] of its own gets the line printed instead.

Git hooks under npm 12 and pnpm 11

Both block lefthook's own postinstall, which is what used to install the git hooks. Measured with npm 12.2.0, pnpm 11.8.0 and lefthook 2.2.2:

  • npm 12 installs the package but leaves .git/hooks empty, warning that the script is "not covered by allowScripts".
  • pnpm 11 fails the install (ERR_PNPM_IGNORED_BUILDS, exit 1) until the build is allowed or denied.
  • The lefthook binary comes from a platform optional dependency and runs without the postinstall.

So the repo installs the hooks itself and denies lefthook's script, which works under both:

  • package.json: "scripts": { "prepare": "lefthook install" }, and under npm "allowScripts": { "lefthook": false }.
  • pnpm: pnpm-workspace.yaml holding allowBuilds: with lefthook: false.

lefthook install, and lefthook run, which syncs the hooks first, write to the repo's shared .git/hooks: an install in any worktree installs the hooks for all of them.

A pnpm repo also lists '@duyquangnvx/harness' under minimumReleaseAgeExclude in pnpm-workspace.yaml, so upgrade installs a release the day it ships instead of a day later.

Release this package

It releases from this folder with @duyquangnvx/release. The tests run against the built dist/: pnpm test builds first.

版本与发布记录

@duyquangnvx/harness 共发布 18 个版本,首次发布于 2026/10/16;过去 12 个月发布了 18 个版本。

版本发布日期距今
0.6.3latest2026/11/084 天前
0.6.22026/11/066 天前
0.6.12026/11/0310 天前
0.6.02026/11/0211 天前
0.5.22026/10/2420 天前
0.5.12026/10/2420 天前

关于 @duyquangnvx/harness 的常见问题

@duyquangnvx/harness 是什么?

Shared repo config: Biome, tsconfig, lefthook and Ruff presets and a lint-on-edit hook for Claude Code

如何安装 @duyquangnvx/harness?

在项目目录运行 npm install @duyquangnvx/harness,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 harness,可以用 npx harness 直接运行。

@duyquangnvx/harness 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/duyquangnvx__harness 是否存在。

@duyquangnvx/harness 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@duyquangnvx/harness 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.18.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@duyquangnvx/harness 可以商用吗?

@duyquangnvx/harness 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@duyquangnvx/harness 的下载量有多少?

根据 npm 官方下载统计,@duyquangnvx/harness 在 2026/10/13 – 2026/11/11 这 30 天内被下载 3,921 次,日均约 131 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@duyquangnvx/harness 还在维护吗?

最新版本 0.6.3 发布于 2026/11/08(4 天前);至今共发布 18 个版本,其中过去 12 个月发布了 18 个;首次发布于 2026/10/16。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。