Skip to content

Comments

Feat/bubblegum create#55

Merged
tonyboylehub merged 8 commits intomainfrom
feat/bubblegum-create
Dec 22, 2025
Merged

Feat/bubblegum create#55
tonyboylehub merged 8 commits intomainfrom
feat/bubblegum-create

Conversation

@tonyboylehub
Copy link
Contributor

No description provided.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 19, 2025

Summary by CodeRabbit

  • New Features

    • Added Bubblegum compressed NFT support with full CLI command suite
    • Tree creation and management with interactive wizards
    • NFT operations: create, fetch, transfer, burn, and update
    • Metaplex Core collection integration
    • Persistent local tree storage for easy management
  • Documentation

    • Comprehensive Bubblegum guides, quick reference, and troubleshooting documentation

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Adds Bubblegum V2 compressed-NFT support: CLI commands and prompts for Merkle tree and NFT lifecycle (create, fetch, update, transfer, burn), on-disk tree persistence, UMI middleware registration for mpl-bubblegum and DAS API, package dependency additions, extensive docs, and new tests (some DAS-dependent tests skipped).

Changes

Cohort / File(s) Summary
Documentation
docs/bubblegum/README.md, docs/bubblegum/quick-reference.md, docs/bubblegum/troubleshooting.md, docs/bubblegum/tree-create.md, docs/bubblegum/tree-list.md, docs/bubblegum/nft-create.md, docs/bubblegum/nft-fetch.md, docs/bubblegum/nft-update.md, docs/bubblegum/nft-transfer.md, docs/bubblegum/nft-burn.md
New and expanded Bubblegum V2 docs: overviews, quick reference, command guides, workflows, examples, cost estimates, and troubleshooting.
CLI Root & Groups
src/commands/bg/index.ts, src/commands/bg/nft/index.ts, src/commands/bg/tree/index.ts
New bg top-level command and group help entries exposing tree and nft subcommands with help text.
Tree Commands
src/commands/bg/tree/create.ts, src/commands/bg/tree/list.ts
New commands to create Merkle trees (wizard/flags, predefined configs), persist saved trees, and list/filter trees by network.
NFT Commands
src/commands/bg/nft/create.ts, src/commands/bg/nft/fetch.ts, src/commands/bg/nft/update.ts, src/commands/bg/nft/transfer.ts, src/commands/bg/nft/burn.ts
New compressed-NFT commands: create (wizard/file/flags), fetch (asset+proof, download), update (editor/flags), transfer, burn — include ownership checks, transaction building, JSON/human outputs, and spinners.
Collection Command
src/commands/bg/collection/create.ts
Command to create Metaplex Core collections configured for Bubblegum V2 with optional royalties plugin.
Tree Persistence
src/lib/treeStorage.ts
New on-disk storage module for StoredTree: save/load/list/remove, network/genesis resolution, file management, validation, and duplicate checks.
Context & UMI Integration
src/lib/Context.ts
Registers mplBubblegum() and dasApi() middleware in UMI chain and updates Context.umi typing to include DasApiInterface.
Genesis Map
src/lib/util.ts
Updated GENESIS_HASH_MAP mainnet/devnet hashes and added a localnet mapping.
Interactive Prompts
src/prompts/createBubblegumMetadataPrompt.ts, src/prompts/selectTreePrompt.ts
New interactive prompts for metadata creation and tree selection with file/path validation, attribute collection, collection checks, and tilde expansion.
Package & Validator
package.json, .validator.cjs
Added deps: @metaplex-foundation/mpl-bubblegum@^5.0.2, @metaplex-foundation/digital-asset-standard-api@^2.0.0; validator augmented with bubblegum/compression/noop program entries.
Minor Fix
src/commands/tm/create.ts
Guard added when accessing jsonFile.properties.files[0] to avoid runtime errors when files array is missing.
Tests & Helpers
test/commands/bg/*, test/commands/bg/bghelpers.ts, test/commands/bg/bgcollectionhelpers.ts, test/commands/bg/README.md, test/commands/bg/common.ts
New unit/integration tests and helpers for collection/tree/nft create flows; several DAS-dependent tests (transfer/update/burn/fetch) are skipped.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant TreeStorage
    participant DASAPI
    participant UMI
    participant Explorer

    User->>CLI: bg nft create [--wizard | flags]
    alt wizard
        CLI->>User: interactive prompts (metadata, files, tree)
        CLI->>TreeStorage: resolve saved tree by name
    else flags/files
        CLI->>TreeStorage: resolve tree (name/address) or prompt manual
    end
    CLI->>DASAPI: upload asset & metadata (optional)
    DASAPI-->>CLI: asset/uri response
    CLI->>UMI: invoke mintV2 (mpl-bubblegum)
    UMI-->>CLI: tx signature
    CLI->>CLI: derive assetId (attempt from tx)
    CLI->>Explorer: build explorer URL
    CLI-->>User: print summary / JSON
Loading
sequenceDiagram
    participant User
    participant CLI
    participant DASAPI
    participant FS

    User->>CLI: bg nft fetch <assetId> [--proof-only] [--download]
    CLI->>DASAPI: getAsset(assetId) & getAssetProof(assetId) (parallel)
    DASAPI-->>CLI: asset data, merkle proof
    alt download
        CLI->>FS: write <assetId>-asset.json / <assetId>-proof.json
    else
        CLI->>CLI: format & display asset + proof
    end
    CLI-->>User: output (human or JSON)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Points needing extra attention:

  • src/commands/bg/nft/create.ts — multi-flow logic, metadata upload, assetId derivation, and error paths.
  • src/commands/bg/tree/create.ts and src/lib/treeStorage.ts — persistence semantics, duplicate-name/address checks, file IO and network/genesis resolution.
  • src/lib/Context.ts — middleware registration order and Context.umi typing including DasApiInterface.
  • src/prompts/createBubblegumMetadataPrompt.ts — file/path validation and collection verification.
  • DAS-dependent tests are skipped; consider CI test gating or mocks.

Possibly related PRs

Suggested reviewers

  • MarkSackerberg
  • blockiosaurus
  • nhanphan

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning No description was provided by the author; the PR body is empty, making it impossible to assess whether any description exists. Add a meaningful pull request description explaining the changes, features added, and any relevant context for reviewers.
Title check ❓ Inconclusive The title 'Feat/bubblegum create' is partially related to the changeset but is overly broad and lacks clarity about the full scope of changes. Revise the title to be more descriptive of the main change, such as 'Add Bubblegum V2 compressed NFT CLI commands and documentation' or similar, to better convey the comprehensive feature scope.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/bubblegum-create

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.

❤️ Share

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

Copy link
Contributor

@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: 24

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 441337e and 14c7262.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (27)
  • docs/bubblegum/README.md (1 hunks)
  • docs/bubblegum/nft-burn.md (1 hunks)
  • docs/bubblegum/nft-create.md (1 hunks)
  • docs/bubblegum/nft-fetch.md (1 hunks)
  • docs/bubblegum/nft-transfer.md (1 hunks)
  • docs/bubblegum/nft-update.md (1 hunks)
  • docs/bubblegum/quick-reference.md (1 hunks)
  • docs/bubblegum/tree-create.md (1 hunks)
  • docs/bubblegum/tree-list.md (1 hunks)
  • docs/bubblegum/troubleshooting.md (1 hunks)
  • package.json (1 hunks)
  • src/commands/bg/index.ts (1 hunks)
  • src/commands/bg/nft/burn.ts (1 hunks)
  • src/commands/bg/nft/create.ts (1 hunks)
  • src/commands/bg/nft/fetch.ts (1 hunks)
  • src/commands/bg/nft/index.ts (1 hunks)
  • src/commands/bg/nft/transfer.ts (1 hunks)
  • src/commands/bg/nft/update.ts (1 hunks)
  • src/commands/bg/tree/create.ts (1 hunks)
  • src/commands/bg/tree/index.ts (1 hunks)
  • src/commands/bg/tree/list.ts (1 hunks)
  • src/commands/tm/create.ts (1 hunks)
  • src/lib/Context.ts (5 hunks)
  • src/lib/treeStorage.ts (1 hunks)
  • src/lib/util.ts (1 hunks)
  • src/prompts/createBubblegumMetadataPrompt.ts (1 hunks)
  • src/prompts/selectTreePrompt.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-05-28T23:08:38.782Z
Learnt from: tonyboylehub
Repo: metaplex-foundation/cli PR: 18
File: src/commands/core/asset/burn.ts:179-182
Timestamp: 2025-05-28T23:08:38.782Z
Learning: Burned/destroyed assets should not include explorer URLs for the asset itself in CLI output because burned assets no longer exist on the blockchain and would result in "not found" errors when viewed in explorers.

Applied to files:

  • docs/bubblegum/nft-burn.md
  • src/commands/bg/nft/burn.ts
📚 Learning: 2025-03-27T21:39:44.269Z
Learnt from: tonyboylehub
Repo: metaplex-foundation/cli PR: 4
File: src/commands/core/asset/index.ts:1-18
Timestamp: 2025-03-27T21:39:44.269Z
Learning: In this project, base commands rely on OCLIF's built-in help system (--help flag) to display available subcommands rather than implementing custom help text in the run method.

Applied to files:

  • src/commands/bg/nft/index.ts
📚 Learning: 2025-05-28T23:09:37.361Z
Learnt from: tonyboylehub
Repo: metaplex-foundation/cli PR: 18
File: test/commands/core/core.create.test.ts:92-97
Timestamp: 2025-05-28T23:09:37.361Z
Learning: In the Metaplex CLI test suite, the user prefers to avoid redundant assertions in tests when helper functions like `createCoreAsset` and `createCoreCollection` already perform comprehensive validation (exit codes, success messages, ID format validation, and throw errors on failure). The test can rely on the helper functions to fail appropriately rather than duplicating assertions.

Applied to files:

  • src/prompts/createBubblegumMetadataPrompt.ts
📚 Learning: 2025-06-05T23:37:11.513Z
Learnt from: tonyboylehub
Repo: metaplex-foundation/cli PR: 27
File: src/lib/Context.ts:152-153
Timestamp: 2025-06-05T23:37:11.513Z
Learning: In src/lib/Context.ts, the non-null assertions on config.rpcUrl! and config.commitment! are safe because consolidateConfigs() merges DEFAULT_CONFIG (which provides both values) with user config and overrides, only overwriting with non-undefined values, ensuring these properties are always present.

Applied to files:

  • src/lib/Context.ts
🧬 Code graph analysis (8)
src/commands/bg/tree/list.ts (1)
src/lib/treeStorage.ts (1)
  • listTrees (119-127)
src/commands/bg/nft/burn.ts (3)
src/lib/LedgerSigner.ts (1)
  • publicKey (39-41)
src/lib/util.ts (1)
  • txSignatureToString (13-13)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
src/prompts/selectTreePrompt.ts (1)
src/lib/treeStorage.ts (1)
  • listTrees (119-127)
src/commands/bg/nft/update.ts (3)
src/commands/core/asset/update.ts (1)
  • uploadJson (172-182)
src/lib/util.ts (2)
  • txSignatureToString (13-13)
  • chain (123-131)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
src/commands/bg/tree/create.ts (3)
src/lib/treeStorage.ts (3)
  • isValidTreeName (150-153)
  • getNetworkInfo (34-43)
  • saveTree (85-102)
src/lib/util.ts (1)
  • txSignatureToString (13-13)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
src/commands/bg/nft/fetch.ts (2)
src/lib/file.ts (1)
  • ensureDirectoryExists (13-15)
src/lib/util.ts (1)
  • jsonStringify (15-16)
src/commands/bg/nft/transfer.ts (3)
src/lib/LedgerSigner.ts (1)
  • publicKey (39-41)
src/lib/util.ts (1)
  • txSignatureToString (13-13)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
src/commands/bg/nft/create.ts (4)
src/lib/util.ts (2)
  • txSignatureToString (13-13)
  • chain (123-131)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
src/lib/treeStorage.ts (1)
  • getTreeByNameOrAddress (104-117)
src/prompts/createBubblegumMetadataPrompt.ts (1)
  • CreateBubblegumMetadataPromptResult (10-20)
🪛 LanguageTool
docs/bubblegum/README.md

[grammar] ~11-~11: Ensure spelling is correct
Context: ...ures - Massive Cost Savings: After tree rent, minting costs ~0.000005 SOL per N...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
docs/bubblegum/troubleshooting.md

10-10: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


10-10: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


17-17: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


36-36: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


36-36: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


43-43: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


54-54: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


68-68: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


68-68: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


75-75: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


88-88: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


104-104: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


104-104: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


111-111: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


130-130: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


130-130: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


138-138: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


153-153: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


153-153: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


160-160: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


183-183: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


183-183: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


190-190: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


201-201: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


216-216: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


216-216: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


223-223: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


240-240: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


240-240: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


247-247: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


262-262: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


280-280: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


280-280: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


289-289: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


308-308: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


308-308: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


315-315: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


332-332: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


332-332: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


339-339: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


350-350: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


350-350: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


357-357: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


377-377: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


377-377: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


384-384: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


401-401: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


401-401: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


408-408: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


422-422: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


422-422: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


429-429: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


444-444: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


460-460: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


476-476: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


476-476: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


483-483: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


497-497: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


497-497: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


504-504: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


518-518: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


518-518: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


523-523: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


541-541: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


551-551: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


551-551: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


558-558: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


575-575: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


588-588: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


607-607: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


619-619: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


619-619: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


624-624: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

docs/bubblegum/tree-list.md

31-31: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


95-95: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


95-95: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


175-175: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


175-175: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


203-203: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


217-217: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


227-227: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

docs/bubblegum/tree-create.md

64-64: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


64-64: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


116-116: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


116-116: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


152-152: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


261-261: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


261-261: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


280-280: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


280-280: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


294-294: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


306-306: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


319-319: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


329-329: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

docs/bubblegum/nft-fetch.md

31-31: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


95-95: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


95-95: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


175-175: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


175-175: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


203-203: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


217-217: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


227-227: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

docs/bubblegum/nft-burn.md

44-44: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


95-95: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


132-132: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


145-145: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


158-158: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


167-167: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

docs/bubblegum/nft-create.md

31-31: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


95-95: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


95-95: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


175-175: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


175-175: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


203-203: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


217-217: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


227-227: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

docs/bubblegum/README.md

31-31: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


95-95: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


95-95: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


175-175: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


175-175: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


203-203: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


217-217: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

docs/bubblegum/nft-update.md

221-221: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


232-232: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


245-245: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


255-255: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

docs/bubblegum/nft-transfer.md

41-41: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


92-92: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


133-133: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


146-146: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


158-158: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


171-171: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: unit-tests (ubuntu-latest, lts/*)
  • GitHub Check: unit-tests (ubuntu-latest, latest)
  • GitHub Check: unit-tests (ubuntu-latest, lts/-1)
🔇 Additional comments (15)
docs/bubblegum/quick-reference.md (2)

24-29: Verify cost estimates for accuracy.

The cost estimates shown in the table should be verified against current network conditions:

  • 16,384 NFTs: ~0.2 SOL
  • 131,072 NFTs: ~1.5 SOL
  • 1,048,576 NFTs: ~12 SOL

These costs can vary based on rent exemption requirements and network conditions. Consider adding a note that these are approximate estimates and may vary.

#!/bin/bash
# Verify current rent costs for tree creation on devnet
# This script would need the actual tree creation parameters to calculate exact costs

echo "Note: Verify these cost estimates against current Solana rent calculations"
echo "Tree costs depend on:"
echo "- Max depth (affects account size)"
echo "- Buffer size (affects account size)"
echo "- Canopy depth (affects account size)"
echo "- Current rent exemption rates"

Also consider adding a disclaimer:

 | Max NFTs | Approximate Cost |
 |----------|-----------------|
 | 16,384 | ~0.2 SOL |
 | 131,072 | ~1.5 SOL |
 | 1,048,576 | ~12 SOL |

+*Note: Costs are approximate and may vary based on network conditions and exact tree configuration.*

1-472: Excellent quick reference documentation!

This is a well-organized, comprehensive quick reference that covers all major Bubblegum commands, workflows, and use cases. The authority reference table and common workflows sections are particularly valuable for users.

Key strengths:

  • Clear command examples with all options
  • Common workflows provide end-to-end guidance
  • Authority reference table clarifies permissions
  • Use-case-specific examples (music, video, 3D NFTs)
  • Troubleshooting quick fixes
src/commands/tm/create.ts (1)

143-148: Good defensive programming fix!

The guard prevents a potential runtime error when jsonFile.properties.files is undefined or empty. This is a solid improvement that makes the code more robust.

The fix properly uses optional chaining and checks array length before accessing the first element.

src/commands/bg/nft/index.ts (1)

1-57: Well-structured NFT command index!

The implementation provides helpful, comprehensive guidance for users while following the project's established patterns. The recommended workflow section effectively guides users through the typical Bubblegum NFT lifecycle.

Key strengths:

  • Clear subcommand descriptions
  • Step-by-step workflow with command examples
  • Note about Metaplex Core collections requirement
  • References to --help for detailed subcommand documentation
src/commands/bg/index.ts (1)

1-49: Well-structured namespace command with clear guidance.

The Bg command provides a comprehensive overview of Bubblegum functionality with clear examples and workflow guidance. The help text effectively guides users through the complete process from tree creation to NFT operations.

src/commands/bg/tree/index.ts (1)

1-29: Clear and concise tree management command.

The BgTreeCommand provides appropriate help text for tree management operations with relevant examples and clear subcommand descriptions.

src/commands/bg/tree/list.ts (1)

21-54: Well-implemented tree listing command with good UX.

The implementation handles empty states gracefully, formats data appropriately for display, and provides helpful contextual information. The use of emojis to highlight public trees (line 43) and the tip about network filtering (lines 51-53) enhance user experience.

package.json (1)

31-32: No issues found—versions are valid and secure.

The specified package versions exist and are free from known vulnerabilities:

  • @metaplex-foundation/digital-asset-standard-api@^2.0.0 resolves to 2.0.0 (only available version in range)
  • @metaplex-foundation/mpl-bubblegum@^5.0.2 resolves to 5.0.2 (latest in range)
  • No security advisories detected for either package
src/commands/bg/nft/burn.ts (1)

55-70: Burn flow and explorer output align with ownership rules and prior burn guidance.

The burn command correctly:

  • Verifies the signer is either the owner or delegate before calling burnV2.
  • Uses the transaction signature (not the asset address) when generating explorer URLs in both the summary and JSON output, so users don’t get broken asset links after a burn. This matches the earlier guidance to avoid asset explorer URLs for destroyed assets. Based on learnings.

Also applies to: 79-100, 107-123

src/commands/bg/nft/update.ts (2)

301-305: Add error handling around JSON upload spinner in flag-based update path.

In the flag-based path you start a spinner for uploading JSON but don’t handle failures, so on uploadJson rejection the spinner never .fail() and the user gets no clear “upload failed” message:

const jsonUploadSpinner = ora('Uploading JSON file...').start()
newMetadataUri = await uploadJson(umi, newMetadata)
jsonUploadSpinner.succeed('Uploaded JSON')

Mirror the interactive path and fail the spinner on error:

-      const jsonUploadSpinner = ora('Uploading JSON file...').start()
-      newMetadataUri = await uploadJson(umi, newMetadata)
-      jsonUploadSpinner.succeed('Uploaded JSON')
+      const jsonUploadSpinner = ora('Uploading JSON file...').start()
+      try {
+        newMetadataUri = await uploadJson(umi, newMetadata)
+        jsonUploadSpinner.succeed('Uploaded JSON')
+      } catch (error) {
+        jsonUploadSpinner.fail('JSON upload failed')
+        throw error
+      }
⛔ Skipped due to learnings
Learnt from: tonyboylehub
Repo: metaplex-foundation/cli PR: 32
File: src/commands/toolbox/storage/upload.ts:57-63
Timestamp: 2025-07-18T21:03:17.621Z
Learning: In the Metaplex CLI toolbox storage upload command (src/commands/toolbox/storage/upload.ts), failed uploads are intentionally included in the cache with empty URIs using `uri || ''`. This behavior is designed to maintain a complete record of all upload attempts and preserve index mapping between files and cache entries.

30-45: Disregard the original suggestion to pass the full Collection struct.

According to mpl-bubblegum documentation, when calling updateMetadataV2, the collection field should receive the collection's PublicKey (or none()/null to indicate no collection), not the full Collection struct. The practical examples show passing collectionOption?.key ?? null.

The current code does pass collectionData.key (albeit wrapped in some()), which aligns with the correct approach. However, verify whether the some() wrapper is necessary or if the field should receive the key directly.

The original review's suggested fix (passing { key, verified }) would make the code more incorrect, not less.

Likely an incorrect or invalid review comment.

src/lib/Context.ts (1)

2-27: Context Umi extension and DAS/Bubblegum middleware wiring are correct.

Widening umi to Umi & { rpc: DasApiInterface } is safe because dasApi() registers RPC helpers on umi.rpc when the middleware is invoked, and your code unconditionally applies .use(mplBubblegum()).use(dasApi()) before returning. The existing non‑null assertions on config.rpcUrl! and config.commitment! remain valid per prior learnings—consolidateConfigs(DEFAULT_CONFIG, ...) guarantees these fields are always present.

src/commands/bg/nft/fetch.ts (1)

130-231: Fetch flow, error handling, and output look solid

Asset ID validation, DAS RPC calls (with clear errors on unsupported endpoints), download vs. inline display, and JSON output handling are all wired cleanly. The asset/proof summary covers key compression, ownership, royalty, and proof details and should be very useful for users.

Also applies to: 291-324

src/prompts/createBubblegumMetadataPrompt.ts (1)

89-107: Review comment is incorrect—tilde expansion works as implemented

The review's core technical claim is unfounded. You asserted that path.join(os.homedir(), value.slice(1)) fails on POSIX because path.join('/home/user', '/foo') yields /foo, dropping the home directory.

Actual test results show:

  • path.join('/home/user', '/foo') = /home/user/foo
  • For input '~/dir/file': both value.slice(1) and value.slice(2) produce /home/user/dir/file

The current code with value.slice(1) expands tilde paths correctly in the tested environment. The paths are validated, files are checked, and stored paths are normalized properly as implemented.

While extracting tilde expansion into a helper function would reduce duplication across createBubblegumMetadataPrompt.ts and createTokenMetadataPrompt.ts (6 instances total), this is a stylistic refactor, not a correctness fix. The claimed bug does not exist.

Likely an incorrect or invalid review comment.

src/commands/bg/nft/create.ts (1)

141-214: Overall run() orchestration looks solid

Tree selection, owner/metadata resolution, mintV2 invocation, and result reporting (including optional JSON + explorer URL) are wired coherently and defensively. No blocking issues in the main flow.

Copy link
Contributor

@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: 13

♻️ Duplicate comments (2)
src/commands/bg/nft/transfer.ts (1)

57-58: Unify spinner handling to avoid leaving transferSpinner active on errors

You currently start a spinner for fetch and a separate transferSpinner for verification/transfer, but the catch only fails spinner. If an error occurs after transferSpinner starts, it can remain “spinning” while the earlier spinner is flipped to failed, which is confusing UX.

Simplest fix: reuse a single spinner and just update its text across phases:

-    const spinner = ora('Fetching asset and proof data...').start()
+    const spinner = ora('Fetching asset and proof data...').start()
@@
-      spinner.succeed('Asset and proof data fetched')
-
-      const transferSpinner = ora('Verifying ownership...').start()
+      spinner.text = 'Verifying ownership...'
@@
-      if (signerKey !== ownerKey && signerKey !== delegateKey) {
-        transferSpinner.fail('Transfer failed')
+      if (signerKey !== ownerKey && signerKey !== delegateKey) {
+        spinner.fail('Transfer failed')
@@
-      transferSpinner.text = 'Executing transfer...'
+      spinner.text = 'Executing transfer...'
@@
-      transferSpinner.succeed('Compressed NFT transferred successfully!')
+      spinner.succeed('Compressed NFT transferred successfully!')
@@
-    } catch (error) {
-      spinner.fail('Failed to transfer compressed NFT')
+    } catch (error) {
+      spinner.fail('Failed to transfer compressed NFT')
       throw error
     }

This guarantees whichever phase fails/succeeds is always reflected by the same spinner.

Also applies to: 65-65, 67-67, 85-85, 97-97, 117-120

src/commands/bg/nft/create.ts (1)

236-244: Handle RpcChain.Testnet explicitly in getNetworkLabel

NetworkLabel includes 'testnet', and getTreeByNameOrAddress accepts 'testnet' | 'localnet' | ..., but getNetworkLabel only maps Mainnet and Devnet and sends everything else to 'localnet'. If RpcChain has a Testnet variant, testnet will be misclassified as localnet:

  • Tree lookups will read/write under the wrong network label.
  • Error messages will incorrectly say “on localnet” when actually on testnet.

Add an explicit Testnet case:

  private getNetworkLabel(chain: RpcChain): NetworkLabel {
    switch (chain) {
      case RpcChain.Mainnet:
        return 'mainnet'
      case RpcChain.Devnet:
        return 'devnet'
+      case RpcChain.Testnet:
+        return 'testnet'
      default:
        return 'localnet'
    }
  }

If RpcChain genuinely lacks a Testnet variant in your codebase, this can be skipped, but given the existing NetworkLabel and tree storage support, it’s likely intended to be handled.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 14c7262 and 668ad7a.

⛔ Files ignored due to path filters (6)
  • .amman/mpl_account_compression.so is excluded by !**/*.so
  • .amman/mpl_bubblegum.so is excluded by !**/*.so
  • .amman/mpl_core.so is excluded by !**/*.so
  • .amman/mpl_noop.so is excluded by !**/*.so
  • .amman/spl_account_compression.so is excluded by !**/*.so
  • .amman/spl_noop.so is excluded by !**/*.so
📒 Files selected for processing (19)
  • .validator.cjs (1 hunks)
  • src/commands/bg/collection/create.ts (1 hunks)
  • src/commands/bg/nft/burn.ts (1 hunks)
  • src/commands/bg/nft/create.ts (1 hunks)
  • src/commands/bg/nft/transfer.ts (1 hunks)
  • src/commands/bg/nft/update.ts (1 hunks)
  • src/commands/bg/tree/create.ts (1 hunks)
  • src/lib/treeStorage.ts (1 hunks)
  • src/prompts/selectTreePrompt.ts (1 hunks)
  • test/commands/bg/README.md (1 hunks)
  • test/commands/bg/bg.collection.create.test.ts (1 hunks)
  • test/commands/bg/bg.integration.test.ts (1 hunks)
  • test/commands/bg/bg.nft.burn.test.ts (1 hunks)
  • test/commands/bg/bg.nft.create.test.ts (1 hunks)
  • test/commands/bg/bg.nft.transfer.test.ts (1 hunks)
  • test/commands/bg/bg.nft.update.test.ts (1 hunks)
  • test/commands/bg/bg.tree.create.test.ts (1 hunks)
  • test/commands/bg/bgcollectionhelpers.ts (1 hunks)
  • test/commands/bg/bghelpers.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-05-28T23:09:37.361Z
Learnt from: tonyboylehub
Repo: metaplex-foundation/cli PR: 18
File: test/commands/core/core.create.test.ts:92-97
Timestamp: 2025-05-28T23:09:37.361Z
Learning: In the Metaplex CLI test suite, the user prefers to avoid redundant assertions in tests when helper functions like `createCoreAsset` and `createCoreCollection` already perform comprehensive validation (exit codes, success messages, ID format validation, and throw errors on failure). The test can rely on the helper functions to fail appropriately rather than duplicating assertions.

Applied to files:

  • test/commands/bg/bg.nft.update.test.ts
  • test/commands/bg/README.md
  • test/commands/bg/bg.integration.test.ts
  • test/commands/bg/bg.collection.create.test.ts
  • test/commands/bg/bghelpers.ts
  • test/commands/bg/bgcollectionhelpers.ts
  • test/commands/bg/bg.nft.create.test.ts
  • test/commands/bg/bg.nft.burn.test.ts
  • test/commands/bg/bg.nft.transfer.test.ts
  • src/commands/bg/nft/create.ts
📚 Learning: 2025-05-28T23:08:38.782Z
Learnt from: tonyboylehub
Repo: metaplex-foundation/cli PR: 18
File: src/commands/core/asset/burn.ts:179-182
Timestamp: 2025-05-28T23:08:38.782Z
Learning: Burned/destroyed assets should not include explorer URLs for the asset itself in CLI output because burned assets no longer exist on the blockchain and would result in "not found" errors when viewed in explorers.

Applied to files:

  • src/commands/bg/nft/burn.ts
  • test/commands/bg/bg.nft.burn.test.ts
📚 Learning: 2025-05-30T13:45:51.808Z
Learnt from: tonyboylehub
Repo: metaplex-foundation/cli PR: 19
File: src/prompts/pluginInquirer.ts:0-0
Timestamp: 2025-05-30T13:45:51.808Z
Learning: In src/prompts/pluginInquirer.ts, changing `let` to `const` for authority variables in plugin configurations causes errors upon blockchain operations. The variables should remain declared with `let` even though they appear to only be assigned once, due to blockchain-specific requirements.

Applied to files:

  • src/prompts/selectTreePrompt.ts
  • src/commands/bg/nft/create.ts
🧬 Code graph analysis (16)
src/commands/bg/nft/update.ts (2)
src/lib/util.ts (2)
  • txSignatureToString (13-13)
  • chain (123-131)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
test/commands/bg/bg.nft.update.test.ts (1)
test/runCli.ts (1)
  • runCli (6-58)
src/commands/bg/tree/create.ts (3)
src/lib/treeStorage.ts (4)
  • isValidTreeName (147-150)
  • getNetworkInfo (35-44)
  • loadTrees (63-74)
  • saveTree (82-99)
src/lib/util.ts (1)
  • txSignatureToString (13-13)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
src/commands/bg/collection/create.ts (3)
src/commands/cm/create.ts (1)
  • createCollection (368-414)
src/lib/util.ts (1)
  • txSignatureToString (13-13)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
src/commands/bg/nft/burn.ts (3)
src/lib/LedgerSigner.ts (1)
  • publicKey (39-41)
src/lib/util.ts (1)
  • txSignatureToString (13-13)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
test/commands/bg/bg.integration.test.ts (3)
test/runCli.ts (1)
  • runCli (6-58)
test/commands/bg/bghelpers.ts (2)
  • createBubblegumTree (169-169)
  • createCompressedNFT (170-170)
test/commands/bg/bgcollectionhelpers.ts (1)
  • createBubblegumCollection (68-68)
test/commands/bg/bg.collection.create.test.ts (1)
test/runCli.ts (1)
  • runCli (6-58)
src/commands/bg/nft/transfer.ts (3)
src/lib/LedgerSigner.ts (1)
  • publicKey (39-41)
src/lib/util.ts (1)
  • txSignatureToString (13-13)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
test/commands/bg/bghelpers.ts (1)
test/runCli.ts (1)
  • runCli (6-58)
src/prompts/selectTreePrompt.ts (1)
src/lib/treeStorage.ts (1)
  • listTrees (116-124)
test/commands/bg/bgcollectionhelpers.ts (1)
test/runCli.ts (1)
  • runCli (6-58)
test/commands/bg/bg.nft.create.test.ts (1)
test/runCli.ts (1)
  • runCli (6-58)
test/commands/bg/bg.nft.burn.test.ts (2)
test/runCli.ts (1)
  • runCli (6-58)
test/commands/bg/bghelpers.ts (4)
  • createBubblegumTree (169-169)
  • createCompressedNFT (170-170)
  • stripAnsi (174-174)
  • extractSignature (173-173)
test/commands/bg/bg.tree.create.test.ts (1)
test/runCli.ts (1)
  • runCli (6-58)
test/commands/bg/bg.nft.transfer.test.ts (2)
test/runCli.ts (1)
  • runCli (6-58)
test/commands/bg/bghelpers.ts (4)
  • createBubblegumTree (169-169)
  • createCompressedNFT (170-170)
  • stripAnsi (174-174)
  • extractSignature (173-173)
src/commands/bg/nft/create.ts (4)
src/lib/util.ts (2)
  • txSignatureToString (13-13)
  • chain (123-131)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
src/lib/treeStorage.ts (1)
  • getTreeByNameOrAddress (101-114)
src/prompts/createBubblegumMetadataPrompt.ts (1)
  • CreateBubblegumMetadataPromptResult (10-20)
🪛 Biome (2.1.2)
test/commands/bg/bghelpers.ts

[error] 5-5: Unexpected control character in a regular expression.

Control characters are unusual and potentially incorrect inputs, so they are disallowed.

(lint/suspicious/noControlCharactersInRegex)

test/commands/bg/bgcollectionhelpers.ts

[error] 5-5: Unexpected control character in a regular expression.

Control characters are unusual and potentially incorrect inputs, so they are disallowed.

(lint/suspicious/noControlCharactersInRegex)

🪛 markdownlint-cli2 (0.18.1)
test/commands/bg/README.md

9-9: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


21-21: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


33-33: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


45-45: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


61-61: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


71-71: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


82-82: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


120-120: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: unit-tests (ubuntu-latest, lts/-1)
  • GitHub Check: unit-tests (ubuntu-latest, latest)
  • GitHub Check: unit-tests (ubuntu-latest, lts/*)
🔇 Additional comments (11)
.validator.cjs (1)

25-49: I need to verify the remaining two program IDs. Let me search for those:

Based on the verification results, here's the rewritten review comment:

All configurations verified successfully.

✓ All five .so files exist in the .amman/ directory
✓ Program IDs confirmed:

  • BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY is the Metaplex Bubblegum program for creating and managing compressed NFTs
  • noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV is the SPL Noop program used with SPL Account Compression
  • cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK is the SPL Account Compression program ID

The configuration adds proper Bubblegum support with corresponding noop and compression programs. The Metaplex fork provides SDKs for both mpl-account-compression and mpl-noop programs, which are referenced in the validator configuration.

src/lib/treeStorage.ts (1)

1-150: LGTM! Tree storage implementation is well-structured.

The file implements a robust persistence layer for Bubblegum tree metadata with proper error handling, duplicate prevention, and network scoping. The use of os.homedir() (line 2, 47) addresses the past review comment and provides reliable cross-platform home directory resolution.

test/commands/bg/bg.nft.create.test.ts (1)

1-168: LGTM! Comprehensive test coverage for NFT creation.

The test suite effectively covers multiple NFT creation scenarios including royalties (0%, 5%, 100%), symbols, collections, and multiple NFTs. The tests rely on helper functions for validation and include appropriate assertions for transaction outputs.

Based on learnings.

test/commands/bg/bg.nft.update.test.ts (1)

1-218: LGTM! Well-structured test scaffolding for future enablement.

The skipped test suite is properly structured with clear documentation explaining the DAS API dependency. The defensive checks for missing asset IDs and try/catch error handling demonstrate good test design for when these tests are eventually enabled.

test/commands/bg/bg.nft.transfer.test.ts (1)

1-144: LGTM! Test scaffolding ready for DAS-enabled environments.

The skipped test suite properly documents DAS API requirements and includes appropriate defensive checks. The use of generateSigner for creating new owner addresses demonstrates good test design.

test/commands/bg/bg.nft.burn.test.ts (1)

1-168: LGTM! Burn test scaffolding is well-designed.

The skipped test suite appropriately documents DAS API requirements and includes defensive checks for missing asset IDs. The tests verify distinct signatures for sequential burns and include proper output validation. The Explorer URL assertion (line 102) correctly refers to the transaction explorer link, which is appropriate for burn operations.

Based on learnings.

test/commands/bg/bg.collection.create.test.ts (1)

1-159: LGTM! Thorough collection creation test coverage.

The test suite comprehensively covers collection creation scenarios including various royalty percentages (0-100%), validation of required flags, output formatting, and usage instructions. The tests follow established patterns and rely on helper functions for validation.

Based on learnings.

src/commands/bg/nft/update.ts (3)

80-112: Editor fallback is now correctly implemented.

The implementation properly uses findAvailableEditor(['nano', 'vi']) (line 108) to probe for available editors before falling back. This addresses the past review comment and ensures nano is tried first with vi as a fallback.


233-351: Robust metadata update logic with comprehensive error handling.

The updateWithFlags method properly handles:

  • Asset validation and proof fetching
  • Metadata fetch failure scenarios with clear error messages (lines 280-286)
  • Image upload with mime type inference (lines 289-296)
  • Metadata structure preservation including additional files (lines 299-316)

The defensive requirement for all fields when metadata fetch fails (line 281-284) is appropriate, ensuring the NFT isn't updated with incomplete metadata.


353-401: Clean command orchestration with proper validation.

The run method correctly:

  • Routes between interactive editor mode and flag-based updates
  • Validates that at least one update flag is provided (lines 370-374)
  • Generates appropriate transaction explorer URLs
  • Supports JSON output mode
src/commands/bg/tree/create.ts (1)

125-175: Tree name validation and early duplicate checks are solid

Both the wizard and flag flows now:

  • Trim and validate names via isValidTreeName.
  • Check for per‑network duplicate names using getNetworkInfo + loadTrees before starting the transaction.
  • Still leave a secondary safeguard in saveTree if the pre‑check cannot run.

This aligns the --name behavior with the wizard and avoids wasted transactions on duplicate names while keeping error messages consistent. Nicely done.

Also applies to: 270-298

Copy link
Contributor

@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: 8

♻️ Duplicate comments (2)
test/commands/bg/bgcollectionhelpers.ts (1)

71-92: Redundant exit code assertion.

runCli rejects on any non-zero exit code (see test/runCli.ts:33-36), so by the time this helper reaches line 88, code is guaranteed to be 0. The explicit assertion and destructuring are unnecessary.

Based on learnings.

Remove the redundant check:

-    const { stdout, stderr, code } = await runCli(cliInput)
+    const { stdout, stderr } = await runCli(cliInput)
 
     const cleanStderr = stripAnsi(stderr)
     const cleanStdout = stripAnsi(stdout)
     const combined = cleanStdout + '\n' + cleanStderr
 
     const collectionId = extractCollectionId(combined)
 
     if (!collectionId) {
         console.log('Collection creation output:', combined)
         throw new Error('Collection ID not found in output')
     }
 
     // Extract signature
     const sigMatch = combined.match(/Transaction: ([a-zA-Z0-9]+)/)
     const signature = sigMatch ? sigMatch[1] : ''
 
-    expect(code).to.equal(0)
     expect(combined).to.contain('Collection created with Bubblegum V2 plugin')
     expect(collectionId).to.match(/^[a-zA-Z0-9]+$/)
test/commands/bg/bghelpers.ts (1)

167-193: Redundant exit code assertion in NFT helper.

Same pattern: runCli rejects on non-zero exit, making the assertion on line 185 unnecessary.

Based on learnings.

-    const { stdout, stderr, code } = await runCli(cliInput)
+    const { stdout, stderr } = await runCli(cliInput)
 
     const cleanStderr = stripAnsi(stderr)
     const cleanStdout = stripAnsi(stdout)
     const combined = cleanStdout + '\n' + cleanStderr
 
     const assetId = extractAssetId(combined)
     const signature = extractSignature(combined)
 
     // Extract owner (should be the test keypair address)
     const ownerMatch = combined.match(/Owner: ([a-zA-Z0-9]+)/)
     const owner = ownerMatch ? ownerMatch[1] : ''
 
     if (!signature) {
         console.log('NFT creation output:', combined)
         throw new Error('Signature not found in output')
     }
 
-    expect(code).to.equal(0)
     expect(combined).to.contain('Compressed NFT created')
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 668ad7a and 13d7c6e.

📒 Files selected for processing (5)
  • src/commands/bg/collection/create.ts (1 hunks)
  • src/commands/bg/tree/create.ts (1 hunks)
  • test/commands/bg/bg.collection.create.test.ts (1 hunks)
  • test/commands/bg/bgcollectionhelpers.ts (1 hunks)
  • test/commands/bg/bghelpers.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-05-28T23:09:37.361Z
Learnt from: tonyboylehub
Repo: metaplex-foundation/cli PR: 18
File: test/commands/core/core.create.test.ts:92-97
Timestamp: 2025-05-28T23:09:37.361Z
Learning: In the Metaplex CLI test suite, the user prefers to avoid redundant assertions in tests when helper functions like `createCoreAsset` and `createCoreCollection` already perform comprehensive validation (exit codes, success messages, ID format validation, and throw errors on failure). The test can rely on the helper functions to fail appropriately rather than duplicating assertions.

Applied to files:

  • test/commands/bg/bg.collection.create.test.ts
  • test/commands/bg/bgcollectionhelpers.ts
  • test/commands/bg/bghelpers.ts
📚 Learning: 2025-05-30T13:45:51.808Z
Learnt from: tonyboylehub
Repo: metaplex-foundation/cli PR: 19
File: src/prompts/pluginInquirer.ts:0-0
Timestamp: 2025-05-30T13:45:51.808Z
Learning: In src/prompts/pluginInquirer.ts, changing `let` to `const` for authority variables in plugin configurations causes errors upon blockchain operations. The variables should remain declared with `let` even though they appear to only be assigned once, due to blockchain-specific requirements.

Applied to files:

  • src/commands/bg/collection/create.ts
🧬 Code graph analysis (5)
src/commands/bg/tree/create.ts (3)
src/lib/treeStorage.ts (4)
  • isValidTreeName (147-150)
  • getNetworkInfo (35-44)
  • loadTrees (63-74)
  • saveTree (82-99)
src/lib/util.ts (1)
  • txSignatureToString (13-13)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
test/commands/bg/bg.collection.create.test.ts (3)
test/runCli.ts (1)
  • runCli (6-58)
test/commands/bg/bgcollectionhelpers.ts (2)
  • createBubblegumCollection (95-95)
  • stripAnsi (95-95)
test/commands/bg/bghelpers.ts (1)
  • stripAnsi (201-201)
test/commands/bg/bgcollectionhelpers.ts (1)
test/runCli.ts (1)
  • runCli (6-58)
test/commands/bg/bghelpers.ts (1)
test/runCli.ts (1)
  • runCli (6-58)
src/commands/bg/collection/create.ts (3)
src/commands/cm/create.ts (1)
  • createCollection (368-414)
src/lib/util.ts (1)
  • txSignatureToString (13-13)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: unit-tests (ubuntu-latest, lts/*)
  • GitHub Check: unit-tests (ubuntu-latest, latest)
  • GitHub Check: unit-tests (ubuntu-latest, lts/-1)
🔇 Additional comments (3)
test/commands/bg/bgcollectionhelpers.ts (1)

5-32: LGTM! Scanner approach resolves Biome lint error.

The stripAnsi implementation correctly avoids control characters in regex by using a character-by-character scanner, addressing the previous noControlCharactersInRegex lint violation.

src/commands/bg/collection/create.ts (1)

59-84: LGTM! Royalties plugin correctly wired and configured.

All issues from the previous review have been addressed:

  • Line 67: Basis points correctly computed as Math.round(flags.royalties * 100) (percentage → basis points)
  • Line 71: Creator percentage correctly set to 100 (single creator receives full share)
  • Line 83: The plugins array (including optional Royalties) is now passed to createCollection
  • Line 97: Plugin summary dynamically reflects the actual plugins configured

The royalties flag now has the intended on-chain effect.

src/commands/bg/tree/create.ts (1)

24-32: TREE_CONFIG definitions look consistent and well structured

The predefined configurations (depth, buffer, canopy, maxNfts, costs, proofs) are coherent (maxNfts are 2^maxDepth, etc.) and give a clear UX in the wizard. No issues spotted here.

@MarkSackerberg
Copy link
Contributor

Thank you very much!
Looking good from my point of view. Only three nitpicks related to descriptions:

  1. When running just the CLI the bg description is a bit weird
grafik 2. same for "mplx bg". Why is namespace mentioned? grafik 3. I like the description of the workflow, but the "Note" seems to be redundant since it would be step 1 already? grafik

Copy link
Contributor

@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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13d7c6e and 5d95996.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (1)
  • src/commands/bg/index.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: unit-tests (ubuntu-latest, latest)
  • GitHub Check: unit-tests (ubuntu-latest, lts/*)
  • GitHub Check: unit-tests (ubuntu-latest, lts/-1)
🔇 Additional comments (1)
src/commands/bg/index.ts (1)

15-42: Code displays clear help documentation with appropriate workflow structure.

The run() method in src/commands/bg/index.ts provides well-organized help text with:

  • Clear listing of available commands (lines 19–26)
  • Well-structured three-step workflow (lines 28–36)
  • Appropriate "(Optional)" marker for step 1
  • Helpful references to subcommand documentation (lines 38–40)

No issues identified in the code snippet.

Copy link
Contributor

@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: 3

♻️ Duplicate comments (5)
src/commands/bg/index.ts (1)

1-43: LGTM! Description updated per previous feedback.

The description has been shortened to a single sentence, aligning with codebase conventions. The run() method provides a helpful workflow overview for users.

One observation: The PR reviewer noted the CLI description appears "strange" with a namespace mention. If the issue persists, consider whether the summary field (Line 7) is being rendered unexpectedly in the help output—you may need to adjust or remove it depending on how oclif displays both fields.

src/commands/bg/tree/create.ts (2)

253-302: LGTM! Name validation now aligned between wizard and flag paths.

The --name flag handling now properly:

  • Uses flags.name !== undefined to catch empty string edge case (Line 272)
  • Validates with isValidTreeName before transaction (Line 279)
  • Checks for duplicate names early to avoid wasting the transaction (Lines 284-291)

This addresses the previous review feedback about inconsistent validation between wizard and flag flows.


125-150: Network info fetched on every validation keystroke.

The validate callback calls getNetworkInfo(this.context.umi) on each validation run, resulting in an RPC call per keystroke. While functionally correct, this could be optimized by fetching the network info once before the input() call and capturing it in the closure.

This is a minor performance nitpick; current behavior is acceptable.

test/commands/bg/bgcollectionhelpers.ts (1)

43-65: Redundant exit code assertion.

The expect(code).to.equal(0) assertion on line 60 is redundant because runCli already rejects on non-zero exit codes or when stderr contains error indicators. By the time execution reaches line 43, the code is guaranteed to be 0.

Based on learnings.

Apply this diff to remove the redundant assertion:

-    const { stdout, stderr, code } = await runCli(cliInput)
+    const { stdout, stderr } = await runCli(cliInput)

     const cleanStderr = stripAnsi(stderr)
     const cleanStdout = stripAnsi(stdout)
     const combined = cleanStdout + '\n' + cleanStderr

     const collectionId = extractCollectionId(combined)

     if (!collectionId) {
         console.log('Collection creation output:', combined)
         throw new Error('Collection ID not found in output')
     }

     // Extract signature
     const sigMatch = combined.match(/Transaction: ([a-zA-Z0-9]+)/)
     const signature = sigMatch ? sigMatch[1] : ''

-    expect(code).to.equal(0)
     expect(combined).to.contain('Collection created with Bubblegum V2 plugin')
     expect(collectionId).to.match(/^[a-zA-Z0-9]+$/)

     return { collectionId, signature }
test/commands/bg/bghelpers.ts (1)

77-102: Redundant exit code assertion in tree helper.

The expect(code).to.equal(0) assertion on line 96 is redundant because runCli rejects on non-zero exit codes. By the time line 77 resolves, the code is guaranteed to be 0.

Based on learnings.

Apply this diff to remove the redundant assertion:

-    const { stdout, stderr, code } = await runCli(cliInput)
+    const { stdout, stderr } = await runCli(cliInput)

     const cleanStderr = stripAnsi(stderr)
     const cleanStdout = stripAnsi(stdout)
     const combined = cleanStdout + '\n' + cleanStderr

     const treeAddress = extractTreeAddress(combined)
     const signature = extractSignature(combined)

     if (!treeAddress) {
         console.log('Tree creation output:', combined)
         throw new Error('Tree address not found in output')
     }

     if (!signature) {
         console.log('Tree creation output:', combined)
         throw new Error('Signature not found in output')
     }

-    expect(code).to.equal(0)
     expect(combined).to.contain('Merkle tree created')
     expect(treeAddress).to.match(/^[a-zA-Z0-9]{32,44}$/)
     expect(signature).to.match(/^[a-zA-Z0-9]{32,}$/)

     return { treeAddress, signature }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5d95996 and 63ee01c.

📒 Files selected for processing (5)
  • src/commands/bg/index.ts (1 hunks)
  • src/commands/bg/tree/create.ts (1 hunks)
  • test/commands/bg/bgcollectionhelpers.ts (1 hunks)
  • test/commands/bg/bghelpers.ts (1 hunks)
  • test/commands/bg/common.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-05-28T23:09:37.361Z
Learnt from: tonyboylehub
Repo: metaplex-foundation/cli PR: 18
File: test/commands/core/core.create.test.ts:92-97
Timestamp: 2025-05-28T23:09:37.361Z
Learning: In the Metaplex CLI test suite, the user prefers to avoid redundant assertions in tests when helper functions like `createCoreAsset` and `createCoreCollection` already perform comprehensive validation (exit codes, success messages, ID format validation, and throw errors on failure). The test can rely on the helper functions to fail appropriately rather than duplicating assertions.

Applied to files:

  • test/commands/bg/bgcollectionhelpers.ts
  • test/commands/bg/bghelpers.ts
🧬 Code graph analysis (3)
test/commands/bg/bgcollectionhelpers.ts (2)
test/runCli.ts (1)
  • runCli (6-58)
test/commands/bg/common.ts (1)
  • stripAnsi (2-29)
src/commands/bg/tree/create.ts (2)
src/lib/treeStorage.ts (4)
  • isValidTreeName (147-150)
  • getNetworkInfo (35-44)
  • loadTrees (63-74)
  • saveTree (82-99)
src/explorers.ts (1)
  • generateExplorerUrl (34-37)
test/commands/bg/bghelpers.ts (2)
test/runCli.ts (1)
  • runCli (6-58)
test/commands/bg/common.ts (1)
  • stripAnsi (2-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: unit-tests (ubuntu-latest, lts/*)
  • GitHub Check: unit-tests (ubuntu-latest, latest)
  • GitHub Check: unit-tests (ubuntu-latest, lts/-1)
🔇 Additional comments (4)
test/commands/bg/common.ts (1)

1-29: LGTM! Proper ANSI-stripping implementation.

The scanner-based approach correctly handles CSI sequences and avoids the Biome control-character-in-regex error. Centralizing stripAnsi here addresses the duplication concern from past reviews.

test/commands/bg/bgcollectionhelpers.ts (1)

7-19: LGTM! Robust extraction logic.

The multiple-pattern approach handles CLI output variations gracefully.

test/commands/bg/bghelpers.ts (2)

6-45: LGTM! Consistent extraction utilities.

All three extraction helpers use a sensible multi-pattern approach to handle CLI output variations.


165-172: LGTM! Clean exports.

All test utilities are properly exported for reuse across the test suite.

Copy link
Contributor

@MarkSackerberg MarkSackerberg left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Contributor

@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: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63ee01c and 0e138f5.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • .validator.cjs (1 hunks)
  • package.json (1 hunks)
  • src/lib/Context.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use utilities in src/lib/file.ts for file handling operations

Files:

  • src/lib/Context.ts
src/lib/Context.ts

📄 CodeRabbit inference engine (CLAUDE.md)

src/lib/Context.ts: Extend ConfigJson type for new configuration options and update CONFIG_KEYS array when adding new config fields
Use consolidateConfigs function for merging configuration sources

Files:

  • src/lib/Context.ts
🧠 Learnings (4)
📚 Learning: 2025-12-02T16:21:02.107Z
Learnt from: CR
Repo: metaplex-foundation/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-02T16:21:02.107Z
Learning: Applies to src/commands/**/*.ts : Access Umi instance through this.context.umi for blockchain interactions

