Commit Graph
457 Commits
Author SHA1 Message Date
GMGN.AIandGitHub bd5faae13a Merge pull request #257 from dylanzhong662-max/feature-all-chains-endpoint-coverage
fix(cli): support avalanche, defer trenches defaults to the server, sync chain docs
2026-10-29 17:14:31 +08:00
zhongsongzhi-cmykandClaude 9657e45393 fix(cli): support avalanche and defer trenches defaults to the server
- validate: add avalanche to VALID_CHAINS / EVM address check / callout
  chains / V1 smart_trade restriction
- trenches: drop the stale client-side quote_address_type table (missing
  RWA 26, no entries for the 5 newer chains); the server now fills
  launchpad_platform and quote_address_type for every chain
- market signal: accept base / eth / arbitrum / avalanche (server
  TokenSignalChains); hyperevm is rejected upstream
- strategy cancel: document --order-type smart_trade_v2
- help text, SKILL.md, cli-usage, Readme: chain lists and trenches
  default-platform table synced with gmgn-openapi-service

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-29 16:17:13 +08:00
GMGN.AI a0c9e84450 1.7.0 v1.7.0 2026-10-28 11:46:18 +08:00
GMGN.AIandGitHub 9ba0e97f9b Merge pull request #255 from dylanzhong662-max/fix-config-env-precedence
fix(config): make project .env override global config as documented
2026-10-28 11:44:58 +08:00
zhongsongzhi-cmykandClaude 227011b834 fix(config): make project .env override global config as documented
src/config.ts loaded ~/.config/gmgn/.env with override:true and then the
cwd .env without override, so the global file always won — the opposite
of what Readme, Readme.zh, CLAUDE.md and the gmgn-cooking skill promise.
A user who updated GMGN_PRIVATE_KEY in a project .env kept signing with
the stale global key and got AUTH_SIGNATURE_INVALID.

Load the cwd .env first and the global file second, both without
override, so precedence is shell env > project .env > global .env.
`config --check` reuses the same loader instead of its own copy.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-25 23:04:53 +08:00
GMGN.AI b0f4ac7135 1.6.9 v1.6.9 2026-10-23 11:37:29 +08:00
GMGN.AIandGitHub ad51d9ebf0 Merge pull request #253 from dylanzhong662-max/docs-remove-fee-bounds
docs(swap,cooking): drop per-chain fee bounds from CLI help and docs
2026-10-23 11:36:54 +08:00
zhongsongzhi-cmykandClaude 35c5e05cbc docs(swap,cooking): drop per-chain fee bounds from CLI help and docs
gmgn-openapi-service no longer clamps priority_fee / tip_fee / gas_price /
max_fee_per_gas / max_priority_fee_per_gas to per-chain min/max (openapi MR
!114, merged 2026-10-19). Fees are forwarded as-is and the trade backend
enforces its global ceilings; the server only rejects non-numeric or
negative values, plus BSC "0" for the EIP-1559 fields.

Remove the stale "≥ X" / "Clamped per chain minimums" wording from swap.ts
option help, gmgn-swap and gmgn-cooking SKILL.md, and docs/cli-usage.md, and
replace the gmgn-swap "EIP-1559 minimum values" note with the current rules.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-23 11:28:48 +08:00
GMGN.AI 8422988cce 1.6.8 v1.6.8 2026-10-21 11:41:22 +08:00
GMGN.AIandGitHub 4a100e663f Merge pull request #245 from dylanzhong662-max/remove-interactive-trade-confirmations
fix(cli): remove interactive trade confirmations
2026-10-21 11:40:40 +08:00
GMGN.AIandGitHub 402671e2ab Merge pull request #251 from Edwardchennnn/feat/callout-score-skill
feat(callout-score): add gmgn-callout-score skill
2026-10-16 18:00:53 +08:00
chenjunfengandClaude Opus 5 f960a67f1d fix(callout-score): pin the half-threshold arithmetic and five undefined edges
Two further self-review passes: one for internal consistency, one simulating
execution against boundary inputs.

The consequential one is the "at least half" test, which is what separates
回避 from 不可跟 and was left to the reader with a sample size of three. Now
stated as arithmetic: counting x 2 >= sampled, so 2 of 3, 2 of 4, 3 of 5, and
one of three does not fire it.

Five edges that had no defined answer:

- Two handle candidates with equal total_calls: break on the more recent newest
  call, then on richer wallet_tags, then lexicographically, and say which.
- Two chains with equal call counts: score the one whose newest call is more
  recent, name the other with its count.
- next_page_token expiring part-way through paging: stop and score what was
  fetched, never restart or retry, and report the coverage as a floor.
- created-tokens truncation: the tokens array caps at 100 while
  inner_count + open_count is the real total, so an empty intersection is only
  evidence at full coverage. A caller measured at 1,551 launches with 101 rows
  (6.5%) had an empty intersection that proves nothing — report the launch
  record and say the self-launch check could not be completed. Same when
  created_token_count > 0 but tokens comes back empty.
