Skip to content

fix(cli): update the workspace member that declares the CLI - #251

Open
detail-app[bot] wants to merge 1 commit into
devfrom
detail/bug-fix/fix-cli-update-the-workspace-member-that-declares-a60bce
Open

fix(cli): update the workspace member that declares the CLI#251
detail-app[bot] wants to merge 1 commit into
devfrom
detail/bug-fix/fix-cli-update-the-workspace-member-that-declares-a60bce

Conversation

@detail-app

@detail-app detail-app Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Detail bug report: View on Detail

Closes #247

Bug

javachat update exited 1 in an npm workspaces monorepo when @wcallahan/javachat-cli was declared by a workspace member package. npm hoists node_modules/.bin/javachat and the CLI package to the workspace root, but resolveNpmInstallTarget (cli/bin/javachat.js) treated the .bin owner (the workspace root) as the project to update, read only the root's package.json, and threw The project at <root> does not declare @wcallahan/javachat-cli before ever invoking npm. The guard conflated "the directory that owns .bin/javachat" with "the project that declares the dependency" — identical in a single-package project, divergent under hoisting. Introduced by e6844cfb (the self-update feature).

Fix

In the .bin branch of resolveNpmInstallTarget, when the root manifest does not declare @wcallahan/javachat-cli, the guard now consults the root's workspaces field, expands the patterns (literal paths plus */** globs) the way npm does, and — if a member declares the package under dependencies, devDependencies, or optionalDependencies — runs the unchanged command npm install @wcallahan/javachat-cli@latest with workingDirectory set to that member directory.

  • npmArguments is identical for the single-package and workspaces-member paths; only workingDirectory changes (the README's literal command, run "in that project").
  • The existing refusal is preserved verbatim as the fallback: non-workspaces root, no declaring member, or any enumeration/manifest-read failure all fail closed with the original message. No new success path is reachable from an enumeration error.
  • The npx guard, the repo-checkout guard, and the source-entrypoint guard all run before the workspaces logic, so the new code cannot bypass them.
  • Helpers: manifestDeclaresJavaChat, findDeclaringWorkspaceMember, extractWorkspacePatterns, expandWorkspaceGlob, matchWorkspaceSegments, workspaceSegmentToRegex.

Testing

  • Unit tests (cli/test/javachat.test.js, npm test): 50/50 pass (43 pre-existing + 7 new). New tests are observable-boundary tests using the existing fake-npm harness (runCli + recorded npm invocations + mkdtemp fixtures), covering:
    • member declares via dependencies with a packages/* glob → install runs in the member
    • member declares via devDependencies, and via optionalDependencies with a literal workspace path
    • recursive ** glob finds a nested declaring member
    • multiple declaring members → exactly one install in a member, never the root
    • workspaces root with no declaring member → existing refusal fires, npm never invoked
    • non-workspaces project that doesn't declare the CLI → existing refusal fires, npm never invoked
    • pre-existing single-package, global, repo-checkout, and npm-failure-propagation tests still pass (no regression)
  • npm run pack:check (== npm pack --dry-run): unchanged 4-file manifest (bin/javachat.js, README.md, LICENSE.md, package.json); no new shipped files, test/ excluded.
  • End-to-end against the real npm registry (Node v24.18.0 / npm 11.16.0, the supported platform):
    • Reproduced the bug on the published 0.0.3 in a workspaces monorepo (member declares the CLI, .bin hoisted to root): javachat update refused with the exact error and exit 1 — confirming the defect on the unmodified release.
    • Applied the fix into the hoisted install and ran javachat update via PATH: member spec advanced 0.0.1^0.0.3, hoisted install updated to 0.0.3, root manifest unchanged (no pollution), exit 0.
    • Single-package regression on the real registry: spec advanced to ^0.0.3, install 0.0.3, exit 0 (install runs at the project root, not a member).
    • Global-install regression: globally installed the published 0.0.3, overwrote with the fixed source, ran javachat update → exit 0 running the --global install. Global package uninstalled afterward; environment verified clean.
    • .npmrc install-strategy=nested (nohoist) topology: .bin/javachat lives in the member's own node_modules/.bin, so the .bin branch resolves projectRoot = the member (which declares the CLI) → member spec advanced to ^0.0.3, root clean, exit 0. Confirms the nohoist topology is unaffected by the fix.
    • Source-entrypoint smoke: node bin/javachat.js update still refuses with the entrypoint guard and exit 1 (expected guard, not a regression).

No tests/procedures were skipped; every item in the plan ran successfully.


Automatic Fixes PRs can be configured here.

@detail-app
detail-app Bot requested a review from WilliamAGH September 8, 2026 16:09
@detail-app detail-app Bot added the bug Something isn't working label Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Detail Bug] CLI: javachat update fails in npm workspaces when the CLI is a dependency of a workspace member

1 participant