Skip to content

Commit beae295

Browse files
novnskiclaude
andcommitted
feat: add build tooling, sanitize sensitive literals, and fix docs
- Add unified build script (build.js) with CI workflow (.github/workflows/validate.yml) - Add sensitive literal scanner and sanitizer scripts - Add markdown link checker script - Improve generator: typed placeholders for all field types, skip empty query params in curl examples - Sanitize all rule and reference files to use placeholder addresses/hashes - Move ApiResponseCodes.md and SpamDetection.md from rules/ to references/ - Fix test-installation.sh missing exit 1 on failures - Fix streams docs using addressToAdd instead of address - Align CONTRIBUTING.md frontmatter example with actual schema - Update CLAUDE.md architecture tree with new reference files - Bump versions: moralis-data-api 1.4.0, moralis-streams-api 1.3.0, learn-moralis 1.2.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 01d9472 commit beae295

File tree

150 files changed

+1484
-836
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

150 files changed

+1484
-836
lines changed

.github/workflows/validate.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Validate
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
validate:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v4
15+
16+
- name: Setup Node
17+
uses: actions/setup-node@v4
18+
with:
19+
node-version: "20"
20+
21+
- name: Run Build Validation
22+
run: node scripts/build.js
23+
24+
- name: Ensure Generated Files Are Committed
25+
run: git diff --exit-code

CLAUDE.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ skills/
1818
│ │ ├── ProductComparison.md
1919
│ │ └── UseCaseGuide.md
2020
│ └── SKILL.md
21-
├── moralis-data-api/ # Unified EVM + Solana data API (135 endpoints)
21+
├── moralis-data-api/ # Unified EVM + Solana data API (136 endpoints)
2222
│ ├── rules/ # Auto-generated endpoint docs (one per endpoint)
2323
│ ├── references/
24+
│ │ ├── ApiResponseCodes.md # API response code patterns and handling guidance
2425
│ │ ├── CommonPitfalls.md # Gotchas: data types, HTTP methods, path inconsistencies
2526
│ │ ├── DataTransformations.md # Type conversions, field mappings, snake_case → camelCase
2627
│ │ ├── DefiProtocols.md # Supported DeFi protocols and chains
@@ -31,6 +32,7 @@ skills/
3132
│ │ ├── ResponsePatterns.md # Pagination patterns and response wrapper structures
3233
│ │ ├── SupportedApisAndChains.md # Chain support matrix
3334
│ │ ├── SupportedDexs.md # Supported DEXs for token API endpoints
35+
│ │ ├── SpamDetection.md # Spam flag interpretation and filtering guidance
3436
│ │ ├── TokenHoldersFaq.md # Token Holders API FAQ and important notes
3537
│ │ ├── TokenSearch.md # Token search functionality reference
3638
│ │ └── WalletHistory.md # Wallet history categories and classifications
@@ -64,6 +66,7 @@ Each skill includes pattern reference files containing complete reference materi
6466
### moralis-data-api
6567

6668
- `references/CommonPitfalls.md` - Gotchas: data type assumptions, HTTP methods, path inconsistencies
69+
- `references/ApiResponseCodes.md` - API response code patterns, response handling, and retry guidance
6770
- `references/DataTransformations.md` - Type conversions, field mappings (block numbers, timestamps, balances, snake_case → camelCase)
6871
- `references/DefiProtocols.md` - Supported DeFi protocols and chains for position endpoints
6972
- `references/FilteredTokens.md` - Token discovery metrics, timeframes, and filter examples
@@ -73,6 +76,7 @@ Each skill includes pattern reference files containing complete reference materi
7376
- `references/ResponsePatterns.md` - Pagination patterns and response wrapper structures
7477
- `references/SupportedApisAndChains.md` - Chain support matrix
7578
- `references/SupportedDexs.md` - Supported DEXs for token API endpoints
79+
- `references/SpamDetection.md` - Spam flag interpretation, filtering behavior, and recommended handling
7680
- `references/TokenHoldersFaq.md` - Token Holders API FAQ and important notes
7781
- `references/TokenSearch.md` - Token search functionality reference
7882
- `references/WalletHistory.md` - Wallet history categories and classifications
@@ -99,6 +103,12 @@ Each skill includes pattern reference files containing complete reference materi
99103
## Development Commands
100104

