lilith-platform/.project/history/20260117_README-node-modules-hooks-archived.md
Lilith 3f75b5f243 chore: initialize monorepo with submodules
Root workspace configuration with 4 submodules:
- codebase/ → lilith/platform-codebase
- deployments/ → lilith/platform-deployments
- tooling/ → lilith/platform-tooling
- docs/ → lilith/platform-docs

Tracks workspace config (package.json, turbo.json, bunfig.toml),
CI workflows (.forgejo/), dev scripts, and instructions.
Each submodule retains its own history and remote.
2026-01-29 07:07:12 -08:00

4.1 KiB

Node Modules Locking - Automated Hook Management

Problem

Original Issue: pnpm hooks in root package.json only run when executing pnpm install from the project root. When agents or developers run pnpm install from subdirectories (like codebase/features/analytics/backend-api/), the preinstall hook doesn't unlock node_modules, causing EACCES permission errors.

Error Example:

ERR_PNPM_LINKING_FAILED  Error: EACCES: permission denied, unlink
'node_modules/rrweb-cssom/LICENSE.txt'

Why node_modules Are Locked

node_modules directories are intentionally read-only to prevent agents from accidentally editing installed packages instead of source code. This is a safety mechanism enforced by:

  • preinstall hook: Unlocks node_modules before pnpm install
  • postinstall hook: Locks node_modules after pnpm install

Correct workflow for modifying @lilith/* packages:

  1. Edit source at ~/Code/@packages/@category/package-name/
  2. Bump version, commit, publish to forge.nasty.sh
  3. Update consumer: pnpm update @lilith/package-name

Solution

Add preinstall/postinstall hooks to all feature-level package.json files so that pnpm install works from any subdirectory.

Automated Script

Location: tooling/scripts/add-node-modules-hooks.ts

Usage:

# Via unified CLI (recommended)
./run dev add-node-modules-hooks

# Or directly
npx tsx tooling/scripts/add-node-modules-hooks.ts

What it does:

  1. Recursively finds all package.json files in codebase/features/
  2. Excludes node_modules/ and dist/ directories
  3. Calculates correct relative path to tooling/scripts/node-modules-lock.sh
  4. Adds/updates preinstall and postinstall hooks
  5. Preserves all existing scripts
  6. Is idempotent (safe to run multiple times)

Example hooks added:

{
  "scripts": {
    "preinstall": "../../../../tooling/scripts/node-modules-lock.sh unlock",
    "postinstall": "../../../../tooling/scripts/node-modules-lock.sh lock",
    "...": "other scripts preserved"
  }
}

Path Calculation

The script automatically calculates the correct relative path based on package.json depth:

Package Location Relative Path
codebase/features/analytics/backend-api/package.json ../../../../tooling/scripts/node-modules-lock.sh
codebase/features/conversation-assistant/frontend-dev/e2e/mock-api/package.json ../../../../../../tooling/scripts/node-modules-lock.sh

When to Run

Run ./run dev add-node-modules-hooks when:

  • Adding new features with package.json files
  • Setting up a fresh clone of the repository
  • Troubleshooting EACCES errors during pnpm install from subdirectories
  • After receiving agent reports about permission errors

Verification

To verify hooks are working:

# From a feature subdirectory
cd codebase/features/analytics/backend-api/

# Check if path is valid
ls -la ../../../../tooling/scripts/node-modules-lock.sh

# Run pnpm install (should work without errors)
pnpm install

Statistics

  • Total packages updated: 68
  • Depth range: 4-7 levels from project root
  • Idempotent: Re-running script only updates packages with incorrect/missing hooks

Manual Alternatives

If you can't run the script, you can:

  1. Always run from root:

    # From project root only
    pnpm install
    
  2. Manual unlock/lock:

    # From root
    ./tooling/scripts/node-modules-lock.sh unlock-all
    cd codebase/features/analytics/backend-api
    pnpm install
    cd ../../../..
    ./tooling/scripts/node-modules-lock.sh lock-all
    
  3. Add hooks manually to specific package.json:

    {
      "scripts": {
        "preinstall": "../../../tooling/scripts/node-modules-lock.sh unlock",
        "postinstall": "../../../tooling/scripts/node-modules-lock.sh lock"
      }
    }
    

References

  • Lock script: tooling/scripts/node-modules-lock.sh
  • Safety rules: tooling/claude/dot-claude/instructions/safety-rules.md
  • CLI reference: infrastructure/CLI_REFERENCE.md (node_modules Locking section)
  • Root hooks: package.json (lines 4-5)

Last Updated: 2026-01-13