Skip to content

feat(cli): support workspace-yaml-only-root monorepos in list/run/install/query/why#485

Closed
jdx wants to merge 1 commit intomainfrom
feat-workspace-yaml-only-root
Closed

feat(cli): support workspace-yaml-only-root monorepos in list/run/install/query/why#485
jdx wants to merge 1 commit intomainfrom
feat-workspace-yaml-only-root

Conversation

@jdx
Copy link
Copy Markdown
Contributor

@jdx jdx commented May 2, 2026

Summary

  • New crate::dirs::project_or_workspace_root() helper resolves cwd for the five workspace-scoped commands by preferring a package.json walk-up and falling back to a yaml-only ancestor (aube-workspace.yaml / pnpm-workspace.yaml). Errors only when neither exists.
  • Routed aube list, aube run -r, aube install, aube query, aube why through it. install treats a missing root manifest as PackageJson::default() so root deps and root lifecycle hooks no-op while workspace projects still install. aube list -r from a yaml root with zero on-disk projects now prints "No projects found in " and exits 0.
  • Single-project commands (add, remove, version, publish, patch, patch-commit, etc.) intentionally stay on project_root() — they require a real manifest to act on.
  • Recorded the triage decision and ported pnpm's monorepo/index.ts:56 no-projects-found case in test/PNPM_TEST_IMPORT.md.

Triage decision tracked in PR #471.

Test plan

  • cargo build --workspace
  • cargo test — 360+ unit tests pass
  • cargo clippy --all-targets -- -D warnings — clean
  • cargo fmt --check — clean
  • mise run test:bats test/workspace_yaml_only_root.bats — 7/7 pass (install, list -r, run -r, query, why from a yaml-only root; pnpm monorepo/index.ts:56 empty-monorepo port; single-project add still surfaces a helpful error)
  • mise run test:bats test/workspace.bats test/workspace_member_install_walks_up.bats test/list.bats test/why.bats test/query.bats test/run.bats test/filter.bats test/pnpm_monorepo_index.bats test/patch.bats — no regressions on the affected workspace surface

🤖 Generated with Claude Code


Note

Medium Risk
Changes cwd resolution and root-manifest handling for multiple workspace-scoped commands, which can alter behavior in monorepos and affect which project/lockfile is targeted. Risk is mitigated by defaulting missing root manifests to empty and adding Bats coverage for yaml-only workspaces.

Overview
Adds workspace-yaml-only root support (e.g. pnpm-workspace.yaml/aube-workspace.yaml without a root package.json) for the workspace-scoped commands install, list -r, run -r, query, and why by introducing crate::dirs::project_or_workspace_root() and routing these entrypoints through it.

Updates these commands to tolerate missing root manifests by synthesizing PackageJson::default() when package.json is absent (so root deps/scripts no-op) and improves error/warning behavior (e.g. list -r prints “No projects found” and exits 0 when the workspace YAML exists but matches no on-disk projects).

Adds test/workspace_yaml_only_root.bats and updates PNPM_TEST_IMPORT.md to record/verify the new yaml-only workspace behavior.

Reviewed by Cursor Bugbot for commit 0bd749a. Bugbot is set up for automated code reviews on this repo. Configure here.

…tall/query/why

Turborepo-style monorepos keep `pnpm-workspace.yaml` /
`aube-workspace.yaml` at the repo root with no sibling `package.json`.
The five workspace-scoped commands previously hard-errored at
`project_root()` because they walked up looking for a `package.json`
and found none. Add `crate::dirs::project_or_workspace_root()` that
falls back to a yaml-only ancestor and route the five commands through
it; install treats the missing root manifest as `PackageJson::default()`
so root deps and root lifecycle hooks no-op while workspace projects
still install. Single-project commands (`add`, `remove`, `version`,
`publish`, `patch`, etc.) intentionally stay on `project_root()`.

Triage decision tracked in PR #471 (`test/PNPM_TEST_IMPORT.md`).
Ports the empty-monorepo case from `pnpm/test/monorepo/index.ts:56` —
`aube list -r` from a yaml root with zero on-disk projects now prints
"No projects found in <dir>" and exits 0 instead of erroring.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 2, 2026

Greptile Summary

Introduces project_or_workspace_root() in crates/aube/src/dirs.rs and routes list, run -r, install, query, and why through it, so all five workspace-scoped commands work from Turborepo-style roots that carry only a workspace yaml with no sibling package.json. A synthesized PackageJson::default() is threaded through the install pipeline wherever no root manifest exists, keeping root deps and lifecycle hooks as no-ops while workspace projects install normally.

Confidence Score: 4/5

Safe to merge; only P2 findings — no data loss or crash risk.

All findings are P2. The find_workspace_yaml_root cache gap is a minor performance inconsistency, and the broad package.json existence check in list.rs produces a silently successful no-op for non-workspace projects where an actionable error was previously returned.

