86 Commits

Author SHA1 Message Date
David Lau fa556acb93 docs(market): extend signal_type range to 21 and name new types
Backend now accepts signal_type 1-21. Add types 19 (PlatformCallV2),
20 (KOLBuy), 21 (BankerClaims) to the signal-types tables and update the
1-18 -> 1-21 range references.

- src/commands/market.ts: --signal-type help text 1-18 -> 1-21
- docs/cli-usage.md: param note + signal-types table rows 19/20/21
- skills/gmgn-market/SKILL.md: param note, response-field note, signal-types table

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 12:23:36 +08:00
David Lau e68e889c3c feat(chains): support robinhood on kol, smartmoney, signal and cooking
- Remove the robinhood-rejection guards in kol, smartmoney, signal and
  cooking create-token so the CLI matches the backend, which now accepts
  robinhood on these endpoints
- Update --chain / --dex / --raised-token help text and error messages
- cooking: robinhood launchpad uses trench / pons DEXes, native raised
  token only
- Sync SKILL.md files, cli-usage.md and both Readmes with the new chain
  support

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 19:53:25 +08:00
David Lau 06f5a70291 feat(cli): surface a notice when output sanitization strips metadata
Give operators/agents visibility into when the CLI neutralizes suspicious token
metadata in an API response.

- sanitizeForOutputWithCount returns the cleaned value plus the number of altered
  strings; sanitizeForOutput kept as a thin wrapper
- printResult prints "[gmgn-cli] Notice: neutralized N suspicious metadata value(s)"
  to stderr when anything was filtered (extra detail under GMGN_DEBUG); sanitized
  JSON still goes to stdout so piping is unaffected
- document the notice in the gmgn-token SKILL untrusted-data warning

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:24:50 +08:00
David Lau fdc7a3fb16 fix(security): defend against prompt injection via token metadata
Address HackenProof report GMGNWM-143, where attacker-controlled token metadata
could hijack an AI agent driving gmgn-cli into executing an unauthorized trade.
Move guardrails from overridable SKILL.md text into code.

- Add src/sanitize.ts: neutralize prompt-injection framing and hidden/control
  characters in API output (via printResult) and validate create-token metadata
- Add src/confirm.ts: code-enforced human confirmation for financial writes
  (swap, multi-swap, order strategy create, cooking create) — reads a typed
  "yes" from /dev/tty; automation requires GMGN_ALLOW_AUTOMATED_TRADES=1 + --yes
- Harden config.ts: tighten ~/.config/gmgn/.env to 0600 and warn if world-readable
- Update SKILL.md files, Readme.md and Readme.zh.md to document the gate,
  the --yes flag, and untrusted-metadata handling

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 19:24:50 +08:00
David Lau 316840d84d fix(trenches): omit empty launchpad/quote filters as a safety net
Complements the robinhood config: buildTrenchesBody now only sends
launchpad_platform / quote_address_type when non-empty. These fields are
allow-list filters, so an empty array returned an all-empty response. A
future chain missing from the config maps now degrades to API defaults
instead of silently returning nothing (chains should still be added to
both maps).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 20:37:35 +08:00
David Lau f6e3bc4ef7 fix(trenches): add robinhood launchpad/quote-address config
market trenches returned all-empty on robinhood because
TRENCHES_PLATFORMS and TRENCHES_QUOTE_ADDRESS_TYPES had no robinhood
entry, so an empty quote_address_type filtered out every result.

Add robinhood config:
- platforms: noxa, virtuals_v2, bankr, dyorswap, pool_uniswap_v2/v3/v4
- quote_address_type: [11, 20, 24, 12, 0]

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 17:52:43 +08:00
David Lau 2670188acb docs(market): hot-searches default set is 5 chains incl. robinhood
Update hot-searches default-set docs from 4 chains to 5
(sol/bsc/base/eth/robinhood) and fix two stale "7-chain" leftovers.

- skills/gmgn-market/SKILL.md, docs/cli-usage.md: 5-chain default
- src/commands/market.ts: update default-config comment

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 11:49:43 +08:00
David Lau 2d7d0043e6 feat(chain): add robinhood support; disable monad/megaeth/hyperevm/tron
Add robinhood as a supported chain across the CLI and skills, and stop
advertising chains that are not publicly supported yet.