- Fields read now states its scope: derived quantities are not API fields,
  fields named only as empty or excluded are not read, and error-response keys
  are read only on 429/403. The common name fields the cleaning rule references
  (name / nick_name / twitter_name / twitter_username) are now listed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-16 17:40:47 +08:00
chenjunfengandClaude Opus 5 384765662e fix(callout-score): state the two time units, add the missing verdict row
Four findings from a final self-review of the whole file.

Time units, the consequential one: callout record's create_time is Unix
milliseconds while portfolio activity's timestamp is Unix seconds, and every
conduct quantity — lead_seconds, minutes_to_first_sell, the window ages — is a
difference across that boundary. The file never said so. A missed factor of
1000 does not throw; it silently yields either an absurd number or a plausible
wrong one. Also warns against taking .timestamp() of a naive UTC datetime,
which is read as local time and shifts every age by the machine's offset.

Step 5 had no row for "selection axis not issued", which is the common case
rather than the exception — only two of the tested callers could issue a
comparable hit rate. Added, with the asymmetry stated: conduct alone can
condemn, because a measured sell into one's own call needs no comparison, but
it can never endorse, because "did not dump on the three sampled coins" is not
a reason to follow someone whose record cannot be compared.

post_call_net was labelled "the primary quantity" while clauses (a) and (b) do
not use it — the same kind of wording contradiction already caught once in
review. Now stated as what the report reads from and what Credit is decided on,
with the reason it stays out of (a) and (b).

Name-field cleaning now covers twitter_username / twitter_name / common.name /
nick_name explicitly, and the title line is pinned to the handle as the user
typed it or the truncated address, never a name read back from market search —
closing the last position where a forged heading could land.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-16 17:20:02 +08:00
chenjunfengandClaude Opus 5 6a2eb4c60b fix(callout-score): sanitise caller-controlled text, and make the conduct tiers consistent
Two review findings.

Injection: the skill told the reader to print `token_symbol` verbatim inside
「」, which is not escaping — the field can carry the closing bracket itself, a
line break that starts a fake 跟单建议 heading, a markdown link, or a `|` that
tears open a table row. That is the hole gmgn-wallet-analysis already closes in
clean_text(), reopened here. The rule now spells out the same cleaning in five
ordered steps: truncate to 8x the cap, map Cc/Cs/Co/Zl/Zp and Cf except U+200D
to spaces, delete [ ] < > * ` plus `|` and the 「」 wrapper characters, break
`://` / `www.` / email `@`, cap at 32 characters. If cleaning leaves only
spaces, no symbol is printed and the contract address stands alone. Also states
explicitly that call_thesis never reaches the reader in any section.

Logic: the Systematic tier defined two clauses, but a trailing sentence listed
`exit_vs_call_price > 1` as a third qualifying condition for the same
"at least half" test, while the Frequent tier used it as a severity marker —
the two readings disagreed on a contract with a non-applicable ratio and
exit above the call price. The tier now enumerates (a) and (b) as its only
clauses, states that a non-applicable ratio can satisfy (b) but never (a) and
stays in the denominator either way, and keeps `exit_vs_call_price > 1` as a
severity marker inside Frequent and never a Systematic trigger.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-16 17:13:00 +08:00
chenjunfengandClaude Opus 5 2f6828533d feat(callout-score): add gmgn-callout-score skill
Scores one call-out wallet on two axes — selection (do the coins he calls
actually run) and post-call conduct (does he sell into the attention his own
call created) — and returns a follow / don't-follow verdict.

The conduct axis is proved from the caller's own per-trade ledger rather than
from any self-reported field: for each sampled contract it compares the first
buy, the call timestamp and every sell after it, so "he sold into his own call"
is a measured sequence rather than an inference.

gmgn-callout keeps every raw read of the callout endpoints; this skill calls
the same commands and adds a verdict on top, the same split as gmgn-dev-score
has with gmgn-portfolio.

