skillware 0.2.8__tar.gz → 0.2.9__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.
Files changed (81) hide show
  1. {skillware-0.2.8/skillware.egg-info → skillware-0.2.9}/PKG-INFO +31 -6
  2. {skillware-0.2.8 → skillware-0.2.9}/README.md +13 -2
  3. {skillware-0.2.8 → skillware-0.2.9}/pyproject.toml +27 -4
  4. skillware-0.2.9/skillware/cli.py +162 -0
  5. {skillware-0.2.8 → skillware-0.2.9/skillware.egg-info}/PKG-INFO +31 -6
  6. {skillware-0.2.8 → skillware-0.2.9}/skillware.egg-info/SOURCES.txt +3 -0
  7. skillware-0.2.9/skillware.egg-info/entry_points.txt +2 -0
  8. skillware-0.2.9/skillware.egg-info/requires.txt +32 -0
  9. skillware-0.2.9/tests/test_cli.py +140 -0
  10. skillware-0.2.8/skillware.egg-info/requires.txt +0 -12
  11. {skillware-0.2.8 → skillware-0.2.9}/LICENSE +0 -0
  12. {skillware-0.2.8 → skillware-0.2.9}/MANIFEST.in +0 -0
  13. {skillware-0.2.8 → skillware-0.2.9}/setup.cfg +0 -0
  14. {skillware-0.2.8 → skillware-0.2.9}/skills/__init__.py +0 -0
  15. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/__init__.py +0 -0
  16. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/mica_module/__init__.py +0 -0
  17. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/mica_module/card.json +0 -0
  18. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/mica_module/instructions.md +0 -0
  19. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/mica_module/manifest.yaml +0 -0
  20. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/mica_module/mica_corpus.json +0 -0
  21. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/mica_module/skill.py +0 -0
  22. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/pii_masker/__init__.py +0 -0
  23. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/pii_masker/card.json +0 -0
  24. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/pii_masker/instructions.md +0 -0
  25. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/pii_masker/manifest.yaml +0 -0
  26. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/pii_masker/skill.py +0 -0
  27. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/tos_evaluator/__init__.py +0 -0
  28. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/tos_evaluator/card.json +0 -0
  29. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/tos_evaluator/instructions.md +0 -0
  30. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/tos_evaluator/manifest.yaml +0 -0
  31. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/tos_evaluator/skill.py +0 -0
  32. {skillware-0.2.8 → skillware-0.2.9}/skills/compliance/tos_evaluator/test_skill.py +0 -0
  33. {skillware-0.2.8 → skillware-0.2.9}/skills/data_engineering/__init__.py +0 -0
  34. {skillware-0.2.8 → skillware-0.2.9}/skills/data_engineering/synthetic_generator/__init__.py +0 -0
  35. {skillware-0.2.8 → skillware-0.2.9}/skills/data_engineering/synthetic_generator/card.json +0 -0
  36. {skillware-0.2.8 → skillware-0.2.9}/skills/data_engineering/synthetic_generator/instructions.md +0 -0
  37. {skillware-0.2.8 → skillware-0.2.9}/skills/data_engineering/synthetic_generator/manifest.yaml +0 -0
  38. {skillware-0.2.8 → skillware-0.2.9}/skills/data_engineering/synthetic_generator/skill.py +0 -0
  39. {skillware-0.2.8 → skillware-0.2.9}/skills/dev_tools/__init__.py +0 -0
  40. {skillware-0.2.8 → skillware-0.2.9}/skills/dev_tools/issue_resolver/__init__.py +0 -0
  41. {skillware-0.2.8 → skillware-0.2.9}/skills/dev_tools/issue_resolver/card.json +0 -0
  42. {skillware-0.2.8 → skillware-0.2.9}/skills/dev_tools/issue_resolver/instructions.md +0 -0
  43. {skillware-0.2.8 → skillware-0.2.9}/skills/dev_tools/issue_resolver/manifest.yaml +0 -0
  44. {skillware-0.2.8 → skillware-0.2.9}/skills/dev_tools/issue_resolver/skill.py +0 -0
  45. {skillware-0.2.8 → skillware-0.2.9}/skills/dev_tools/issue_resolver/test_skill.py +0 -0
  46. {skillware-0.2.8 → skillware-0.2.9}/skills/finance/__init__.py +0 -0
  47. {skillware-0.2.8 → skillware-0.2.9}/skills/finance/wallet_screening/__init__.py +0 -0
  48. {skillware-0.2.8 → skillware-0.2.9}/skills/finance/wallet_screening/card.json +0 -0
  49. {skillware-0.2.8 → skillware-0.2.9}/skills/finance/wallet_screening/data/entities.ftm.json +0 -0
  50. {skillware-0.2.8 → skillware-0.2.9}/skills/finance/wallet_screening/data/malicious_scs_2025.json +0 -0
  51. {skillware-0.2.8 → skillware-0.2.9}/skills/finance/wallet_screening/data/normalized_fbi_lazarus_20250722_180019.json +0 -0
  52. {skillware-0.2.8 → skillware-0.2.9}/skills/finance/wallet_screening/data/normalized_israel_nbctf_20250722_180019.json +0 -0
  53. {skillware-0.2.8 → skillware-0.2.9}/skills/finance/wallet_screening/data/normalized_uniswap_trm.json +0 -0
  54. {skillware-0.2.8 → skillware-0.2.9}/skills/finance/wallet_screening/instructions.md +0 -0
  55. {skillware-0.2.8 → skillware-0.2.9}/skills/finance/wallet_screening/maintenance/normalization_tool.py +0 -0
  56. {skillware-0.2.8 → skillware-0.2.9}/skills/finance/wallet_screening/maintenance/normalize_uniswap_trm.py +0 -0
  57. {skillware-0.2.8 → skillware-0.2.9}/skills/finance/wallet_screening/manifest.yaml +0 -0
  58. {skillware-0.2.8 → skillware-0.2.9}/skills/finance/wallet_screening/skill.py +0 -0
  59. {skillware-0.2.8 → skillware-0.2.9}/skills/office/__init__.py +0 -0
  60. {skillware-0.2.8 → skillware-0.2.9}/skills/office/pdf_form_filler/__init__.py +0 -0
  61. {skillware-0.2.8 → skillware-0.2.9}/skills/office/pdf_form_filler/card.json +0 -0
  62. {skillware-0.2.8 → skillware-0.2.9}/skills/office/pdf_form_filler/instructions.md +0 -0
  63. {skillware-0.2.8 → skillware-0.2.9}/skills/office/pdf_form_filler/manifest.yaml +0 -0
  64. {skillware-0.2.8 → skillware-0.2.9}/skills/office/pdf_form_filler/skill.py +0 -0
  65. {skillware-0.2.8 → skillware-0.2.9}/skills/office/pdf_form_filler/utils.py +0 -0
  66. {skillware-0.2.8 → skillware-0.2.9}/skills/optimization/__init__.py +0 -0
  67. {skillware-0.2.8 → skillware-0.2.9}/skills/optimization/prompt_rewriter/__init__.py +0 -0
  68. {skillware-0.2.8 → skillware-0.2.9}/skills/optimization/prompt_rewriter/card.json +0 -0
  69. {skillware-0.2.8 → skillware-0.2.9}/skills/optimization/prompt_rewriter/instructions.md +0 -0
  70. {skillware-0.2.8 → skillware-0.2.9}/skills/optimization/prompt_rewriter/manifest.yaml +0 -0
  71. {skillware-0.2.8 → skillware-0.2.9}/skills/optimization/prompt_rewriter/skill.py +0 -0
  72. {skillware-0.2.8 → skillware-0.2.9}/skillware/__init__.py +0 -0
  73. {skillware-0.2.8 → skillware-0.2.9}/skillware/core/__init__.py +0 -0
  74. {skillware-0.2.8 → skillware-0.2.9}/skillware/core/base_skill.py +0 -0
  75. {skillware-0.2.8 → skillware-0.2.9}/skillware/core/env.py +0 -0
  76. {skillware-0.2.8 → skillware-0.2.9}/skillware/core/loader.py +0 -0
  77. {skillware-0.2.8 → skillware-0.2.9}/skillware.egg-info/dependency_links.txt +0 -0
  78. {skillware-0.2.8 → skillware-0.2.9}/skillware.egg-info/top_level.txt +0 -0
  79. {skillware-0.2.8 → skillware-0.2.9}/tests/test_loader.py +0 -0
  80. {skillware-0.2.8 → skillware-0.2.9}/tests/test_mica_module.py +0 -0
  81. {skillware-0.2.8 → skillware-0.2.9}/tests/test_skill_issuer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skillware
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: A framework for modular, self-contained AI skills.
5
5
  Author-email: ARPA Hellenic Logical Systems <skillware-os@arpacorp.net>