robinhood:
- validate.ts: add to VALID_CHAINS and treat as EVM for address checks
- reject guards for commands that do not support it: track kol,
  track smartmoney, cooking create (mirrors market signal)
- --chain help updated for supported commands (token/portfolio/swap/
  market kline·trending·trenches·hot-searches/track follow-*/order get/
  gas-price); OpenApiClient chain comment
- docs: all SKILL.md, Readme.md, Readme.zh.md (split-out row for
  unsupported commands), cli-usage.md, plugin/marketplace descriptions

disable monad/megaeth/hyperevm/tron:
- comment monad out of VALID_CHAINS (kept for quick re-enable)
- strip these chains from all --chain help, SKILL.md, cli-usage.md
  (hot-searches default now 4 chains: sol/bsc/base/eth)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 22:48:54 +08:00
GMGN.AI b7b6250766 Merge pull request #166 from gina888666/feat/auto-config
feat(config): add gmgn-cli config command for API Key onboarding
2026-07-01 11:21:20 +08:00
gina888666 e3b713b150 fix(config): combine stderr+stdout when detecting auth errors in verify
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 21:12:27 +08:00
gina888666 ac1ea2e336 fix(config): distinguish auth vs network failures in verify step
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 21:08:55 +08:00
gina888666 b782743c8c fix(config): load dotenv before --check so ~/.config/gmgn/.env is read
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 20:12:11 +08:00
David Lau bdf1996e2f feat(market): hot-searches range filters, flattened params, long-form fields
- Add --min-*/--max-* range flags to `market hot-searches` (same metric names as
  market trending); flatten filter fields onto each param (no nested filter object)
- Document response as long-form RankItem fields (server maps shortcodes); drop the
  filter_id field from the documented response shape
- Sync SKILL.md, cli-usage.md, Readme.md, Readme.zh.md, and OpenApiClient types

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 16:02:59 +08:00
David Lau 36771529d7 feat(market): add hot-searches command for hot-search ranking
- Add `market hot-searches` CLI command for POST /v1/market/hot_searches
- Add getHotSearches() + HotSearchesParam/HotSearchesFilter to OpenApiClient
- Support --chain (repeatable), --interval, --limit, --filter, --params override
- Omitting --chain falls back to server default 7-chain config
- Validate --interval against 1m/5m/1h/6h/24h
- Sync SKILL.md, cli-usage.md, Readme.md, Readme.zh.md docs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 11:22:27 +08:00
gina888666 88643ca05c feat(config): add --check flag and improve BEFORE RUNNING ANY COMMAND detection
- Add gmgn-cli config --check: pure local check for GMGN_API_KEY, exit 0 = found, exit 1 = not found
- Update BEFORE RUNNING ANY COMMAND in all 6 SKILL.md files to use --check instead of file path detection
- Add fallback instruction for outdated gmgn-cli versions (unknown option → prompt user to npm install -g gmgn-cli)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 21:39:57 +08:00
gina888666 d701420705 refactor(config): move setup flow into CLI, remove gmgn-config skill
- gmgn-cli config: add multi-language guidance (zh-CN/zh-TW/en) based on system locale
- gmgn-cli config --apply <key>: write GMGN_API_KEY + GMGN_PRIVATE_KEY to ~/.config/gmgn/.env and verify via track follow-wallet
- Delete skills/gmgn-config/SKILL.md — all logic now lives in CLI source
- Simplify BEFORE RUNNING ANY COMMAND in all 6 SKILL.md files to two CLI commands, no duplicated text
- Remove stale First-time setup sections (openssl-based) from all 6 SKILL.md files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 21:39:57 +08:00
gina888666 ec35707d15 feat(config): extract setup flow into gmgn-config skill; simplify CLI output
- Add skills/gmgn-config/SKILL.md with complete setup flow: key pair
  generation, credential writing, and verification via track follow-wallet.
  Verification result is output in the user's conversation language.
- Simplify all 6 SKILL.md BEFORE RUNNING ANY COMMAND to one line that
  delegates to gmgn-config skill, removing duplicate logic.
- Remove locale detection from CLI — output only the link; Agent handles
  all user-facing guidance in the conversation language.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 21:39:57 +08:00