Every threshold was calibrated against eleven call-out wallets measured live on
SOL and BSC on 2026-10-15 and 2026-10-16.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-16 16:52:07 +08:00
GMGN.AIandGitHub a322ae5d8b Merge pull request #249 from dylanzhong662-max/fix-robinhood-quote-address-type-26
fix(market): add quote address type 26 to robinhood trenches defaults
2026-10-16 14:20:09 +08:00
zhongsongzhi-cmykandClaude 866876a65e fix(market): add quote address type 26 to robinhood trenches defaults
Align with the server-side default (gmgn-openapi-service trenchesDefaults["robinhood"].QuoteAddressType)
that went live in July. Without it, default trenches queries on robinhood miss tokens paired
with quote type 26 (RWA pools).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-16 14:16:15 +08:00
GMGN.AIandGitHub 24ccedc502 Merge pull request #244 from Edwardchennnn/feat/wallet-analysis-self-launch-share
feat(wallet-analysis): veto a wallet whose profit is mostly its own launches
2026-10-15 16:47:51 +08:00
GMGN.AI 4a13174297 1.6.7 v1.6.7 2026-10-15 16:47:31 +08:00
GMGN.AIandGitHub d40eb71b6a Merge pull request #248 from dylanzhong662-max/feat-cli-callout-read
feat(cli): add callout record / global read commands and gmgn-callout skill
2026-10-15 16:46:38 +08:00
GMGN.AIandGitHub 6fc9961c6a Merge pull request #247 from dylanzhong662-max/fix-ratelimit-banned-max-plan-upgrade
feat(cli): explain RATE_LIMIT_BANNED_MAX and PLAN_UPGRADE_REQUIRED
2026-10-15 16:46:22 +08:00
zhongsongzhi-cmykandClaude fb65aa487a feat(cli): add callout record / global read commands and gmgn-callout skill
gmgn-openapi-service already exposes two read-only call-out routes on
openapi.gmgn.ai behind the normal API-key auth, so the CLI can reach them
without the separate papi AK/SK channel:

- gmgn-cli callout record -> POST /v1/callout/get_record: one wallet's
  call-outs over the last 30 days plus the 7d/30d summary card
- gmgn-cli callout global -> POST /v1/callout/global: multi-chain feed with
  only-kol / only-declaration / only-safe / holding-range filters

Both are weight 3. Chains are validated against the service's 13-chain
callout list, kept separate from the tradable VALID_CHAINS. Limit is checked
against the service's 0-50 bound; holding bounds are sent as decimal
strings. /v1/callout/token is deliberately left out: the service removed it
(prod returns 404) and publishing call-outs is not part of OpenAPI.

Docs: new skills/gmgn-callout/SKILL.md, CLI reference in docs/cli-usage.md
and both Readmes, routing rows in CLAUDE.md.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-15 16:32:21 +08:00
chenjunfengandClaude Opus 5.5 3d72ef4598 fix(wallet-analysis): never looser than main, even when the wash check is blocked
- A wash check blocked by an unreadable launch list is now an open
  question ranked below every measured G1 failure (thin sample, one
  coin, concentration, count veto), so it can only stop a pass.
- conviction_share uses main's exact arithmetic, non-finite rows
  included, so a never-launched wallet reads exactly as on main.
- Stale comments from the removed logic fixed; caps raised so they never
  shorten ordinary tags or funding labels; doc wording clarified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 16:22:03 +08:00
chenjunfengandClaude Opus 5.5 662adc8bf1 refactor(wallet-analysis): the self-launch attribution only adds a veto
Narrow the rule after repeated review findings: proving that a
launcher's profit is NOT from its own launches needs the whole book, a
complete launch list and clean timing, and every inconsistency there
became a false pass. The attribution now only adds a veto.

- Remove every path that lifted the launch-count veto (proven / partial
  clear states, the "every listed launch seen" proof, verdict row 10c,
  the related archetype tags). A launcher by count is vetoed exactly as
  on main; a clear reading is printed for reference only.
- conviction_share is computed on the open book exactly as on main. The
  wash test uses the lower of that share and the share without own and
  suspected launches, so it can never refute a tag main would not.
- Malformed row addresses are left out instead of counted external.
- "Not measured" in G1 names an unreadable launch list when that is the
  cause; ZH gap wording fixed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 16:09:16 +08:00
chenjunfengandClaude Opus 5.5 7fa9074451 fix(wallet-analysis): close the remaining self-launch bypasses
- A page longer than the row cap is a cut-off, never a complete book.
- A token appearing more than once counts once, at its largest gain.
- Launch-list and creator addresses are stripped and must be
  well-formed; a junk creator leaves a token a suspect.
- With an incomplete list, unknown buy timing or a buy within 5 s
  either side of creation is a suspect.
- A clear reading is proven only when the whole book was read or every
  listed launch already appeared; until then paging continues, for any
  launcher. Unproven: launcher by count gets G1 ⚪, others pass with a
  "not verified" line.
- A blocked wash check never softens a launcher's count veto; an
  unproven clear reading never prints the launcher headline over
  another G1 failure.
- No paging without an own-token set, no suspect lookup on an
  unmeasured sample, a falsy cursor ends paging.
- Malformed or deeply nested JSON on the new routes becomes a gap.
- Gap lines keep "[gmgn-cli]" but never form a link; e-mail addresses
  in third-party text are broken.
- ZH wording aligned with the existing 先别动 / 发 terms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 15:45:02 +08:00
chenjunfengandClaude Opus 5.5 76abc2ca9b fix(wallet-analysis): only a fully read book can lift the count veto
- A clear reading on a truncated book no longer lifts a launcher's
  count veto: unread older positions can hold any amount of own-token
  gains offset by losses the all-time net cannot reveal, so no sample
  ratio can bound them. Such a launcher now gets G1 ⚪ and
  "HOLD OFF · its launches look minor, but its older record was not
  read"; a clear reading keeps paging toward the book's natural end.
