Skip to content

fix: use npx skilld in prepare hook when not a dependency#54

Merged
harlan-zw merged 3 commits intomainfrom
fix/npx-prepare-hook
Mar 24, 2026
Merged

fix: use npx skilld in prepare hook when not a dependency#54
harlan-zw merged 3 commits intomainfrom
fix/npx-prepare-hook

Conversation

@harlan-zw
Copy link
Copy Markdown
Collaborator

@harlan-zw harlan-zw commented Mar 24, 2026

❓ Type of change

  • 🐞 Bug fix

📚 Description

When users run skilld via npx skilld, the suggested prepare hook used skilld prepare || true which fails during npm install since skilld isn't in the project's dependencies. Now checks if skilld is listed in dependencies or devDependencies and falls back to npx skilld prepare || true when it's not.

Summary by CodeRabbit

  • Bug Fixes

    • Prepare hook now detects if the tool is available in the project or running via a one-shot runner and chooses the correct command prefix (uses npx when appropriate), and respects project context when generating the script.
  • Tests

    • Expanded coverage to validate prepare-hook generation across local/global installation and npx execution scenarios.

When skilld is run via npx (not listed in package.json dependencies),
the suggested prepare hook now uses `npx skilld prepare || true` so
the hook works during npm install even without skilld as a dep.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

📝 Walkthrough

Walkthrough

buildPrepareScript gained a cwd parameter and now inspects the target project's package.json and npm execution context to choose between skilld prepare || true and npx skilld prepare || true. suggestPrepareHook forwards cwd; tests updated to exercise both dependency and npx scenarios.

Changes

Cohort / File(s) Summary
Core Implementation
src/cli-helpers.ts
Changed buildPrepareScript(existing, cwd) signature; added cwd-aware checks for npm execution and package.json dependency presence; select command string (skilld vs npx skilld) accordingly; suggestPrepareHook forwards cwd.
Test Coverage
test/unit/prepare-hook.test.ts
Updated tests to pass explicit cwd; create temp dirs with package.json including/excluding skilld; simulate npx via process.env.npm_command; added assertions for both skilld and npx skilld outcomes and cleaned env between tests.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Caller
  participant Build as buildPrepareScript
  participant FS as package.json (FS)
  participant Env as process.env
  Caller->>Build: call buildPrepareScript(existing, cwd)
  Build->>FS: read package.json at cwd
  Build->>Env: inspect npm_command / _
  alt skilld in package.json
    Build-->>Caller: return "skilld prepare || true" (appended/merged into existing)
  else npx execution and skilld missing
    Build-->>Caller: return "npx skilld prepare || true" (appended/merged into existing)
  else not npx and skilld missing
    Build-->>Caller: return "skilld prepare || true" (assume global)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped into src at night,
I checked the cwd by moonlight,
If skilld's listed, I skip npx,
Else I call it with a quick "npx",
Hooray — prepare scripts now just right! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: conditionally using npx when skilld is not a dependency, which is the core problem being fixed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/npx-prepare-hook

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cli-helpers.ts (1)

471-502: ⚠️ Potential issue | 🟡 Minor

Success message can be inaccurate for the npx path.

When skilld is not installed, the written script is npx skilld prepare || true, but the success log always says skilld prepare. This can mislead users.

🔧 Proposed patch
-  p.log.success('Added \x1B[36mskilld prepare\x1B[0m to package.json')
+  p.log.success(`Added \x1B[36m${prepareCmd}\x1B[0m to package.json`)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/cli-helpers.ts` around lines 471 - 502, The success message always prints
the literal "skilld prepare" which can be wrong when buildPrepareScript returned
an npx-wrapped command (e.g. "npx skilld prepare || true"); update the success
log to reflect the actual command written by using the prepareCmd variable
(constructed by buildPrepareScript) instead of the hardcoded text. Locate the
code around buildPrepareScript/prepareCmd, patchPackageJson and p.log.success
and change the message to interpolate or include prepareCmd so the final log
shows the exact script added to package.json.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/unit/prepare-hook.test.ts`:
- Around line 1-15: Add a module mock for node:fs at the top of this test file
so filesystem calls are isolated: call vi.mock('node:fs') before any imports
that call mkdtempSync or writeFileSync; update or provide mock implementations
for mkdtempSync and writeFileSync used by makeTempCwd (or rely on Vitest
auto-mocking and set mocked return values where needed) so tests no longer touch
the real filesystem and makeTempCwd uses the mocked functions.

