Commit Graph
391 Commits
Author SHA1 Message Date
GMGN.AIandGitHub ec95135eca Merge pull request #225 from dylanzhong662-max/docs-clarify-cli-trenches
docs: correct market trenches --limit / --launchpad-platform description
2026-09-10 17:17:27 +08:00
GMGN.AIandGitHub 965b492fa1 Merge pull request #222 from Edwardchennnn/feat/gmgn-heat-rank
feat(skills): add gmgn-heat-rank — screened cross-chain hot list
2026-09-10 17:13:16 +08:00
chenjunfengandClaude Opus 5 176eb126ac gmgn-heat-rank: print contract addresses without a gmgn.ai link
The CA block was headed `CA (verify at https://gmgn.ai/<chain>/token/<CA>)`.
That path shape is the one thing in this file nobody was allowed to check: the
skill's own rules forbid reaching gmgn.ai by web search, WebFetch, curl or a
browser, so the URL was printed on the strength of an assumption. If the shape
is wrong the reader lands on a 404 and doubts the list rather than the link.

The addresses themselves were already complete and one per line, so dropping
the link costs nothing -- an address pastes into whatever front-end the reader
actually uses. The heading now says exactly that and names no host.

Behaviour is otherwise unchanged: replayed against five stored sweeps, the
scorer's output differs on that single heading line and nowhere else.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 17:02:18 +08:00
chenjunfengandClaude Opus 5 1ab16cb76e fix(heat-rank): iterate the chain list as an array, and match whole names
Step 1 held the chain list in a plain string and iterated it as
`for ch in $CHAINS`. That works under bash and silently does not under zsh,
which performs no word splitting on an unquoted parameter expansion: the loop
runs once with all seven names in one variable. The sweep then collapses to a
single call with `--chain "sol bsc base eth robinhood arc stable"`, which the
CLI refuses -- no data, no list, on every run in a zsh shell.

The whitelist did not catch it either. `case " sol bsc ... " in *" $ch "*` is a
substring test, so the whole concatenated string matched its own haystack and
was waved through; it accepts any run of adjacent names. It is now an exact
alternation over whole names, which also refuses `sol bsc` and anything
carrying shell metacharacters. `$ch` and the two output redirections are
quoted.

Introduced by b4b54ad, whose own verification ran the extracted block under
bash and so could not see it.

Verified: `-n` clean under zsh, bash and bash --posix; seven iterations with
the correct per-chain filters in all three; and a live run under zsh now makes
15 calls across all seven chains (eth/arc/stable short-circuiting on an empty
24h window as designed), 56 candidates -> 10 passed -> 3 listed. The scorer
block is byte-identical, so scoring is untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 16:37:34 +08:00
chenjunfengandClaude Opus 5 dfbbb7c688 fix(gmgn-heat-rank): an unreadable age is unknown, not zero
`rage` fell back to `now` when a row carried neither `open_timestamp` nor
`creation_timestamp`, so a row with no age read as "launched this instant":
it walked through the `MAX_AGE_D` ceiling -- the one gate the whole list
rests on -- was judged on the wrong (new-launch) track, and would have taken
full freshness credit had it cleared that track's gates. That is the same
mistake as reading a missing risk field as clean, which this skill refuses
to make anywhere else.

An unreadable age is now placed past the ceiling and refused as
`no timestamp (age unknown)`, reported as the data fault it is rather than
as a token that failed a check. Both timestamp fields are normalised as
counts, so an unparseable value arrives as 0 and is caught by the same test
as an absent one.

Verified: on a row with both timestamps stripped, before/after is
rage=0.000 age=0.500 young=True (age gate silent) -> rage=8.000 young=False
fail=['no timestamp (age unknown)'], with no duplicate age-backstop reason.
Output is byte-identical on four unmodified fixtures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 16:19:19 +08:00
chenjunfengandClaude Opus 5 17d312bd1b fix(heat-rank): drop --filter is_out_market, which the API does not recognise
`is_out_market` is not a filter tag the API knows, and an unrecognised tag is not
refused -- it is silently ignored. Worse, a filters list made only of unrecognised
tags does not fall back to the server's defaults: it replaces them with nothing.