- Tag keys are stripped again, so " wash_trader " still matches.
- A wash check blocked by an unreadable launch list advises re-running,
  not configuring a key.
- An empty own-token set while the wallet launched is "not measured".
- stats_30d catches a CLI timeout as a gap.
- Docs: verdict row 10c, sixteen outcomes, paging and timeout wording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 15:05:02 +08:00
chenjunfengandClaude Opus 5.5 76f9408198 fix(wallet-analysis): harden the self-launch rule after review
- Sample floor: the share needs at least 3 winning positions.
- An unconfirmed range never softens a measured G1 failure; a launcher
  by count keeps its count veto unless attribution clears it.
- The launch list counts as complete only when it names
  max(inner+open, created_token_count) tokens; a missing total is never
  complete. A clear reading on a truncated sample only lifts the count
  veto once it covers half the all-time realized profit.
- Paging cut-offs (failed call, timeout, malformed or empty-but-continuing
  page, repeated cursor) are never read as the end of the book.
- Own and suspected launch tokens are left out of conviction_share, and
  an unreadable launch list can no longer let it refute a wash tag.
- EVM addresses compared case-insensitively, base58 exactly.
- stats_30d moves ahead of the self-launch tier (G2 reads it).
- Third-party text (symbols, X name/handle, funding label, tags, gap
  lines) is cleaned of line breaks, control/format characters and
  markup before printing; ordinary text prints unchanged.
- Malformed API shapes and non-finite numbers no longer crash the report.
- Docs: holdings weight 2, verdict table order and rows, call order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 14:08:52 +08:00
zhongsongzhi-cmykandClaude c4259c267e feat(cli): explain RATE_LIMIT_BANNED_MAX and PLAN_UPGRADE_REQUIRED
The OpenAPI service now returns two new rate-limit codes:

- RATE_LIMIT_BANNED_MAX (429): the ban has reached its 5-minute ceiling
  because requests kept arriving during the cooldown. The CLI now says to
  stop all requests until reset_at instead of the generic retry hint.
- PLAN_UPGRADE_REQUIRED (403): the route's weight exceeds the plan's bucket
  capacity (e.g. Free on swap / quote / follow-wallet), so it can never
  succeed. The CLI surfaces the upgrade message; previously 403s were
  printed without it.

Skills and docs/cli-usage.md tell agents not to retry either code, and the
stale rate=10/capacity=10 note is replaced by the plan tiers.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-15 11:57:18 +08:00
zhongsongzhi-cmyk b1c6bd095c fix(cli): remove interactive trade confirmations 2026-10-14 17:50:02 +08:00
chenjunfengandClaude Opus 5.5 ce145159f0 feat(wallet-analysis): judge a launcher by where its profit came from, not by how many tokens it launched
G1 decided "launcher" by a count (created_token_count > half of
token_num), which says nothing about where the money came from. It now
attributes positive realized gains by token contract for any wallet that
launched a token: lo = confirmed own share, hi = own + suspect share.
lo >= 50% vetoes, a range straddling 50% holds G1 open, hi < 20% lifts
the count veto. Closed positions are read via holdings sorted by
last_active_timestamp and also feed the wash-trade conviction share.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:45:47 +08:00
zhongsongzhi-cmykandClaude b5cca5cc6d fix(confirm): address review findings on the trade confirmation gate
- Escape every non-visible character in the confirmation block by Unicode
  property (\p{C}, \p{Z}, \p{M}, Default_Ignorable_Code_Point, U+2800) instead
  of a hand-kept list; soft hyphens, Hangul fillers, tag characters etc. can no
  longer make a fee-share username look like another.
- Fold only fields a command declares as blobs (cooking --image); key fields
  are always shown in full. multi-swap and strategy create/cancel now validate
  their addresses like swap does.
- Judge protections on the values being sent (min_output 0, anti_mev_mode off,
  expire_in 0 now warn), and reject NaN / Infinity / negative --slippage,
  --percent, --expire-in, --dev-wallet-bps before they reach a signed body.
  --gas-price is converted gwei→wei exactly (was float-rounded).
- Total tip / priority fees per container, including sell_param / buy_param and
  buy_trade_config / sell_trade_config, so section ① no longer understates.
- Anti-MEV warning only where accurate: none for swap (documented default on),
  "server default applies" for multi-swap / strategy on sol / bsc / eth, and
  an explicit "off" warning for cooking create on sol.
- Bind confirmations to method + route + query + body, and require one for
  every signed non-GET request (deny by default) instead of a path allowlist.
- In interactive mode write the block to /dev/tty, so the process launcher
  cannot filter what the human reads before typing yes.
- Display: swap --percent shows the bps line first and marks input_amount as
  ignored; strategy amount_in_percent and multi-swap per-wallet bps show "% of
  balance"; multi-swap shows the wallet count.
- Group risky leaves in linear time; load credentials before confirmation so a
  missing key is reported before the user types yes.