Applied to files:

  • src/lib/Context.ts
📚 Learning: 2025-12-02T16:21:02.107Z
Learnt from: CR
Repo: metaplex-foundation/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-02T16:21:02.107Z
Learning: Use Metaplex Umi as the core blockchain interaction layer

Applied to files:

  • src/lib/Context.ts
📚 Learning: 2025-12-02T16:21:02.107Z
Learnt from: CR
Repo: metaplex-foundation/cli PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-02T16:21:02.107Z
Learning: Applies to src/lib/Context.ts : Extend ConfigJson type for new configuration options and update CONFIG_KEYS array when adding new config fields

Applied to files:

  • src/lib/Context.ts
📚 Learning: 2025-06-05T23:37:11.513Z
Learnt from: tonyboylehub
Repo: metaplex-foundation/cli PR: 27
File: src/lib/Context.ts:152-153
Timestamp: 2025-06-05T23:37:11.513Z
Learning: In src/lib/Context.ts, the non-null assertions on config.rpcUrl! and config.commitment! are safe because consolidateConfigs() merges DEFAULT_CONFIG (which provides both values) with user config and overrides, only overwriting with non-undefined values, ensuring these properties are always present.

Applied to files:

  • src/lib/Context.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: unit-tests (ubuntu-latest, latest)
  • GitHub Check: unit-tests (ubuntu-latest, lts/-1)
  • GitHub Check: unit-tests (ubuntu-latest, lts/*)
🔇 Additional comments (3)
src/lib/Context.ts (3)

1-2: LGTM!

The imports for mplBubblegum and dasApi/DasApiInterface are correctly added and align with the new package dependencies.

Also applies to: 27-27


160-168: LGTM!

The middleware registration for mplBubblegum() and dasApi() follows the established pattern. Placing dasApi() at the end is appropriate since it augments the RPC layer rather than registering program-specific functionality.


55-55: Error handling already implemented; DAS API availability is documented.

The type augmentation correctly exposes DAS API methods on umi.rpc. Error handling for DAS API calls is already in place:

  • transfer.ts and burn.ts wrap getAssetWithProof in try-catch blocks with error handlers
  • fetch.ts includes specific error messages mentioning "RPC does not support DAS API"
  • update.ts uses .catch() with error handling

The troubleshooting documentation (docs/bubblegum/troubleshooting.md) already addresses DAS API RPC requirements with a dedicated section recommending DAS-enabled providers like Helius and Triton, noting that standard Solana RPCs don't support DAS.

Likely an incorrect or invalid review comment.

Comment on lines +31 to +32
"@metaplex-foundation/digital-asset-standard-api": "^2.0.0",
"@metaplex-foundation/mpl-bubblegum": "^5.0.2",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check latest versions and peer dependencies
echo "=== Checking latest versions on npm ==="
echo "digital-asset-standard-api:"
npm view @metaplex-foundation/digital-asset-standard-api version 2>/dev/null || echo "Failed to fetch"

echo -e "\nmpl-bubblegum:"
npm view @metaplex-foundation/mpl-bubblegum version 2>/dev/null || echo "Failed to fetch"

echo -e "\n=== Checking peer dependencies ==="
echo "mpl-bubblegum peer deps:"
npm view @metaplex-foundation/mpl-bubblegum peerDependencies 2>/dev/null || echo "Failed to fetch"

echo -e "\ndigital-asset-standard-api peer deps:"
npm view @metaplex-foundation/digital-asset-standard-api peerDependencies 2>/dev/null || echo "Failed to fetch"

Repository: metaplex-foundation/cli

Length of output: 362


Peer dependency incompatibility detected.

These packages have conflicting Umi version requirements. The digital-asset-standard-api@2.0.0 requires @metaplex-foundation/umi >= 0.8.2 <= 1, but the project uses Umi v1.4.1, which exceeds the upper bound. The mpl-bubblegum@5.0.2 is compatible, but the DAS API package will cause installation/runtime issues. Downgrade digital-asset-standard-api to a version compatible with Umi v1.4.1, or upgrade the entire Umi ecosystem.

🤖 Prompt for AI Agents
In package.json around lines 31-32, the dependency
"@metaplex-foundation/digital-asset-standard-api": "^2.0.0" declares a Umi peer
that is incompatible with the project's installed @metaplex-foundation/umi
v1.4.1; resolve by either (A) pinning
"@metaplex-foundation/digital-asset-standard-api" to a release that explicitly
supports Umi v1.4.1 (find the correct version in that package's
changelog/registry and update the version specifier), or (B) upgrade the
project's Umi ecosystem (@metaplex-foundation/umi and any related
@metaplex-foundation packages) to a version within the peer range required by
digital-asset-standard-api (then run a full install and smoke tests). After
changing package.json, run npm/yarn install, update lockfile, and run the test
suite to verify no runtime breakage.

@tonyboylehub tonyboylehub merged commit 1405647 into main Dec 22, 2025
4 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Feb 7, 2026
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.

2 participants