gina888666 e8af4b88b5 feat(config): add gmgn-cli config command for API Key onboarding
Add a new `gmgn-cli config` command that automates Ed25519 key pair
generation for new users. The command reuses an existing keypair.pem if
present, or generates a new one, then outputs a pre-filled API Key
creation link with the public key embedded. Guidance text is output in
the user's system locale (zh-CN, zh-TW, or English).

All 6 SKILL.md files are updated with a BEFORE RUNNING ANY COMMAND
block that detects missing GMGN_API_KEY, triggers gmgn-cli config, and
instructs the Agent to write both GMGN_API_KEY and GMGN_PRIVATE_KEY
into ~/.config/gmgn/.env once the user provides their API Key.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 21:39:57 +08:00
David Lau ed58e67180 docs(market): address PR review — ETH trenches docs + created-unit clarity
ETH trenches/trending (SKILL.md, cli-usage.md):
- Add per-chain --launchpad-platform value tables incl. ETH for `market trenches`
- Add ETH trenches usage examples (all / new_creation / completed)
- Rename "Trending — ETH (No Platform Filter)" to "Trending — ETH by Launchpad
  Platform" and add --platform ETH examples (platform list is now supported)

min_created/max_created units (market trending):
- Clarify that the raw upstream rank interface accepts minutes only; the
  openapi-service does NOT forward this field — it evaluates the age window itself
  (cutoff = now - duration, native for m/h/d), so 6h/7d work via this CLI.
- Note a bare number with no unit suffix is rejected. Update --help text and
  code comment accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 21:15:22 +08:00
David Lau ca55a80d12 feat(market): add min/max range filters to trending command
- Register dynamic --min-*/--max-* flags on `market trending`, mirroring the
  trenches pattern; forwarded as min_<metric>/max_<metric> query params to
  /v1/market/rank
- Add RANK_RANGE_FIELDS covering the 18 numeric metrics plus min/max-created
  token-age windows (m/h/d duration strings, passed through as-is)
- Document range filters and web-client filter aliases in SKILL.md, cli-usage.md,
  Readme.md and Readme.zh.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 20:30:25 +08:00
David Lau 4205bda0af feat(market): add eth chain support for trenches
- Add eth to TRENCHES_PLATFORMS and TRENCHES_QUOTE_ADDRESS_TYPES
- Allow --chain eth for market trenches
- Document eth platforms in CLI help, SKILL.md, and cli-usage.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 16:21:19 +08:00
David Lau 0b140b4a5f feat(market): support 30s kline resolution
- Add 30s to --resolution help text in market kline command
- Sync SKILL.md argument-hint and parameters table
- Sync docs/cli-usage.md resolution values

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 15:00:46 +08:00
GMGN.AI 22d2ad25d5 Merge pull request #167 from GMGNAI/feat-quote-exist-auth
docs(order-quote): order quote now uses normal auth, no private key needed
2026-06-24 16:21:20 +08:00
David Lau 25f4207351 fix(order-quote): switch quoteOrder to exist auth and update CLI help text
- OpenApiClient.quoteOrder now calls authExistRequest instead of authSignedRequest
- Remove all GMGN_PRIVATE_KEY references from the quote command description and options

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 16:13:37 +08:00
gumponchain d34315ce98 fix(bsc): update --platform hint text with new BSC launchpad names
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 11:52:04 +08:00
gumponchain b6c4ff50f2 feat(bsc): add new BSC launchpad platforms
Add cubepeg, likwid, goplus_creator, goplus_skills, openfour after
four_xmode_agent; add flap_stocks and flap_aioracle after flap.
Updates both TRENCHES_PLATFORMS defaults and SKILL.md examples.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 11:47:18 +08:00
deepfeature d5c43eed51 feat(client): send User-Agent header on all API requests
Add a `User-Agent: gmgn-cli/<version>` header (version read from
package.json) to both authExistRequest and authSignedRequest, so
upstream can identify CLI traffic and gateways that reject empty/default
UAs do not block requests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 11:12:10 +08:00
GMGN.AI eda0ac9f52 Merge pull request #158 from GMGNAI/feat-add-follow-tokens-command
Feat add follow tokens command
2026-06-11 11:53:40 +08:00
prodev-nova 1964e927cd fix(cooking): remove deprecated --interval-seconds flag
The interval_seconds param is no longer used. Removed from the CLI
option, params assembly, CreateTokenParams, SKILL.md and cli-usage.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 21:03:23 +08:00
David Lau 0f3d550736 feat(track): add follow-token-groups subcommand
Adds `track follow-token-groups` command that calls
GET /v1/user/follow_token_groups to retrieve a wallet's token follow
group names (id, name, rank) for a given chain.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 17:08:44 +08:00
David Lau ca51ee6a7a fix(swap): add --sell-param and --buy-param to order strategy create
Add CLI options and StrategyCreateParams fields for sell_param and
buy_param, which were documented in SKILL.md but missing from the
source. Reuses the existing TradeParam interface.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 12:04:47 +08:00
David Lau 73d81fa0b2 fix(docs): align slippage to integer 0–100 in all remaining files
Update Readme.md, Readme.zh.md, src/commands/swap.ts and cooking.ts
with integer 0–100 slippage examples (e.g. 30 = 30%) to match the
updated API validation and skill docs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 20:09:27 +08:00
David Lau dd41e38755 feat(track): add follow-tokens subcommand
Adds gmgn-cli track follow-tokens to query a wallet's followed token
list on a given chain via GET /v1/user/follow_tokens.