- Sync skills/gmgn-swap, skills/gmgn-cooking and docs/cli-usage.md with the
  gated order strategy cancel and its --yes flag.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-29 16:45:26 +08:00
zhongsongzhi-cmykandClaude 2d6f565590 fix(confirm): derive trade confirmation from the signed body and verify it before signing
The confirmation prompt for swap / multi-swap / order strategy create /
cooking create printed a hand-written summary that omitted fields still sent
and signed: tip / priority fees, gas, min_output_amount, condition_orders
(auto TP/SL), check_price, sell_param / buy_param, buy_wallets,
snip_buy_wallets, dev_wallet_bps and the creator fee-share lists. A
prompt-injected agent could hide any of them behind a normal-looking summary.

Layer 1 - confirmTrade now takes the request object itself, flattens it to
every leaf field and prints four sections: key fields; risky fields, each
with a plain-language consequence and never folded; protections that are NOT
set (no min output, no slippage limit, anti-MEV not requested, no expiry);
and every remaining field. Section 4 is "whatever 1-3 did not print", so new
parameters appear automatically. Long strings (e.g. --image base64) fold to
<string, size, sha256>, gas_price is echoed in gwei, and command-side totals
are added (extra fees, total buy spend across dev / bundle / snipe wallets,
creator-fee recipients). Control, C1, bidi and zero-width characters are
escaped so a value cannot rewrite or hide lines.

Layer 2 - confirmTrade records the canonical-JSON sha256 of the confirmed
body; authSignedRequest refuses to sign swap, multi_swap, strategy/create,
strategy/cancel and create_token unless the exact body being signed was
confirmed, consuming the confirmation so it authorizes one signature. This
blocks a new command that forgets confirmTrade and a body mutated after
confirmation.

Layer 3 - order strategy cancel gets the same gate and --yes flag; cooking
create JSON options fail with a clear error instead of a raw SyntaxError;
Readme / Readme.zh now state the confirmation block is the complete request
body about to be signed.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-29 15:38:44 +08:00
GMGN.AIandGitHub 4575ef539e Merge pull request #239 from Edwardchennnn/feat/gmgn-wallet-review
feat(wallet skills): add gmgn-wallet-review and gmgn-wallet-style, route both
2026-09-28 20:59:12 +08:00
chenjunfengandClaude Opus 5.5 e6574f95e4 feat(wallet skills): add gmgn-wallet-review and gmgn-wallet-style, route both
Adds two wallet skills and wires them into the routing.

gmgn-wallet-review (new): a glanceable trading portrait of one wallet. It
gives one style tag, a one-sentence recap of the record and a one-sentence
next step, for 1D / 7D / 30D / all, voiced 你 for the user's own wallet and
他 for anyone else's. It is a single SKILL.md with read-only gmgn-cli
portfolio calls, fixed rules and closed sentence templates. It covers SOL and
all 8 EVM chains, probing the chain for a 0x address.
- The recap names only problems in the trader's own trading: fees, holding
  time, loss cutting, sizing, adds, open bags, return trend and launches.
  Follower count appears only as a warning in visitor advice.
- Discipline rules from holdings, checked on 22 wallets: losers held 2x
  longer, losers sized 1.5x bigger (median per-buy ticket), adds 10 points
  worse, and 3+ open bags down 50% with $50+ cost.
- The next step never repeats the recap's problem.

gmgn-wallet-style (new to the repo): the full style tagging, with a 20-cell
title, speed subtitle and badges, each tag stating its triggering number.
It has a nine-chain allowlist and address patterns checked before any
command, and escapes every string it renders.

Both skills' tag rules match freq_level / pnl_level / style_title /
style_speed in gmgn-wallet-analysis/analyze.py. P5 needs ROI > 50% plus
token_num >= 5 plus ONE of {win rate >= 50%, heavy-loss share < 15%}. A diff
against the imported functions over 5,000+ cases gave 0 mismatches. The one
intended difference is gmgn-wallet-review's 3% 净盈 line at machine speed.

Routing:
- CLAUDE.md lists both skills and sends 这个钱包什么风格 / 交易风格 /
  wallet style to gmgn-wallet-style instead of the smart-money-profile
  workflow.
- gmgn-wallet-analysis hands style-only asks and one-line recap / next-step
  asks to the new skills.
- gmgn-wallet-score hands one-line recap asks to gmgn-wallet-review. Its
  description becomes a >- block (the old plain scalar failed yaml.safe_load)
  with identical text.
- Readme.md and Readme.zh.md list both skills.
No trigger phrase is claimed by two skill descriptions.

