fix(cli): version-pin scoped monorepo tags like @tanstack/react-query@5.101.2#122
Conversation
…@5.101.2 Scoped monorepos (@tanstack/*, @trpc/*, ...) tag releases with the FULL scoped package name, which contains a slash. Two compounding defects blocked version pinning for them: - The npm resolver only generated unscoped monorepo tag candidates (react-query@5.101.2), which do not exist on scoped monorepos. Add the full scoped name (@tanstack/react-query@5.101.2) ahead of the unscoped candidates when repository.directory is present. - githubStorePath rejected slash-containing tags outright (unsafe-path guard), killing the git-clone path even when the real tag was known. Encode path separators as __ in the tag store segment (same convention as the skills spec-key encoding) instead of rejecting; traversal inputs are still rejected after encoding. Since the store dir basename is now an ENCODED tag, callers can no longer recover the real ref from the path: the github source store-hit path now reports the winning candidate in meta.ref, and ensureCheckout prefers meta.ref over the checkout basename for its ref contract. Applied to both the TS CLI (packages/cli) and the Rust port (crates/ask); parity harness passes on all 9 cases. Closes #121
|
Warning Review limit reached
Next review available in: 50 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (11)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 5 |
| Duplication | 4 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Deploying ask with
|
| Latest commit: |
716d716
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://58a8b462.ask-6im.pages.dev |
| Branch Preview URL: | https://amondnet-121.ask-6im.pages.dev |
Greptile SummaryThis PR fixes two compounding bugs that caused scoped monorepo packages like
Confidence Score: 4/5The resolver and store-path encoding changes are correct and well-tested, but the tar.gz fallback path in both ports still interpolates slash-containing candidates directly into HTTP archive URLs without percent-encoding, meaning scoped monorepo tag resolution will fail whenever git is unavailable and the clone path falls through. The core logic — scoped-first candidate ordering, injective encodeRefSegment, and meta.ref propagation — is sound and validated by an extensive test suite including cross-language parity checks. The outstanding gap is the tar.gz fallback: a candidate like @tanstack/react-query@5.101.2 embedded verbatim in the archive URL produces a malformed path that will 404, leaving the scoped-tag fix incomplete on git-less environments. This was flagged in the prior review round and remains unaddressed. packages/cli/src/sources/github.ts (fetchFromTarGz) and crates/ask/src/sources/github.rs (fetch_from_tar_gz) — both embed candidate unencoded into archive URL paths. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["npm resolve('@tanstack/react-query', '5.101.2')"] --> B{monorepo package?}
B -->|No| C["fallbackRefs: ['5.101.2']"]
B -->|Yes + scoped| D["push '@tanstack/react-query@5.101.2'\npush '@tanstack/react-query@v5.101.2'"]
D --> E["push 'react-query@5.101.2'\npush 'react-query@v5.101.2'"]
E --> F["push '5.101.2'"]
F --> G["GithubSource.fetch(fallbackRefs)"]
G --> H{store-cache hit?}
H -->|Yes| I["meta.ref = candidate\n(winning unencoded tag)"]
H -->|No| J{has git?}
J -->|Yes| K["shallow clone\nwinningCandidate = '@tanstack/react-query@5.101.2'"]
K --> L["github_store_path\n→ encodeRefSegment\n→ '@tanstack__react-query@5.101.2-8cd04c22'"]
L --> M["meta.ref = winningCandidate\n(unencoded)"]
J -->|No| N["fetchFromTarGz\nURL: /archive/refs/tags/{candidate}.tar.gz\n⚠ slash unencoded — known P1"]
I --> O["ensureCheckout\nactualRef = meta.ref\n= '@tanstack/react-query@5.101.2'"]
M --> O
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["npm resolve('@tanstack/react-query', '5.101.2')"] --> B{monorepo package?}
B -->|No| C["fallbackRefs: ['5.101.2']"]
B -->|Yes + scoped| D["push '@tanstack/react-query@5.101.2'\npush '@tanstack/react-query@v5.101.2'"]
D --> E["push 'react-query@5.101.2'\npush 'react-query@v5.101.2'"]
E --> F["push '5.101.2'"]
F --> G["GithubSource.fetch(fallbackRefs)"]
G --> H{store-cache hit?}
H -->|Yes| I["meta.ref = candidate\n(winning unencoded tag)"]
H -->|No| J{has git?}
J -->|Yes| K["shallow clone\nwinningCandidate = '@tanstack/react-query@5.101.2'"]
K --> L["github_store_path\n→ encodeRefSegment\n→ '@tanstack__react-query@5.101.2-8cd04c22'"]
L --> M["meta.ref = winningCandidate\n(unencoded)"]
J -->|No| N["fetchFromTarGz\nURL: /archive/refs/tags/{candidate}.tar.gz\n⚠ slash unencoded — known P1"]
I --> O["ensureCheckout\nactualRef = meta.ref\n= '@tanstack/react-query@5.101.2'"]
M --> O
Reviews (2): Last reviewed commit: "fix(cli): make encodeRefSegment injectiv..." | Re-trigger Greptile |
Deploying ask-registry with
|
| Latest commit: |
716d716
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://88099202.ask-registry.pages.dev |
| Branch Preview URL: | https://amondnet-121.ask-registry.pages.dev |
There was a problem hiding this comment.
No issues found across 11 files
Architecture diagram
sequenceDiagram
participant CLI as ask CLI
participant Npm as NpmResolver
participant Github as GithubSource
participant Store as Store Paths
participant Git as Git
participant Filesystem as Filesystem Cache
Note over CLI,Filesystem: Version-pinned scoped monorepo resolution flow
CLI->>Npm: resolve("npm:@tanstack/react-query", "latest")
Npm->>Npm: Fetch npm registry metadata
Npm->>Npm: Detect repository.directory (monorepo)
Npm->>Npm: Build fallbackRefs: [@tanstack/react-query@5.101.2, @tanstack/react-query@v5.101.2, react-query@5.101.2, react-query@v5.101.2, 5.101.2]
Npm-->>CLI: ResolveResult { repo: TanStack/query, ref: v5.101.2, fallbackRefs }
CLI->>Github: fetch({ repo, tag: v5.101.2, fallbackRefs })
alt Store cache hit (encoded ref dir exists)
Github->>Store: githubStorePath(askHome, github.com, TanStack, query, @tanstack/react-query@5.101.2)
Store->>Store: encodeRefSegment(@tanstack/react-query@5.101.2) -> @tanstack__react-query@5.101.2
Store->>Store: assertSafeSegment(tag, @tanstack__react-query@5.101.2)
Store-->>Github: Path: .../TanStack/query/@tanstack__react-query@5.101.2
Github->>Filesystem: Check path exists
Filesystem-->>Github: Exists
Github-->>CLI: FetchResult { storePath, meta.ref: @tanstack/react-query@5.101.2, fromStoreCache: true }
else No store cache hit
Github->>Git: Try clone with tag: v5.101.2
alt Tag not found
Github->>Git: Try clone with fallbackRefs (in order)
Git->>Git: Clone @tanstack/react-query@5.101.2 tag
Git-->>Github: Clone successful
end
Github->>Store: githubStorePath(askHome, github.com, TanStack, query, @tanstack/react-query@5.101.2)
Store->>Store: encodeRefSegment(@tanstack/react-query@5.101.2) -> @tanstack__react-query@5.101.2
Store-->>Github: Encoded store path
Github->>Github: Move clone to encoded store dir
Github-->>CLI: FetchResult { storePath, meta.ref: @tanstack/react-query@5.101.2 }
end
CLI->>CLI: ensureCheckout(fetchResult)
CLI->>CLI: resolvedCheckoutDir = fetchResult.storePath
CLI->>CLI: actualRef = fetchResult.meta.ref (prefer over basename)
CLI-->>CLI: Return { ref: @tanstack/react-query@5.101.2, path: .../@tanstack__react-query@5.101.2/docs }
Note over CLI,Filesystem: Key invariants:
Note over CLI,Filesystem: - Store dir: @tanstack__react-query@5.101.2 (encoded)
Note over CLI,Filesystem: - meta.ref: @tanstack/react-query@5.101.2 (real tag)
Note over CLI,Filesystem: - assertSafeSegment still rejects .. traversal after encoding
Auto-approved: Fixes scoped monorepo tag resolution and path encoding; well-tested, low risk, no logic changes outside the bug scope.
Re-trigger cubic
The plain `__` substitution collides a `release/v1.0.0` branch with a literal `release__v1.0.0` tag — whichever is fetched first would shadow the other with a silent stale-cache hit. Append an 8-char sha256 of the ORIGINAL ref when it contains a separator; refs without separators (the common case) still map to themselves unchanged. Addresses greptile review on #122.
|
There was a problem hiding this comment.
0 issues found across 5 files (changes from recent commits).
Auto-approved: Fixes scoped monorepo tag resolution and store path encoding, both in Rust and TypeScript. All changes are isolated to the resolver and store-path logic, with thorough tests in both ports.
Re-trigger cubic
* feat(tanstack): add TanStack intent-based skills plugin Single use-tanstack skill that drives the official @tanstack/intent CLI to discover and load Agent Skills shipped inside the installed @tanstack/* npm packages, so guidance always matches the exact library versions in the project. Falls back to tanstack.com markdown docs for packages that do not ship intent skills yet. * feat(tanstack): prefer ask CLI for docs fallback When a package ships no intent skills, prefer a locally cached ask docs/source tree over curling tanstack.com pages. Pinned to @main because ask cannot resolve TanStack monorepo release tags (@tanstack/<pkg>@<version>) yet. * fix(tanstack): use version-pinned ask npm: specifiers in docs fallback ask >= 0.4.9 (pleaseai/ask#122) resolves scoped monorepo tags like @tanstack/react-query@5.101.2, so the fallback no longer needs to pin github:TanStack/query@main. Keep the @main path as a caveat for older ask versions. * chore: update agent memory * chore(tanstack): apply AI code review suggestions - fix skill description: react-router -> @tanstack/react-router (P1: wrong-library activation) - harden ask docs path extraction against format/notice shifts (P2) - README: use bun run plugins:multi-format script instead of direct cli.ts call * chore(tanstack): apply gemini review suggestions - handle missing trailing newline in while-read loops (|| [ -n "$p" ]) - apply the same existing-dir filter to ASK_SRC as ASK_DOCS - curl -fsSL for tanstack.com doc fallbacks (follow redirects, fail on error)



Summary
Fixes #121 —
ask docs npm:@tanstack/react-query(and any scoped monorepo package) could not resolve a version-pinned checkout. Two compounding defects:react-query@5.101.2), which 404s on scoped monorepos. The resolver now prepends the full scoped name (@tanstack/react-query@5.101.2, plus the@vvariant) ahead of the unscoped candidates whenrepository.directoryis present.githubStorePathtreated the/in scoped tags as path traversal and threw, killing the git-clone path even when the real tag was discovered. Path separators in the tag segment are now encoded as__(same convention as the skills spec-key encoding) —.../TanStack/query/@tanstack__react-query@5.101.2/— while..traversal inputs are still rejected after encoding. This also unblocks slash-containing branch refs likerelease/v1.2.3.Because the store dir basename is now an encoded tag, callers can no longer recover the real ref from the path: the github source store-hit path reports the winning candidate in
meta.ref, andensureCheckoutprefersmeta.refover the checkout basename for itsrefcontract (ask src --json).Applied to both the TS CLI (
packages/cli) and the Rust port (crates/ask).Verification
meta.ref+ cache-hit)ALL PARITY CASES IDENTICAL(9 cases)Closes #121
Summary by cubic
Fixes version-pinned checkouts for scoped monorepo packages like
@tanstack/react-query@5.101.2in both the TS CLI and Rust port. Adds injective encoding for slash-containing refs to prevent cache collisions and reports the actual winning ref.<name>@<version>,<name>@v<version>), then unscoped; keeps unscoped for repos likevercel/ai./and\in the ref segment as__and append an 8-char sha256 when separators are present; still reject..; avoids collisions likerelease/v1.0.0vsrelease__v1.0.0; supports tags like@tanstack/react-query@5.101.2and branches likerelease/v1.2.3.meta.reffor clones and store hits.meta.refover the checkout dir basename when reportingref.Written for commit 716d716. Summary will update on new commits.