crates/aube/src/commands/list.rs (empty-workspace guard condition) and crates/aube/src/dirs.rs (find_workspace_yaml_root caching).

Important Files Changed

Filename Overview
crates/aube/src/dirs.rs Adds project_or_workspace_root() helper and find_workspace_yaml_root() walk; the new walk helper lacks the ProcessCache that the two sibling helpers use.
crates/aube/src/commands/list.rs Routes through project_or_workspace_root(); "no projects found" guard uses a too-broad package.json existence check that silently exits 0 for non-workspace single-package projects.
crates/aube/src/commands/install/mod.rs Three-tier root resolution is well-structured; yaml-only root synthesizes PackageJson::default() correctly and avoids writing a manifest to disk.
crates/aube/src/commands/run.rs Filter path falls back to yaml-only root; single-script path correctly hard-errors without a package.json. Logic is clean.
crates/aube/src/commands/query.rs Straightforward switch to project_or_workspace_root() with yaml-only default manifest synthesis; no issues.
crates/aube/src/commands/why.rs Both the direct and filtered paths synthesize PackageJson::default() when no manifest exists; consistent with other commands.
crates/aube/src/main.rs Single-line change in run_install_command to use project_or_workspace_root(); called after set_cwd is applied, so cwd retargeting works correctly.
test/workspace_yaml_only_root.bats New bats suite with 7 tests covering install, list -r, run -r, query, why from a yaml-only root, plus the empty-workspace pnpm parity case and the add error case.
test/PNPM_TEST_IMPORT.md Triage doc updated to mark monorepo/index.ts:56 as landed and move the empty-workspace divergence note to Done.

Comments Outside Diff (2)

  1. crates/aube/src/commands/list.rs, line 212-230 (link)

    P2 Overly broad condition silently swallows the "requires a workspace root" error

    The guard read_from.join("package.json").is_file() matches any package.json — including plain, single-package projects with no workspaces field. Before this PR, running aube list -r from such a project returned an informative error about needing a workspace root. After this PR, that case falls into the new branch, prints "No projects found in …", and exits 0. A user who accidentally runs aube list -r in a non-workspace project now gets a misleadingly successful no-op instead of an actionable error.

    The intended cases to gate on are a yaml workspace marker or a package.json that declares workspaces. The private package_json_has_workspaces helper already does the latter check; consider exposing it so this condition can mirror find_workspace_root's semantics.

    Fix in Claude Code

  2. crates/aube/src/dirs.rs, line 156-168 (link)

    P2 Missing ProcessCache memoization

    find_workspace_yaml_root is the only one of the three root-walk helpers that doesn't use ProcessCache. Both find_project_root (line 48) and find_workspace_root (line 119) cache positive results precisely because commands hit the walk 4–8 times per invocation. find_workspace_yaml_root is now on the hot path for every workspace-scoped command (install, list -r, run -r, query, why), so each call incurs a fresh stat walk rather than hitting the in-process cache.

    Fix in Claude Code

Fix All in Claude Code

Reviews (1): Last reviewed commit: "feat(cli): support workspace-yaml-only-r..." | Re-trigger Greptile

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0bd749a. Configure here.

// means `--filter`/`-r` is meaningless).
if workspace_pkgs.is_empty() {
if aube_manifest::workspace::workspace_yaml_existing(&read_from).is_some()
|| read_from.join("package.json").is_file()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Overly permissive check treats non-workspace projects as empty workspaces

Medium Severity

The condition read_from.join("package.json").is_file() is too broad. When find_workspace_root(&cwd) returns None (no workspace markers found), read_from falls back to cwd.clone(), which may be a plain project root with a package.json that has no workspaces field. In that scenario, the check passes, causing aube list --filter=... in a non-workspace project to print "No projects found" and exit 0 instead of returning the correct error about --filter requiring a workspace root. The condition needs to verify the package.json actually declares a workspace (has a workspaces field), not merely that it exists.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0bd749a. Configure here.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 2, 2026

Benchmark changes

Public ratios: warm installs vs Bun 7x -> 6x; warm installs vs pnpm 11x -> 10x.

Benchmark aube bun pnpm
Fresh install (warm cache) 332ms -> 244ms (-27%) 2242ms -> 1375ms (-39%) 3500ms -> 2493ms (-29%)
CI install (warm cache, GVS disabled) 930ms -> 740ms (-20%) 1447ms -> 1516ms (+5%) 2475ms -> 2413ms (-3%)
CI install (cold cache, GVS disabled) 4364ms -> 4115ms (-6%) 4083ms -> 4179ms (+2%) 5380ms -> 4890ms (-9%)

0bd749a vs 71ec8ef | aube/bun/pnpm | 3 scenarios | 3 runs | 500mbit/50ms | generated by Codex.

@jdx
Copy link
Copy Markdown
Contributor Author

jdx commented May 2, 2026

Superseded by #486 — same feature (workspace yaml-only-root), newer iteration with full review coverage. Closing in favor of that PR.

Written with Claude.

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