skillware 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ARPA Hellenic Logical Systems
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,224 @@
1
+ Metadata-Version: 2.4
2
+ Name: skillware
3
+ Version: 0.1.0
4
+ Summary: A framework for modular, self-contained AI skills.
5
+ Author-email: ARPA Hellenic Logic Systems <skillware-os@arpacorp.net>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 ARPA Hellenic Logical Systems
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://arpacorp.net
29
+ Project-URL: Repository, https://github.com/arpahls/skillware
30
+ Classifier: Programming Language :: Python :: 3
31
+ Classifier: License :: OSI Approved :: MIT License
32
+ Classifier: Operating System :: OS Independent
33
+ Requires-Python: >=3.10
34
+ Description-Content-Type: text/markdown
35
+ License-File: LICENSE
36
+ Requires-Dist: requests
37
+ Requires-Dist: pyyaml
38
+ Requires-Dist: anthropic
39
+ Requires-Dist: google-generativeai
40
+ Requires-Dist: pymupdf
41
+ Provides-Extra: dev
42
+ Requires-Dist: pytest; extra == "dev"
43
+ Requires-Dist: pytest-mock; extra == "dev"
44
+ Requires-Dist: flake8; extra == "dev"
45
+ Requires-Dist: black; extra == "dev"
46
+ Dynamic: license-file
47
+
48
+ <div align="center">
49
+ <img src="assets/skillware_logo.png" alt="Skillware Logo" width="400px" />
50
+
51
+ A Python framework for modular, self-contained skill management for machines.
52
+ </div>
53
+
54
+ <br/>
55
+
56
+ <div align="center">
57
+ <img src="https://img.shields.io/badge/License-MIT-efcefa?style=flat-square" alt="License">
58
+ <img src="https://img.shields.io/badge/Python-3.11+-bae6fd?style=flat-square" alt="Python Version">
59
+ <img src="https://img.shields.io/badge/PyPI-v0.1.0-bbf7d0?style=flat-square" alt="PyPI Version">
60
+ </div>
61
+
62
+ <br/>
63
+
64
+ <div align="center">
65
+ <a href="#mission">Mission</a> •
66
+ <a href="#repository-structure">Architecture</a> •
67
+ <a href="#quick-start">Quick Start</a> •
68
+ <a href="#documentation">Documentation</a> •
69
+ <a href="#contributing">Contributing</a> •
70
+ <a href="#comparison">Comparison</a> •
71
+ <a href="#contact">Contact</a>
72
+ </div>
73
+
74
+ ---
75
+
76
+ **Skillware** is an open-source framework and registry for modular, actionable Agent capabilities. It treats **Skills** as installable content, decoupling capability from intelligence. Just as `apt-get` installs software and `pip` installs libraries, `skillware` installs *know-how* for AI agents.
77
+
78
+ > "I know Kung Fu." - Neo
79
+
80
+ ## Mission
81
+
82
+ The AI ecosystem is fragmented. Developers often re-invent tool definitions, system prompts, and safety rules for every project. **Skillware** supplies a standard to package capabilities into self-contained units that work across **Gemini**, **Claude**, **GPT**, and **Llama**.
83
+
84
+ A **Skill** in this framework provides everything an Agent needs to master a domain:
85
+
86
+ 1. **Logic**: Executable Python code.
87
+ 2. **Cognition**: System instructions and "cognitive maps".
88
+ 3. **Governance**: Constitution and safety boundaries.
89
+ 4. **Interface**: Standardized schemas for LLM tool calling.
90
+
91
+ ## Repository Structure
92
+
93
+ This repository is organized into a core framework, a registry of skills, and documentation.
94
+
95
+ ```text
96
+ Skillware/
97
+ ├── skillware/ # Core Framework Package
98
+ │ └── core/
99
+ │ ├── base_skill.py # Abstract Base Class for skills
100
+ │ ├── loader.py # Universal Skill Loader & Model Adapter
101
+ │ └── env.py # Environment Management
102
+ ├── skills/ # Skill Registry (Domain-driven)
103
+ │ └── finance/
104
+ │ └── wallet_screening/
105
+ │ ├── skill.py # Logic
106
+ │ ├── manifest.yaml # Metadata & Constitution
107
+ │ ├── instructions.md # Cognitive Map
108
+ │ ├── card.json # UI Presentation
109
+ │ ├── data/ # Integrated Knowledge Base
110
+ │ └── maintenance/ # Maintenance Tools
111
+ │ └── office/
112
+ │ └── pdf_form_filler/
113
+ │ ├── skill.py # Logic
114
+ │ ├── manifest.yaml # Metadata
115
+ │ ├── instructions.md # Cognitive Map
116
+ │ ├── utils.py # PDF Processing
117
+ │ └── card.json # UI Presentation
118
+ ├── templates/ # New Skill Templates
119
+ │ └── python_skill/ # Standard Python Skill Template
120
+ ├── examples/ # Reference Implementations
121
+ │ ├── gemini_wallet_check.py # Google Gemini Integration
122
+ │ ├── claude_wallet_check.py # Anthropic Claude Integration
123
+ │ ├── gemini_pdf_form_filler.py
124
+ │ └── claude_pdf_form_filler.py
125
+ ├── docs/ # Comprehensive Documentation
126
+ │ ├── introduction.md # Philosophy & Design
127
+ │ ├── usage/ # Integration Guides
128
+ │ └── skills/ # Skill Reference Cards
129
+ └── COMPARISON.md # Comparison vs. Anthropic Skills / MCP
130
+ ```
131
+
132
+ ## Quick Start
133
+
134
+ ### 1. Installation
135
+
136
+ You can install Skillware directly from GitHub:
137
+
138
+ ```bash
139
+ pip install git+https://github.com/arpahls/skillware.git
140
+ ```
141
+
142
+ Or for development, clone the repository and install in editable mode:
143
+
144
+ ```bash
145
+ git clone https://github.com/arpahls/skillware.git
146
+ cd skillware
147
+ pip install -e .
148
+ ```
149
+
150
+ > **Note**: Individual skills may have their own dependencies. The `SkillLoader` validates `manifest.yaml` and warns of missing packages (e.g., `requests`, `pandas`) upon loading a skill.
151
+
152
+ ### 2. Configuration
153
+
154
+ Create a `.env` file with your API keys (e.g., Google Gemini API Key):
155
+
156
+ ```ini
157
+ GOOGLE_API_KEY="your_key"
158
+ ```
159
+
160
+ ### 3. Usage Example (Gemini)
161
+
162
+ ```python
163
+ import google.generativeai as genai
164
+ from skillware.core.loader import SkillLoader
165
+ from skillware.core.env import load_env_file
166
+
167
+ # Load Environment
168
+ load_env_file()
169
+
170
+ # 1. Load the Skill
171
+ # The loader reads the code, manifest, and instructions automatically
172
+ skill_bundle = SkillLoader.load_skill("finance/wallet_screening")
173
+
174
+ # 2. Model & Chat Setup
175
+ model = genai.GenerativeModel(
176
+ 'gemini-2.5-flash',
177
+ tools=[SkillLoader.to_gemini_tool(skill_bundle)], # The "Adapter"
178
+ system_instruction=skill_bundle['instructions'] # The "Mind"
179
+ )
180
+ chat = model.start_chat(enable_automatic_function_calling=True)
181
+
182
+ # 3. Agent Loop
183
+ # The SDK handles the loop: model -> tool call -> execution -> result -> model reply.
184
+ response = chat.send_message("Screen wallet 0xd8dA... for risks.")
185
+ print(response.text)
186
+ ```
187
+
188
+ ## Documentation
189
+
190
+ * **[Core Logic & Philosophy](docs/introduction.md)**: Details on how Skillware decouples Logic, Cognition, and Governance.
191
+ * **[Usage Guide: Gemini](docs/usage/gemini.md)**: Integration with Google's GenAI SDK.
192
+ * **[Usage Guide: Claude](docs/usage/claude.md)**: Integration with Anthropic's SDK.
193
+ * **[Skill Library](docs/skills/README.md)**: Available capabilities.
194
+
195
+ ## Contributing
196
+
197
+ We are building the "App Store" for Agents and require professional, robust, and safe skills.
198
+
199
+ Please read **[CONTRIBUTING.md](CONTRIBUTING.md)** for guidelines on folder structure, manifest schemas, and safety constitutions.
200
+
201
+ ## Comparison
202
+
203
+ Skillware differs from the Model Context Protocol (MCP) or Anthropic's Skills repository in the following ways:
204
+
205
+ * **Model Agnostic**: Native adapters for Gemini, Claude, and OpenAI.
206
+ * **Code-First**: Skills are executable Python packages, not just server specs.
207
+ * **Runtime-Focused**: Provides tools for the application, not just recipes for an IDE.
208
+
209
+ [Read the full comparison here](COMPARISON.md).
210
+
211
+ ## Contact
212
+
213
+ For questions, suggestions, or contributions, please open an issue or reach out to us:
214
+
215
+ * **Email**: [skillware-os@arpacorp.net](mailto:skillware-os@arpacorp.net)
216
+ * **Issues**: [GitHub Issues](https://github.com/arpahls/skillware/issues)
217
+
218
+ ---
219
+
220
+ <div align="center">
221
+ <img src="assets/arpalogo.png" alt="ARPA Logo" width="50px" />
222
+ <br/>
223
+ Built & Maintained by ARPA Hellenic Logical Systems & the Community
224
+ </div>
@@ -0,0 +1,177 @@
1
+ <div align="center">
2
+ <img src="assets/skillware_logo.png" alt="Skillware Logo" width="400px" />
3
+
4
+ A Python framework for modular, self-contained skill management for machines.
5
+ </div>
6
+
7
+ <br/>
8
+
9
+ <div align="center">
10
+ <img src="https://img.shields.io/badge/License-MIT-efcefa?style=flat-square" alt="License">
11
+ <img src="https://img.shields.io/badge/Python-3.11+-bae6fd?style=flat-square" alt="Python Version">
12
+ <img src="https://img.shields.io/badge/PyPI-v0.1.0-bbf7d0?style=flat-square" alt="PyPI Version">
13
+ </div>
14
+
15
+ <br/>
16
+
17
+ <div align="center">
18
+ <a href="#mission">Mission</a> •
19
+ <a href="#repository-structure">Architecture</a> •
20
+ <a href="#quick-start">Quick Start</a> •
21
+ <a href="#documentation">Documentation</a> •
22
+ <a href="#contributing">Contributing</a> •
23
+ <a href="#comparison">Comparison</a> •
24
+ <a href="#contact">Contact</a>
25
+ </div>
26
+
27
+ ---
28
+
29
+ **Skillware** is an open-source framework and registry for modular, actionable Agent capabilities. It treats **Skills** as installable content, decoupling capability from intelligence. Just as `apt-get` installs software and `pip` installs libraries, `skillware` installs *know-how* for AI agents.
30
+
31
+ > "I know Kung Fu." - Neo
32
+
33
+ ## Mission
34
+
35
+ The AI ecosystem is fragmented. Developers often re-invent tool definitions, system prompts, and safety rules for every project. **Skillware** supplies a standard to package capabilities into self-contained units that work across **Gemini**, **Claude**, **GPT**, and **Llama**.
36
+
37
+ A **Skill** in this framework provides everything an Agent needs to master a domain:
38
+
39
+ 1. **Logic**: Executable Python code.
40
+ 2. **Cognition**: System instructions and "cognitive maps".
41
+ 3. **Governance**: Constitution and safety boundaries.
42
+ 4. **Interface**: Standardized schemas for LLM tool calling.
43
+
44
+ ## Repository Structure
45
+
46
+ This repository is organized into a core framework, a registry of skills, and documentation.
47
+
48
+ ```text
49
+ Skillware/
50
+ ├── skillware/ # Core Framework Package
51
+ │ └── core/
52
+ │ ├── base_skill.py # Abstract Base Class for skills
53
+ │ ├── loader.py # Universal Skill Loader & Model Adapter
54
+ │ └── env.py # Environment Management
55
+ ├── skills/ # Skill Registry (Domain-driven)
56
+ │ └── finance/
57
+ │ └── wallet_screening/
58
+ │ ├── skill.py # Logic
59
+ │ ├── manifest.yaml # Metadata & Constitution
60
+ │ ├── instructions.md # Cognitive Map
61
+ │ ├── card.json # UI Presentation
62
+ │ ├── data/ # Integrated Knowledge Base
63
+ │ └── maintenance/ # Maintenance Tools
64
+ │ └── office/
65
+ │ └── pdf_form_filler/
66
+ │ ├── skill.py # Logic
67
+ │ ├── manifest.yaml # Metadata
68
+ │ ├── instructions.md # Cognitive Map
69
+ │ ├── utils.py # PDF Processing
70
+ │ └── card.json # UI Presentation
71
+ ├── templates/ # New Skill Templates
72
+ │ └── python_skill/ # Standard Python Skill Template
73
+ ├── examples/ # Reference Implementations
74
+ │ ├── gemini_wallet_check.py # Google Gemini Integration
75
+ │ ├── claude_wallet_check.py # Anthropic Claude Integration
76
+ │ ├── gemini_pdf_form_filler.py
77
+ │ └── claude_pdf_form_filler.py
78
+ ├── docs/ # Comprehensive Documentation
79
+ │ ├── introduction.md # Philosophy & Design
80
+ │ ├── usage/ # Integration Guides
81
+ │ └── skills/ # Skill Reference Cards
82
+ └── COMPARISON.md # Comparison vs. Anthropic Skills / MCP
83
+ ```
84
+
85
+ ## Quick Start
86
+
87
+ ### 1. Installation
88
+
89
+ You can install Skillware directly from GitHub:
90
+
91
+ ```bash
92
+ pip install git+https://github.com/arpahls/skillware.git
93
+ ```
94
+
95
+ Or for development, clone the repository and install in editable mode:
96
+
97
+ ```bash
98
+ git clone https://github.com/arpahls/skillware.git
99
+ cd skillware
100
+ pip install -e .
101
+ ```
102
+
103
+ > **Note**: Individual skills may have their own dependencies. The `SkillLoader` validates `manifest.yaml` and warns of missing packages (e.g., `requests`, `pandas`) upon loading a skill.
104
+
105
+ ### 2. Configuration
106
+
107
+ Create a `.env` file with your API keys (e.g., Google Gemini API Key):
108
+
109
+ ```ini
110
+ GOOGLE_API_KEY="your_key"
111
+ ```
112
+
113
+ ### 3. Usage Example (Gemini)
114
+
115
+ ```python
116
+ import google.generativeai as genai
117
+ from skillware.core.loader import SkillLoader
118
+ from skillware.core.env import load_env_file
119
+
120
+ # Load Environment
121
+ load_env_file()
122
+
123
+ # 1. Load the Skill
124
+ # The loader reads the code, manifest, and instructions automatically
125
+ skill_bundle = SkillLoader.load_skill("finance/wallet_screening")
126
+
127
+ # 2. Model & Chat Setup
128
+ model = genai.GenerativeModel(
129
+ 'gemini-2.5-flash',
130
+ tools=[SkillLoader.to_gemini_tool(skill_bundle)], # The "Adapter"
131
+ system_instruction=skill_bundle['instructions'] # The "Mind"
132
+ )
133
+ chat = model.start_chat(enable_automatic_function_calling=True)
134
+
135
+ # 3. Agent Loop
136
+ # The SDK handles the loop: model -> tool call -> execution -> result -> model reply.
137
+ response = chat.send_message("Screen wallet 0xd8dA... for risks.")
138
+ print(response.text)
139
+ ```
140
+
141
+ ## Documentation
142
+
143
+ * **[Core Logic & Philosophy](docs/introduction.md)**: Details on how Skillware decouples Logic, Cognition, and Governance.
144
+ * **[Usage Guide: Gemini](docs/usage/gemini.md)**: Integration with Google's GenAI SDK.
145
+ * **[Usage Guide: Claude](docs/usage/claude.md)**: Integration with Anthropic's SDK.
146
+ * **[Skill Library](docs/skills/README.md)**: Available capabilities.
147
+
148
+ ## Contributing
149
+
150
+ We are building the "App Store" for Agents and require professional, robust, and safe skills.
151
+
152
+ Please read **[CONTRIBUTING.md](CONTRIBUTING.md)** for guidelines on folder structure, manifest schemas, and safety constitutions.
153
+
154
+ ## Comparison
155
+
156
+ Skillware differs from the Model Context Protocol (MCP) or Anthropic's Skills repository in the following ways:
157
+
158
+ * **Model Agnostic**: Native adapters for Gemini, Claude, and OpenAI.
159
+ * **Code-First**: Skills are executable Python packages, not just server specs.
160
+ * **Runtime-Focused**: Provides tools for the application, not just recipes for an IDE.
161
+
162
+ [Read the full comparison here](COMPARISON.md).
163
+
164
+ ## Contact
165
+
166
+ For questions, suggestions, or contributions, please open an issue or reach out to us:
167
+
168
+ * **Email**: [skillware-os@arpacorp.net](mailto:skillware-os@arpacorp.net)
169
+ * **Issues**: [GitHub Issues](https://github.com/arpahls/skillware/issues)
170
+
171
+ ---
172
+
173
+ <div align="center">
174
+ <img src="assets/arpalogo.png" alt="ARPA Logo" width="50px" />
175
+ <br/>
176
+ Built & Maintained by ARPA Hellenic Logical Systems & the Community
177
+ </div>
@@ -0,0 +1,38 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "skillware"
7
+ version = "0.1.0"
8
+ description = "A framework for modular, self-contained AI skills."
9
+ readme = "README.md"
10
+ authors = [
11
+ { name = "ARPA Hellenic Logic Systems", email = "skillware-os@arpacorp.net" },
12
+ ]
13
+ urls = { "Homepage" = "https://arpacorp.net", "Repository" = "https://github.com/arpahls/skillware" }
14
+ license = { file = "LICENSE" }
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ ]
20
+ dependencies = [
21
+ "requests",
22
+ "pyyaml",
23
+ "anthropic",
24
+ "google-generativeai",
25
+ "pymupdf",
26
+ ]
27
+ requires-python = ">=3.10"
28
+
29
+ [project.optional-dependencies]
30
+ dev = [
31
+ "pytest",
32
+ "pytest-mock",
33
+ "flake8",
34
+ "black",
35
+ ]
36
+
37
+ [tool.setuptools.packages.find]
38
+ include = ["skillware*", "skills*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from .skill import WalletScreeningSkill
2
+
3
+ __all__ = ["WalletScreeningSkill"]
@@ -0,0 +1,134 @@
1
+ import os
2
+ import csv
3
+ import json
4
+ import shutil
5
+ from datetime import datetime
6
+
7
+ # Paths relative to this script (skillware/skills/finance/wallet_screening/maintenance/)
8
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
9
+ NEW_NORM_DIR = os.path.join(BASE_DIR, "new_norm")
10
+ PAST_NORM_DIR = os.path.join(BASE_DIR, "past_norm")
11
+ # Data goes up one level to the skill's data folder
12
+ DATASETS_DIR = os.path.abspath(os.path.join(BASE_DIR, "..", "data"))
13
+ LOGS_DIR = os.path.join(BASE_DIR, "norm_logs")
14
+
15
+ os.makedirs(NEW_NORM_DIR, exist_ok=True)
16
+ os.makedirs(PAST_NORM_DIR, exist_ok=True)
17
+ os.makedirs(DATASETS_DIR, exist_ok=True)
18
+ os.makedirs(LOGS_DIR, exist_ok=True)
19
+
20
+
21
+ def normalize_israel_nbctf_csv(filepath):
22
+ norm = []
23
+ with open(filepath, encoding="utf-8-sig") as f:
24
+ reader = csv.DictReader(f)
25
+ for row in reader:
26
+ if row.get("schema", "").strip().lower() == "wallet":
27
+ address = row.get("account/wallet_id", "").strip().lower()
28
+ if not address:
29
+ continue
30
+ norm.append(
31
+ {
32
+ "address": address,
33
+ "network": row.get("platform", "").strip() or "Unknown",
34
+ "label": "Israel NBCTF",
35
+ "source": "Israel NBCTF",
36
+ "source_url": row.get("order_url", "").strip(),
37
+ "reason": f"Sanctions Order {row.get('order_id', '').strip()}",
38
+ "jurisdiction": "IL",
39
+ "extra": {
40
+ k: v
41
+ for k, v in row.items()
42
+ if k
43
+ not in [
44
+ "account/wallet_id",
45
+ "platform",
46
+ "order_url",
47
+ "order_id",
48
+ ]
49
+ },
50
+ }
51
+ )
52
+ return norm
53
+
54
+
55
+ def normalize_fbi_lazarus_csv(filepath):
56
+ norm = []
57
+ with open(filepath, encoding="utf-8-sig") as f:
58
+ reader = csv.DictReader(f)
59
+ for row in reader:
60
+ address = row.get("Address", "").strip().lower()
61
+ if not address:
62
+ continue
63
+ norm.append(
64
+ {
65
+ "address": address,
66
+ "network": row.get("Network", "").strip(),
67
+ "label": row.get("Linked to", "").strip() or "FBI",
68
+ "source": "FBI",
69
+ "source_url": row.get("Source URL", "").strip(),
70
+ "reason": "Sanctions/Blacklist",
71
+ "jurisdiction": "US",
72
+ "extra": {
73
+ k: v
74
+ for k, v in row.items()
75
+ if k not in ["Address", "Network", "Linked to", "Source URL"]
76
+ },
77
+ }
78
+ )
79
+ return norm
80
+
81
+
82
+ def normalize_file(filepath):
83
+ filename = os.path.basename(filepath)
84
+ if "nbctf" in filename.lower():
85
+ return normalize_israel_nbctf_csv(filepath), "israel_nbctf"
86
+ elif "lazarus" in filename.lower():
87
+ return normalize_fbi_lazarus_csv(filepath), "fbi_lazarus"
88
+ else:
89
+ # Try to guess structure or skip
90
+ return [], None
91
+
92
+
93
+ def main():
94
+ print(f"Scanning for new files in: {NEW_NORM_DIR}")
95
+ log_entries = []
96
+ if not os.path.exists(NEW_NORM_DIR):
97
+ print("No new_norm directory found.")
98
+ return
99
+
100
+ files = os.listdir(NEW_NORM_DIR)
101
+ if not files:
102
+ print("No files found to normalize.")
103
+ return
104
+
105
+ for fname in files:
106
+ fpath = os.path.join(NEW_NORM_DIR, fname)
107
+ if not os.path.isfile(fpath):
108
+ continue
109
+ norm_data, tag = normalize_file(fpath)
110
+ if norm_data and tag:
111
+ outname = (
112
+ f'normalized_{tag}_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
113
+ )
114
+ outpath = os.path.join(DATASETS_DIR, outname)
115
+ with open(outpath, "w", encoding="utf-8") as outf:
116
+ json.dump(norm_data, outf, indent=2, ensure_ascii=False)
117
+ log_entries.append(
118
+ f"Normalized {fname} to {outname} ({len(norm_data)} entries)"
119
+ )
120
+ else:
121
+ log_entries.append(f"Skipped {fname} (unrecognized format or no data)")
122
+ # Move original to past_norm
123
+ shutil.move(fpath, os.path.join(PAST_NORM_DIR, fname))
124
+ # Write log
125
+ logname = f'normlog_{datetime.now().strftime("%Y%m%d_%H%M%S")}.txt'
126
+ logpath = os.path.join(LOGS_DIR, logname)
127
+ with open(logpath, "w", encoding="utf-8") as logf:
128
+ for entry in log_entries:
129
+ logf.write(entry + "\n")
130
+ print(f"Normalization complete. Log saved to {logpath}")
131
+
132
+
133
+ if __name__ == "__main__":
134
+ main()
@@ -0,0 +1,80 @@
1
+ import csv
2
+ import json
3
+ import os
4
+
5
+ # Paths relative to this script
6
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
7
+ NEW_NORM_DIR = os.path.join(BASE_DIR, "new_norm")
8
+ # Output goes to skill's data folder
9
+ DATASETS_DIR = os.path.abspath(os.path.join(BASE_DIR, "..", "data"))
10
+
11
+
12
+ def get_etherscan_label(address):
13
+ # Optionally, use Etherscan API to get contract label
14
+ # For now, just return None (or use a mapping for known addresses)
15
+ known_labels = {
16
+ "0x000000000000000000000000000000000000dead": "Ethereum Dead Address (Burn Address)",
17
+ "0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF": "Tornado Cash",
18
+ # Add more known addresses here if desired
19
+ }
20
+ return known_labels.get(address.lower())
21
+
22
+
23
+ def normalize_uniswap_trm_csv(csv_path):
24
+ seen = set()
25
+ entries = []
26
+ with open(csv_path, newline="", encoding="utf-8") as f:
27
+ reader = csv.DictReader(f)
28
+ for row in reader:
29
+ address = row["address"]
30
+ category = row["category"]
31
+ risk = row["categoryRiskScoreLevelLabel"]
32
+ if not address.lower().startswith("0x"):
33
+ continue # skip non-Ethereum addresses
34
+ if (
35
+ category not in ["Mixer", "Sanctions", "Scam", "Hacked or Stolen Funds"]
36
+ ) or (risk not in ["High", "Severe"]):
37
+ continue
38
+ key = (address.lower(), category, risk)
39
+ if key in seen:
40
+ continue
41
+ seen.add(key)
42
+ label = get_etherscan_label(address) or category
43
+ entry = {
44
+ "address": address,
45
+ "name": label,
46
+ "created_at": "", # Not available in CSV
47
+ "creator": "",
48
+ "reason": f"{category} ({risk})",
49
+ "source": "Uniswap-TRM Risk List",
50
+ "jurisdictions_blocked": [],
51
+ "severity": "critical" if risk == "Severe" else "high",
52
+ "known_victims": [],
53
+ "related_hashes": [],
54
+ "references": [],
55
+ "tags": [category.lower(), "uniswap-trm"],
56
+ "notes": (
57
+ f"category: {category}, risk: {risk}, riskType: {row.get('riskType', '')}, "
58
+ f"totalVolumeUsd: {row.get('totalVolumeUsd', '')}"
59
+ ),
60
+ }
61
+ entries.append(entry)
62
+ return entries
63
+
64
+
65
+ if __name__ == "__main__":
66
+ if not os.path.exists(NEW_NORM_DIR):
67
+ print(f"Directory not found: {NEW_NORM_DIR}")
68
+ exit(1)
69
+
70
+ csv_path = os.path.join(NEW_NORM_DIR, "uniswap-trm.csv")
71
+ if not os.path.exists(csv_path):
72
+ print(f"File not found: {csv_path}")
73
+ exit(1)
74
+
75
+ output_path = os.path.join(DATASETS_DIR, "normalized_uniswap_trm.json")
76
+
77
+ normalized = normalize_uniswap_trm_csv(csv_path)
78
+ with open(output_path, "w", encoding="utf-8") as f:
79
+ json.dump(normalized, f, indent=2, ensure_ascii=False)
80
+ print(f"Normalized {len(normalized)} entries to {output_path}")