- Add getFollowTokens() to OpenApiClient (authExist)
- Add track follow-tokens command with --chain, --wallet (required),
  --group-id, --interval, --order-by, --direction, --limit, --cursor,
  --search optional flags
- Update SKILL.md: sub-commands table, rate limit table (weight 3),
  usage examples, options table, response fields
- Update docs/cli-usage.md and Readme.md / Readme.zh.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 18:37:43 +08:00
GMGN.AI ddc025bb97 Merge pull request #152 from prodev-nova/feat/cooking
docs(cooking): worked examples, platform matrix, TradeParam, anti-mev-mode fix
2026-06-04 17:11:17 +08:00
GMGN.AI f96519941e Merge pull request #151 from GMGNAI/feat/smart-trade-order-strategy
feat(swap): add smart_trade order type to order strategy create
2026-06-04 17:04:37 +08:00
prodev-nova fd8a1c4d2a fix(cooking): anti-mev-mode values off / normal / secure
Correct the --anti-mev-mode enum to off / normal / secure
(across cooking.ts, SKILL.md, cli-usage.md, and the TradeParam examples).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 17:00:23 +08:00
prodev-nova 0282a9a09b docs(cooking): add worked examples, platform matrix, TradeParam; fix anti-mev-mode values
- Fix --anti-mev-mode values: off / jito / 0slot (was normal / secure)
- Add platform capability matrix (per-platform fields, bundle/sniper
  wallet limits, cashback/mayhem support)
- Add Quote Token conversion guidance for --raised-token launches
- Add full TradeParam field table for --buy/sell-trade-config
- Add two full worked CLI examples (Pump bundle+sniper+autosell+buyback,
  FourMeme fee-split + USDT raise token)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 16:34:27 +08:00
David Lau 17d7de841e feat(swap): add smart_trade order type to order strategy create
- Add --order-type smart_trade and --sub-order-type mix_trade support
- Make --check-price optional (required for limit_order, omitted for smart_trade)
- Add --open-price, --quote-investment, --condition-orders options
- Update StrategyCreateParams interface: check_price optional, add condition_orders/quote_investment
- Update SKILL.md and cli-usage.md docs with smart_trade examples and parameter table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 14:27:01 +08:00
prodev-nova 134a11593c fix(cooking): correct pump fee-share provider values
The pump_fee_share_list `provider` field rejected "wallet" with
"unknown provider: wallet". Valid values are solana / twitter / github.

- OpenApiClient: update PumpFeeShareInfo provider comment
- SKILL.md / cli-usage.md: provider enum solana / twitter / github,
  username is a SOL address when provider = solana

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 20:50:54 +08:00
David Lau a161bd733d fix(portfolio): rename activity type values to camelCase
- Rename transfer_in/transfer_out → transferIn/transferOut in portfolio activity --type filter
- Update SKILL.md and portfolio.ts help text to reflect new enum values

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 16:27:53 +08:00
prodev-nova ec5719ce12 feat(cooking): complete create params, fix Flap split & pump provider
- Add advanced create_token params: description, fee, dev_max_fee_per_gas,
  raised_token, anti_mev_mode, dev_wallet_bps, source, approve_vision
- Add fee-share configs: pump_fee_share_list, bags_fee_share_list,
  flap_rate_conf (+ V6 buy/sell_tax_rate), fourmeme_rate_conf