Measured on sol with the skill's own pool parameters: `--filter is_out_market` and
`--filter zzz_fake_tag_qqq` returned the identical 22 rows, both a superset of the
18 returned with no --filter at all.

On sol / bsc / base / eth the tag sat alongside real tags and was inert -- dropping
it returns the identical address set on both chains tested. On robinhood / arc /
stable it was the *only* tag, so those three chains were fetched with server-side
screening switched off. On one robinhood sweep that admitted 6 rows the defaults
exclude: 4 with is_honeypot=1, plus CLUBY and BRIAN, neither renounced nor open
source and neither caught by any local gate in this file.

So the fourth branch now sends no --filter and lets the server's defaults apply,
with a comment saying not to invent a tag to fill it, and `## Known limits` records
the measurement.

Verified end to end on a live sweep with the fix in place: 15 calls, the three
empty chains short-circuited, robinhood 20 rows instead of 26, and zero
is_honeypot=1 rows anywhere in the candidate pool. 52 candidates, 10 passed,
3 listed. Step 1 passes bash -n; an empty F array expands to no arguments; the
scorer is byte-identical to the previous commit's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 15:56:53 +08:00
zhongsongzhi-cmykandClaude Opus 4.8 6572db0e2d docs: correct market trenches --limit / --launchpad-platform description
The CLI docs promised behavior the API does not deliver:

- --limit: was "max 80 (default 80)". In reality it is best-effort only —
  the new_creation category is served from a fixed live window (~60), so
  larger and smaller --limit values both tend to return ~60. Documented as
  best-effort.
- --launchpad-platform: was "default: all platforms for the chain". In
  reality, when omitted the server injects a fixed per-chain default
  allow-list (not literally all platforms), and passing values REPLACES that
  allow-list (fewer platforms => less data). Platforms outside the list
  (incl. some newer RWA / tokenized-stock launchpads) are filtered out.
- Added the previously-missing robinhood platform slug list, and noted that
  arc/stable apply no launchpad filter (all platforms).

Docs only; no code change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-10 15:36:35 +08:00
chenjunfengandClaude Opus 5 dd485face1 perf(heat-rank): fetch 24h first and skip a chain's other two windows when it is empty
A candidate has to be present in the 24h window to count at all -- the scorer
only uses a chain when a 24h file loaded, and keys candidates off it. So for a
chain whose 24h window comes back empty, the 1h and 6h calls cannot produce a
candidate no matter what they contain. The sweep was spending them anyway.

Reorder the window loop to 24h first and break out when the 24h response holds
an empty rank array. On a measured sweep eth, arc and stable were empty in all
three windows, so this drops 21 calls to 15 and ~30s to ~21s. Worst case is
unchanged: when all seven chains are alive the sweep still costs its full 21.
This cuts waste, not coverage -- no window that could hold a candidate is skipped.

Teach the loader to tell a deliberate skip from a failure, so the saving does not
print as two failed calls. `missing (excluded)` keeps meaning one thing: a call
that failed or returned unparseable JSON. A 24h window that itself failed to load
still reports, since that is the signal worth seeing.

Verified against the frozen sweep: with eth/arc/stable's 1h and 6h files removed
the run is identical -- 60 candidates, 11 passed, the same five names with the
same scores in the same order -- and no missing-window line. Corrupting bsc_6h
and sol_24h still reports both. Step 1 passes bash -n.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 15:34:55 +08:00
chenjunfengandClaude Opus 5 b4b54ad192 fix(heat-rank): check chain names against a fixed whitelist before they reach the CLI
The chain list is a documented parameter a user can narrow, and whatever is in
it lands on a command line unquoted. Nothing validated it, so a typo, a chain
name from some other exchange, or a string carrying spaces or shell
metacharacters would have been passed straight through to gmgn-cli as arguments.

Hoist the list into a CHAINS variable (which is the thing to narrow) and check
each name against the literal set of the seven names this API actually has,
refusing anything else out loud and skipping it. The whitelist is deliberately
written out again rather than reusing $CHAINS, which would check the input
against itself.