6
6
  License: MIT License
@@ -35,15 +35,29 @@ Description-Content-Type: text/markdown
35
35
  License-File: LICENSE
36
36
  Requires-Dist: requests
37
37
  Requires-Dist: pyyaml
38
- Requires-Dist: anthropic
39
- Requires-Dist: google-generativeai
40
- Requires-Dist: pymupdf
38
+ Requires-Dist: python-dotenv
41
39
  Requires-Dist: beautifulsoup4
42
40
  Provides-Extra: dev
43
41
  Requires-Dist: pytest; extra == "dev"
44
42
  Requires-Dist: pytest-mock; extra == "dev"
45
43
  Requires-Dist: flake8; extra == "dev"
46
44
  Requires-Dist: black; extra == "dev"
45
+ Provides-Extra: cli
46
+ Requires-Dist: rich>=13.0; extra == "cli"
47
+ Provides-Extra: gemini
48
+ Requires-Dist: google-generativeai; extra == "gemini"
49
+ Provides-Extra: claude
50
+ Requires-Dist: anthropic; extra == "claude"
51
+ Provides-Extra: openai
52
+ Requires-Dist: openai; extra == "openai"
53
+ Provides-Extra: office
54
+ Requires-Dist: pymupdf; extra == "office"
55
+ Provides-Extra: all
56
+ Requires-Dist: rich>=13.0; extra == "all"
57
+ Requires-Dist: google-generativeai; extra == "all"
58
+ Requires-Dist: anthropic; extra == "all"
59
+ Requires-Dist: openai; extra == "all"
60
+ Requires-Dist: pymupdf; extra == "all"
47
61
  Dynamic: license-file
