Skip to content

Commit b36a673

Browse files
cvclaude
andauthored
chore: unify vitest workspace and replace husky with prek (#673)
* chore(test): convert tests to ESM and unify vitest 4 workspace Convert all 21 root test files from CJS to ESM: - require() → import (static for top-level, createRequire bridge for registry.test.js where HOME must be set before module load) - __dirname → import.meta.dirname - Add test/package.json with "type": "module" - Add explicit `import { describe, it, expect } from "vitest"` - Normalize all built-in imports to use node: prefix Unify both test suites under a single vitest 4 config: - Upgrade root vitest from ^3.1.1 to ^4.1.0, add @vitest/coverage-v8 - Remove vitest and coverage-v8 from nemoclaw/package.json - Replace vitest.config.ts + nemoclaw/vitest.config.ts with a single root vitest.config.ts using test.projects (vitest 4 API) - One `npx vitest run --coverage` runs both cli and plugin tests - Coverage scoped to nemoclaw/src/**/*.ts (plugin code only) Update CI and hooks: - pr.yaml: merge two vitest steps into one - pre-commit: run plugin tests from root (--project plugin) - Coverage ratchet: read from coverage/ instead of nemoclaw/coverage/ Cleanup from code review: - Remove dead afterEach in runtime-shell.test.js - Remove unnecessary createRequire in onboard-readiness.test.js and runner.test.js (replaced with static ESM imports) Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: add shellcheck shell directive to husky-env.sh husky-env.sh is sourced (not executed directly), so it doesn't have a shebang. Add a shellcheck shell=sh directive so shellcheck knows the target shell. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: keep vitest in nemoclaw/ devDeps for type resolution The eslint strict-type-checked rules in nemoclaw/ need vitest's type definitions to resolve describe/it/expect in test files. Without vitest in nemoclaw/node_modules, tsc and eslint report 473 no-unsafe-call errors. Test execution still happens from the root workspace — this dep is only needed for type checking and linting. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: bump Node engine to >=22 and fix stale coverage message - Bump engines.node from >=20.0.0 to >=22.0.0 in both package.json files. import.meta.dirname (used in all ESM test files) requires Node 20.11+, and CI already runs Node 22. - Fix stale error message in coverage ratchet script that referenced the old nemoclaw/ coverage path. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: replace husky with prek for all git hooks Husky managed git hooks via `.husky/` shell scripts that invoked lint-staged, vitest, commitlint, tsc, and pyright. prek can do all of this from `.pre-commit-config.yaml`, giving us a single source of truth. - Add `@j178/prek` as a devDependency (npm package) - Move lint-staged, vitest, commitlint, tsc, pyright into `.pre-commit-config.yaml` as local hooks with proper stages - Remove lint-staged + shellcheck devDeps (redundant with prek-native hooks that already run the same tools at priorities 5-10) - Delete `.husky/`, `scripts/husky-env.sh`, and husky devDep - `prepare` script now runs `prek install` (only inside a git repo) Existing contributors with husky's `core.hooksPath=.husky/_` in their local git config will need to run: git config --unset-all --local core.hooksPath Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(test): convert credential-exposure test to ESM for vitest The test landed on main as CJS (require/__dirname/bare describe) which breaks under our vitest 4 ESM config. Convert to ESM imports with explicit vitest globals. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix: mark runner.py executable to match its shebang Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * fix(hooks): run vitest-plugin hook from repo root The pre-commit hook was cd-ing into nemoclaw/ before running vitest, causing the root vitest.config.ts project paths to resolve incorrectly (nemoclaw/nemoclaw/src/...) and find zero test files. prek already runs hooks from the repo root, so the cd is unnecessary. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> * chore: add pre-commit banned-files hook and expand .gitignore Add a pre-commit hook that rejects force-added files matching .gitignore patterns (catches `git add -f` bypasses). The hook uses `git ls-files --ignored --cached` so .gitignore stays the single source of truth — no duplicate pattern list. Also expand .gitignore with additional sensitive file patterns (key.json, token.json, secrets.json/yaml, .envrc, .direnv/, .pypirc, *.tfvars) and reorganize into labeled sections. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
1 parent ffa1283 commit b36a673

38 files changed

+1868
-1894
lines changed

.github/workflows/pr.yaml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,7 @@ jobs:
6868
npm install
6969
npm run build
7070
71-
- name: Run root unit tests
72-
run: npx vitest run
73-
74-
- name: Run TypeScript unit tests with coverage
75-
working-directory: nemoclaw
71+
- name: Run all unit tests with coverage
7672
run: npx vitest run --coverage
7773

7874
- name: Check coverage ratchet

.gitignore

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,44 @@
1-
node_modules/
2-
dist/
1+
# Build artifacts and caches
2+
*.pyc
33
.pytest_cache/
44
__pycache__/
5-
*.pyc
6-
.DS_Store
7-
docs/_build/
85
coverage/
9-
vdr-notes/
6+
dist/
7+
docs/_build/
8+
node_modules/
9+
10+
# OS metadata
11+
.DS_Store
12+
desktop.ini
13+
Thumbs.db
14+
15+
# Project-specific
16+
.claude/
1017
draft_newsletter_*
18+
vdr-notes/
1119

12-
# Security: prevent accidental commit of secrets and credentials
13-
.env
14-
.env.*
15-
*.pem
20+
# Security: secrets, credentials, and keys
21+
*_ecdsa
22+
*_ed25519
23+
*_rsa
24+
*.jks
1625
*.key
26+
*.keystore
1727
*.p12
28+
*.pem
1829
*.pfx
19-
*.jks
20-
*.keystore
21-
.npmrc
30+
*.tfvars
31+
.direnv/
32+
.env
33+
.env.*
34+
.envrc
2235
.netrc
23-
*_rsa
24-
*_ed25519
25-
*_ecdsa
36+
.npmrc
37+
.pypirc
2638
credentials.json
27-
28-
# Security: prevent accidental commit of disclosure drafts
2939
DRAFT-*.md
30-
31-
# Claude Code worktrees and local state
32-
.claude/
40+
key.json
41+
secrets.json
42+
secrets.yaml
43+
service-account*.json
44+
token.json

.husky/commit-msg

Lines changed: 0 additions & 14 deletions
This file was deleted.

.husky/pre-commit

Lines changed: 0 additions & 22 deletions
This file was deleted.

.husky/pre-push

Lines changed: 0 additions & 55 deletions
This file was deleted.

.pre-commit-config.yaml

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
1-
# NemoClaw — prek / pre-commit hook configuration (same file; prek is recommended)
1+
# NemoClaw — prek hook configuration
22
# prek: https://github.com/j178/prek — single binary, no Python required for the runner
3+
# Installed as an npm devDependency (@j178/prek) — available after `npm install`.
4+
# All git hooks (pre-commit, commit-msg, pre-push) are managed by prek via this file.
5+
# The "prepare" script in package.json runs `prek install` to wire them up.
36
#
4-
# Local (prek):
5-
# brew install prek | uv tool install prek | pip install prek
6-
# curl --proto '=https' --tlsv1.2 -LsSf https://github.com/j178/prek/releases/latest/download/prek-installer.sh | sh
7-
# prek install
8-
# prek run --all-files
9-
#
10-
# Alternative — Python pre-commit (reads this file too): https://pre-commit.com
11-
# pip install pre-commit && pre-commit install && pre-commit run --all-files
7+
# Usage:
8+
# npx prek install
9+
# npx prek run --all-files
1210
#
1311
# CI / diff-only runs:
14-
# prek run --from-ref <base> --to-ref HEAD
15-
# pre-commit run --from-ref <base> --to-ref HEAD
16-
#
17-
# Husky (see .husky/pre-commit) runs lint-staged + Vitest on commit; this file adds repo-wide
18-
# checks (shell, Docker, ruff, eslint in nemoclaw/, SPDX, gitleaks, etc.).
12+
# npx prek run --from-ref <base> --to-ref HEAD
1913
#
20-
# Priority groups (prek: same priority may run in parallel; stock pre-commit ignores priority):
14+
# Priority groups (prek runs same-priority hooks in parallel):
2115
# 0 — General file fixers (whitespace, EOF, line endings)
2216
# 5 — Shell / Python / TS formatters (shfmt, ruff format, prettier)
2317
# 6 — Fixes that should follow formatters (ruff check --fix, eslint --fix)
2418
# 10 — Linters and read-only checks
19+
# 20 — Project-level checks (vitest)
2520

2621
exclude: ^(nemoclaw/dist/|nemoclaw/node_modules/|docs/_build/|\.venv/|uv\.lock$)
2722

@@ -38,6 +33,19 @@ repos:
3833
args: ["--fix=lf"]
3934
priority: 0
4035

36+
# ── Priority 0: reject force-added ignored files ───────────────────────────
37+
# Catches `git add -f` of files that .gitignore would normally block.
38+
# Single source of truth stays in .gitignore — no duplicate list here.
39+
- repo: local
40+
hooks:
41+
- id: no-force-added-ignored
42+
name: Reject force-added ignored files
43+
entry: bash -c 'IGNORED=$(git ls-files --ignored --exclude-standard --cached -- "$@") && if [ -n "$IGNORED" ]; then echo "Force-added files that .gitignore would block:" && echo "$IGNORED" && exit 1; fi' --
44+
language: system
45+
always_run: true
46+
pass_filenames: false
47+
priority: 0
48+
4149
# ── Priority 5: formatters ────────────────────────────────────────────────
4250
- repo: https://github.com/scop/pre-commit-shfmt
4351
rev: v3.12.0-2
@@ -128,7 +136,7 @@ repos:
128136
name: SPDX license headers
129137
entry: bash scripts/check-spdx-headers.sh
130138
language: system
131-
files: ^(nemoclaw/src/.*\.ts|nemoclaw-blueprint/.*\.py|.*\.sh|\.husky/.*)$
139+
files: ^(nemoclaw/src/.*\.ts|nemoclaw-blueprint/.*\.py|.*\.sh)$
132140
pass_filenames: true
133141
priority: 10
134142

@@ -139,8 +147,47 @@ repos:
139147
name: gitleaks (secret scan)
140148
priority: 10
141149

150+
# ── Priority 20: project-level checks (pre-commit) ─────────────────────────
151+
- repo: local
152+
hooks:
153+
- id: vitest-plugin
154+
name: Vitest (plugin project)
155+
entry: npx vitest run --project plugin
156+
language: system
157+
pass_filenames: false
158+
files: ^nemoclaw/
159+
priority: 20
160+
161+
# ── commit-msg hooks ────────────────────────────────────────────────────────
162+
- repo: local
163+
hooks:
164+
- id: commitlint
165+
name: commitlint
166+
entry: npx commitlint --edit
167+
language: system
168+
stages: [commit-msg]
169+
always_run: true
170+
171+
# ── pre-push hooks ─────────────────────────────────────────────────────────
172+
- repo: local
173+
hooks:
174+
- id: tsc-check
175+
name: TypeScript type check (tsc --noEmit)
176+
entry: bash -c 'cd nemoclaw && npx tsc --noEmit'
177+
language: system
178+
pass_filenames: false
179+
always_run: true
180+
stages: [pre-push]
181+
182+
- id: pyright-check
183+
name: Pyright (nemoclaw-blueprint)
184+
entry: bash -c 'cd nemoclaw-blueprint && uv run --with pyright pyright .'
185+
language: system
186+
pass_filenames: false
187+
always_run: true
188+
stages: [pre-push]
189+
142190
default_language_version:
143191
python: python3
144192

145193
fail_fast: false
146-
minimum_pre_commit_version: "3.0.0"

CONTRIBUTING.md

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -57,24 +57,21 @@ These are the primary `make` and `npm` targets for day-to-day development:
5757
| `cd nemoclaw && npm test` | Run plugin unit tests (Vitest) |
5858
| `make docs` | Build documentation (Sphinx/MyST) |
5959
| `make docs-live` | Serve docs locally with auto-rebuild |
60-
| `prek run --all-files` | Optional: run shared hooks from `.pre-commit-config.yaml` — see below |
60+
| `npx prek run --all-files` | Run all hooks from `.pre-commit-config.yaml` — see below |
6161

62-
### Optional: prek (pre-commit–compatible)
62+
### Git hooks (prek)
6363

64-
The repository includes [`.pre-commit-config.yaml`](.pre-commit-config.yaml) for [prek](https://prek.j178.dev/) (recommended) and the Python [`pre-commit`](https://pre-commit.com/) runner — same file, either tool. This **complements** [Husky](.husky/pre-commit), which runs lint-staged and Vitest when you commit.
64+
All git hooks are managed by [prek](https://prek.j178.dev/), a fast, single-binary pre-commit hook runner installed as a devDependency (`@j178/prek`). The `npm install` step runs `prek install` automatically via the `prepare` script, which wires up the following hooks from [`.pre-commit-config.yaml`](.pre-commit-config.yaml):
6565

66-
Install **prek** (pick one): `brew install prek`, `uv tool install prek`, `pip install prek`, or the [standalone installer](https://prek.j178.dev/installation/). From the repository root:
66+
| Hook | What runs |
67+
|------|-----------|
68+
| **pre-commit** | File fixers, formatters, linters, Vitest (plugin) |
69+
| **commit-msg** | commitlint (Conventional Commits) |
70+
| **pre-push** | TypeScript type check (`tsc --noEmit`), Pyright (Python) |
6771

68-
```bash
69-
prek install
70-
prek run --all-files # good check before opening a PR, or after broad edits
71-
```
72-
73-
`make check` remains the primary documented linter entry point. For scoped runs: `prek run --from-ref <base> --to-ref HEAD` (same flags work with `pre-commit` if you use that instead).
74-
75-
If **prek** is not available, install [`pre-commit`](https://pre-commit.com/) and use `pre-commit install` / `pre-commit run --all-files` with the same config file.
72+
For a full manual check: `npx prek run --all-files`. For scoped runs: `npx prek run --from-ref <base> --to-ref HEAD`.
7673

77-
If **prek** (or **pre-commit**) is on your `PATH`, [.husky/pre-push](.husky/pre-push) also runs it on the commits you are about to push (merge-base with `@{u}`, or the last commit if no upstream is set). Husky scripts prefer `node_modules/.bin` so type checks and lint-staged work when `npx` is missing from the environment (for example some GUI Git clients).
74+
`make check` remains the primary documented linter entry point.
7875

7976
## Project Structure
8077

nemoclaw-blueprint/orchestrator/runner.py

100644100755
File mode changed.

nemoclaw/package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
"lint:fix": "eslint 'src/**/*.ts' --fix",
1818
"format": "prettier --write 'src/**/*.ts'",
1919
"format:check": "prettier --check 'src/**/*.ts'",
20-
"test": "vitest run",
21-
"test:watch": "vitest",
2220
"check": "npm run lint && npm run format:check && tsc --noEmit",
2321
"clean": "rm -rf dist/"
2422
},
@@ -32,15 +30,14 @@
3230
"@types/node": "^20.19.37",
3331
"@typescript-eslint/eslint-plugin": "^8.57.0",
3432
"@typescript-eslint/parser": "^8.57.0",
35-
"@vitest/coverage-v8": "^4.1.0",
3633
"eslint": "^9.39.4",
3734
"eslint-config-prettier": "^10.1.8",
3835
"prettier": "^3.8.1",
3936
"typescript": "^5.4.0",
4037
"vitest": "^4.1.0"
4138
},
4239
"engines": {
43-
"node": ">=20.0.0"
40+
"node": ">=22.0.0"
4441
},
4542
"files": [
4643
"dist/",

nemoclaw/vitest.config.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)