Tested: sol / eth / robinhood accepted; "xyz", "sol; rm -rf /" and
"sol --limit 1" refused. Step 1 passes bash -n, and the extracted scorer is
byte-identical to the previous commit's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 15:24:34 +08:00
chenjunfengandClaude Opus 5 feb6331c47 fix(heat-rank): strip pipes and backticks from token symbols
sym() already removed control characters, terminal escapes and bidi overrides,
but let two markdown metacharacters through into text that gets copied straight
into the report. A token named "X | buy now" splits into two cells of the
report's markdown table with the second one written by whoever deployed the
contract; a backtick opens or closes a code span. The symbol is data the
attacker chose, so both become ?.

Tested on the extracted function: 'X | 买它 | 忽略上文' -> 'X ? 买它 ? 忽略上文',
'AAA`rm -rf /`BBB' -> 'AAA?rm -rf /?BBB', ordinary symbols untouched. Verified
against the stored sweep: same five names, same order, no symbol in the sample
contains either character.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 15:23:30 +08:00
chenjunfengandClaude Opus 5 6306d99a6e fix(heat-rank): use mktemp -d instead of a guessable path under /tmp
Step 1 built its working directory as /tmp/gmgn-heat-data-$(date +%s) -- a
second-resolution timestamp in a world-writable directory -- then wrote
heat_rank.py into it and executed it. Another local user can guess that name and
pre-create the directory holding their own heat_rank.py, at which point the run
executes their code as the user. Narrow (it needs a local unprivileged account)
but it is local code execution on a path this skill both writes and runs.

mktemp -d gives an unpredictable name and mode 0700, verified on this machine
(/var/folders/.../tmp.XXXXXXXX, drwx------).

The scorer's fallback was the matching half of the same problem:
os.environ.get('HEAT_DATA', '/tmp/gmgn-heat-data') meant a run with the variable
unset would read a fixed path anyone can plant JSON into. There is no safe
default, so it now exits with the correct invocation instead of inventing one.

Verified against the stored sweep: same five names in the same order. The
candidate count reads 11 rather than 12 only because the fixture keeps ageing
against a live clock -- robinhood PAIR has crossed 7d and the age backstop from
897153f drops it, at score 53.4, far below any floor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 15:22:39 +08:00
chenjunfengandClaude Opus 5 f6105c5ae0 docs(heat-rank): close the last two routing overlaps, and say no input is needed
Rescanned the current upstream skill set (13 skills on up/main, aa4d29a) and
grepped every description for hot-list vocabulary. Only gmgn-market matches,
and two of its claims were still unanswered here:

- "discover early-stage opportunities" and its Chinese equivalents. The
  description already sent "just launched" / "新盘" to `market trenches`, but not
  the ambush framing (值得埋伏吗, 新币筛选, 早期机会), which is exactly where the two
  skills read alike. Named, with the reason: the pool here starts at 500k market
  cap and 100k liquidity and never holds a bonding-curve token.
- gmgn-track's "what smart money is buying" / "on-chain alpha" /
  "copy-trade signals". The body table routed it; the description, which is what
  a router actually reads, did not.

Also added what the description never said about using it: no input is needed at
all -- the trigger phrase alone runs it, no address, chain or amount -- and the
four values a user can narrow. Runtime and pacing stay out of the description,
where no skill in this repo puts them; they are already in ## Pacing.

Documentation only: the extracted scorer is byte-identical to the previous
commit's and the stored sweep returns the same five names in the same order.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 15:10:01 +08:00
chenjunfengandClaude Opus 5 bc53c7d34c docs(heat-rank): stop advertising four flags the CLI does not have
argument-hint offered "--chains sol,bsc,... --max-age 7d --top 10
--min-score 60". Measured against `gmgn-cli market trending --help`, none of
the four exists: the CLI takes one `--chain` at a time (the seven-chain sweep
is the Step 1 loop, not a list argument), the age flag is spelled
`--max-created`, and TOP_N / MIN_SCORE are Python constants no flag reaches.
So a request like "this time only sol and bsc" or "raise the floor to 70" led
straight to an invalid command line, even though all four values are in fact
tunable and already listed in Parameters under their real names.