48
62
 
49
63
  <div align="center">
@@ -106,6 +120,7 @@ Skillware/
106
120
  │ ├── instructions.md # Cognitive map for the LLM
107
121
  │ └── test_skill.py # Unit tests & schema validation
108
122
  ├── skillware/ # Core Framework Package
123
+ │ ├── cli.py # Command-line interface
109
124
  │ └── core/
110
125
  │ ├── base_skill.py # Abstract Base Class for skills
111
126
  │ ├── env.py # Environment Management
@@ -135,7 +150,16 @@ pip install -e .
135
150
 
136
151
  > **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.
137
152
 
138
- ### 2. Configuration
153
+ ### 2. Verify your installation
154
+
155
+ ```bash
156
+ pip install "skillware[cli]"
157
+ skillware list
158
+ ```
159
+
160
+ This prints a table of all locally available skills and confirms the install and path resolution are working.
161
+
162
+ ### 3. Configuration
139
163
 
140
164
  Create a `.env` file with your API keys (e.g., Google Gemini API Key):
141
165
 
@@ -143,7 +167,7 @@ Create a `.env` file with your API keys (e.g., Google Gemini API Key):
143
167
  GOOGLE_API_KEY="your_key"
144
168
  ```
145
169
 
146
- ### 3. Usage Example (Gemini)
170
+ ### 4. Usage Example (Gemini)
147
171
 
148
172
  ```python
149
173
  import google.generativeai as genai
@@ -184,6 +208,7 @@ print(response.text)
184
208
  * **[Usage Guide: Ollama](docs/usage/ollama.md)**: Native integration for local models via Ollama.
185
209
  * **[API Keys for Skills](docs/usage/api_keys.md)**: Environment variables, cloud/CI setup, and security for skills that call external APIs.
