price-alert
Skill785 starsChanged 12 days agoMITView on GitHub
Fire when the tracked token does something - new ATH, sharp 1h move, or operator-set target crossed. Silent on normal days.
We split this skill into the rules it gives an agent
14 rules. About 0 are common, 1 is distinctive, and 13 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
13
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 35
No new secrets. Uses keyless DexScreener.
- Very specific: line 79
If after filtering the remainder was non-empty but yielded zero valid targets → log PRICE_ALERT_BAD_VAR: ${var} and exit.
- Very specific: line 80
If the remainder was empty, TARGETS=() is fine — ATH and sharp-move gates still run.
- Very specific: line 93
If the state file's contract is set and differs from the resolved contract → log PRICE_ALERT_TOKEN_CHANGED and reset the state file to …
- Very specific: line 101
If the response is empty, falsy, or jq can't parse it → fall back to WebFetch on the same URL with prompt "Return the raw JSON body …
- Very specific: line 108
If CURRENT_PRICE is missing, zero, or non-numeric → log PRICE_ALERT_BAD_PRICE and exit ERROR.
- Very specific: line 118
Always update state.ath.price_usd = CURRENT_PRICE, state.ath.observed_at = NOW.
- Very specific: line 120
If suppressed because prior_ath is null (first run), record verdict ATH_BASELINE for the log.
- Very specific: line 130
If state.last_alerts.sharp_move is within the last 4h → verdict SHARP_MOVE_DEDUPED, no notify.
- Very specific: line 139
If the target is in state.targets and hit_at is already set → skip.
- Distinctive: line 149
Run-level verdict precedence (used for the log status line and the notification ordering):
- Very specific: line 155
If multiple gates fired in the same run, send one notification per gate.
- Very specific: line 197
If MODE == dry-run: build the messages, log the planned notifications, but skip ./notify.
- Very specific: line 291
State writes are atomic + validated: Every state write goes through a tmpfile + jq empty validation step.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
${var} — Optional. Pass one or more
target_pricelevels (comma-separated USD numbers, scientific notation allowed) to fire a one-time alert when the price crosses any of them. Empty = only ATH and sharp-move gates run. Passdry-runto skip notify (state still updates). Passset-target:<price>— the shape the Telegram force-reply sends (step 7) — to register a target and get a one-line confirmation.
Today is ${today}. The scheduled digests (digest, token-movers, ...) run at fixed hours. None of them tell the operator "the price just hit a new high" or "the token moved 28% in the last hour" — both are events that warrant attention the moment they happen, not 14 hours later in a daily digest. This skill closes that window.
Why this exists
Scheduled daily digests are calm summaries. Real moves need real-time signal. Three classes of move are worth a same-day ping:
- New all-time high. The single most narrative-shaping price event a token can have. Worth marking on the timeline regardless of size.
- Sharp 1h moves (±20%). Either a buyer wave or a liquidation cascade — both change what the operator does next (post about it, watch for follow-through, check the chart).
- Operator-set target crossings. The operator may want to know when price clears $X (often a personal stretch goal or a level tied to a tweet/launch). One alert per target per direction.
Everything else is noise not worth a same-day ping.
Config
Reads:
memory/MEMORY.md"Tracked Token" section — the tracked token's contract/chain. If absent, exit silently.memory/topics/price-alert-state.json— last-known ATH, last-alert timestamps per event type, target-crossing history. Created with defaults on first run.
Writes:
memory/topics/price-alert-state.json— updated state every run.memory/logs/${today}.md— one log block per run, even onOK.- Notification via
./notify— only when a gate fires.
No new secrets. Uses keyless DexScreener; falls back to WebFetch when a public curl GET is flaky (there is no network sandbox).
State schema
{
"contract": "0xbf8e8f0e8866a7052f948c16508644347c57aba3",
"chain": "base",
"ath": {
"price_usd": 0.000003757,
"observed_at": "2026-05-10T19:00:00Z",
"announced_at": "2026-05-10T19:30:00Z"
},
"last_alerts": {
"ath": "2026-05-10T19:30:00Z",
"sharp_move": null,
"target_hit": null
},
"targets": {
"0.000005": {
"side": "above",
"first_seen_below_at": "2026-05-11T08:00:00Z",
"hit_at": null,
"announced_at": null
}
}
}Key invariants:
ath.price_usdonly ever monotonically increases. If a run sees a lower price than the stored ATH, leave the ATH alone.last_alerts.*powers the 4h dedup window. Each gate has its own clock.targets.${price}.sideisaboveifcurrent_price < targetwhen the target was first observed (operator is waiting for the price to climb to it), andbelowotherwise. Set once, never flipped.targets.${price}.hit_atis set the run the cross happens.announced_atis set the run the notification fires. They differ only if the run lands inside a dedup window — but target alerts never re-fire, sohit_at == announced_atin practice.
Steps
1. Parse var
- If
${var}starts withset-target:→ setFROM_REPLY=1,MODE=execute. Strip theset-target:prefix (${var#set-target:}); the remainder is the target(s) list, parsed exactly like the comma-split below. This is the shapescripts/telegram-route.shsends when the operator replies to the step-7 force-reply prompt. The run proceeds normally (register the target, don't fire on first observation) and closes the loop with a confirmation in step 7. - Else if
${var}matches^dry-run→MODE=dry-run. Strip the prefix; remainder (if any) is treated as targets. - Otherwise
MODE=execute. - Split the remainder on
,(commas) and strip whitespace. - For each token: if it parses as a positive float (scientific notation OK, e.g.
5e-6), include it. Reject zero / negative / non-numeric tokens and logPRICE_ALERT_BAD_TARGET: ${token}— continue with the surviving targets. - If after filtering the remainder was non-empty but yielded zero valid targets → log
PRICE_ALERT_BAD_VAR: ${var}and exit. Normally no notify — but ifFROM_REPLY=1, first close the loop with./notify "Couldn't read \"${var#set-target:}\" as a price. Reply with a number like 0.000005."(a force-reply always deserves an acknowledgement). - If the remainder was empty,
TARGETS=()is fine — ATH and sharp-move gates still run.
2. Resolve tracked token
mkdir -p memory/topics
[ -f memory/topics/price-alert-state.json ] || cat > memory/topics/price-alert-state.json <<'EOF'
{"contract":null,"chain":null,"ath":null,"last_alerts":{"ath":null,"sharp_move":null,"target_hit":null},"targets":{}}
EOFParse the "Tracked Token" table in memory/MEMORY.md. Pull CONTRACT (column 2 of the first data row) and CHAIN (column 3, lowercased). If the section is missing, the table is empty, or the contract field doesn't match ^0x[0-9a-fA-F]{40}$ → log PRICE_ALERT_NO_TOKEN and exit (no notify, no state write).
If the state file's contract is set and differs from the resolved contract → log PRICE_ALERT_TOKEN_CHANGED and reset the state file to defaults with the new contract/chain. ATH starts over with the new token.
3. Fetch current price (DexScreener primary)
RESP=$(curl -fsS "https://api.dexscreener.com/latest/dex/tokens/${CONTRACT}" 2>/dev/null || echo "")If the response is empty, falsy, or jq can't parse it → fall back to WebFetch on the same URL with prompt "Return the raw JSON body verbatim." and try again. If both paths fail → log PRICE_ALERT_FETCH_FAIL and exit with status ERROR (no notify, no state mutation beyond touching last_run_at).
From the parsed JSON:
- Filter
.pairs[]to entries where.chainId == "${CHAIN}". - Of those, pick the entry with the highest
.liquidity.usd— call this the deepest pool. If no pair matches the chain, fall back to the highest-liquidity pair across all chains and markchain=fallbackin the log. - Extract:
CURRENT_PRICE=.priceUsd(float),H1_CHANGE_PCT=.priceChange.h1(float, may be missing → treat as 0),H24_CHANGE_PCT=.priceChange.h24(float, may be missing → treat as 0),POOL_URL=.url.
If CURRENT_PRICE is missing, zero, or non-numeric → log PRICE_ALERT_BAD_PRICE and exit ERROR.
4. Evaluate ATH gate
prior_ath = state.ath.price_usd (null on first run)
new_ath = (prior_ath is null) OR (CURRENT_PRICE > prior_ath)If new_ath:
- Always update
state.ath.price_usd = CURRENT_PRICE,state.ath.observed_at = NOW. - Notify only if the last
athalert was >4h ago (or never). On the first run after token initialisation, suppress the notification (we don't know if this is genuinely an ATH or just the starting baseline) — setstate.ath.announced_at = NOWso subsequent strict-higher prices alert correctly. - If suppressed because
prior_ath is null(first run), record verdictATH_BASELINEfor the log; no notify. - Otherwise verdict for this gate is
ATH.
5. Evaluate sharp-move gate
sharp = abs(H1_CHANGE_PCT) >= 20.0If sharp:
- If
state.last_alerts.sharp_moveis within the last 4h → verdictSHARP_MOVE_DEDUPED, no notify. - Otherwise verdict
SHARP_MOVE, notify, setstate.last_alerts.sharp_move = NOW.
Sign of H1_CHANGE_PCT decides the direction word in the message (up for ≥0, down for <0).
6. Evaluate target-crossing gate
For each target in TARGETS:
- If the target isn't yet in
state.targets: this is the first time the operator has set it. Recordside(aboveifCURRENT_PRICE < target, elsebelow),first_seen_below_at=NOW,hit_at=null,announced_at=null. Do not notify on first observation. This avoids "the price was already there when you set the target" noise. - If the target is in
state.targetsandhit_atis already set → skip; target-hit alerts are one-shot per side. - Otherwise check the cross:
side=above&CURRENT_PRICE >= target→ crossed.side=below&CURRENT_PRICE <= target→ crossed.
- On cross: set
hit_at=NOW,announced_at=NOW, verdictTARGET_HITfor this target, notify (subject to 4h dedup onlast_alerts.target_hit).
Targets that crossed in prior runs stay in the state file with hit_at set so they don't refire if the price wobbles back.
7. Combine verdicts and notify
Run-level verdict precedence (used for the log status line and the notification ordering):
ATH— strict new high after baseline.TARGET_HIT— operator-set level crossed.SHARP_MOVE— ±20% in 1h.OK— no gate fired.
If multiple gates fired in the same run, send one notification per gate. Each gate independently respects its own dedup clock — an ATH and a TARGET_HIT in the same run produce two messages; an ATH and a SHARP_MOVE_DEDUPED produce one.
Notification templates
ATH:
*$TOKEN — New ATH — ${today}*
$TOKEN just printed a new all-time high at $X.XXXXe-N.
Previous ATH: $Y.YYYYe-N (set ${prior_ath_age} ago).
24h move: ±Z.Z% · 1h move: ±W.W%
Chart: ${POOL_URL}SHARP_MOVE:
*$TOKEN — Sharp 1h Move — ${today}*
$TOKEN ${up|down} ${abs(h1):.1f}% in the last hour — now $X.XXXXe-N.
24h: ±Z.Z%
${one of: "Buyer wave — watch for follow-through." | "Selling pressure — watch the next hour for stabilisation." }
Chart: ${POOL_URL}(Direction phrase is hard-coded by sign: positive = buyer wave; negative = selling pressure. No freelance commentary.)
TARGET_HIT:
*$TOKEN — Target Hit — ${today}*
$TOKEN just crossed $${target} (now $X.XXXXe-N).
Direction: ${above|below}
24h: ±Z.Z% · 1h: ±W.W%
Chart: ${POOL_URL}If MODE == dry-run: build the messages, log the planned notifications, but skip ./notify. State still updates so dedup clocks advance correctly.
Cap each message at ~2500 chars; price-alert messages are short by nature and shouldn't approach this.
Send the alert
Build each firing gate's message to a file and send it with ./notify -f alert.md (one send per
gate). State still advances even on a deduped run, so the dedup clocks stay correct.
Set-a-target follow-up (force-reply)
Buttons and force_reply can't share one message, so this is a separate send. After a genuine
ATH alert (post-baseline, not a deduped repeat), and only when no un-hit operator target
currently sits above CURRENT_PRICE (don't nag operators who already queued one), offer to capture
the next level:
./notify "New high — want an alert when $SYMBOL clears a level above this? Reply with a price." \
--force-reply --placeholder "e.g. 0.000005" \
--context "price-alert::set-target"The reply routes back as var=set-target:<price> (handled in step 1). On that run, once the target
is registered (step 6, first-observation — no cross alert), close the loop with a one-line
confirmation: ./notify "Target set: \$<price> for $SYMBOL — I'll alert you when it crosses."
(Send it only when a new target was actually registered this run.)
8. Persist state
Rewrite memory/topics/price-alert-state.json atomically:
TMP=$(mktemp)
jq --arg ts "$(date -u +%FT%TZ)" '
.last_run_at = $ts |
.contract = $contract |
.chain = $chain |
.ath = $ath_obj |
.last_alerts = $last_alerts_obj |
.targets = $targets_obj
' memory/topics/price-alert-state.json > "$TMP"
mv "$TMP" memory/topics/price-alert-state.jsonValidate with jq empty memory/topics/price-alert-state.json after writing; if it fails, restore from a .bak copy and log PRICE_ALERT_STATE_CORRUPT. Keep one .bak rolling.
Cap state.targets to 20 most-recent entries (LRU by first_seen_below_at) so a long-running fork doesn't accumulate stale operator targets.
9. Log
Append to memory/logs/${today}.md under a ### price-alert heading:
### price-alert
- **Skill**: price-alert
- **Token**: ${SYMBOL} (${CONTRACT})
- **Current**: $X.XXXXe-N | 1h: ±W.W% | 24h: ±Z.Z%
- **ATH**: $Y.YYYYe-N (set ${YYYY-MM-DD HH:MM} UTC) [${UNCHANGED|NEW}]
- **Sharp-move gate**: ${FIRED|QUIET|DEDUPED}
- **Targets evaluated**: ${comma-list of target prices} → ${comma-list of per-target verdicts}
- **Verdicts fired**: ${comma-list, or NONE}
- **Notifications sent**: ${N}
- **Status**: ${PRICE_ALERT_OK | PRICE_ALERT_ATH | PRICE_ALERT_SHARP_MOVE | PRICE_ALERT_TARGET_HIT | PRICE_ALERT_DRY_RUN | PRICE_ALERT_NO_TOKEN | PRICE_ALERT_TOKEN_CHANGED | PRICE_ALERT_FETCH_FAIL | PRICE_ALERT_BAD_PRICE | PRICE_ALERT_BAD_VAR | PRICE_ALERT_STATE_CORRUPT}The status field carries the highest-priority gate fired this run, or the most relevant error. OK means the run completed cleanly and no gate fired.
Exit taxonomy
| Status | Meaning | Notify? |
PRICE_ALERT_OK | Run completed, no gate fired | No |
PRICE_ALERT_ATH | New strict all-time high (post-baseline) | Yes |
PRICE_ALERT_SHARP_MOVE | ±20% in 1h, outside dedup window | Yes |
PRICE_ALERT_TARGET_HIT | Operator target crossed for the first time | Yes |
PRICE_ALERT_DRY_RUN | var=dry-run mode | No (state still updates) |
PRICE_ALERT_NO_TOKEN | No tracked token configured in MEMORY.md | No |
PRICE_ALERT_TOKEN_CHANGED | Tracked contract changed since last run; state reset | No |
PRICE_ALERT_FETCH_FAIL | Both curl and WebFetch failed | No |
PRICE_ALERT_BAD_PRICE | API returned malformed/zero price | No |
PRICE_ALERT_BAD_VAR | ${var} had non-empty, non-dry-run text but yielded zero valid targets | No |
PRICE_ALERT_STATE_CORRUPT | jq validation failed after write; restored from .bak | No |
Network note
DexScreener is keyless and public — curl works; there is no network sandbox. If a public curl GET is flaky, the WebFetch fallback kicks in (built-in Claude tool, prompt: "Return the raw JSON body verbatim."). There's no auth header, and the URL doesn't change between runs. Notify goes through ./notify, which stages to .pending-notify/; the workflow re-delivers any messages that failed to send after the run — no extra script needed.
Constraints
- One alert per event per 4h window. The state file's
last_alerts.*map is the only authority on whether to suppress. - Target alerts are one-shot per side. Once
hit_atis set, the target never re-fires — even if the price wobbles back through the level. Operators add new targets if they want continued signal. - No baseline alert. The first run after a fresh state file or token change must NOT send an ATH notification — the stored price is the baseline, not a "new high." Subsequent strict-higher prices alert.
- No freelance interpretation. The sharp-move direction phrase is hard-coded by sign. Do not embellish with TA opinions ("looks like distribution", "could test support"). The verdict tells the operator what happened; the operator decides what it means.
- Liquid-pool selection only. The deepest-liquidity pair on the configured chain wins. Don't compute prices from blended pool averages — the deepest pool's
priceUsdis the canonical mark. - State writes are atomic + validated. Every state write goes through a tmpfile +
jq emptyvalidation step. Corrupt writes restore from.bak. - Read-only across
memory/logs/. This skill never modifies past log files. It only appends to today's. - Targets are absolute USD, not percentages. This avoids ambiguity ("20% from where?"). If operators want move-from-now alerts they have the sharp-move gate.
- Idempotent under same-minute reruns. Same-minute reruns with identical price input produce identical state and zero new notifications (every gate dedup-suppressed).
Other versions4 projects use a version of this.
Word for word · 3
- aaronjmars/aeon★ 776100% same
- aaronjmars/miroshark-aeon★ 19100% same
- MiroShark/miroshark-aeon★ 19100% same
Edited · 0
None found.
Wrote their own version · 1
- ssurmic/claude-investment-skills★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository90 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- llms.txtllms.txt
- aeonSkill
- action-converterSkill
- aeon-doctorSkill
- aeon-updateSkill
- anatomySkill
- arc-studioSkill
- articleSkill
- auto-mergeSkill
- autoresearchSkill
- auto-workflowSkill
- base-mcpSkill
- bd-radarSkill
- changelogSkill
- competitor-monitorSkill
- compute-resellSkill
- cortx-reliabilitySkill
- create-proveSkill
- create-skillSkill
- defi-overviewSkill
- deploy-prototypeSkill
- deploy-uni-hookSkill
- digestSkill
- distribute-tokensSkill
- executor-mcpSkill
- featureSkill
- feedback-builderSkill
- fetch-tweetsSkill
- finance-district-mcpSkill
- fleet-controlSkill
- fork-fleetSkill
- github-filing-registrySkill
- github-monitorSkill
- github-trendingSkill
- glim-mcpSkill
- heartbeatSkill
- higgsfieldSkill
- hunter-22Skill
- idea-forgeSkill
- idea-pipelineSkill
- inbox-triageSkill
- install-skillSkill
- investigation-reportSkill
- last30Skill
- memory-flushSkill
- mention-radarSkill
- miroshark-matchdaySkill
- monitor-polymarketSkill
- narrative-convergenceSkill
- narrative-trackerSkill
- onchain-monitorSkill
- operator-scorecardSkill
- pack-submitSkill
- picks-trackerSkill
- pm-manipulationSkill
- posthog-errorsSkill
- pr-reviewSkill
- pr-triageSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