Rename the hint to the names used in the body, and add a paragraph under
Parameters saying these are spoken options rather than flags and mapping each
one to the single place it changes -- including that max-created now moves
together with MAX_AGE_D.

Documentation only: the extracted scorer is byte-identical to the previous
commit's and the stored sweep produces the same five names in the same order.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 15:04:36 +08:00
chenjunfengandClaude Opus 5 897153fddd fix(heat-rank): re-check age locally instead of trusting the server alone
"Recently hot" is the premise of this list, and ## Rules calls age a hard gate
with no compensation logic. Its only enforcer was --max-created 7d in Step 1 --
a server-side filter. The script computes each row's true age from
open_timestamp and used it for the young/mature track split and the 24h-change
blanking, but never compared it to the ceiling, so an endpoint that ignored or
re-interpreted the parameter would put a months-old token on the list under the
word "recent" with nothing local to stop it.

Add MAX_AGE_D = 7.0 and reject any row above it alongside the other
unconditional gates, with its own rejection reason. Document it in Parameters as
tied to --max-created, and say in ## Rules that the gate is enforced in both
places and the two values move together.

Verified against the stored seven-chain sweep: no row is affected -- the oldest
candidate is 6.99d, so the gate is silent, which is the intended steady state.
Re-running the same data with MAX_AGE_D lowered to 5.0 drops robinhood ZZZ
(5.3d) and MEME (6.4d) with reason "age>5d(local backstop)", 5 listed -> 3,
confirming the gate fires and reports itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 15:03:44 +08:00
chenjunfengandClaude Opus 5 dc69d8fc08 fix(heat-rank): print both score floors instead of only the lower one
The floor is two-valued -- MIN_SCORE for a row every gate could judge,
MIN_SCORE + U_SCORE_ADD for one no manipulation gate could -- and floor_for()
applies it correctly. The diagnostic line printed only MIN_SCORE, so the one
number carried into the report was wrong for half the rows.

The visible damage was in the near-miss block, which exists so the boundary can
be inspected and instead made the skill look broken: on a measured sweep bsc
BREW at 63.4 was dropped while sol LOOM at 63.3 was listed. Correct -- BREW
sits on a chain with no rug model and needed 68 -- but a report claiming a
floor of 60 alongside those two lines reads as padding or a bug.

Three changes, none touching selection:
- the diagnostic line names both floors and how many rows fall in the higher
  tier ("floor 60, or 68 for the 6 of 12 rows no manipulation gate could judge")
- each near miss prints the bar it actually had to clear
- the reporting checklist forbids writing the floor as a single number, and
  still forbids explaining the two tiers, which would disclose what ## Rules
  keeps out of the report

Verified against the stored seven-chain sweep: same five names, same order,
same three near misses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 15:02:54 +08:00
chenjunfengandClaude Opus 5 9ddda22279 fix(heat-rank): blank the 24h change cell for tokens under a day old
The 24h% column is only meaningful once the token has 24h of history. Below
that the window opens before the token existed, so price_change_percent is
measured from the launch price and prints figures like +128168.0% -- correct
arithmetic, no information about momentum, and wide enough to break the
column's alignment.

This was latent until the previous commit: the reference row used to be
whichever window loaded last, so a young token showed its 1h change under the
24h heading and happened to look plausible. Reading the right window exposed
the real number.

Print n/a instead when age < 1 day, matching how an unknown ATH position is
already rendered. The age column sits immediately left of the cell, so the
reason the cell is empty is visible without a footnote. Rows a day or older
are untouched.