186
210
  * **[Skill Library](docs/skills/README.md)**: Available capabilities.
211
+ * **[CLI Reference](docs/usage/cli.md)**: Discover and inspect locally installed skills from the terminal.
187
212
 
188
213
  ## Contributing
189
214
 
@@ -58,6 +58,7 @@ Skillware/
58
58
  │ ├── instructions.md # Cognitive map for the LLM
59
59
  │ └── test_skill.py # Unit tests & schema validation
60
60
  ├── skillware/ # Core Framework Package
61
+ │ ├── cli.py # Command-line interface
61
62
  │ └── core/
62
63
  │ ├── base_skill.py # Abstract Base Class for skills
63
64
  │ ├── env.py # Environment Management
@@ -87,7 +88,16 @@ pip install -e .
87
88
 
88
89
  > **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.
89
90
 
90
- ### 2. Configuration
91
+ ### 2. Verify your installation
92
+
93
+ ```bash
94
+ pip install "skillware[cli]"
95
+ skillware list
96
+ ```
97
+
98
+ This prints a table of all locally available skills and confirms the install and path resolution are working.
99
+
100
+ ### 3. Configuration
91
101
 
92
102
  Create a `.env` file with your API keys (e.g., Google Gemini API Key):
93
103
 
@@ -95,7 +105,7 @@ Create a `.env` file with your API keys (e.g., Google Gemini API Key):
95
105
  GOOGLE_API_KEY="your_key"
96
106
  ```
97
107
 
98
- ### 3. Usage Example (Gemini)
108
+ ### 4. Usage Example (Gemini)
99
109
 
100
110
  ```python
101
111
  import google.generativeai as genai
@@ -136,6 +146,7 @@ print(response.text)
136
146
  * **[Usage Guide: Ollama](docs/usage/ollama.md)**: Native integration for local models via Ollama.
137
147
  * **[API Keys for Skills](docs/usage/api_keys.md)**: Environment variables, cloud/CI setup, and security for skills that call external APIs.
138
148
  * **[Skill Library](docs/skills/README.md)**: Available capabilities.
149
+ * **[CLI Reference](docs/usage/cli.md)**: Discover and inspect locally installed skills from the terminal.
139
150
 
140
151
  ## Contributing