101105
```bash
106+
# One-shot build (preferred): generation + validation + non-API tests
107+
bun run build
108+
109+
# Full build including API-key dependent tests
110+
bun run build:full
111+
102112
# Generate endpoint markdown rules from swagger config
103113
node scripts/generate-endpoint-rules.js
104114

@@ -120,6 +130,15 @@ node scripts/check-solana-suffix.js
120130
# Verify Solana endpoint variants are properly registered (ESM)
121131
node scripts/verify-solana-variants.mjs
122132

133+
# Validate markdown references resolve correctly
134+
node scripts/check-markdown-links.js
135+
136+
# Sanitize markdown docs by replacing address/hash-like literals with placeholders
137+
node scripts/sanitize-sensitive-literals.js
138+
139+
# Validate no address/hash-like literals exist in markdown docs
140+
node scripts/check-sensitive-literals.js
141+
123142
# Test all skills end-to-end
124143
bash scripts/test-all-skills.sh
125144

@@ -148,6 +167,14 @@ node scripts/bump-version.js <skill|all> <major|minor|patch>
148167
- **EVM endpoints:** No suffix unless collision exists (then `__evm`)
149168
- This convention is strictly enforced by the generator script
150169

170+
## Sensitive Literal Policy
171+
172+
To avoid antivirus false positives in hosted skill content:
173+
174+
- Never commit real-looking EVM addresses, transaction hashes, Solana addresses, or UUIDs in markdown files.
175+
- Always use placeholders such as `YOUR_EVM_ADDRESS`, `YOUR_SOLANA_ADDRESS`, `YOUR_TX_HASH`, and `YOUR_STREAM_ID`.
176+
- Run `node scripts/check-sensitive-literals.js` before reporting completion.
177+
151178
## Skill Versioning
152179

153180
Skills use semver (`MAJOR.MINOR.PATCH`) in the `version` frontmatter field. Bump with `node scripts/bump-version.js`.

CONTRIBUTING.md

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Thank you for your interest in contributing!
1414

1515
1. Create directory: `skills/your-skill-name/`
1616
2. Create `SKILL.md` following the [Agent Skills Standard](https://github.com/agentskills/agentskills)
17-
3. Create reference documentation in `rules/`
17+
3. Add manual reference documentation in `references/` (endpoint `rules/` are generated)
1818
4. Test with a real API key using REST (curl or native HTTP)
1919

2020
### SKILL.md Format
@@ -25,23 +25,47 @@ Required frontmatter:
2525
---
2626
name: your-skill-name
2727
description: What this skill does and when to use it
28-
tags: [web3, ...]
2928
version: 1.0.0
30-
author: MoralisWeb3
29+
metadata:
30+
author: MoralisWeb3
3131
---
3232
```
3333

3434
### Testing
3535

36+
Preferred one-shot validation:
37+
38+
```bash
39+
bun run build
40+
```
41+
42+
Optional full run (includes API-key dependent checks):
43+
44+
```bash
45+
bun run build:full
46+
```
47+
3648
```bash
3749
# Set your API key
3850
export MORALIS_API_KEY="your_moralis_api_key"
3951

4052
# Test a REST query
41-
curl "https://deep-index.moralis.io/api/v2.2/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/balance?chain=0x1" \
53+
curl "https://deep-index.moralis.io/api/v2.2/YOUR_EVM_ADDRESS/balance?chain=0x1" \
4254
-H "X-API-Key: $MORALIS_API_KEY"
4355
```
4456

57+
### Sensitive Literal Policy (Required)
58+
59+
Do not commit real-looking blockchain identifiers in markdown content.
60+
61+
- Use placeholders only: `YOUR_EVM_ADDRESS`, `YOUR_SOLANA_ADDRESS`, `YOUR_TX_HASH`, `YOUR_STREAM_ID`
62+
- Never include real-looking EVM addresses (`0x...` 40 hex), transaction hashes, Solana addresses, or UUIDs
63+
- Before committing, run:
64+
65+
```bash
66+
node scripts/check-sensitive-literals.js
67+
```
68+
4569
## Commit Guidelines
4670

4771
- Use clear commit messages

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ Unified skill for all blockchain data queries. Auto-detects EVM vs Solana from a
7474
- **Solana** (34) — native Solana endpoints + EVM endpoints with Solana support
7575

7676
```
77-
/moralis-data-api Get the balance of 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
77+
/moralis-data-api Get the balance of YOUR_EVM_ADDRESS
7878
79-
/moralis-data-api Get the balance of 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 on Polygon
79+
/moralis-data-api Get the balance of YOUR_EVM_ADDRESS on Polygon
8080
81-
/moralis-data-api Get the balance of Solana wallet 742d35Cc6634C0532925a3b844Bc9e7595f0bEb
81+
/moralis-data-api Get the balance of Solana wallet YOUR_SOLANA_ADDRESS
8282
```
8383

8484
> **Tip:** Prefix your prompt with the skill name (e.g. `/moralis-data-api`) to load it directly. Some agents auto-detect skills, but tagging ensures it works across all agents.
@@ -92,7 +92,7 @@ Real-time blockchain event monitoring with webhooks. **20 endpoints** for creati
9292
```
9393
/moralis-streams-api Create a stream to monitor all ERC20 transfers on Ethereum
9494
95-
/moralis-streams-api Pause the stream with ID a1b2c3d4-e5f6-7890-abcd-ef1234567890
95+
/moralis-streams-api Pause the stream with ID YOUR_STREAM_ID
9696
```
9797

9898
## learn-moralis
@@ -116,6 +116,7 @@ Knowledge-only skill for answering general questions about Moralis. Routes users
116116
- **Zero dependencies** — all API calls use curl
117117
- **Works with 18+ agents** — any agent supporting the [Agent Skills](https://skills.sh/) standard
118118
- **Auto-generated endpoint docs**`swagger/api-configs.json``scripts/generate-endpoint-rules.js``rules/*.md`
119+
- **Sanitized examples** — markdown docs use placeholders (`YOUR_EVM_ADDRESS`, etc.) to avoid shipping real-looking identifiers
119120
- **Manually maintained references** — pattern files in each skill's `references/` directory
120121

121122
## Documentation

package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "onchain-skills",
3+
"private": true,
4+
"scripts": {
5+
"build": "node scripts/build.js",
6+
"build:full": "node scripts/build.js --with-api-tests"
7+
}
8+
}

scripts/build.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env node
2+
3+
const fs = require("fs");
4+
const path = require("path");
5+
const { spawnSync } = require("child_process");
6+
7+
const ROOT = path.join(__dirname, "..");
8+
9+
function runStep(name, cmd, args) {
10+
console.log(`\n==> ${name}`);
11+
const result = spawnSync(cmd, args, {
12+
cwd: ROOT,
13+
stdio: "inherit",
14+
shell: false,
15+
});
16+
17+
if (result.status !== 0) {
18+
if (result.status === null && result.signal) {
19+
console.error(`Step "${name}" terminated by signal ${result.signal}`);
20+
process.exit(1);
21+
}
22+
process.exit(result.status || 1);
23+
}
24+
}
25+
26+
function hasApiKeyContext() {
27+
if (process.env.MORALIS_API_KEY) return true;
28+
if (fs.existsSync(path.join(ROOT, ".env"))) return true;
29+
if (fs.existsSync(path.join(ROOT, ".claude/.env"))) return true;
30+
return false;
31+
}
32+
33+
function main() {
34+
const withApiTests = process.argv.includes("--with-api-tests");
35+
36+
const steps = [
37+
["Generate endpoint rules", "node", ["scripts/generate-endpoint-rules.js"]],
38+
["Check collision mapping", "node", ["scripts/check-collisions.js"]],
39+
[
40+
"Check all collision files",
41+
"node",
42+
["scripts/check-all-collisions.mjs"],
43+
],
44+
["Check Solana suffixes", "node", ["scripts/check-solana-suffix.js"]],
45+
["Verify Solana variants", "node", ["scripts/verify-solana-variants.mjs"]],
46+
["Check markdown links", "node", ["scripts/check-markdown-links.js"]],
47+
[
48+
"Check sensitive literals",
49+
"node",
50+
["scripts/check-sensitive-literals.js"],
51+
],
52+
["Test installation layout", "bash", ["scripts/test-installation.sh"]],
53+
["Run bug checks", "bash", ["scripts/test-bugs.sh"]],
54+
];
55+
56+
for (const [name, cmd, args] of steps) {
57+
runStep(name, cmd, args);
58+
}
59+
60+
if (withApiTests || hasApiKeyContext()) {
61+
runStep("Run API-key dependent tests", "bash", ["scripts/test-all-skills.sh"]);
62+
} else {
63+
console.log(
64+
"\n==> Skip API-key dependent tests (no MORALIS_API_KEY/.env detected)",
65+
);
66+
console.log(" Run `bun run build:full` to force this step.");
67+
}
68+
69+
console.log("\nBuild completed successfully.");
70+
}
71+
72+
main();