Verified against the stored seven-chain sweep: same five names in the same
order, the four rows aged 2.2d-6.4d keep their 24h figures unchanged
(-21.2 / -0.4 / -18.7 / +39.7), and the 8.2h-old row now reads n/a.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 14:53:46 +08:00
chenjunfengandClaude Opus 5 ad9ff57741 fix(heat-rank): take the reference row from the 24h window, not whichever loaded last
`U.setdefault(key,{})['ref']=t` only guaranteed the key existed -- the assignment
that followed ran unconditionally, so the reference row ended up being whichever
window was loaded last. Iteration order is 24h, 6h, 1h, so for any token that
appeared in the 1h list the reference row was the 1h row.

Most fields read off that row are a current snapshot and are unaffected, but
`price_change_percent` is the change over that row's own window, so the column
headed `24h%` printed a 1h move -- and printed a 24h move for tokens that only
appeared in the 24h list, so the column was not even consistent between rows.
Measured on a stored 7-chain sweep, all five listed tokens were showing their 1h
change: 4Stock printed +29.7% where its 24h move was -21.2%, and MEME printed
+0.1% against a real -18.7%. A reader comparing the list against the exchange
would have found two of five names moving the opposite way.

setdefault now keeps the first window that carried the token, which is 24h for
every listed name (a token absent from the 24h list is already rejected).
Re-scored against the same sweep: same five names, same order, and all five
percentages now match the 24h files exactly. Scores shift by up to 0.5 because a
few snapshot fields differ slightly between the 24h and 1h responses, which were
fetched seconds apart.

One thing this exposes rather than fixes: a token younger than a day now shows
its true 24h change, and for an 8-hour-old token that is +128168%. The number is
correct and useless; a separate change should blank the cell below a day of age.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 14:46:39 +08:00
chenjunfengandClaude Opus 5 3c49980398 fix(heat-rank): defend the boundary against gmgn-market, and drop three dead symbols
The routing collision was one-sided. This skill's description told the reader
where a raw ranking dump belongs, but `gmgn-market` still claims "trending
tokens, what's pumping, hot coins" and cannot point back here, so an English
"hot coins" or a bare Chinese 热门币 could land on either. Rather than edit that
skill, the deciding test now lives here: what shape of answer is being asked
for. An untouched ranking of N rows in the exchange's own order is
`gmgn-market trending`; a short list that survived risk gates and carries a
score is this skill. A bare ambiguous ask defaults here, because this skill can
point at the raw ranking while the raw ranking cannot screen itself. The
"what this skill is not" table now names the colliding wording too.

Also removed three symbols that never did anything:

- `MAX_BUNDLER = 0.40`, defined and never read. The ceiling that actually fires
  is each chain's leave-one-out p90, and a flat 40% is precisely the mistake
  that deleted whole chains before -- leaving the constant in suggests
  otherwise to the next reader.
- `RANK`, a dict of per-window ranks that nothing consumes.
- `import glob`, unused.

And `near` compared whole candidate dicts by value to decide what was already
listed; it now compares (chain, address), which is what identity means here.

No threshold, weight or gate changed. Re-scored against a stored 7-chain sweep:
the same five names in the same order, the same three near-misses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 14:45:46 +08:00
chenjunfengandClaude Opus 5 a0ef19d543 feat(skills): add gmgn-heat-rank — screened cross-chain hot list
A screening skill that starts from no address: it sweeps all seven chains
across three time windows (21 paced `market trending` calls), gates every
candidate on liquidity, real volume, turnover, concentration and the
rug/dev risk fields, scores the survivors on one cross-chain scale, and
returns however many clear the score floor, capped at ten. It never pads
the list to fill the quota.

Two things it is careful about:

- Bot share is applied as a volume discount rather than a switch, and the
  bundler ceiling is each chain's own leave-one-out p90, so no flat
  threshold silently deletes a whole chain.
- Several manipulation fields read a literal 0 on chains GMGN's analytics
  do not cover — `bot_degen_rate` and `bundler_rate` on base/eth/arc/stable,
  and `rug_ratio` on six of the seven chains. That is "never measured", not
  "clean", so those rows must clear a set of compensating thresholds and a
  score floor 8 points higher instead of inheriting a pass. A row that
  cannot is dropped.