Both skills stop on a rejected API key (401 AUTH_KEY_INVALID) as well as on a
rate limit, and reference only skills that exist in this repo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 17:20:45 +08:00
GMGN.AIandGitHub e742b4e830 Merge pull request #235 from Edwardchennnn/fix/holder-analysis-two-false-positives
fix(holder-analysis): two false positives rejecting tokens with sound chip structure
2026-09-23 17:30:36 +08:00
chenjunfengandClaude Opus 5 ff663aa3b9 fix(holder-analysis): a missing buy count was read as proof of zero-cost chips
`buy_tx_count_cur == 0` was taken to mean "these chips were transferred or
airdropped in, the holder paid nothing". Measured data refutes that inference.

On musebook (robinhood) 38 wallets read `buy_tx_count_cur: 0` and
`avg_cost: null`, but 34 of them carry a finite `unrealized_pnl` ratio — +2.7%,
+76.5%, +546% — and a chip with no cost cannot produce a finite PnL ratio; a
+2.7% ratio is only possible against a cost basis near the current price. 32 of
the 38 also carry a `fomo` frontend tag and 2 a `gmgn` tag, which is the trace of
active trading, the opposite of receiving an airdrop. What is actually missing is
buy-transaction indexing on that chain, while the PnL fields come from a separate
computation and arrive populated.

The error was systematic per chain rather than per token. Never-bought share of
supply across one batch: robinhood 27.0% and 41.2%, arc 26.3%, sol 4.8%. The 20%
airdrop warn gate therefore fired on essentially every robinhood and arc token and
essentially never on a sol token — a difference in index coverage, not in chip
structure. Reporting an unpopulated field as a risk conclusion is what this skill
explicitly forbids elsewhere.

So zero cost now requires positive evidence of no cost:

    def has_cost_basis(h):
        return (h.get('avg_cost') or 0) > 0 or h.get('unrealized_pnl') is not None

Six consumers of the old predicate move to it, all the same root cause:

  - the `airdrop` cohort and its >20% warn gate, plus the chip-quality buckets and
    the exit-signal line that read it
  - `diamond`, whose own stated rationale is "a wallet that never paid has no cost
    to hold through" — a proven cost basis satisfies that, and requiring an indexed
    buy count discarded most real diamond hands (JOLLY: 34 → 75 wallets,
    21.1% → 55.7% of float)
  - `idle_airdrop`, whose "zero cost" caption was a false statement for those rows
  - Top5 sell-risk classification, which labelled a wallet sitting at +6.6x as
    "zero-cost airdrop — can dump anytime" and simultaneously dropped it from the
    trapped and in-profit counts

Wallets with no indexed buy count but a real cost basis are not dropped from the
report — they get their own neutral line, `买入未记录 / Buy count unindexed`, with
their float share. That upstream did not index their buys is true and stays
visible; it just no longer drives a zero-cost gate.

Known limit, stated in SKILL.md: a genuine airdrop wallet whose `unrealized_pnl`
is a huge ratio computed against a dust-level cost is missed here. No second ratio
threshold is added — there is no measured sample to calibrate one on.

Also in the Advice section: a ⚠️ Caution rating needs two or more warns by
definition, but only `warns[0]` was printed, so a reader could never see why the
rating was what it was. Every triggered reason is now listed.

Ratings, up/main → this branch, measured end to end:

  musebook robinhood  🔴 Not Recommended → 🟡 Light Position
  JOLLY    robinhood  🟡 Light Position  → ✅ Normal
  ARGUS    arc        ⚠️ Caution         → 🟡 Light Position
  SI       sol        🟡 Light Position  → 🟡 Light Position   (control)

SI is the control: sol indexes buy counts normally, so it barely moves and its
🔴 risk-wallet reading of 45.36% is untouched. This is not a blanket loosening —
it only takes effect where the index is thin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 16:06:23 +08:00
chenjunfengandClaude Opus 5 338b0efdb9 fix(holder-analysis): the token's own contract address was scored as a holder wallet
Upstream returns the token's own CA in `token holders` with `addr_type: 0`, i.e.
as an ordinary wallet. Measured on musebook (robinhood,
0x91a2dae9699f0b82540b5886b0d8759c22820ba3): `addr_type: 0`, 0 buys, 0 sells,
0 transfer-out, 15.01% of total supply, $5.06M, carrying a `fresh_wallet` tag.

Left in the wallet cohort that one row polluted `biggest`, Top10, Top20, airdrop,
fresh wallets, risk wallets and the Top5 display at once, and on its own tripped
the danger gate as "largest wallet holds 16.30% — extreme concentration", rating
the token 🔴 Not Recommended. The largest genuine wallet there is 4.45% of float.

Controls on the same batch — JOLLY (robinhood), SI (sol), ARGUS (arc) — carry no
such row, so this is a per-token upstream classification gap, not a chain
convention, and cannot be handled by skipping a chain.

The address is partitioned out of `normal` and reported on its own line on a
total-supply basis, with its own warn gate at >10% of supply. It is not removed
from the judgement: contract-held supply reaches the market as soon as one release
transaction lands, so it stays visible and still counts toward the rating. Warn
rather than danger because that release is an observable prior step, unlike a
whale who can sell at will; no higher danger tier is added, because one measured
sample cannot calibrate a second threshold.