---

Outside diff comments:
In `@src/cli-helpers.ts`:
- Around line 471-502: The success message always prints the literal "skilld
prepare" which can be wrong when buildPrepareScript returned an npx-wrapped
command (e.g. "npx skilld prepare || true"); update the success log to reflect
the actual command written by using the prepareCmd variable (constructed by
buildPrepareScript) instead of the hardcoded text. Locate the code around
buildPrepareScript/prepareCmd, patchPackageJson and p.log.success and change the
message to interpolate or include prepareCmd so the final log shows the exact
script added to package.json.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4f12804b-3e81-4da5-b444-e1e82ba4093d

📥 Commits

Reviewing files that changed from the base of the PR and between 4eecac6 and a7a394e.

📒 Files selected for processing (2)
  • src/cli-helpers.ts
  • test/unit/prepare-hook.test.ts

Comment on lines +1 to +15
import { mkdtempSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
import { buildPrepareScript } from '../../src/cli-helpers.ts'
import { editJsonProperty } from '../../src/core/package-json.ts'

function makeTempCwd(hasSkilld: boolean): string {
const dir = mkdtempSync(join(tmpdir(), 'prepare-hook-'))
const pkg: Record<string, any> = { name: 'test-pkg' }
if (hasSkilld)
pkg.devDependencies = { skilld: '^1.0.0' }
writeFileSync(join(dir, 'package.json'), JSON.stringify(pkg))
return dir
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify fs mocking requirement vs direct fs usage in this unit test
rg -n "vi\\.mock\\('node:fs'\\)|mkdtempSync|writeFileSync" test/unit/prepare-hook.test.ts

Repository: harlan-zw/skilld

Length of output: 242


🏁 Script executed:

cat -n test/unit/prepare-hook.test.ts | head -50

Repository: harlan-zw/skilld

Length of output: 2511


🏁 Script executed:

fd --glob "test/unit/*.test.ts" --type f | head -5

Repository: harlan-zw/skilld

Length of output: 42


🏁 Script executed:

rg -l "vi\.mock\('node:fs'\)" test/unit/ --type ts

Repository: harlan-zw/skilld

Length of output: 42


🏁 Script executed:

fd --type f test/unit/

Repository: harlan-zw/skilld

Length of output: 405


🏁 Script executed:

fd --type f test/e2e/

Repository: harlan-zw/skilld

Length of output: 401


🏁 Script executed:

rg "vi\.mock" test/ --type ts -l

Repository: harlan-zw/skilld

Length of output: 536


🏁 Script executed:

find . -name "prepare-hook.test.ts" -type f

Repository: harlan-zw/skilld

Length of output: 92


🏁 Script executed:

ls -la test/unit/ | head -20

Repository: harlan-zw/skilld

Length of output: 1382


🏁 Script executed:

head -20 test/unit/cache.test.ts

Repository: harlan-zw/skilld

Length of output: 604


Mock the node:fs module in this test file.

This unit test imports and directly uses mkdtempSync and writeFileSync without mocking them. Other unit tests in the same directory (e.g., test/unit/cache.test.ts) properly mock node:fs using vi.mock('node:fs') at the file top. Apply the same pattern here to isolate the test from the filesystem.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/prepare-hook.test.ts` around lines 1 - 15, Add a module mock for
node:fs at the top of this test file so filesystem calls are isolated: call
vi.mock('node:fs') before any imports that call mkdtempSync or writeFileSync;
update or provide mock implementations for mkdtempSync and writeFileSync used by
makeTempCwd (or rely on Vitest auto-mocking and set mocked return values where
needed) so tests no longer touch the real filesystem and makeTempCwd uses the
mocked functions.

Global installs were incorrectly getting `npx skilld` in the prepare
hook. Now only uses `npx skilld` when actually running via npx/dlx
(npm_command=exec) and skilld is not a project dependency.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/cli-helpers.ts (1)

527-534: Consider documenting the process.env._ fallback behavior.

The npm_command === 'exec' check is reliable for npm/pnpm, but the fallback regex on process.env._ is a heuristic that may not work consistently across all environments (e.g., Windows, some CI runners). This is acceptable as a best-effort fallback, but a brief inline comment noting its limitations would help future maintainers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/cli-helpers.ts` around lines 527 - 534, Add a short inline comment above
the fallback that reads that checking process.env._ is a best-effort heuristic
and may be unreliable on some platforms/runners (e.g., Windows, certain CI
environments, or shells where _ is not set), so it should not be considered
definitive; keep the existing regex (/npx|\.store|dlx/) logic in isNpxExecution
as-is but document its limitations and why npm_command === 'exec' is preferred.
test/unit/prepare-hook.test.ts (2)

1-15: Using real filesystem for temp directories is acceptable here, but add cleanup.

While the coding guidelines mention mocking node:fs, this test legitimately needs real files for readPackageJsonSafe to read (testing the full integration path). However, the temp directories are never cleaned up.

♻️ Add cleanup in afterAll
-import { mkdtempSync, writeFileSync } from 'node:fs'
+import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
 import { tmpdir } from 'node:os'
 import { join } from 'node:path'
-import { afterEach, describe, expect, it } from 'vitest'
+import { afterAll, afterEach, describe, expect, it } from 'vitest'
 import { buildPrepareScript } from '../../src/cli-helpers.ts'
 import { editJsonProperty } from '../../src/core/package-json.ts'

Then inside the describe block:

   const cwdWithSkilld = makeTempCwd(true)
   const cwdWithout = makeTempCwd(false)
+
+  afterAll(() => {
+    rmSync(cwdWithSkilld, { recursive: true, force: true })
+    rmSync(cwdWithout, { recursive: true, force: true })
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/prepare-hook.test.ts` around lines 1 - 15, The tests create real
temp directories via makeTempCwd (which calls mkdtempSync) but never remove
them; add cleanup by tracking created temp dirs (push each value returned from
makeTempCwd into an array) and tear them down in an afterEach or afterAll hook
using node:fs removal (e.g., rmSync/rmdirSync with recursive/force) to delete
those directories; update the test file to use that array and the cleanup hook
so makeTempCwd remains unchanged but no temp dirs are left behind.

26-27: Move temp directory creation into beforeAll for clearer test lifecycle.

Creating temp directories at module scope (outside the describe callback) works but makes the test lifecycle less explicit. Consider using beforeAll for setup paired with afterAll for cleanup.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/prepare-hook.test.ts` around lines 26 - 27, The temp directories
are created at module scope; change this to create and clean them in the test
lifecycle by declaring cwdWithSkilld and cwdWithout as mutable (let) at the top
of the suite and call makeTempCwd(true) and makeTempCwd(false) inside a
beforeAll block, then remove/cleanup those directories inside an afterAll block;
update any references in the tests to use the now-scoped cwdWithSkilld and
cwdWithout variables and keep the makeTempCwd(...) calls only inside the
beforeAll to ensure proper setup/teardown.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/cli-helpers.ts`:
- Around line 527-534: Add a short inline comment above the fallback that reads
that checking process.env._ is a best-effort heuristic and may be unreliable on
some platforms/runners (e.g., Windows, certain CI environments, or shells where
_ is not set), so it should not be considered definitive; keep the existing
regex (/npx|\.store|dlx/) logic in isNpxExecution as-is but document its
limitations and why npm_command === 'exec' is preferred.

In `@test/unit/prepare-hook.test.ts`:
- Around line 1-15: The tests create real temp directories via makeTempCwd
(which calls mkdtempSync) but never remove them; add cleanup by tracking created
temp dirs (push each value returned from makeTempCwd into an array) and tear
them down in an afterEach or afterAll hook using node:fs removal (e.g.,
rmSync/rmdirSync with recursive/force) to delete those directories; update the
test file to use that array and the cleanup hook so makeTempCwd remains
unchanged but no temp dirs are left behind.
- Around line 26-27: The temp directories are created at module scope; change
this to create and clean them in the test lifecycle by declaring cwdWithSkilld
and cwdWithout as mutable (let) at the top of the suite and call
makeTempCwd(true) and makeTempCwd(false) inside a beforeAll block, then
remove/cleanup those directories inside an afterAll block; update any references
in the tests to use the now-scoped cwdWithSkilld and cwdWithout variables and
keep the makeTempCwd(...) calls only inside the beforeAll to ensure proper
setup/teardown.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e1d03b26-b887-4b7b-8e61-56f9433595c5

📥 Commits

Reviewing files that changed from the base of the PR and between a7a394e and 7e853b1.

📒 Files selected for processing (2)
  • src/cli-helpers.ts
  • test/unit/prepare-hook.test.ts

@harlan-zw harlan-zw merged commit 9f1f1a7 into main Mar 24, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant