skillpp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPATIBILITY.md +58 -0
- package/LICENSE +21 -0
- package/README.md +307 -0
- package/README.zh-CN.md +307 -0
- package/SKILL.md +490 -0
- package/adapters/binance-ai.md +22 -0
- package/adapters/claude.md +21 -0
- package/adapters/gemini.md +26 -0
- package/adapters/gpt.md +28 -0
- package/adapters/kimi.md +26 -0
- package/adapters/mimo.md +22 -0
- package/adapters/openclaw.md +29 -0
- package/assets/skillpp-banner.png +0 -0
- package/package.json +59 -0
- package/pipelines.md +310 -0
- package/prompts/newbie-mode.md +48 -0
- package/prompts/router-prompt.md +32 -0
- package/prompts/universal-system-prompt.md +41 -0
- package/registry.md +209 -0
- package/rules.md +323 -0
- package/schemas/audit.schema.json +67 -0
- package/schemas/checkpoint.schema.json +86 -0
- package/schemas/handoff.schema.json +82 -0
- package/schemas/token.schema.json +36 -0
- package/scripts/compatibility-check.mjs +130 -0
- package/scripts/selftest.mjs +384 -0
- package/scripts/skillpp.mjs +448 -0
- package/scripts/validate-skillpp.mjs +140 -0
- package/skillpp.manifest.json +714 -0
- package/skills/audit-plus/SKILL.md +612 -0
- package/skills/binance/binance/CHANGELOG.md +112 -0
- package/skills/binance/binance/LICENSE.md +9 -0
- package/skills/binance/binance/SKILL.md +69 -0
- package/skills/binance/binance/references/algo.md +21 -0
- package/skills/binance/binance/references/alpha.md +9 -0
- package/skills/binance/binance/references/auth.md +32 -0
- package/skills/binance/binance/references/c2c.md +5 -0
- package/skills/binance/binance/references/convert.md +19 -0
- package/skills/binance/binance/references/copy-trading.md +6 -0
- package/skills/binance/binance/references/crypto-loan.md +27 -0
- package/skills/binance/binance/references/derivatives-options-streams.md +25 -0
- package/skills/binance/binance/references/derivatives-options.md +85 -0
- package/skills/binance/binance/references/derivatives-portfolio-margin-pro-streams.md +5 -0
- package/skills/binance/binance/references/derivatives-portfolio-margin-pro.md +34 -0
- package/skills/binance/binance/references/derivatives-portfolio-margin-streams.md +5 -0
- package/skills/binance/binance/references/derivatives-portfolio-margin.md +146 -0
- package/skills/binance/binance/references/dual-investment.md +15 -0
- package/skills/binance/binance/references/fiat.md +9 -0
- package/skills/binance/binance/references/futures-coin-streams.md +29 -0
- package/skills/binance/binance/references/futures-coin.md +109 -0
- package/skills/binance/binance/references/futures-usds-streams.md +35 -0
- package/skills/binance/binance/references/futures-usds.md +144 -0
- package/skills/binance/binance/references/gift-card.md +10 -0
- package/skills/binance/binance/references/margin-trading-streams.md +6 -0
- package/skills/binance/binance/references/margin-trading.md +101 -0
- package/skills/binance/binance/references/mining.md +17 -0
- package/skills/binance/binance/references/pay.md +5 -0
- package/skills/binance/binance/references/rebate.md +5 -0
- package/skills/binance/binance/references/simple-earn.md +56 -0
- package/skills/binance/binance/references/spot-streams.md +25 -0
- package/skills/binance/binance/references/spot.md +114 -0
- package/skills/binance/binance/references/staking.md +59 -0
- package/skills/binance/binance/references/sub-account.md +67 -0
- package/skills/binance/binance/references/vip-loan.md +27 -0
- package/skills/binance/binance/references/wallet.md +75 -0
- package/skills/binance/fiat/CHANGELOG.md +11 -0
- package/skills/binance/fiat/LICENSE.md +9 -0
- package/skills/binance/fiat/SKILL.md +169 -0
- package/skills/binance/fiat/references/authentication.md +126 -0
- package/skills/binance/fiat/references/sapi-endpoints.md +217 -0
- package/skills/binance/onchain-pay/.local.md.example +10 -0
- package/skills/binance/onchain-pay/CHANGELOG.md +20 -0
- package/skills/binance/onchain-pay/LICENSE.md +9 -0
- package/skills/binance/onchain-pay/SKILL.md +466 -0
- package/skills/binance/onchain-pay/references/authentication.md +92 -0
- package/skills/binance/onchain-pay/scripts/sign_and_call.sh +52 -0
- package/skills/binance/p2p/CHANGELOG.md +33 -0
- package/skills/binance/p2p/LICENSE.md +9 -0
- package/skills/binance/p2p/SKILL.md +1082 -0
- package/skills/binance/p2p/references/agent-sapi-api.md +795 -0
- package/skills/binance/p2p/references/authentication.md +100 -0
- package/skills/binance/payment/SKILL.md +824 -0
- package/skills/binance/payment/common.py +560 -0
- package/skills/binance/payment/payment_skill.py +86 -0
- package/skills/binance/payment/receive.py +109 -0
- package/skills/binance/payment/references/setup-guide.md +77 -0
- package/skills/binance/payment/requirements.txt +4 -0
- package/skills/binance/payment/send.py +952 -0
- package/skills/binance/payment/send_extension/__init__.py +43 -0
- package/skills/binance/payment/send_extension/base.py +48 -0
- package/skills/binance/payment/send_extension/c2c.py +193 -0
- package/skills/binance/payment/send_extension/pix.py +316 -0
- package/skills/binance/square-post/README.md +62 -0
- package/skills/binance/square-post/SKILL.md +171 -0
- package/skills/binance/square-post/scripts/lib.mjs +175 -0
- package/skills/binance/square-post/scripts/post-image.mjs +80 -0
- package/skills/binance/square-post/scripts/post-text.mjs +41 -0
- package/skills/binance/square-post/scripts/post-video.mjs +110 -0
- package/skills/binance/square-post/scripts/save-key.mjs +34 -0
- package/skills/binance-web3/binance-agentic-wallet/SKILL.md +150 -0
- package/skills/binance-web3/binance-agentic-wallet/references/authentication.md +136 -0
- package/skills/binance-web3/binance-agentic-wallet/references/limit-order.md +204 -0
- package/skills/binance-web3/binance-agentic-wallet/references/market-order.md +179 -0
- package/skills/binance-web3/binance-agentic-wallet/references/prediction.md +489 -0
- package/skills/binance-web3/binance-agentic-wallet/references/preflight.md +66 -0
- package/skills/binance-web3/binance-agentic-wallet/references/security.md +47 -0
- package/skills/binance-web3/binance-agentic-wallet/references/send.md +53 -0
- package/skills/binance-web3/binance-agentic-wallet/references/wallet-setting.md +86 -0
- package/skills/binance-web3/binance-agentic-wallet/references/wallet-view.md +312 -0
- package/skills/binance-web3/binance-agentic-wallet/references/x402-payment.md +259 -0
- package/skills/binance-web3/binance-tokenized-securities-info/SKILL.md +613 -0
- package/skills/binance-web3/crypto-market-rank/SKILL.md +91 -0
- package/skills/binance-web3/crypto-market-rank/references/cli.md +219 -0
- package/skills/binance-web3/crypto-market-rank/scripts/cli.mjs +149 -0
- package/skills/binance-web3/meme-rush/SKILL.md +72 -0
- package/skills/binance-web3/meme-rush/references/cli.md +158 -0
- package/skills/binance-web3/meme-rush/scripts/cli.mjs +101 -0
- package/skills/binance-web3/query-address-info/SKILL.md +61 -0
- package/skills/binance-web3/query-address-info/references/cli.md +56 -0
- package/skills/binance-web3/query-address-info/scripts/cli.mjs +132 -0
- package/skills/binance-web3/query-token-audit/SKILL.md +162 -0
- package/skills/binance-web3/query-token-info/SKILL.md +83 -0
- package/skills/binance-web3/query-token-info/references/cli.md +135 -0
- package/skills/binance-web3/query-token-info/scripts/cli.mjs +112 -0
- package/skills/binance-web3/trading-signal/SKILL.md +66 -0
- package/skills/binance-web3/trading-signal/references/cli.md +90 -0
- package/skills/binance-web3/trading-signal/scripts/cli.mjs +92 -0
- package/skills/four-meme/four-guard/API-Contract-TaxToken.md +277 -0
- package/skills/four-meme/four-guard/API-CreateToken.02-02-2026.md +285 -0
- package/skills/four-meme/four-guard/API-Documents.03-03-2026.md +789 -0
- package/skills/four-meme/four-guard/AgentIdentifier.abi +585 -0
- package/skills/four-meme/four-guard/README.md +21 -0
- package/skills/four-meme/four-guard/SKILL.md +31 -0
- package/skills/four-meme/four-guard/TaxToken.abi +969 -0
- package/skills/four-meme/four-guard/TokenIdentifierSample.js_ +81 -0
- package/skills/four-meme/four-guard/TokenIdentifierSample.sol +69 -0
- package/skills/four-meme/four-guard/TokenManager.lite.abi +836 -0
- package/skills/four-meme/four-guard/TokenManager2.lite.abi +2325 -0
- package/skills/four-meme/four-guard/TokenManagerHelper3.abi +999 -0
- package/skills/four-meme/four-guard/go.mod +36 -0
- package/skills/four-meme/four-guard/go.sum +127 -0
- package/skills/four-meme/four-guard/main.go +183 -0
- package/skills/four-meme/four-meme-ai/SKILL.md +31 -0
- package/skills/four-meme/four-meme-ai/references/agent-creator-and-wallets.md +87 -0
- package/skills/four-meme/four-meme-ai/references/api-create-token.md +55 -0
- package/skills/four-meme/four-meme-ai/references/contract-addresses.md +47 -0
- package/skills/four-meme/four-meme-ai/references/create-token-scripts.md +131 -0
- package/skills/four-meme/four-meme-ai/references/errors.md +29 -0
- package/skills/four-meme/four-meme-ai/references/event-listening.md +75 -0
- package/skills/four-meme/four-meme-ai/references/execute-trade.md +31 -0
- package/skills/four-meme/four-meme-ai/references/tax-token-query.md +38 -0
- package/skills/four-meme/four-meme-ai/references/token-query-api.md +44 -0
- package/skills/four-meme/four-meme-ai/references/token-tax-info.md +77 -0
- package/skills/four-meme/four-meme-ai/scripts/8004-balance.ts +52 -0
- package/skills/four-meme/four-meme-ai/scripts/8004-register.ts +108 -0
- package/skills/four-meme/four-meme-ai/scripts/create-token-api.ts +321 -0
- package/skills/four-meme/four-meme-ai/scripts/create-token-chain.ts +102 -0
- package/skills/four-meme/four-meme-ai/scripts/create-token-instant.ts +106 -0
- package/skills/four-meme/four-meme-ai/scripts/execute-buy.ts +198 -0
- package/skills/four-meme/four-meme-ai/scripts/execute-sell.ts +150 -0
- package/skills/four-meme/four-meme-ai/scripts/get-public-config.ts +25 -0
- package/skills/four-meme/four-meme-ai/scripts/get-recent-events.ts +76 -0
- package/skills/four-meme/four-meme-ai/scripts/get-tax-token-info.ts +69 -0
- package/skills/four-meme/four-meme-ai/scripts/get-token-info.ts +94 -0
- package/skills/four-meme/four-meme-ai/scripts/quote-buy.ts +85 -0
- package/skills/four-meme/four-meme-ai/scripts/quote-sell.ts +66 -0
- package/skills/four-meme/four-meme-ai/scripts/send-token.ts +98 -0
- package/skills/four-meme/four-meme-ai/scripts/token-get.ts +31 -0
- package/skills/four-meme/four-meme-ai/scripts/token-list.ts +134 -0
- package/skills/four-meme/four-meme-ai/scripts/token-rankings.ts +162 -0
- package/skills/four-meme/four-meme-ai/scripts/verify-events.ts +47 -0
- package/skills/four-meme/four-meme-integration/SKILL.md +374 -0
- package/skills/four-meme/four-meme-integration/references/agent-creator-and-wallets.md +87 -0
- package/skills/four-meme/four-meme-integration/references/api-create-token.md +55 -0
- package/skills/four-meme/four-meme-integration/references/contract-addresses.md +47 -0
- package/skills/four-meme/four-meme-integration/references/create-token-scripts.md +131 -0
- package/skills/four-meme/four-meme-integration/references/errors.md +29 -0
- package/skills/four-meme/four-meme-integration/references/event-listening.md +75 -0
- package/skills/four-meme/four-meme-integration/references/execute-trade.md +31 -0
- package/skills/four-meme/four-meme-integration/references/tax-token-query.md +38 -0
- package/skills/four-meme/four-meme-integration/references/token-query-api.md +44 -0
- package/skills/four-meme/four-meme-integration/references/token-tax-info.md +77 -0
- package/skills/four-meme/four-meme-integration/scripts/8004-balance.ts +52 -0
- package/skills/four-meme/four-meme-integration/scripts/8004-register.ts +108 -0
- package/skills/four-meme/four-meme-integration/scripts/create-token-api.ts +321 -0
- package/skills/four-meme/four-meme-integration/scripts/create-token-chain.ts +102 -0
- package/skills/four-meme/four-meme-integration/scripts/create-token-instant.ts +106 -0
- package/skills/four-meme/four-meme-integration/scripts/execute-buy.ts +198 -0
- package/skills/four-meme/four-meme-integration/scripts/execute-sell.ts +150 -0
- package/skills/four-meme/four-meme-integration/scripts/get-public-config.ts +25 -0
- package/skills/four-meme/four-meme-integration/scripts/get-recent-events.ts +76 -0
- package/skills/four-meme/four-meme-integration/scripts/get-tax-token-info.ts +69 -0
- package/skills/four-meme/four-meme-integration/scripts/get-token-info.ts +94 -0
- package/skills/four-meme/four-meme-integration/scripts/quote-buy.ts +85 -0
- package/skills/four-meme/four-meme-integration/scripts/quote-sell.ts +66 -0
- package/skills/four-meme/four-meme-integration/scripts/send-token.ts +98 -0
- package/skills/four-meme/four-meme-integration/scripts/token-get.ts +31 -0
- package/skills/four-meme/four-meme-integration/scripts/token-list.ts +134 -0
- package/skills/four-meme/four-meme-integration/scripts/token-rankings.ts +162 -0
- package/skills/four-meme/four-meme-integration/scripts/verify-events.ts +47 -0
- package/skills/skillpp/contract-profiler/SKILL.md +118 -0
- package/skills/skillpp/newbie-tutor/SKILL.md +85 -0
- package/skills/skillpp/opportunity-board/SKILL.md +87 -0
- package/skills/skillpp/risk-fusion/SKILL.md +146 -0
- package/skills/skillpp/scam-pattern-lab/SKILL.md +115 -0
- package/skills/skillpp/wallet-doctor/SKILL.md +119 -0
- package/skills/skillpp/watchtower/SKILL.md +72 -0
- package/tests/compatibility/v0.1.0.json +117 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// skillpp.mjs - Skill++ Unified Executor v0.1
|
|
3
|
+
// Usage: node scripts/skillpp.mjs <command> '<input>'
|
|
4
|
+
//
|
|
5
|
+
// Commands:
|
|
6
|
+
// parse Parse input only (no execution)
|
|
7
|
+
// analyze Full token analysis (P_TOKEN_ANALYSIS)
|
|
8
|
+
// scan Chain opportunity scan (P_CHAIN_SCAN)
|
|
9
|
+
// audit Deep contract audit (P_DEEP_AUDIT)
|
|
10
|
+
// wallet Wallet portfolio analysis (P_WALLET_XRAY)
|
|
11
|
+
// signals Smart money signals (P_SMART_MONEY)
|
|
12
|
+
|
|
13
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
14
|
+
import { join, resolve, dirname } from 'path';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
import { spawnSync } from 'child_process';
|
|
17
|
+
|
|
18
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const ROOT = resolve(__dirname, '..');
|
|
20
|
+
const MANIFEST = JSON.parse(readFileSync(join(ROOT, 'skillpp.manifest.json'), 'utf-8'));
|
|
21
|
+
|
|
22
|
+
// Config
|
|
23
|
+
const CMD_PIPELINE_MAP = {
|
|
24
|
+
analyze: 'P_TOKEN_ANALYSIS',
|
|
25
|
+
scan: 'P_CHAIN_SCAN',
|
|
26
|
+
trade: 'P_TRADE_SAFETY',
|
|
27
|
+
audit: 'P_DEEP_AUDIT',
|
|
28
|
+
wallet: 'P_WALLET_XRAY',
|
|
29
|
+
signals: 'P_SMART_MONEY',
|
|
30
|
+
create: 'P_FOURMEME_CREATE',
|
|
31
|
+
};
|
|
32
|
+
const DRY_RUN = process.argv.includes('--dry-run');
|
|
33
|
+
const SHOW_PATHS = process.argv.includes('--show-paths');
|
|
34
|
+
|
|
35
|
+
// Input Parser
|
|
36
|
+
function parseInput(raw) {
|
|
37
|
+
const result = { raw, type: 'unknown' };
|
|
38
|
+
const value = raw.trim();
|
|
39
|
+
const chainAliases = {
|
|
40
|
+
bsc: { chain: 'BSC', chainId: '56' },
|
|
41
|
+
bnb: { chain: 'BSC', chainId: '56' },
|
|
42
|
+
ethereum: { chain: 'Ethereum', chainId: '1' },
|
|
43
|
+
eth: { chain: 'Ethereum', chainId: '1' },
|
|
44
|
+
base: { chain: 'Base', chainId: '8453' },
|
|
45
|
+
polygon: { chain: 'Polygon', chainId: '137' },
|
|
46
|
+
matic: { chain: 'Polygon', chainId: '137' },
|
|
47
|
+
solana: { chain: 'Solana', chainId: 'CT_501' },
|
|
48
|
+
sol: { chain: 'Solana', chainId: 'CT_501' },
|
|
49
|
+
};
|
|
50
|
+
const explorerPatterns = [
|
|
51
|
+
{ regex: /bscscan\.com\/address\/(0x[a-fA-F0-9]{40})/, chain: 'BSC', chainId: '56' },
|
|
52
|
+
{ regex: /etherscan\.io\/address\/(0x[a-fA-F0-9]{40})/, chain: 'Ethereum', chainId: '1' },
|
|
53
|
+
{ regex: /basescan\.org\/address\/(0x[a-fA-F0-9]{40})/, chain: 'Base', chainId: '8453' },
|
|
54
|
+
{ regex: /polygonscan\.com\/address\/(0x[a-fA-F0-9]{40})/, chain: 'Polygon', chainId: '137' },
|
|
55
|
+
];
|
|
56
|
+
for (const p of explorerPatterns) {
|
|
57
|
+
const m = raw.match(p.regex);
|
|
58
|
+
if (m) { result.type = 'explorer_url'; result.chain = p.chain; result.chainId = p.chainId; result.contractAddress = m[1]; result.sourceAvailable = raw.includes('#code'); return result; }
|
|
59
|
+
}
|
|
60
|
+
const alias = chainAliases[value.toLowerCase()];
|
|
61
|
+
if (alias) { result.type = 'chain_id'; result.chain = alias.chain; result.chainId = alias.chainId; return result; }
|
|
62
|
+
if (/^(1|56|8453|137|43114|42161|10|CT_501)$/.test(value)) { result.type = 'chain_id'; result.chainId = value; return result; }
|
|
63
|
+
const addrMatch = raw.match(/(0x[a-fA-F0-9]{40})/);
|
|
64
|
+
if (addrMatch) { result.type = 'address'; result.contractAddress = addrMatch[1]; return result; }
|
|
65
|
+
if (/^[A-Za-z][A-Za-z0-9]{1,10}$/.test(value)) { result.type = 'token_symbol'; result.tokenSymbol = value; return result; }
|
|
66
|
+
if (/\b(contract|pragma solidity|function)\b/.test(raw)) { result.type = 'source_code'; return result; }
|
|
67
|
+
result.type = 'intent';
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Checkpoint Engine
|
|
72
|
+
// Design: checkpoints pause execution with structured JSON.
|
|
73
|
+
// - BLOCKING checkpoints -> exit(10), AI must present to user, wait for confirmation
|
|
74
|
+
// - ADVISORY checkpoints -> inform only, continue execution
|
|
75
|
+
// - Resume support is intentionally explicit; blocking checkpoints stop execution until implemented
|
|
76
|
+
function emitCheckpoint(id, data) {
|
|
77
|
+
const def = MANIFEST.checkpoints?.find(c => c.id === id);
|
|
78
|
+
const output = {
|
|
79
|
+
checkpoint: id,
|
|
80
|
+
securityLevel: def?.securityLevel || 'ADVISORY',
|
|
81
|
+
canBypass: def?.canBypass !== false,
|
|
82
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
83
|
+
data,
|
|
84
|
+
action: def?.securityLevel === 'BLOCKING' ? 'STOP_AND_WAIT' : 'INFORM',
|
|
85
|
+
};
|
|
86
|
+
console.log(`\n[CHECKPOINT: ${id}] (${output.securityLevel})`);
|
|
87
|
+
console.log(JSON.stringify(output, null, 2));
|
|
88
|
+
if (output.securityLevel === 'BLOCKING' && !DRY_RUN) {
|
|
89
|
+
console.log(`\nExecution paused. AI must present this to user and wait for confirmation.`);
|
|
90
|
+
process.exit(10); // exit code 10 = checkpoint reached
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// CLI Runner
|
|
95
|
+
function runSkillCLI(skillName, command, paramsJson) {
|
|
96
|
+
const skill = MANIFEST.skills.find(s => s.name === skillName);
|
|
97
|
+
if (!skill) return { error: `skill not found: ${skillName}` };
|
|
98
|
+
if (!skill.execCommand) return { error: `no execCommand for: ${skillName}`, skill: skillName };
|
|
99
|
+
|
|
100
|
+
// Text-only skills (no CLI)
|
|
101
|
+
if (skill.execCommand.startsWith('AI ') || skill.execCommand.startsWith('curl') || skill.execCommand.startsWith('read-only')) {
|
|
102
|
+
return { note: `manual execution: ${skill.execCommand}`, skill: skillName };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Node CLI skills
|
|
106
|
+
if (skill.execCommand.startsWith('node ')) {
|
|
107
|
+
const scriptRel = skill.execCommand.replace('node ', '');
|
|
108
|
+
const scriptPath = join(ROOT, scriptRel);
|
|
109
|
+
if (!existsSync(scriptPath)) return { error: `script not found: ${scriptPath}`, skill: skillName };
|
|
110
|
+
|
|
111
|
+
if (DRY_RUN) return { note: `[DRY-RUN] node ${scriptRel} ${command}`, skill: skillName };
|
|
112
|
+
|
|
113
|
+
const args = paramsJson ? [scriptPath, command, paramsJson] : [scriptPath, command];
|
|
114
|
+
const child = spawnSync(process.execPath, args, {
|
|
115
|
+
encoding: 'utf-8',
|
|
116
|
+
timeout: 15000,
|
|
117
|
+
windowsHide: true,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (child.error) {
|
|
121
|
+
return { error: child.error.message, skill: skillName, exitCode: child.status ?? 1 };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const stdout = (child.stdout || '').trim();
|
|
125
|
+
const stderr = (child.stderr || '').trim();
|
|
126
|
+
if (child.status !== 0) {
|
|
127
|
+
return { error: stderr || stdout || `exit ${child.status}`, skill: skillName, exitCode: child.status };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try { return JSON.parse(stdout); } catch { return { raw: stdout, skill: skillName }; }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return { note: `manual: ${skill.execCommand}`, skill: skillName };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function commandExists(dep) {
|
|
137
|
+
const checker = process.platform === 'win32'
|
|
138
|
+
? { file: 'where.exe', args: [dep] }
|
|
139
|
+
: { file: 'which', args: [dep] };
|
|
140
|
+
const result = spawnSync(checker.file, checker.args, { stdio: 'pipe', windowsHide: true });
|
|
141
|
+
return result.status === 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function listDirFiles(relDir, suffix = '') {
|
|
145
|
+
const abs = join(ROOT, relDir);
|
|
146
|
+
if (!existsSync(abs)) return [];
|
|
147
|
+
return readdirSync(abs, { withFileTypes: true })
|
|
148
|
+
.filter(entry => entry.isFile() && (!suffix || entry.name.endsWith(suffix)))
|
|
149
|
+
.map(entry => entry.name)
|
|
150
|
+
.sort();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function pathCheck(relPath) {
|
|
154
|
+
const abs = join(ROOT, relPath);
|
|
155
|
+
const check = { path: relPath, exists: existsSync(abs) };
|
|
156
|
+
if (SHOW_PATHS) check.absolutePath = abs;
|
|
157
|
+
return check;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function externalCommandForSkill(skill) {
|
|
161
|
+
if (skill.execCommand === 'baw') return 'baw';
|
|
162
|
+
if (skill.execCommand === 'binance-cli') return 'binance-cli';
|
|
163
|
+
if (skill.execCommand === 'npx fourmeme') return 'fourmeme';
|
|
164
|
+
if (skill.execCommand?.startsWith('python3 ')) return process.platform === 'win32' ? 'python' : 'python3';
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function runtimeDependencyForSkill(skill, dep) {
|
|
169
|
+
const externalCommand = externalCommandForSkill(skill);
|
|
170
|
+
if (externalCommand) return externalCommand;
|
|
171
|
+
return dep;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function runDoctor() {
|
|
175
|
+
const adapters = listDirFiles('adapters', '.md');
|
|
176
|
+
const prompts = listDirFiles('prompts', '.md');
|
|
177
|
+
const schemas = listDirFiles('schemas', '.json');
|
|
178
|
+
const externalCli = {};
|
|
179
|
+
|
|
180
|
+
for (const skill of MANIFEST.skills) {
|
|
181
|
+
const command = externalCommandForSkill(skill);
|
|
182
|
+
if (!command) continue;
|
|
183
|
+
externalCli[command] = {
|
|
184
|
+
available: commandExists(command),
|
|
185
|
+
skill: skill.name,
|
|
186
|
+
execCommand: skill.execCommand,
|
|
187
|
+
installCmd: skill.installCmd || 'install manually',
|
|
188
|
+
requiredForWriteOps: !skill.readOnly,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const output = {
|
|
193
|
+
command: 'doctor',
|
|
194
|
+
status: 'ok',
|
|
195
|
+
packageRoot: SHOW_PATHS ? ROOT : '<redacted>',
|
|
196
|
+
privacy: SHOW_PATHS
|
|
197
|
+
? 'absolute paths shown because --show-paths was provided'
|
|
198
|
+
: 'absolute local paths redacted; run with --show-paths only in a private local session',
|
|
199
|
+
checks: {
|
|
200
|
+
manifest: pathCheck('skillpp.manifest.json'),
|
|
201
|
+
skillsDir: pathCheck('skills'),
|
|
202
|
+
scriptsDir: pathCheck('scripts'),
|
|
203
|
+
adaptersDir: pathCheck('adapters'),
|
|
204
|
+
promptsDir: pathCheck('prompts'),
|
|
205
|
+
schemasDir: pathCheck('schemas'),
|
|
206
|
+
rootSkill: pathCheck('SKILL.md'),
|
|
207
|
+
},
|
|
208
|
+
counts: {
|
|
209
|
+
skills: MANIFEST.skills.length,
|
|
210
|
+
pipelines: MANIFEST.pipelines.length,
|
|
211
|
+
checkpoints: MANIFEST.checkpoints?.length || 0,
|
|
212
|
+
adapters: adapters.length,
|
|
213
|
+
prompts: prompts.length,
|
|
214
|
+
schemas: schemas.length,
|
|
215
|
+
},
|
|
216
|
+
adapters,
|
|
217
|
+
prompts,
|
|
218
|
+
externalCli,
|
|
219
|
+
guidance: [
|
|
220
|
+
'Use relative paths from this report unless the user explicitly provides local paths.',
|
|
221
|
+
'Do not ask for machine-specific workspace paths in public issues or shared logs.',
|
|
222
|
+
'Missing external write CLIs are warnings until wallet, exchange, or Four.meme write operations are requested.',
|
|
223
|
+
],
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
console.log(JSON.stringify(output, null, 2));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function listSkills() {
|
|
230
|
+
const output = {
|
|
231
|
+
command: 'skills',
|
|
232
|
+
count: MANIFEST.skills.length,
|
|
233
|
+
skills: MANIFEST.skills.map(skill => ({
|
|
234
|
+
name: skill.name,
|
|
235
|
+
group: skill.group,
|
|
236
|
+
path: skill.path,
|
|
237
|
+
entry: skill.entry,
|
|
238
|
+
readOnly: skill.readOnly,
|
|
239
|
+
needsCheckpoint: skill.needsCheckpoint,
|
|
240
|
+
execCommand: skill.execCommand,
|
|
241
|
+
})),
|
|
242
|
+
};
|
|
243
|
+
console.log(JSON.stringify(output, null, 2));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Pipeline Executor
|
|
247
|
+
function executePipeline(pipelineId, parsed) {
|
|
248
|
+
const pipeline = MANIFEST.pipelines.find(p => p.id === pipelineId);
|
|
249
|
+
if (!pipeline) return { error: `pipeline not found: ${pipelineId}` };
|
|
250
|
+
|
|
251
|
+
// Default chainId for bare addresses (BSC)
|
|
252
|
+
const chainId = parsed.chainId || '56';
|
|
253
|
+
const addr = parsed.contractAddress; // unified address field
|
|
254
|
+
|
|
255
|
+
console.log(`\nPipeline: ${pipeline.name} (${pipelineId})`);
|
|
256
|
+
console.log(` Steps: ${pipeline.skills.join(' -> ')}`);
|
|
257
|
+
|
|
258
|
+
const results = {};
|
|
259
|
+
|
|
260
|
+
for (const [idx, skillName] of pipeline.skills.entries()) {
|
|
261
|
+
console.log(`\n-- Step ${idx + 1}: ${skillName} --`);
|
|
262
|
+
|
|
263
|
+
let result;
|
|
264
|
+
switch (skillName) {
|
|
265
|
+
case 'query-token-info':
|
|
266
|
+
if (pipelineId === 'P_DEEP_AUDIT' && parsed.type === 'source_code' && !addr) {
|
|
267
|
+
result = { note: 'skipped: source-code audit has no token address for token metadata lookup' };
|
|
268
|
+
} else {
|
|
269
|
+
result = runSkillCLI('query-token-info', 'search',
|
|
270
|
+
JSON.stringify({ keyword: addr || parsed.tokenSymbol || parsed.raw, chainIds: chainId }));
|
|
271
|
+
}
|
|
272
|
+
break;
|
|
273
|
+
case 'query-token-audit':
|
|
274
|
+
// Some pipelines discover token addresses inside previous results; the AI layer expands those.
|
|
275
|
+
if (pipelineId === 'P_WALLET_XRAY') {
|
|
276
|
+
result = { note: 'deferred: will audit each token from positions (not wallet address itself)' };
|
|
277
|
+
} else if (pipelineId === 'P_SMART_MONEY' && !addr) {
|
|
278
|
+
result = { note: 'deferred: will audit token addresses extracted from smart-money signals' };
|
|
279
|
+
} else if (addr) {
|
|
280
|
+
result = { note: 'step entered: curl POST audit API (pending AI execution)', contractAddress: addr, chainId };
|
|
281
|
+
} else {
|
|
282
|
+
result = { note: 'skipped: no address available' };
|
|
283
|
+
}
|
|
284
|
+
break;
|
|
285
|
+
case 'trading-signal':
|
|
286
|
+
result = runSkillCLI('trading-signal', 'smart-money',
|
|
287
|
+
JSON.stringify({ chainId, page: 1, pageSize: 10 }));
|
|
288
|
+
break;
|
|
289
|
+
case 'meme-rush':
|
|
290
|
+
if (pipelineId === 'P_CHAIN_SCAN') {
|
|
291
|
+
result = {
|
|
292
|
+
'meme-rush': runSkillCLI('meme-rush', 'meme-rush',
|
|
293
|
+
JSON.stringify({ chainId, rankType: 10, limit: 10 })),
|
|
294
|
+
'topic-rush': runSkillCLI('meme-rush', 'topic-rush',
|
|
295
|
+
JSON.stringify({ chainId, rankType: 10, sort: 10 })),
|
|
296
|
+
};
|
|
297
|
+
} else {
|
|
298
|
+
result = runSkillCLI('meme-rush', 'meme-rush',
|
|
299
|
+
JSON.stringify({ chainId, rankType: 10, limit: 10 }));
|
|
300
|
+
}
|
|
301
|
+
break;
|
|
302
|
+
case 'crypto-market-rank':
|
|
303
|
+
if (pipelineId === 'P_CHAIN_SCAN') {
|
|
304
|
+
result = {
|
|
305
|
+
'token-rank': runSkillCLI('crypto-market-rank', 'token-rank',
|
|
306
|
+
JSON.stringify({ chainId, rankType: 10, page: 1, size: 10 })),
|
|
307
|
+
'smart-money-inflow': runSkillCLI('crypto-market-rank', 'smart-money-inflow',
|
|
308
|
+
JSON.stringify({ chainId, period: '24h', page: 1, size: 10 })),
|
|
309
|
+
};
|
|
310
|
+
} else {
|
|
311
|
+
result = runSkillCLI('crypto-market-rank', 'token-rank',
|
|
312
|
+
JSON.stringify({ chainId, rankType: 10, page: 1, size: 10 }));
|
|
313
|
+
}
|
|
314
|
+
break;
|
|
315
|
+
case 'audit-plus':
|
|
316
|
+
case 'contract-profiler':
|
|
317
|
+
case 'risk-fusion':
|
|
318
|
+
case 'wallet-doctor':
|
|
319
|
+
case 'newbie-tutor':
|
|
320
|
+
case 'opportunity-board':
|
|
321
|
+
result = { note: `text-based skill, AI executes by reading ${skillName}/SKILL.md`, skill: skillName };
|
|
322
|
+
break;
|
|
323
|
+
case 'query-address-info':
|
|
324
|
+
if (addr) {
|
|
325
|
+
result = runSkillCLI('query-address-info', 'positions',
|
|
326
|
+
JSON.stringify({ address: addr, chainId, offset: 0 }));
|
|
327
|
+
} else {
|
|
328
|
+
result = { note: 'skipped: no wallet address' };
|
|
329
|
+
}
|
|
330
|
+
break;
|
|
331
|
+
case 'four-meme-integration':
|
|
332
|
+
result = { note: 'requires PRIVATE_KEY + user confirmation, manual execution only' };
|
|
333
|
+
break;
|
|
334
|
+
default:
|
|
335
|
+
result = { note: `no executor mapping for: ${skillName}` };
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
results[skillName] = result;
|
|
339
|
+
if (result?.error) {
|
|
340
|
+
console.error(` FAIL ${skillName}: ${result.error}`);
|
|
341
|
+
} else if (result?.note) {
|
|
342
|
+
console.log(` NOTE ${skillName}: ${result.note}`);
|
|
343
|
+
} else {
|
|
344
|
+
console.log(` PASS ${skillName}: success`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Checkpoints
|
|
349
|
+
if (pipeline.checkpoints.length > 0) {
|
|
350
|
+
console.log(`\n-- Checkpoints --`);
|
|
351
|
+
for (const cpId of pipeline.checkpoints) {
|
|
352
|
+
emitCheckpoint(cpId, { pipeline: pipelineId, results: Object.keys(results) });
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return results;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Main
|
|
360
|
+
async function main() {
|
|
361
|
+
const args = process.argv.slice(2).filter(a => a !== '--dry-run' && a !== '--show-paths');
|
|
362
|
+
const [command, inputStr] = args;
|
|
363
|
+
|
|
364
|
+
if (!command || command === '--help' || command === '-h') {
|
|
365
|
+
console.log(`Skill++ Executor v0.1
|
|
366
|
+
Usage: node scripts/skillpp.mjs <command> '<input>' [--dry-run]
|
|
367
|
+
|
|
368
|
+
Commands:
|
|
369
|
+
parse "<input>" Parse only (no execution)
|
|
370
|
+
analyze "<address|url>" P_TOKEN_ANALYSIS
|
|
371
|
+
scan "<chainId>" P_CHAIN_SCAN
|
|
372
|
+
trade "<address|url>" P_TRADE_SAFETY
|
|
373
|
+
audit "<address|url>" P_DEEP_AUDIT
|
|
374
|
+
wallet "<address>" P_WALLET_XRAY
|
|
375
|
+
signals "<chainId>" P_SMART_MONEY
|
|
376
|
+
create "<intent>" P_FOURMEME_CREATE
|
|
377
|
+
doctor Check package root, manifest, skills, and external CLIs
|
|
378
|
+
skills List registered skills from the manifest
|
|
379
|
+
|
|
380
|
+
Flags:
|
|
381
|
+
--dry-run Show execution plan without running
|
|
382
|
+
--show-paths Include absolute local paths in doctor output
|
|
383
|
+
`);
|
|
384
|
+
process.exit(0);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (command === 'doctor') {
|
|
388
|
+
runDoctor();
|
|
389
|
+
process.exit(0);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (command === 'skills') {
|
|
393
|
+
listSkills();
|
|
394
|
+
process.exit(0);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Parse input
|
|
398
|
+
const parsed = parseInput(inputStr || '');
|
|
399
|
+
console.log(JSON.stringify({ command, parsed }, null, 2));
|
|
400
|
+
|
|
401
|
+
if (command === 'parse') process.exit(0);
|
|
402
|
+
|
|
403
|
+
// Route: command takes priority over parsed input type
|
|
404
|
+
const pipelineId = CMD_PIPELINE_MAP[command];
|
|
405
|
+
if (!pipelineId) {
|
|
406
|
+
console.error(`Unknown command: ${command}. Use --help for usage.`);
|
|
407
|
+
process.exit(1);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (DRY_RUN) console.log('\nDRY RUN MODE - no real API calls\n');
|
|
411
|
+
|
|
412
|
+
// Check deps
|
|
413
|
+
const pipeline = MANIFEST.pipelines.find(p => p.id === pipelineId);
|
|
414
|
+
const issues = [];
|
|
415
|
+
for (const sn of pipeline.skills) {
|
|
416
|
+
const skill = MANIFEST.skills.find(s => s.name === sn);
|
|
417
|
+
if (!skill) continue;
|
|
418
|
+
for (const dep of skill.deps || []) {
|
|
419
|
+
if (dep === 'node' || dep === 'curl') continue;
|
|
420
|
+
const runtimeDep = runtimeDependencyForSkill(skill, dep);
|
|
421
|
+
if (!commandExists(runtimeDep)) {
|
|
422
|
+
issues.push({ skill: sn, dep: runtimeDep, package: dep, installCmd: skill.installCmd || 'manual install' });
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (issues.length > 0) {
|
|
427
|
+
console.log('\nMissing dependencies:');
|
|
428
|
+
issues.forEach(i => console.log(` ${i.skill}: ${i.dep} -> ${i.installCmd}`));
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Execute
|
|
432
|
+
const results = executePipeline(pipelineId, parsed);
|
|
433
|
+
|
|
434
|
+
// Build handoff
|
|
435
|
+
const handoff = {
|
|
436
|
+
_meta: { pipeline: pipelineId, timestamp: Math.floor(Date.now() / 1000), source: 'skillpp-executor' },
|
|
437
|
+
input: { raw: inputStr || '', type: parsed.type, chainId: parseInput(inputStr || '').chainId || '56', address: parseInput(inputStr || '').contractAddress },
|
|
438
|
+
results,
|
|
439
|
+
nextActions: [
|
|
440
|
+
{ action: 'Deep audit contract', pipeline: 'P_DEEP_AUDIT', condition: 'if source code available' },
|
|
441
|
+
{ action: 'Check smart money', pipeline: 'P_SMART_MONEY' },
|
|
442
|
+
{ action: 'View wallet holdings', pipeline: 'P_WALLET_XRAY', condition: 'if wallet address provided' },
|
|
443
|
+
],
|
|
444
|
+
};
|
|
445
|
+
console.log(`\nHandoff:\n${JSON.stringify(handoff, null, 2)}`);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
await main();
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// skillpp-validate.mjs - Skill++ manifest and schema validator
|
|
3
|
+
// Usage: node scripts/validate-skillpp.mjs [--strict]
|
|
4
|
+
// Exits 0 on pass, 1 on warnings, 2 on errors.
|
|
5
|
+
|
|
6
|
+
import { readFileSync, readdirSync, statSync, existsSync } from 'fs';
|
|
7
|
+
import { join, resolve, dirname } from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const ROOT = resolve(__dirname, '..');
|
|
12
|
+
const STRICT = process.argv.includes('--strict');
|
|
13
|
+
|
|
14
|
+
let errors = 0;
|
|
15
|
+
let warnings = 0;
|
|
16
|
+
|
|
17
|
+
function err(msg) { console.error(`FAIL ${msg}`); errors++; }
|
|
18
|
+
function warn(msg) { console.warn(`WARN ${msg}`); warnings++; }
|
|
19
|
+
function ok(msg) { console.log(`PASS ${msg}`); }
|
|
20
|
+
|
|
21
|
+
// 1. Check manifest exists and is valid JSON
|
|
22
|
+
console.log('\n-- Manifest --');
|
|
23
|
+
const manifestPath = join(ROOT, 'skillpp.manifest.json');
|
|
24
|
+
if (!existsSync(manifestPath)) {
|
|
25
|
+
err('skillpp.manifest.json not found');
|
|
26
|
+
} else {
|
|
27
|
+
try {
|
|
28
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
29
|
+
ok(`manifest valid JSON (${manifest.skills.length} skills, ${manifest.pipelines.length} pipelines)`);
|
|
30
|
+
|
|
31
|
+
// Check each skill path exists
|
|
32
|
+
for (const skill of manifest.skills) {
|
|
33
|
+
const skillPath = join(ROOT, skill.path, skill.entry);
|
|
34
|
+
if (!existsSync(skillPath)) {
|
|
35
|
+
err(`skill path not found: ${skill.path}${skill.entry}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} catch (e) {
|
|
39
|
+
err(`manifest parse error: ${e.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 2. Check all SKILL.md files for BOM and frontmatter
|
|
44
|
+
console.log('\n-- SKILL.md Checks --');
|
|
45
|
+
function walkDir(dir) {
|
|
46
|
+
const files = [];
|
|
47
|
+
try {
|
|
48
|
+
for (const entry of readdirSync(dir)) {
|
|
49
|
+
const full = join(dir, entry);
|
|
50
|
+
if (entry.startsWith('.') || entry === 'node_modules') continue;
|
|
51
|
+
try {
|
|
52
|
+
if (statSync(full).isDirectory()) {
|
|
53
|
+
files.push(...walkDir(full));
|
|
54
|
+
} else if (entry === 'SKILL.md') {
|
|
55
|
+
files.push(full);
|
|
56
|
+
}
|
|
57
|
+
} catch {}
|
|
58
|
+
}
|
|
59
|
+
} catch {}
|
|
60
|
+
return files;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const skillFiles = walkDir(join(ROOT, 'skills'));
|
|
64
|
+
// Also check root SKILL.md
|
|
65
|
+
if (existsSync(join(ROOT, 'SKILL.md'))) {
|
|
66
|
+
skillFiles.push(join(ROOT, 'SKILL.md'));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const f of skillFiles) {
|
|
70
|
+
const rel = f.replace(ROOT, '').replace(/\\/g, '/');
|
|
71
|
+
try {
|
|
72
|
+
const raw = readFileSync(f);
|
|
73
|
+
let buf = raw;
|
|
74
|
+
if (raw[0] === 0xEF && raw[1] === 0xBB && raw[2] === 0xBF) {
|
|
75
|
+
buf = raw.subarray(3);
|
|
76
|
+
err(`BOM detected: ${rel}`);
|
|
77
|
+
}
|
|
78
|
+
const text = buf.toString('utf-8');
|
|
79
|
+
|
|
80
|
+
// Frontmatter check
|
|
81
|
+
const fm = /^---\s*\n([\s\S]*?)\n---/.exec(text);
|
|
82
|
+
if (!fm) {
|
|
83
|
+
err(`no frontmatter: ${rel}`);
|
|
84
|
+
} else {
|
|
85
|
+
const frontmatter = fm[1];
|
|
86
|
+
const hasName = /^name:\s*\S/m.test(frontmatter);
|
|
87
|
+
const hasDesc = /^description:\s*\S/m.test(frontmatter);
|
|
88
|
+
|
|
89
|
+
if (!hasName) err(`missing 'name' in frontmatter: ${rel}`);
|
|
90
|
+
if (!hasDesc) err(`missing 'description' in frontmatter: ${rel}`);
|
|
91
|
+
|
|
92
|
+
// Non-standard fields (official spec allows: name/description/license/allowed-tools/metadata)
|
|
93
|
+
const nonStandard = ['version', 'category', 'capabilities', 'trigger'];
|
|
94
|
+
for (const field of nonStandard) {
|
|
95
|
+
if (new RegExp(`^${field}:`, 'm').test(frontmatter)) {
|
|
96
|
+
if (STRICT) {
|
|
97
|
+
err(`non-standard field '${field}' in frontmatter: ${rel}`);
|
|
98
|
+
} else {
|
|
99
|
+
warn(`non-standard field '${field}' in frontmatter: ${rel} (use --strict to error)`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} catch (e) {
|
|
105
|
+
err(`read error ${rel}: ${e.message}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
ok(`${skillFiles.length} SKILL.md files checked`);
|
|
109
|
+
|
|
110
|
+
// 3. Check schemas directory
|
|
111
|
+
console.log('\n-- Schemas --');
|
|
112
|
+
const schemasDir = join(ROOT, 'schemas');
|
|
113
|
+
const requiredSchemas = ['handoff.schema.json', 'token.schema.json', 'audit.schema.json', 'checkpoint.schema.json'];
|
|
114
|
+
for (const s of requiredSchemas) {
|
|
115
|
+
const sp = join(schemasDir, s);
|
|
116
|
+
if (!existsSync(sp)) {
|
|
117
|
+
err(`schema missing: ${s}`);
|
|
118
|
+
} else {
|
|
119
|
+
try {
|
|
120
|
+
JSON.parse(readFileSync(sp, 'utf-8'));
|
|
121
|
+
ok(`${s} valid`);
|
|
122
|
+
} catch (e) {
|
|
123
|
+
err(`${s} invalid JSON: ${e.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 4. Summary
|
|
129
|
+
console.log(`\n-- Result --`);
|
|
130
|
+
console.log(`Errors: ${errors}, Warnings: ${warnings}`);
|
|
131
|
+
if (errors > 0) {
|
|
132
|
+
console.log('STATUS: FAIL');
|
|
133
|
+
process.exit(2);
|
|
134
|
+
} else if (warnings > 0) {
|
|
135
|
+
console.log('STATUS: PASS WITH WARNINGS');
|
|
136
|
+
process.exit(1);
|
|
137
|
+
} else {
|
|
138
|
+
console.log('STATUS: PASS');
|
|
139
|
+
process.exit(0);
|
|
140
|
+
}
|