It is deliberately not deducted from the float denominator. Burn is permanent and
the DEX pool is the market itself, so neither can dump; self-held supply can.
Removing it from the denominator would raise every other wallet's percentage on
every token carrying this row, manufacturing new false positives while fixing one.

The same address is also excluded from `top100_map`, which feeds the dev
sock-puppet danger gate: chips sent back to the token contract, or gas paid to it,
is not "transferred to an internal wallet".

musebook: 🔴 Not Recommended → ⚠️ Caution. Top10 34.7% → 19.4%, Top20 43.4% →
27.8%, risk wallets 21.01% → 4.71%, fresh wallets 18.25% → 1.95%.
Regression on the three control tokens: JOLLY and SI byte-identical, ARGUS differs
only on the market-cap header line (live price drift between runs).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 16:05:19 +08:00
GMGN.AIandGitHub 5c61823fad Merge pull request #234 from Edwardchennnn/fix/heat-rank-age-split
fix(gmgn-heat-rank): read the track and freshness axis off creation time, not pool-open time
2026-09-22 19:22:30 +08:00
chenjunfengandClaude Opus 5 117884db9c fix(gmgn-heat-rank): growth rates off creation time, and stop one bad created.json entry discarding the rest
Three fixes found by testing the age split, not by reading it.

1. holders/day and KOLs/day still divided by the pool-open age. Those counts
   accumulate from the moment the token exists, so a token that spent 18 days on
   a bonding curve and migrated yesterday reported 10,836 holders/day and 22.0
   KOLs/day against a true 993 and 2.0 -- a wrong number printed on the address
   line and fed into an axis carrying weight 0.15. They now divide by the same
   creation age the track and the freshness axis read, which falls back to the
   open-time age when Step 3b has no answer and is clamped never to read younger,
   so a lookup can only lower a row's own rate. `c['age']` had no other reader
   and is retired rather than left beside `cage` as a second age.

2. The created.json loader judged every entry under one try block, and comparing
   a non-number against the plausibility window raises. So a single unreadable
   entry threw away every entry after it, and those rows returned in the next
   NEEDS-CREATED block -- a loop with no exit if the feed keeps answering the same
   value. Each entry is now judged on its own and no comparison can raise.
   Measured: ten bad values x first and last position, 20 runs, every one exits 0,
   the readable entries survive, and each unreadable one falls back to open time.

3. Pass 1 stopped with SystemExit(0), so "stopped to ask for Step 3b" and "ran to
   completion" were the same status. It now exits 3, leaving 0 = the list is the
   answer, 3 = incomplete, 1 = could not read its inputs.

Net effect on the same saved sweep: the same four names in the same order, ARGUS
72.1 -> 69.5 as its growth rate corrects. Two claims the first commits left
imprecise are amended: the second pass is not purely subtractive, since the
growth axis is a within-chain percentile, and only gate-surviving rows are looked
up, so a listed migrated token is measured on its true rate against neighbours
still on their inflated one -- an error that costs listed names points rather
than granting them, now stated under `## Known limits`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 15:19:47 +08:00
EdwardchennnnandClaude Opus 5 5eecc8574b fix(gmgn-heat-rank): close four holes found reviewing the age split
Re-audit of the change in this PR, all four in code or claims the PR itself
introduced.

1. A creation time that was not a readable number crashed the whole run. A
   string-typed timestamp in `created.json` raised `TypeError: unsupported
   operand type(s) for -: 'float' and 'str'` out of `creage()` and took the
   entire scoring pass with it, instead of dropping one row. The loader now
   coerces once, up front.

2. Worse than the crash: the values that did NOT crash were accepted and
   silently moved the list. `NaN` compares False against everything, so it
   passed the track test as "not young" AND came out of the freshness clamp
   at 1.0 — the easier mature track and a full newborn bonus at the same
   time, which is precisely the bug this PR exists to remove. A negative or
   absurdly small number read as "very old" and also bought the mature
   track. Unreadable is now stored as None (asked, no answer), which falls
   back to open time and keeps the row on the stricter track. Verified
   against string / list / NaN / negative / float / null / bool / zero /
   future-dated: nine cases, no crash, and every unreadable one now resolves
   in the strict direction instead of the generous one.

3. A lookup that never landed was indistinguishable from one that came back
   empty, and both were written as null. Exit status cannot separate them —
   `gmgn-cli token info` answers 0 with a fully zeroed body for an address it
   does not know (measured: `creation_timestamp: 0`, empty stderr). So the
   body itself decides: one that parses and echoes the address back is a real
   answer and a zero creation time is written as null, while one that does not
   parse or does not echo it back is a call that never landed, is left out of
   `created.json` entirely, and is named on stdout so Step 3 lists it again
   rather than scoring the row off an age nobody read. Step 3b also keeps its
   stderr now, in an `.err` file beside the response, the way Step 1 does,
   instead of sending it to /dev/null.