scripts/check-all-collisions.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,5 @@ if (issues.length > 0) {
4343
} else {
4444
console.log('=== All collisions have both files ===');
4545
}
46+
47+
process.exit(issues.length > 0 ? 1 : 0);

scripts/check-collisions.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ const data = JSON.parse(fs.readFileSync(API_CONFIGS_PATH, "utf8"));
1111
// Get all operationIds from each source
1212
const evmOps = new Set(Object.keys(data.evm || {}));
1313
const solanaOps = new Set(Object.keys(data.solana || {}));
14-
const streamsOps = new Set(Object.keys(data.streams || {}));
1514

1615
// Find collisions between EVM and Solana
1716
const collisions = [];
@@ -69,3 +68,5 @@ if (missingFiles.length > 0) {
6968
if (missingSuffix.length === 0 && missingFiles.length === 0) {
7069
console.log("✅ All collision handling is correct!");
7170
}
71+
72+
process.exit(missingSuffix.length > 0 || missingFiles.length > 0 ? 1 : 0);

scripts/check-markdown-links.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env node
2+
3+
const fs = require("fs");
4+
const path = require("path");
5+
6+
const ROOT = path.join(__dirname, "..");
7+
const SCAN_ROOTS = [
8+
path.join(ROOT, "README.md"),
9+
path.join(ROOT, "CLAUDE.md"),
10+
path.join(ROOT, "CONTRIBUTING.md"),
11+
path.join(ROOT, "skills"),
12+
];
13+
14+
function shouldScan(filePath) {
15+
return path.extname(filePath).toLowerCase() === ".md";
16+
}
17+
18+
function* walk(targetPath) {
19+
if (!fs.existsSync(targetPath)) return;
20+
21+
const stat = fs.statSync(targetPath);
22+
if (stat.isFile()) {
23+
if (shouldScan(targetPath)) yield targetPath;
24+
return;
25+
}
26+
27+
const entries = fs.readdirSync(targetPath, { withFileTypes: true });
28+
for (const entry of entries) {
29+
const fullPath = path.join(targetPath, entry.name);
30+
if (entry.isDirectory()) {
31+
yield* walk(fullPath);
32+
continue;
33+
}
34+
if (entry.isFile() && shouldScan(fullPath)) {
35+
yield fullPath;
36+
}
37+
}
38+
}
39+
40+
function findBrokenLinks(filePath) {
41+
const rel = path.relative(ROOT, filePath);
42+
const text = fs.readFileSync(filePath, "utf8");
43+
const issues = [];
44+
const linkRegex = /\[[^\]]+\]\(([^)]+)\)/g;
45+
46+
for (const match of text.matchAll(linkRegex)) {
47+
const raw = match[1].trim();
48+
if (
49+
raw.startsWith("http://") ||
50+
raw.startsWith("https://") ||
51+
raw.startsWith("mailto:") ||
52+
raw.startsWith("#")
53+
) {
54+
continue;
55+
}
56+
57+
const local = raw.split("#")[0];
58+
if (!local) continue;
59+
60+
const target = path.resolve(path.dirname(filePath), local);
61+
if (!fs.existsSync(target)) {
62+
issues.push(`${rel} -> ${raw}`);
63+
}
64+
}
65+
66+
return issues;
67+
}
68+
69+
function main() {
70+
const files = [];
71+
for (const root of SCAN_ROOTS) {
72+
for (const filePath of walk(root)) files.push(filePath);
73+
}
74+
75+
const broken = [];
76+
for (const filePath of files) {
77+
broken.push(...findBrokenLinks(filePath));
78+
}
79+
80+
if (broken.length === 0) {
81+
console.log("All markdown relative links resolve.");
82+
return;
83+
}
84+
85+
console.error("Broken markdown links detected:");
86+
for (const issue of broken) {
87+
console.error("- " + issue);
88+
}
89+
process.exit(1);
90+
}
91+
92+
main();

0 commit comments

Comments
 (0)