- Add multi-wallet buy: buy_wallets, snip_buy_wallets, interval_seconds
- Add CondMarket TradeParam (buy/sell_trade_config) and auto-sell sell_configs
- Pump modes: --is-mayhem / --is-cashback / --is-buy-back (Agent Auto Buyback)
- Fix Flap split_conf field: split_bps -> bps
- Pump fee-share provider: github / wallet only (drop twitter)
- Drop is_revoke_fee_auth and is_agent/agent_bps
- Sync SKILL.md and cli-usage.md with all flags, JSON schemas,
  guided launch flow (platform-aware advanced options) and percentage-based
  fee-share UX

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 14:04:35 +08:00
David Lau 412531f04e fix(signer): percent-encode query params in buildMessage to match server
Server's CanonicalQueryString uses url.QueryEscape on all keys and values,
but buildMessage was using raw string interpolation. Cursor values (base64)
contain +, =, / which diverge under encoding — causing 401 on all paginated
signed requests (e.g. portfolio holdings page 2+).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 19:32:54 +08:00
gumponchain d00a1d3ce3 fix(swap): preserve Solana address case when normalizing wallet addresses
toLowerCase() breaks base58 Solana addresses which are case-sensitive.
Only apply lowercase normalization on EVM chains (bsc/base/eth).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 12:06:22 +08:00
deepfeature 6f0d4464e2 Merge pull request #138 from GMGNAI/feat/wallet-holdings-critical-auth
breaking(portfolio): upgrade wallet_holdings to critical auth
2026-05-26 19:48:09 +08:00
David Lau 48e7346d8e fix(swap): normalize wallet addresses to lowercase before API call
Ensures from_address and multi-swap accounts are lowercased at the CLI
boundary to prevent case-sensitivity issues on the API side.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 13:55:25 +08:00
deepfeature 2f76b375c5 breaking(portfolio): upgrade wallet_holdings to critical auth
Align CLI with server-side breaking change: GET /v1/user/wallet_holdings
was upgraded from authExistMW to authCriticalMW on the openapi-service.

- OpenApiClient: switch getWalletHoldings to authSignedRequest
- SKILL.md: split rate-limit table by auth level; update Notes section
- CLAUDE.md: move portfolio holdings from Normal to Critical auth row

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 11:19:30 +08:00
David Lau d51f1c49d3 refactor(auth): rename normalRequest/criticalRequest; align auth terminology
- Rename private methods: normalRequest → authExistRequest,
  criticalRequest → authSignedRequest in OpenApiClient.ts (both gmgn-skills
  and scripts/src)
- Replace all "normal auth" / "critical auth" labels across SKILL.md files,
  cli-usage.md, Readme.md, and CLI command descriptions with:
    exist auth  — API Key only, no private key required
    signed auth — API Key + GMGN_PRIVATE_KEY signature required
- Add explicit "requires GMGN_PRIVATE_KEY" notes to signed-auth commands
  and "API Key only" notes to exist-auth commands throughout docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 17:45:10 +08:00
David Lau 15872373cd feat(swap): promote gas-price to top-level command, support all chains
- Move `order gas-price` → `gmgn-cli gas-price` (top-level command)
- Expand chain support to include sol alongside eth / bsc / base
- Reorder commands in swap.ts to match SKILL.md: swap → multi-swap →
  order quote/get → gas-price → order strategy create/list/cancel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 12:05:39 +08:00
GMGN.AI 2b33ee666c Merge pull request #125 from GMGNAI/feat/gas-level-remove-auto-tip-fee
Feat/gas level remove auto tip fee
2026-05-18 19:13:53 +08:00
GMGN.AI 2b6c220581 fix(trenches): fix apiFieldToCliKey to match Commander.js camelCase for numeric suffixes
Commander.js removes hyphens before digits (e.g. -24h → 24h) but does not
uppercase them. The previous regex only handled -[a-z] patterns, leaving
a residual hyphen: minVolume-24h instead of minVolume24h.

This caused all 24h-suffixed filter flags to be silently ignored:
--min-volume-24h, --max-volume-24h, --min-net-buy-24h, --min-swaps-24h,
--min-buys-24h, --min-sells-24h.

Fix: add .replace(/-(\d)/g, '$1') to strip hyphens before digit sequences.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 19:53:45 +08:00