4. The report could state an age that was off by weeks. The table's age column
   is the pool-open age, which is right for the 7-day ceiling, but after this
   PR a token can be listed whose real age is far older — and nothing printed
   that number, so the writer could not have known. arc/ARGUS would have been
   reported as "1.9d" on a list whose headline promise is recency, when the
   token is 19.5 days old. The address line now prints `created=N.Nd ago`
   whenever the two differ by more than a day, and stating it is required in
   `## What the answer has to contain`.

Two claims elsewhere in the file that this PR had made false are corrected
rather than left to contradict it: the ceiling bullet said `MAX_AGE_D` stops a
months-old token reaching the list, which is now true only of months of
trading and not of the token's age, and the `YOUNG_D` cliff note did not say
which of the two ages the cliff is measured on.

No threshold, weight, gate constant or routing description changes. Against
the same saved 7-chain sweep, pass 1 names the same 5 rows and pass 2 returns
the same 4 names in the same order.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 15:09:13 +08:00
EdwardchennnnandClaude Opus 5 5c4841342a fix(gmgn-heat-rank): read the track and the freshness axis off creation time, not pool-open time
`open_timestamp` was doing all three of the age's jobs: the 7-day recency
ceiling, the new-launch/mature track choice, and the freshness axis. For a
token that sat on a bonding curve for weeks before it migrated, that one
number pulls in two opposite directions at once -- the token reads as hours
old, so it is held to the new-launch run-rate floors (thresholds set for
tokens that have no history to measure) while simultaneously being paid a
newborn's freshness bonus.

Measured on a live sweep this cost a correct listing: arc/ARGUS, real age
18.4d, was dropped for missing the $150,000 1h floor by $824 while carrying
a 21.8h freshness score.

The jobs are now split onto the timestamps that actually mean them:

- the 7-day ceiling (`--max-created` / `MAX_AGE_D`) still reads
  `open_timestamp`, because "recently hot" is a claim about the tape and
  migration is that event;
- the track choice (`YOUNG_D`) and the freshness axis read
  `creation_timestamp` from `token info`, because those are claims about the
  token's own history.

Cost is bounded rather than per-candidate. Step 3 now runs twice: pass 1
emits a `NEEDS-CREATED` block naming only the rows whose outcome can still
turn on their age -- a row already dead on liquidity, rug score or a
one-sided tape gets no call -- new Step 3b fetches exactly those, pass 2
prints the list. On the verification sweep that was 5 lookups against 58
candidates, so a whole run is 21-27 calls instead of 21. The block warns
itself if it ever asks for more than 20.

Guards on the new code:

- a creation time that reads newer than the pool open time is clamped, so a
  lookup can never move a row onto the easier track or buy it a freshness
  bonus it did not already have;
- a key present with a null value means "the lookup ran and there is no
  answer" and falls back to open time, which keeps the row on the stricter
  track instead of re-asking forever;
- Step 3b refuses to run with `DATA` unset, validates each chain name
  against the literal seven and rejects any address that is not
  alphanumeric, and refuses to write an empty `created.json` that would send
  pass 1 back to the same `NEEDS-CREATED` block in a loop.

No thresholds, weights, gate constants or report rules change, and the
frontmatter is untouched. Verified against a saved 7-chain sweep: pass 1
names the same 5 rows, pass 2 returns the same ranking, and the only list
difference the rule itself makes is the one row it was written for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 14:25:22 +08:00
GMGN.AI a9e7036ea2 1.6.6 v1.6.6 2026-09-16 17:08:11 +08:00
GMGN.AIandGitHub debd453a44 Merge pull request #233 from dylanzhong662-max/docs-openapi-contract-sync
docs(cli): synchronize OpenAPI contracts
2026-09-16 17:07:26 +08:00
zhongsongzhi-cmyk 0ffec3f73a docs(cli): synchronize OpenAPI contracts 2026-09-16 16:41:01 +08:00
GMGN.AI 685c46b324 1.6.4 v1.6.4 2026-09-14 20:17:01 +08:00
GMGN.AIandGitHub c300178097 Merge pull request #231 from dylanzhong662-max/feat/support-arbitrum-hyperevm
feat: document Pro-only 1s K-line support
2026-09-14 20:14:39 +08:00
zhongsongzhi-cmyk af73701a8d chore: bump gmgn-cli to 1.6.3 2026-09-14 19:46:20 +08:00
zhongsongzhi-cmyk a8f40a4be3 feat(cli): document Pro-only 1s kline 2026-09-14 19:45:58 +08:00
zhongsongzhi-cmyk d2e4e96865 feat: document Pro-only 1s kline support 2026-09-14 19:45:58 +08:00
GMGN.AIandGitHub 4a48b8a31e Merge pull request #229 from Edwardchennnn/feat/gmgn-narrative-skill
feat(gmgn-narrative): add independent token narrative/due-diligence card skill
2026-09-11 19:25:37 +08:00