141
152
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "skillware"
7
- version = "0.2.8"
7
+ version = "0.2.9"
8
8
  description = "A framework for modular, self-contained AI skills."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -20,13 +20,14 @@ classifiers = [
20
20
  dependencies = [
21
21
  "requests",
22
22
  "pyyaml",
23
- "anthropic",
24
- "google-generativeai",
25
- "pymupdf",
23
+ "python-dotenv",
26
24
  "beautifulsoup4",
27
25
  ]
28
26
  requires-python = ">=3.10"
29
27
 
28
+ [project.scripts]
29
+ skillware = "skillware.cli:main"
30
+
30
31
  [project.optional-dependencies]
31
32
  dev = [
32
33
  "pytest",
@@ -34,6 +35,28 @@ dev = [
34
35
  "flake8",
35
36
  "black",
36
37
  ]
38
+ cli = [
39
+ "rich>=13.0",
40
+ ]
41
+ gemini = [
42
+ "google-generativeai",
43
+ ]
44
+ claude = [
45
+ "anthropic",
46
+ ]
47
+ openai = [
48
+ "openai",
49
+ ]
50
+ office = [
51
+ "pymupdf",
52
+ ]
53
+ all = [
54
+ "rich>=13.0",
55
+ "google-generativeai",
56
+ "anthropic",
57
+ "openai",
58
+ "pymupdf",
59
+ ]
37
60
 
38
61
  [tool.setuptools]
39
62
  include-package-data = true
@@ -0,0 +1,162 @@
1
+ import argparse
2
+ import yaml
3
+ from pathlib import Path
4
+ from typing import List, Dict, Any, Optional
5
+
6
+ from skillware.core.loader import SkillLoader
7
+
8
+
9
+ def _get_skill_roots(skills_root_override: Optional[Path] = None) -> List[Path]:
10
+ """Return the list of roots to search for skills, mirrors SkillLoader resolution order."""
11
+ if skills_root_override is not None:
12
+ if skills_root_override.exists():
13
+ return [skills_root_override]
14
+ return []
15
+
16
+ roots = []
17
+ seen = set()
18
+
19
+ for root in (
20
+ SkillLoader._env_skill_roots()
21
+ + SkillLoader._cwd_skill_roots()
22
+ + [SkillLoader._bundled_skills_root()]
23
+ ):
24
+ resolved = root.resolve()
25
+ if resolved not in seen and resolved.exists():
26
+ seen.add(resolved)
27
+ roots.append(resolved)
28
+
29
+ return roots
30
+
31
+
32
+ def _discover_skills(
33
+ skills_root_override: Optional[Path] = None,
34
+ ) -> List[Dict[str, Any]]:
35
+ """Walk all skill roots and return a list of dicts with each skill's metadata."""
36
+ roots = _get_skill_roots(skills_root_override)
37
+
38
+ skills = []
39
+ seen_ids = set()
40
+
41
+ for root in roots:
42
+ for manifest_path in root.glob("*/*/manifest.yaml"):
43
+
44
+ if not SkillLoader._is_skill_dir(manifest_path.parent):
45
+ continue
46
+
47
+ with open(manifest_path, encoding="utf-8") as f:
48
+ data = yaml.safe_load(f) or {}
49
+
50
+ skill_id = f"{manifest_path.parent.parent.name}/{manifest_path.parent.name}"
51
+
52
+ # skip duplicates found in multiple roots
53
+ if skill_id in seen_ids:
54
+ continue
55
+ seen_ids.add(skill_id)
56
+
57
+ issuer = data.get("issuer") or {}
58
+
59
+ skills.append(
60
+ {
61
+ "id": skill_id,
62
+ "category": manifest_path.parent.parent.name,
63
+ "name": manifest_path.parent.name,
64
+ "version": data.get("version", "?").strip(),
65
+ "description": data.get("description", "").strip(),
66
+ "requirements": ", ".join(data.get("requirements") or []).strip(),
67
+ "issuer": issuer.get("github") or issuer.get("name") or "",
68
+ }
69
+ )
70
+
71
+ return skills
72
+
73
+
74
+ def cmd_list(
75
+ skills_root_override: Optional[Path] = None,
76
+ category_filter: Optional[str] = None,
77
+ issuer_filter: Optional[str] = None,
78
+ console=None,
79
+ ) -> None:
80
+ """Print a formatted table of all available skills."""
81
+ try:
82
+ from rich.table import Table
83
+ from rich.console import Console
84
+ except ImportError:
85
+ raise SystemExit(
86
+ "rich is required for the CLI. Install it with: pip install 'skillware[cli]'"
87
+ )
88
+
89
+ if console is None:
90
+ console = Console()
91
+
92
+ skills = _discover_skills(skills_root_override)
93
+
94
+ if category_filter:
95
+ skills = [s for s in skills if s["category"] == category_filter]
96
+
97
+ if issuer_filter:
98
+ skills = [s for s in skills if s["issuer"] == issuer_filter]
99
+
100
+ if not skills:
101
+ console.print("No skills found.")
102
+ return
103
+
104
+ table = Table()
105
+
106
+ table.add_column("ID")
107
+ table.add_column("VERSION")
108
+ table.add_column("CATEGORY")
109
+ table.add_column("ISSUER")
110
+ table.add_column("DESCRIPTION")
111
+ table.add_column("REQUIREMENTS")
112
+
113
+ for skill in skills:
114
+ table.add_row(
115
+ skill["id"],
116
+ skill["version"],
117
+ skill["category"],
118
+ skill["issuer"],
119
+ skill["description"],
120
+ skill["requirements"],
121
+ )
122
+
123
+ console.print(table)
124
+
125
+
126
+ def main() -> None:
127
+ """CLI entry point."""
128
+ parser = argparse.ArgumentParser(prog="skillware")
129
+ subparsers = parser.add_subparsers(dest="command")
130
+
131
+ list_parser = subparsers.add_parser("list", help="List all available skills.")
132
+ list_parser.add_argument(
133
+ "--skills-root",
134
+ type=Path,
135
+ default=None,
136
+ help="Override the skills directory path.",
137
+ )
138
+ list_parser.add_argument(
139
+ "--category",
140
+ default=None,
141
+ help="Filter skills by category.",
142
+ )
143
+ list_parser.add_argument(
144
+ "--issuer",
145
+ default=None,
146
+ help="Filter skills by issuer GitHub handle or name.",
147
+ )
148
+
149
+ args = parser.parse_args()
150
+
151
+ if args.command == "list":
152
+ cmd_list(
153
+ skills_root_override=args.skills_root,
154
+ category_filter=args.category,
155
+ issuer_filter=args.issuer,
156
+ )
157
+ else:
158
+ parser.print_help()
159
+
160
+
161
+ if __name__ == "__main__":
162
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skillware
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: A framework for modular, self-contained AI skills.
5
5
  Author-email: ARPA Hellenic Logical Systems <skillware-os@arpacorp.net>
6
6
  License: MIT License
@@ -35,15 +35,29 @@ Description-Content-Type: text/markdown
35
35
  License-File: LICENSE
36
36
  Requires-Dist: requests
37
37
  Requires-Dist: pyyaml
38
- Requires-Dist: anthropic
39
- Requires-Dist: google-generativeai
40
- Requires-Dist: pymupdf
38
+ Requires-Dist: python-dotenv
41
39
  Requires-Dist: beautifulsoup4
42
40
  Provides-Extra: dev
43
41
  Requires-Dist: pytest; extra == "dev"
44
42
  Requires-Dist: pytest-mock; extra == "dev"
45
43
  Requires-Dist: flake8; extra == "dev"
46
44
  Requires-Dist: black; extra == "dev"
45
+ Provides-Extra: cli
46
+ Requires-Dist: rich>=13.0; extra == "cli"
47
+ Provides-Extra: gemini
48
+ Requires-Dist: google-generativeai; extra == "gemini"
49
+ Provides-Extra: claude
50
+ Requires-Dist: anthropic; extra == "claude"
51
+ Provides-Extra: openai
52
+ Requires-Dist: openai; extra == "openai"
53
+ Provides-Extra: office
54
+ Requires-Dist: pymupdf; extra == "office"
55
+ Provides-Extra: all
56
+ Requires-Dist: rich>=13.0; extra == "all"
57
+ Requires-Dist: google-generativeai; extra == "all"
58
+ Requires-Dist: anthropic; extra == "all"
59
+ Requires-Dist: openai; extra == "all"
60
+ Requires-Dist: pymupdf; extra == "all"
47
61
  Dynamic: license-file
48
62
 
49
63
  <div align="center">
@@ -106,6 +120,7 @@ Skillware/
106
120
  │ ├── instructions.md # Cognitive map for the LLM
107
121
  │ └── test_skill.py # Unit tests & schema validation
108
122
  ├── skillware/ # Core Framework Package
123
+ │ ├── cli.py # Command-line interface
109
124
  │ └── core/
110
125
  │ ├── base_skill.py # Abstract Base Class for skills
111
126
  │ ├── env.py # Environment Management
@@ -135,7 +150,16 @@ pip install -e .
135
150
 
136
151
  > **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.
137
152
 
138
- ### 2. Configuration
153
+ ### 2. Verify your installation
154
+
155
+ ```bash
156
+ pip install "skillware[cli]"
157
+ skillware list
158
+ ```
159
+
160
+ This prints a table of all locally available skills and confirms the install and path resolution are working.
161
+
162
+ ### 3. Configuration
139
163
 
140
164
  Create a `.env` file with your API keys (e.g., Google Gemini API Key):
141
165
 
@@ -143,7 +167,7 @@ Create a `.env` file with your API keys (e.g., Google Gemini API Key):
143
167
  GOOGLE_API_KEY="your_key"
144
168
  ```
145
169
 
146
- ### 3. Usage Example (Gemini)
170
+ ### 4. Usage Example (Gemini)
147
171
 
148
172
  ```python
149
173
  import google.generativeai as genai
@@ -184,6 +208,7 @@ print(response.text)
184
208
  * **[Usage Guide: Ollama](docs/usage/ollama.md)**: Native integration for local models via Ollama.
185
209
  * **[API Keys for Skills](docs/usage/api_keys.md)**: Environment variables, cloud/CI setup, and security for skills that call external APIs.
186
210
  * **[Skill Library](docs/skills/README.md)**: Available capabilities.
211
+ * **[CLI Reference](docs/usage/cli.md)**: Discover and inspect locally installed skills from the terminal.
187
212
 
188
213
  ## Contributing
189
214
 
@@ -61,15 +61,18 @@ skills/optimization/prompt_rewriter/instructions.md
61
61
  skills/optimization/prompt_rewriter/manifest.yaml
62
62
  skills/optimization/prompt_rewriter/skill.py
63
63
  skillware/__init__.py
64
+ skillware/cli.py
64
65
  skillware.egg-info/PKG-INFO
65
66
  skillware.egg-info/SOURCES.txt
66
67
  skillware.egg-info/dependency_links.txt
68
+ skillware.egg-info/entry_points.txt
67
69
  skillware.egg-info/requires.txt
68
70
  skillware.egg-info/top_level.txt
69
71
  skillware/core/__init__.py
70
72
  skillware/core/base_skill.py
71
73
  skillware/core/env.py
72
74
  skillware/core/loader.py
75
+ tests/test_cli.py
73
76
  tests/test_loader.py
74
77
  tests/test_mica_module.py
75
78
  tests/test_skill_issuer.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ skillware = skillware.cli:main
@@ -0,0 +1,32 @@
1
+ requests
2
+ pyyaml
3
+ python-dotenv
4
+ beautifulsoup4
5
+
6
+ [all]
7
+ rich>=13.0
8
+ google-generativeai
9
+ anthropic
10
+ openai
11
+ pymupdf
12
+
13
+ [claude]
14
+ anthropic
15
+
16
+ [cli]
17
+ rich>=13.0
18
+
19
+ [dev]
20
+ pytest
21
+ pytest-mock
22
+ flake8
23
+ black
24
+
25
+ [gemini]
26
+ google-generativeai
27
+
28
+ [office]
29
+ pymupdf
30
+
31
+ [openai]
32
+ openai
@@ -0,0 +1,140 @@
1
+ from skillware.cli import _discover_skills, cmd_list
2
+
3
+
4
+ def test_discover_skills_returns_skills(tmp_path):
5
+ # Create a fake skill directory structure
6
+ skill_dir = tmp_path / "office" / "pdf_form_filler"
7
+ skill_dir.mkdir(parents=True)
8
+ (skill_dir / "skill.py").touch()
9
+
10
+ manifest = skill_dir / "manifest.yaml"
11
+ manifest.write_text(
12
+ "name: pdf_form_filler\n"
13
+ "version: 0.1.0\n"
14
+ "description: Fills PDF forms.\n"
15
+ "requirements:\n"
16
+ " - pymupdf\n"
17
+ )
18
+
19
+ skills = _discover_skills(tmp_path)
20
+
21
+ assert len(skills) == 1
22
+ assert skills[0]["id"] == "office/pdf_form_filler"
23
+ assert skills[0]["version"] == "0.1.0"
24
+
25
+
26
+ def test_discover_skills_empty_directory(tmp_path):
27
+ # No skills created, directory is empty
28
+ skills = _discover_skills(tmp_path)
29
+
30
+ assert skills == []
31
+
32
+
33
+ def test_discover_skills_nonexistent_override_falls_back(tmp_path, monkeypatch):
34
+ # An override path that does not exist should be ignored
35
+ # and fall back to other roots without crashing
36
+ monkeypatch.chdir(tmp_path)
37
+ fake_path = tmp_path / "nonexistent"
38
+
39
+ # Should not raise, just return empty list since no roots have skills
40
+ skills = _discover_skills(fake_path)
41
+ assert skills == []
42
+
43
+
44
+ def test_discover_skills_missing_optional_fields(tmp_path):
45
+ # Manifest with only required fields, no version, description or requirements
46
+ skill_dir = tmp_path / "office" / "minimal_skill"
47
+ skill_dir.mkdir(parents=True)
48
+ (skill_dir / "skill.py").touch()
49
+
50
+ manifest = skill_dir / "manifest.yaml"
51
+ manifest.write_text("name: minimal_skill\n")
52
+
53
+ skills = _discover_skills(tmp_path)
54
+
55
+ assert len(skills) == 1
56
+ assert skills[0]["version"] == "?"
57
+ assert skills[0]["description"] == ""
58
+ assert skills[0]["requirements"] == ""
59
+
60
+
61
+ def test_discover_skills_ignores_deeply_nested_manifest(tmp_path):
62
+ # manifest.yaml three levels deep should not be picked up
63
+ skill_dir = tmp_path / "office" / "pdf_form_filler" / "extra"
64
+ skill_dir.mkdir(parents=True)
65
+
66
+ manifest = skill_dir / "manifest.yaml"
67
+ manifest.write_text("name: should_not_appear\nversion: 0.1.0\n")
68
+
69
+ skills = _discover_skills(tmp_path)
70
+
71
+ assert skills == []
72
+
73
+
74
+ def test_discover_skills_includes_issuer(tmp_path):
75
+ # Manifest with issuer github handle
76
+ skill_dir = tmp_path / "office" / "pdf_form_filler"
77
+ skill_dir.mkdir(parents=True)
78
+ (skill_dir / "skill.py").touch()
79
+
80
+ manifest = skill_dir / "manifest.yaml"
81
+ manifest.write_text(
82
+ "name: pdf_form_filler\n"
83
+ "version: 0.1.0\n"
84
+ "description: Fills PDF forms.\n"
85
+ "issuer:\n"
86
+ " name: Ross Peili\n"
87
+ " github: rosspeili\n"
88
+ )
89
+
90
+ skills = _discover_skills(tmp_path)
91
+
92
+ assert skills[0]["issuer"] == "rosspeili"
93
+
94
+
95
+ def test_discover_skills_issuer_falls_back_to_name(tmp_path):
96
+ # Manifest with issuer name but no github handle
97
+ skill_dir = tmp_path / "office" / "pdf_form_filler"
98
+ skill_dir.mkdir(parents=True)
99
+ (skill_dir / "skill.py").touch()
100
+
101
+ manifest = skill_dir / "manifest.yaml"
102
+ manifest.write_text(
103
+ "name: pdf_form_filler\n"
104
+ "version: 0.1.0\n"
105
+ "description: Fills PDF forms.\n"
106
+ "issuer:\n"
107
+ " name: Ross Peili\n"
108
+ )
109
+
110
+ skills = _discover_skills(tmp_path)
111
+
112
+ assert skills[0]["issuer"] == "Ross Peili"
113
+
114
+
115
+ def test_cmd_list_filter_by_category(tmp_path):
116
+ # Only skills matching the category should appear
117
+ import io
118
+ from rich.console import Console
119
+
120
+ for category, name in [
121
+ ("office", "pdf_form_filler"),
122
+ ("finance", "wallet_screening"),
123
+ ]:
124
+ skill_dir = tmp_path / category / name
125
+ skill_dir.mkdir(parents=True)
126
+ (skill_dir / "skill.py").touch()
127
+ (skill_dir / "manifest.yaml").write_text(
128
+ f"name: {name}\nversion: 0.1.0\ndescription: Test.\n"
129
+ )
130
+
131
+ buf = io.StringIO()
132
+ cmd_list(
133
+ skills_root_override=tmp_path,
134
+ category_filter="office",
135
+ console=Console(file=buf, force_terminal=False),
136
+ )
137
+
138
+ output = buf.getvalue()
139
+ assert "office" in output
140
+ assert "finance" not in output
@@ -1,12 +0,0 @@
1
- requests
2
- pyyaml
3
- anthropic
4
- google-generativeai
5
- pymupdf
6
- beautifulsoup4
7
-
8
- [dev]
9
- pytest
10
- pytest-mock
11
- flake8
12
- black
File without changes
File without changes
File without changes
File without changes