Self-contained: one SKILL.md, no extra files. Data comes only from
`gmgn-cli market trending`; the embedded scorer is read-only and uses the
Python standard library only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 19:07:35 +08:00
GMGN.AI aa4d29a9b7 1.6.1 v1.6.1 2026-09-04 16:12:14 +08:00
GMGN.AIandGitHub 3d9db45c18 Merge pull request #220 from Edwardchennnn/docs/contract-dd-buy-routing
docs(gmgn-contract-dd): append the gmgn-token-buy boundary
2026-09-04 16:11:28 +08:00
GMGN.AIandGitHub cff0764baf Merge pull request #219 from Edwardchennnn/docs/swap-buy-routing
docs(gmgn-swap): append the gmgn-token-buy boundary to the description
2026-09-04 16:11:16 +08:00
GMGN.AIandGitHub 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
2026-09-04 16:10:59 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-04 15:01:15 +08:00
chenjunfeng 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.
2026-09-04 14:33:10 +08:00
chenjunfeng 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.
2026-09-04 14:33:10 +08:00
chenjunfeng 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.
2026-09-04 14:33:10 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-04 14:33:10 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-04 14:33:10 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-04 14:33:10 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-04 14:33:10 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-04 14:33:10 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-04 14:33:10 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-04 14:33:10 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-04 14:33:10 +08:00
chenjunfeng 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.
2026-09-04 14:33:10 +08:00
chenjunfengandClaude Opus 5 ffc8b741ce docs(gmgn-contract-dd): append the gmgn-token-buy boundary
Purely additive — the existing description, its trigger list (尽调 / 打个分 /
这个币安全吗 / 能不能买 / rug check / bare address), the neighbouring-skills
table and every scoring rule are untouched, so nothing this skill triggers on
today stops triggering.

What is added is only the missing half of one phrase. 能不能买 is claimed by
this skill and by gmgn-token-buy, and only one of them can serve it: the input
here is --address, and this skill resolves no names, so a named token plus buy
intent (帮我买 200u 的 PENGU, PENGU 能不能买, 能不能冲) has no path through it.
The appended clauses say so, and say the two are sequential — token-buy calls
this skill for the safety verdict — so a bare address still scores here exactly
as before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 14:32:48 +08:00
chenjunfengandClaude Opus 5 4a106ee773 docs(gmgn-swap): append the gmgn-token-buy boundary to the description
Purely additive. The original description sentence is kept byte-for-byte;
the appended clauses only say which buys arrive here pre-vetted and which
start at gmgn-token-buy, so no existing trigger is removed or narrowed.

Why the boundary is stated here at all: the pre-swap check in this skill
reads only is_honeypot and rug_ratio, and --output-token takes a contract
address (the only names resolved here are the SOL/BNB/ETH/USDC currencies),
so a same-name copycat is a loss this skill has no way to catch. Selling,
swaps, batch trading, limit/stop/take-profit/trailing orders, order status
and gas prices stay unconditionally this skill's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 14:31:45 +08:00
GMGN.AIandGitHub 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
2026-09-03 19:17:37 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-03 19:00:57 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-03 18:09:21 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-03 16:52:00 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-03 16:26:56 +08:00
chenjunfengandClaude Opus 5 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>
2026-09-03 15:58:15 +08:00
GMGN.AI 7a87b8f09d 1.6.0 v1.6.0 2026-09-01 16:08:22 +08:00
GMGN.AIandGitHub 041a5a3ca9 Merge pull request #216 from dylanzhong662-max/fix/close-dispatcher-on-cli-error
fix(cli): close HTTP dispatcher before exiting on errors
2026-09-01 16:07:52 +08:00
zhongsongzhi-cmyk 25f3452722 fix(cli): close HTTP dispatcher before exiting on errors 2026-09-01 15:34:49 +08:00
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>
2026-09-01 10:34:33 +08:00
GMGN.AIandGitHub 7331bfc375 Merge pull request #210 from Edwardchennnn/feat/contract-dd-skill
feat(contract-dd): add contract due-diligence scoring skill
2026-08-28 19:17:23 +08:00
chenjunfengandClaude Opus 5 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>
2026-08-28 19:06:33 +08:00