echo-agent 0.1.0__py3-none-any.whl

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 (219) hide show
  1. echo_agent/__init__.py +5 -0
  2. echo_agent/__main__.py +538 -0
  3. echo_agent/_bundled/skills/development/plan/SKILL.md +54 -0
  4. echo_agent/_bundled/skills/development/skill-creator/SKILL.md +270 -0
  5. echo_agent/_bundled/skills/development/skill-creator/scripts/init_skill.py +226 -0
  6. echo_agent/_bundled/skills/development/skill-creator/scripts/package_skill.py +146 -0
  7. echo_agent/_bundled/skills/development/skill-creator/scripts/quick_validate.py +222 -0
  8. echo_agent/_bundled/skills/productivity/summarize/SKILL.md +67 -0
  9. echo_agent/_bundled/skills/productivity/weather/SKILL.md +49 -0
  10. echo_agent/_bundled/skills/research/arxiv/SKILL.md +232 -0
  11. echo_agent/_bundled/skills/research/arxiv/scripts/search_arxiv.py +115 -0
  12. echo_agent/a2a/__init__.py +5 -0
  13. echo_agent/a2a/client.py +66 -0
  14. echo_agent/a2a/models.py +98 -0
  15. echo_agent/a2a/protocol.py +85 -0
  16. echo_agent/a2a/server.py +71 -0
  17. echo_agent/agent/__init__.py +0 -0
  18. echo_agent/agent/approval_gate.py +326 -0
  19. echo_agent/agent/compression/__init__.py +14 -0
  20. echo_agent/agent/compression/assembler.py +45 -0
  21. echo_agent/agent/compression/boundary.py +141 -0
  22. echo_agent/agent/compression/compressor.py +181 -0
  23. echo_agent/agent/compression/engine.py +88 -0
  24. echo_agent/agent/compression/pruner.py +150 -0
  25. echo_agent/agent/compression/summarizer.py +181 -0
  26. echo_agent/agent/compression/types.py +41 -0
  27. echo_agent/agent/compression/validator.py +96 -0
  28. echo_agent/agent/consolidation.py +96 -0
  29. echo_agent/agent/context.py +403 -0
  30. echo_agent/agent/executors/__init__.py +0 -0
  31. echo_agent/agent/executors/base.py +211 -0
  32. echo_agent/agent/executors/factory.py +34 -0
  33. echo_agent/agent/executors/remote.py +193 -0
  34. echo_agent/agent/loop.py +891 -0
  35. echo_agent/agent/multi_agent/__init__.py +15 -0
  36. echo_agent/agent/multi_agent/audit.py +19 -0
  37. echo_agent/agent/multi_agent/error_messages.py +35 -0
  38. echo_agent/agent/multi_agent/error_types.py +36 -0
  39. echo_agent/agent/multi_agent/models.py +37 -0
  40. echo_agent/agent/multi_agent/registry.py +41 -0
  41. echo_agent/agent/multi_agent/runtime.py +201 -0
  42. echo_agent/agent/pipeline/__init__.py +14 -0
  43. echo_agent/agent/pipeline/context_stage.py +219 -0
  44. echo_agent/agent/pipeline/inference_stage.py +433 -0
  45. echo_agent/agent/pipeline/response_stage.py +146 -0
  46. echo_agent/agent/pipeline/types.py +40 -0
  47. echo_agent/agent/planning/__init__.py +4 -0
  48. echo_agent/agent/planning/models.py +83 -0
  49. echo_agent/agent/planning/planner.py +57 -0
  50. echo_agent/agent/planning/reflection.py +54 -0
  51. echo_agent/agent/planning/strategies.py +183 -0
  52. echo_agent/agent/tools/__init__.py +167 -0
  53. echo_agent/agent/tools/base.py +149 -0
  54. echo_agent/agent/tools/circuit_breaker.py +82 -0
  55. echo_agent/agent/tools/clarify.py +42 -0
  56. echo_agent/agent/tools/code_exec.py +147 -0
  57. echo_agent/agent/tools/cronjob.py +93 -0
  58. echo_agent/agent/tools/delegate.py +393 -0
  59. echo_agent/agent/tools/filesystem.py +180 -0
  60. echo_agent/agent/tools/image_gen.py +65 -0
  61. echo_agent/agent/tools/knowledge.py +81 -0
  62. echo_agent/agent/tools/memory.py +198 -0
  63. echo_agent/agent/tools/message.py +39 -0
  64. echo_agent/agent/tools/notify.py +35 -0
  65. echo_agent/agent/tools/patch.py +178 -0
  66. echo_agent/agent/tools/process.py +139 -0
  67. echo_agent/agent/tools/registry.py +185 -0
  68. echo_agent/agent/tools/search.py +99 -0
  69. echo_agent/agent/tools/session_search.py +76 -0
  70. echo_agent/agent/tools/shell.py +164 -0
  71. echo_agent/agent/tools/skill_install.py +255 -0
  72. echo_agent/agent/tools/skills.py +177 -0
  73. echo_agent/agent/tools/task.py +104 -0
  74. echo_agent/agent/tools/todo.py +148 -0
  75. echo_agent/agent/tools/tts.py +77 -0
  76. echo_agent/agent/tools/vision.py +71 -0
  77. echo_agent/agent/tools/web.py +208 -0
  78. echo_agent/agent/tools/workflow.py +89 -0
  79. echo_agent/bus/__init__.py +11 -0
  80. echo_agent/bus/events.py +193 -0
  81. echo_agent/bus/queue.py +158 -0
  82. echo_agent/bus/rate_limiter.py +51 -0
  83. echo_agent/channels/__init__.py +0 -0
  84. echo_agent/channels/base.py +185 -0
  85. echo_agent/channels/cli.py +149 -0
  86. echo_agent/channels/cron.py +44 -0
  87. echo_agent/channels/dingtalk.py +195 -0
  88. echo_agent/channels/discord.py +359 -0
  89. echo_agent/channels/email.py +168 -0
  90. echo_agent/channels/feishu.py +240 -0
  91. echo_agent/channels/manager.py +417 -0
  92. echo_agent/channels/matrix.py +281 -0
  93. echo_agent/channels/qqbot.py +638 -0
  94. echo_agent/channels/qqbot_media.py +482 -0
  95. echo_agent/channels/slack.py +297 -0
  96. echo_agent/channels/telegram.py +275 -0
  97. echo_agent/channels/webhook.py +106 -0
  98. echo_agent/channels/wecom.py +152 -0
  99. echo_agent/channels/weixin.py +603 -0
  100. echo_agent/channels/whatsapp.py +138 -0
  101. echo_agent/cli/__init__.py +0 -0
  102. echo_agent/cli/colors.py +42 -0
  103. echo_agent/cli/evolution_cmd.py +299 -0
  104. echo_agent/cli/i18n/__init__.py +123 -0
  105. echo_agent/cli/i18n/en.py +275 -0
  106. echo_agent/cli/i18n/zh.py +275 -0
  107. echo_agent/cli/plugins_cmd.py +205 -0
  108. echo_agent/cli/prompt.py +102 -0
  109. echo_agent/cli/service.py +156 -0
  110. echo_agent/cli/setup.py +1111 -0
  111. echo_agent/cli/status.py +93 -0
  112. echo_agent/config/__init__.py +8 -0
  113. echo_agent/config/default.yaml +199 -0
  114. echo_agent/config/loader.py +125 -0
  115. echo_agent/config/schema.py +652 -0
  116. echo_agent/evaluation/__init__.py +4 -0
  117. echo_agent/evaluation/dataset.py +66 -0
  118. echo_agent/evaluation/metrics.py +70 -0
  119. echo_agent/evaluation/reporter.py +42 -0
  120. echo_agent/evaluation/runner.py +143 -0
  121. echo_agent/evolution/__init__.py +38 -0
  122. echo_agent/evolution/engine.py +335 -0
  123. echo_agent/evolution/evolver.py +397 -0
  124. echo_agent/evolution/gate.py +413 -0
  125. echo_agent/evolution/recorder.py +288 -0
  126. echo_agent/evolution/scheduler.py +133 -0
  127. echo_agent/evolution/store.py +331 -0
  128. echo_agent/evolution/tools.py +110 -0
  129. echo_agent/evolution/types.py +270 -0
  130. echo_agent/gateway/__init__.py +7 -0
  131. echo_agent/gateway/auth.py +178 -0
  132. echo_agent/gateway/editor.py +121 -0
  133. echo_agent/gateway/health.py +51 -0
  134. echo_agent/gateway/hooks.py +86 -0
  135. echo_agent/gateway/media.py +137 -0
  136. echo_agent/gateway/rate_limiter.py +72 -0
  137. echo_agent/gateway/router.py +86 -0
  138. echo_agent/gateway/server.py +570 -0
  139. echo_agent/gateway/session_context.py +57 -0
  140. echo_agent/gateway/session_policy.py +47 -0
  141. echo_agent/gateway/static/index.html +432 -0
  142. echo_agent/knowledge/__init__.py +5 -0
  143. echo_agent/knowledge/index.py +308 -0
  144. echo_agent/mcp/__init__.py +3 -0
  145. echo_agent/mcp/client.py +158 -0
  146. echo_agent/mcp/manager.py +161 -0
  147. echo_agent/mcp/oauth.py +208 -0
  148. echo_agent/mcp/security.py +79 -0
  149. echo_agent/mcp/tool_adapter.py +73 -0
  150. echo_agent/mcp/transport.py +353 -0
  151. echo_agent/memory/__init__.py +0 -0
  152. echo_agent/memory/consolidator.py +273 -0
  153. echo_agent/memory/contradiction.py +287 -0
  154. echo_agent/memory/forgetting.py +114 -0
  155. echo_agent/memory/retrieval.py +184 -0
  156. echo_agent/memory/reviewer.py +192 -0
  157. echo_agent/memory/store.py +706 -0
  158. echo_agent/memory/tiers.py +243 -0
  159. echo_agent/memory/types.py +168 -0
  160. echo_agent/memory/vectors.py +148 -0
  161. echo_agent/models/__init__.py +0 -0
  162. echo_agent/models/credential_pool.py +86 -0
  163. echo_agent/models/inference.py +98 -0
  164. echo_agent/models/provider.py +208 -0
  165. echo_agent/models/providers/__init__.py +209 -0
  166. echo_agent/models/providers/anthropic_provider.py +164 -0
  167. echo_agent/models/providers/bedrock_provider.py +261 -0
  168. echo_agent/models/providers/format_utils.py +198 -0
  169. echo_agent/models/providers/gemini_provider.py +159 -0
  170. echo_agent/models/providers/openai_provider.py +253 -0
  171. echo_agent/models/providers/openrouter_provider.py +38 -0
  172. echo_agent/models/rate_limiter.py +75 -0
  173. echo_agent/models/router.py +325 -0
  174. echo_agent/models/tokenizer.py +111 -0
  175. echo_agent/observability/__init__.py +0 -0
  176. echo_agent/observability/monitor.py +209 -0
  177. echo_agent/observability/spans.py +75 -0
  178. echo_agent/observability/telemetry.py +86 -0
  179. echo_agent/permissions/__init__.py +0 -0
  180. echo_agent/permissions/allowlist.py +97 -0
  181. echo_agent/permissions/manager.py +460 -0
  182. echo_agent/plugins/__init__.py +30 -0
  183. echo_agent/plugins/context.py +145 -0
  184. echo_agent/plugins/errors.py +23 -0
  185. echo_agent/plugins/hooks.py +126 -0
  186. echo_agent/plugins/loader.py +251 -0
  187. echo_agent/plugins/manager.py +216 -0
  188. echo_agent/plugins/manifest.py +70 -0
  189. echo_agent/runtime_paths.py +25 -0
  190. echo_agent/scheduler/__init__.py +0 -0
  191. echo_agent/scheduler/delivery.py +63 -0
  192. echo_agent/scheduler/service.py +398 -0
  193. echo_agent/security/__init__.py +11 -0
  194. echo_agent/security/capabilities.py +54 -0
  195. echo_agent/security/guards.py +265 -0
  196. echo_agent/security/path_policy.py +212 -0
  197. echo_agent/security/risk_classifier.py +75 -0
  198. echo_agent/security/smart_approval.py +60 -0
  199. echo_agent/security/tool_policy.py +159 -0
  200. echo_agent/session/__init__.py +0 -0
  201. echo_agent/session/manager.py +404 -0
  202. echo_agent/skills/__init__.py +0 -0
  203. echo_agent/skills/manager.py +279 -0
  204. echo_agent/skills/reviewer.py +163 -0
  205. echo_agent/skills/store.py +358 -0
  206. echo_agent/storage/__init__.py +0 -0
  207. echo_agent/storage/backend.py +111 -0
  208. echo_agent/storage/sqlite.py +523 -0
  209. echo_agent/tasks/__init__.py +20 -0
  210. echo_agent/tasks/manager.py +108 -0
  211. echo_agent/tasks/models.py +180 -0
  212. echo_agent/tasks/workflow.py +182 -0
  213. echo_agent/utils/__init__.py +0 -0
  214. echo_agent/utils/async_io.py +80 -0
  215. echo_agent/utils/text.py +91 -0
  216. echo_agent-0.1.0.dist-info/METADATA +286 -0
  217. echo_agent-0.1.0.dist-info/RECORD +219 -0
  218. echo_agent-0.1.0.dist-info/WHEEL +4 -0
  219. echo_agent-0.1.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,270 @@
1
+ ---
2
+ name: skill-creator
3
+ description: Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
4
+ ---
5
+
6
+ # Skill Creator
7
+
8
+ This skill provides guidance for creating effective skills.
9
+
10
+ ## About Skills
11
+
12
+ Skills are modular, self-contained packages that extend the agent's capabilities by providing
13
+ specialized knowledge, workflows, and tools. Think of them as "onboarding guides" for specific
14
+ domains or tasks—they transform the agent from a general-purpose agent into a specialized agent
15
+ equipped with procedural knowledge that no model can fully possess.
16
+
17
+ ### What Skills Provide
18
+
19
+ 1. Specialized workflows - Multi-step procedures for specific domains
20
+ 2. Tool integrations - Instructions for working with specific file formats or APIs
21
+ 3. Domain expertise - Company-specific knowledge, schemas, business logic
22
+ 4. Bundled resources - Scripts, references, and assets for complex and repetitive tasks
23
+
24
+ ## Core Principles
25
+
26
+ ### Concise is Key
27
+
28
+ The context window is a public good. Skills share the context window with everything else the agent needs: system prompt, conversation history, other Skills' metadata, and the actual user request.
29
+
30
+ **Default assumption: the agent is already very smart.** Only add context the agent doesn't already have. Challenge each piece of information: "Does the agent really need this explanation?" and "Does this paragraph justify its token cost?"
31
+
32
+ Prefer concise examples over verbose explanations.
33
+
34
+ ### Set Appropriate Degrees of Freedom
35
+
36
+ Match the level of specificity to the task's fragility and variability:
37
+
38
+ **High freedom (text-based instructions)**: Use when multiple approaches are valid, decisions depend on context, or heuristics guide the approach.
39
+
40
+ **Medium freedom (pseudocode or scripts with parameters)**: Use when a preferred pattern exists, some variation is acceptable, or configuration affects behavior.
41
+
42
+ **Low freedom (specific scripts, few parameters)**: Use when operations are fragile and error-prone, consistency is critical, or a specific sequence must be followed.
43
+
44
+ ### Anatomy of a Skill
45
+
46
+ Every skill consists of a required SKILL.md file and optional bundled resources:
47
+
48
+ ```
49
+ skill-name/
50
+ ├── SKILL.md (required)
51
+ │ ├── YAML frontmatter metadata (required)
52
+ │ │ ├── name: (required)
53
+ │ │ └── description: (required)
54
+ │ └── Markdown instructions (required)
55
+ └── Bundled Resources (optional)
56
+ ├── scripts/ - Executable code (Python/Bash/etc.)
57
+ ├── references/ - Documentation intended to be loaded into context as needed
58
+ └── assets/ - Files used in output (templates, icons, fonts, etc.)
59
+ ```
60
+
61
+ #### SKILL.md (required)
62
+
63
+ Every SKILL.md consists of:
64
+
65
+ - **Frontmatter** (YAML): Contains `name` and `description` fields. These are the only fields that the agent reads to determine when the skill gets used, thus it is very important to be clear and comprehensive in describing what the skill is, and when it should be used.
66
+ - **Body** (Markdown): Instructions and guidance for using the skill. Only loaded AFTER the skill triggers (if at all).
67
+
68
+ #### Bundled Resources (optional)
69
+
70
+ ##### Scripts (`scripts/`)
71
+
72
+ Executable code (Python/Bash/etc.) for tasks that require deterministic reliability or are repeatedly rewritten.
73
+
74
+ - **When to include**: When the same code is being rewritten repeatedly or deterministic reliability is needed
75
+ - **Example**: `scripts/rotate_pdf.py` for PDF rotation tasks
76
+ - **Benefits**: Token efficient, deterministic, may be executed without loading into context
77
+
78
+ ##### References (`references/`)
79
+
80
+ Documentation and reference material intended to be loaded as needed into context.
81
+
82
+ - **When to include**: For documentation that the agent should reference while working
83
+ - **Examples**: `references/api_docs.md`, `references/policies.md`
84
+ - **Best practice**: If files are large (>10k words), include grep search patterns in SKILL.md
85
+
86
+ ##### Assets (`assets/`)
87
+
88
+ Files not intended to be loaded into context, but rather used within the output the agent produces.
89
+
90
+ - **When to include**: When the skill needs files that will be used in the final output
91
+ - **Examples**: `assets/logo.png`, `assets/slides.pptx`, `assets/frontend-template/`
92
+
93
+ #### What to Not Include in a Skill
94
+
95
+ A skill should only contain essential files that directly support its functionality. Do NOT create extraneous documentation or auxiliary files, including:
96
+
97
+ - README.md
98
+ - INSTALLATION_GUIDE.md
99
+ - QUICK_REFERENCE.md
100
+ - CHANGELOG.md
101
+
102
+ The skill should only contain the information needed for an AI agent to do the job at hand.
103
+
104
+ ### Progressive Disclosure Design Principle
105
+
106
+ Skills use a three-level loading system to manage context efficiently:
107
+
108
+ 1. **Metadata (name + description)** - Always in context (~100 words)
109
+ 2. **SKILL.md body** - When skill triggers (<5k words)
110
+ 3. **Bundled resources** - As needed by the agent (Unlimited because scripts can be executed without reading into context window)
111
+
112
+ #### Progressive Disclosure Patterns
113
+
114
+ Keep SKILL.md body to the essentials and under 500 lines to minimize context bloat. Split content into separate files when approaching this limit.
115
+
116
+ **Pattern 1: High-level guide with references**
117
+
118
+ ```markdown
119
+ # PDF Processing
120
+
121
+ ## Quick start
122
+ Extract text with pdfplumber:
123
+ [code example]
124
+
125
+ ## Advanced features
126
+ - **Form filling**: See [FORMS.md](FORMS.md) for complete guide
127
+ - **API reference**: See [REFERENCE.md](REFERENCE.md) for all methods
128
+ ```
129
+
130
+ **Pattern 2: Domain-specific organization**
131
+
132
+ ```
133
+ bigquery-skill/
134
+ ├── SKILL.md (overview and navigation)
135
+ └── reference/
136
+ ├── finance.md (revenue, billing metrics)
137
+ ├── sales.md (opportunities, pipeline)
138
+ └── product.md (API usage, features)
139
+ ```
140
+
141
+ **Pattern 3: Conditional details**
142
+
143
+ ```markdown
144
+ # DOCX Processing
145
+
146
+ ## Creating documents
147
+ Use docx-js for new documents. See [DOCX-JS.md](DOCX-JS.md).
148
+
149
+ ## Editing documents
150
+ For simple edits, modify the XML directly.
151
+ **For tracked changes**: See [REDLINING.md](REDLINING.md)
152
+ ```
153
+
154
+ **Important guidelines:**
155
+ - **Avoid deeply nested references** - Keep references one level deep from SKILL.md.
156
+ - **Structure longer reference files** - For files longer than 100 lines, include a table of contents at the top.
157
+
158
+ ## Skill Creation Process
159
+
160
+ Skill creation involves these steps:
161
+
162
+ 1. Understand the skill with concrete examples
163
+ 2. Plan reusable skill contents (scripts, references, assets)
164
+ 3. Initialize the skill (run init_skill.py)
165
+ 4. Edit the skill (implement resources and write SKILL.md)
166
+ 5. Package the skill (run package_skill.py)
167
+ 6. Iterate based on real usage
168
+
169
+ Follow these steps in order, skipping only if there is a clear reason why they are not applicable.
170
+
171
+ ### Skill Naming
172
+
173
+ - Use lowercase letters, digits, and hyphens only; normalize user-provided titles to hyphen-case (e.g., "Plan Mode" -> `plan-mode`).
174
+ - When generating names, generate a name under 64 characters (letters, digits, hyphens).
175
+ - Prefer short, verb-led phrases that describe the action.
176
+ - Name the skill folder exactly after the skill name.
177
+
178
+ ### Step 1: Understanding the Skill with Concrete Examples
179
+
180
+ Skip this step only when the skill's usage patterns are already clearly understood.
181
+
182
+ To create an effective skill, clearly understand concrete examples of how the skill will be used. For example, when building an image-editor skill, relevant questions include:
183
+
184
+ - "What functionality should the image-editor skill support?"
185
+ - "Can you give some examples of how this skill would be used?"
186
+ - "What would a user say that should trigger this skill?"
187
+
188
+ To avoid overwhelming users, avoid asking too many questions at once. Prefer 1-2 targeted questions.
189
+
190
+ ### Step 2: Planning Reusable Skill Contents
191
+
192
+ Before writing the skill, plan what resources it needs:
193
+
194
+ - **Scripts**: For deterministic operations that need consistency
195
+ - **References**: For detailed documentation the agent should reference
196
+ - **Assets**: For files used in output (templates, images, etc.)
197
+
198
+ ### Step 3: Initializing a Skill
199
+
200
+ Use the bundled initialization script:
201
+
202
+ ```bash
203
+ scripts/init_skill.py <skill-name> --path <output-path>
204
+ scripts/init_skill.py <skill-name> --path <output-path> --resources scripts,references
205
+ scripts/init_skill.py <skill-name> --path <output-path> --resources scripts --examples
206
+ ```
207
+
208
+ This creates a skill directory with a SKILL.md template and optional resource directories.
209
+
210
+ ### Step 4: Editing the Skill
211
+
212
+ #### Frontmatter
213
+
214
+ Required fields:
215
+
216
+ ```yaml
217
+ ---
218
+ name: my-skill-name
219
+ description: >-
220
+ Clear description of what this skill does and when to use it.
221
+ Include trigger scenarios, file types, or tasks that should activate this skill.
222
+ Example: "Process and manipulate PDF files including merging, splitting, extracting text,
223
+ filling forms, or any other document tasks"
224
+ ---
225
+ ```
226
+
227
+ Keep frontmatter minimal. `metadata` and `always` are also supported when needed, but avoid adding extra fields unless they are actually required.
228
+
229
+ #### Body
230
+
231
+ Write instructions for using the skill and its bundled resources.
232
+
233
+ ### Step 5: Packaging a Skill
234
+
235
+ Once development of the skill is complete, it must be packaged into a distributable .skill file. The packaging process automatically validates the skill first:
236
+
237
+ ```bash
238
+ scripts/package_skill.py <path/to/skill-folder>
239
+ ```
240
+
241
+ Optional output directory specification:
242
+
243
+ ```bash
244
+ scripts/package_skill.py <path/to/skill-folder> ./dist
245
+ ```
246
+
247
+ The packaging script will:
248
+
249
+ 1. **Validate** the skill automatically, checking:
250
+ - YAML frontmatter format and required fields
251
+ - Skill naming conventions and directory structure
252
+ - Description completeness and quality
253
+ - File organization and resource references
254
+
255
+ 2. **Package** the skill if validation passes, creating a .skill file named after the skill (e.g., `my-skill.skill`) that includes all files and maintains the proper directory structure for distribution. The .skill file is a zip file with a .skill extension.
256
+
257
+ Security restriction: symlinks are rejected and packaging fails when any symlink is present.
258
+
259
+ If validation fails, the script will report the errors and exit without creating a package. Fix any validation errors and run the packaging command again.
260
+
261
+ ### Step 6: Iterate
262
+
263
+ After testing the skill, users may request improvements. Often this happens right after using the skill, with fresh context of how the skill performed.
264
+
265
+ **Iteration workflow:**
266
+
267
+ 1. Use the skill on real tasks
268
+ 2. Notice struggles or inefficiencies
269
+ 3. Identify how SKILL.md or bundled resources should be updated
270
+ 4. Implement changes and test again
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Skill Initializer - Creates a new skill from template
4
+
5
+ Usage:
6
+ init_skill.py <skill-name> --path <path> [--resources scripts,references,assets] [--examples]
7
+
8
+ Examples:
9
+ init_skill.py my-new-skill --path skills/public
10
+ init_skill.py my-new-skill --path skills/public --resources scripts,references
11
+ init_skill.py my-api-helper --path skills/private --resources scripts --examples
12
+ init_skill.py custom-skill --path /custom/location
13
+ """
14
+
15
+ import argparse
16
+ import re
17
+ import sys
18
+ from pathlib import Path
19
+
20
+ MAX_SKILL_NAME_LENGTH = 64
21
+ ALLOWED_RESOURCES = {"scripts", "references", "assets"}
22
+
23
+ SKILL_TEMPLATE = """---
24
+ name: {skill_name}
25
+ description: "Complete this description with what the skill does and when to use it. Include the trigger scenarios, file types, or tasks that should activate this skill."
26
+ ---
27
+
28
+ # {skill_title}
29
+
30
+ ## Overview
31
+
32
+ [Write 1-2 sentences explaining what this skill enables.]
33
+
34
+ ## Structuring This Skill
35
+
36
+ [Choose the structure that best fits this skill's purpose. Common patterns:
37
+
38
+ **1. Workflow-Based** (best for sequential processes)
39
+ - Structure: ## Overview -> ## Workflow Decision Tree -> ## Step 1 -> ## Step 2...
40
+
41
+ **2. Task-Based** (best for tool collections)
42
+ - Structure: ## Overview -> ## Quick Start -> ## Task Category 1 -> ## Task Category 2...
43
+
44
+ **3. Reference/Guidelines** (best for standards or specifications)
45
+ - Structure: ## Overview -> ## Guidelines -> ## Specifications -> ## Usage...
46
+
47
+ **4. Capabilities-Based** (best for integrated systems)
48
+ - Structure: ## Overview -> ## Core Capabilities -> ### 1. Feature -> ### 2. Feature...
49
+
50
+ Delete this entire "Structuring This Skill" section when done - it's just guidance.]
51
+
52
+ ## [Replace with the first main section based on the chosen structure]
53
+
54
+ [Add content here.]
55
+
56
+ ## Resources (optional)
57
+
58
+ Create only the resource directories this skill actually needs. Delete this section if no resources are required.
59
+
60
+ ### scripts/
61
+ Executable code (Python/Bash/etc.) that can be run directly to perform specific operations.
62
+
63
+ ### references/
64
+ Documentation and reference material intended to be loaded into context to inform the agent's process and thinking.
65
+
66
+ ### assets/
67
+ Files not intended to be loaded into context, but rather used within the output the agent produces.
68
+
69
+ ---
70
+
71
+ **Not every skill requires all three types of resources.**
72
+ """
73
+
74
+ EXAMPLE_SCRIPT = '''#!/usr/bin/env python3
75
+ """
76
+ Example helper script for {skill_name}
77
+
78
+ Replace with actual implementation or delete if not needed.
79
+ """
80
+
81
+ def main():
82
+ print("This is an example script for {skill_name}")
83
+
84
+ if __name__ == "__main__":
85
+ main()
86
+ '''
87
+
88
+ EXAMPLE_REFERENCE = """# Reference Documentation for {skill_title}
89
+
90
+ This is a placeholder for detailed reference documentation.
91
+ Replace with actual reference content or delete if not needed.
92
+
93
+ ## When to Load This Reference
94
+
95
+ The agent should load this reference when:
96
+ - [Describe specific scenarios]
97
+ """
98
+
99
+ EXAMPLE_ASSET_README = """# Assets for {skill_title}
100
+
101
+ Place files here that should be used in the agent's output (templates, images, fonts, etc.).
102
+ These files are NOT loaded into context — they are used directly in output.
103
+ """
104
+
105
+
106
+ def normalize_skill_name(raw_name: str) -> str:
107
+ name = raw_name.strip().lower()
108
+ name = re.sub(r"[^a-z0-9]+", "-", name)
109
+ name = name.strip("-")
110
+ return name
111
+
112
+
113
+ def parse_resources(resources_str: str) -> list[str]:
114
+ if not resources_str.strip():
115
+ return []
116
+ resources = [r.strip().lower() for r in resources_str.split(",") if r.strip()]
117
+ invalid = [r for r in resources if r not in ALLOWED_RESOURCES]
118
+ if invalid:
119
+ print(f"[ERROR] Invalid resource types: {', '.join(invalid)}")
120
+ print(f" Allowed: {', '.join(sorted(ALLOWED_RESOURCES))}")
121
+ sys.exit(1)
122
+ return resources
123
+
124
+
125
+ def init_skill(skill_name: str, base_path: str, resources: list[str], examples: bool = False) -> bool:
126
+ skill_title = skill_name.replace("-", " ").title()
127
+ skill_path = Path(base_path) / skill_name
128
+
129
+ if skill_path.exists():
130
+ print(f"[ERROR] Directory already exists: {skill_path}")
131
+ return False
132
+
133
+ skill_path.mkdir(parents=True, exist_ok=True)
134
+
135
+ skill_md = SKILL_TEMPLATE.format(skill_name=skill_name, skill_title=skill_title)
136
+ (skill_path / "SKILL.md").write_text(skill_md, encoding="utf-8")
137
+ print(f" Created: {skill_path / 'SKILL.md'}")
138
+
139
+ for resource in resources:
140
+ resource_dir = skill_path / resource
141
+ resource_dir.mkdir(exist_ok=True)
142
+ print(f" Created: {resource_dir}/")
143
+
144
+ if examples:
145
+ if resource == "scripts":
146
+ example_file = resource_dir / f"example_{skill_name.replace('-', '_')}.py"
147
+ example_file.write_text(
148
+ EXAMPLE_SCRIPT.format(skill_name=skill_name), encoding="utf-8"
149
+ )
150
+ print(f" Created: {example_file}")
151
+ elif resource == "references":
152
+ example_file = resource_dir / "README.md"
153
+ example_file.write_text(
154
+ EXAMPLE_REFERENCE.format(skill_title=skill_title), encoding="utf-8"
155
+ )
156
+ print(f" Created: {example_file}")
157
+ elif resource == "assets":
158
+ example_file = resource_dir / "README.md"
159
+ example_file.write_text(
160
+ EXAMPLE_ASSET_README.format(skill_title=skill_title), encoding="utf-8"
161
+ )
162
+ print(f" Created: {example_file}")
163
+
164
+ print(f"\n[OK] Skill '{skill_name}' initialized at: {skill_path}")
165
+ return True
166
+
167
+
168
+ def main():
169
+ parser = argparse.ArgumentParser(
170
+ description="Initialize a new skill directory with a SKILL.md template.",
171
+ )
172
+ parser.add_argument("skill_name", help="Skill name (normalized to hyphen-case)")
173
+ parser.add_argument("--path", required=True, help="Output directory for the skill")
174
+ parser.add_argument(
175
+ "--resources",
176
+ default="",
177
+ help="Comma-separated list: scripts,references,assets",
178
+ )
179
+ parser.add_argument(
180
+ "--examples",
181
+ action="store_true",
182
+ help="Create example files inside the selected resource directories",
183
+ )
184
+ args = parser.parse_args()
185
+
186
+ raw_skill_name = args.skill_name
187
+ skill_name = normalize_skill_name(raw_skill_name)
188
+ if not skill_name:
189
+ print("[ERROR] Skill name must include at least one letter or digit.")
190
+ sys.exit(1)
191
+ if len(skill_name) > MAX_SKILL_NAME_LENGTH:
192
+ print(
193
+ f"[ERROR] Skill name '{skill_name}' is too long ({len(skill_name)} characters). "
194
+ f"Maximum is {MAX_SKILL_NAME_LENGTH} characters."
195
+ )
196
+ sys.exit(1)
197
+ if skill_name != raw_skill_name:
198
+ print(f"Note: Normalized skill name from '{raw_skill_name}' to '{skill_name}'.")
199
+
200
+ resources = parse_resources(args.resources)
201
+ if args.examples and not resources:
202
+ print("[ERROR] --examples requires --resources to be set.")
203
+ sys.exit(1)
204
+
205
+ path = args.path
206
+
207
+ print(f"Initializing skill: {skill_name}")
208
+ print(f" Location: {path}")
209
+ if resources:
210
+ print(f" Resources: {', '.join(resources)}")
211
+ if args.examples:
212
+ print(" Examples: enabled")
213
+ else:
214
+ print(" Resources: none (create as needed)")
215
+ print()
216
+
217
+ result = init_skill(skill_name, path, resources, args.examples)
218
+
219
+ if result:
220
+ sys.exit(0)
221
+ else:
222
+ sys.exit(1)
223
+
224
+
225
+ if __name__ == "__main__":
226
+ main()
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Skill Packager - Creates a distributable .skill file of a skill folder
4
+
5
+ Usage:
6
+ python package_skill.py <path/to/skill-folder> [output-directory]
7
+
8
+ Example:
9
+ python package_skill.py skills/public/my-skill
10
+ python package_skill.py skills/public/my-skill ./dist
11
+ """
12
+
13
+ import sys
14
+ import zipfile
15
+ from pathlib import Path
16
+
17
+ from quick_validate import validate_skill
18
+
19
+
20
+ def _is_within(path: Path, root: Path) -> bool:
21
+ try:
22
+ path.relative_to(root)
23
+ return True
24
+ except ValueError:
25
+ return False
26
+
27
+
28
+ def _cleanup_partial_archive(skill_filename: Path) -> None:
29
+ try:
30
+ if skill_filename.exists():
31
+ skill_filename.unlink()
32
+ except OSError:
33
+ return None
34
+
35
+
36
+ def package_skill(skill_path, output_dir=None):
37
+ """
38
+ Package a skill folder into a .skill file.
39
+
40
+ Args:
41
+ skill_path: Path to the skill folder
42
+ output_dir: Optional output directory for the .skill file
43
+
44
+ Returns:
45
+ Path to the created .skill file, or None if error
46
+ """
47
+ skill_path = Path(skill_path).resolve()
48
+
49
+ if not skill_path.exists():
50
+ print(f"[ERROR] Skill folder not found: {skill_path}")
51
+ return None
52
+
53
+ if not skill_path.is_dir():
54
+ print(f"[ERROR] Path is not a directory: {skill_path}")
55
+ return None
56
+
57
+ skill_md = skill_path / "SKILL.md"
58
+ if not skill_md.exists():
59
+ print(f"[ERROR] SKILL.md not found in {skill_path}")
60
+ return None
61
+
62
+ print("Validating skill...")
63
+ valid, message = validate_skill(skill_path)
64
+ if not valid:
65
+ print(f"[ERROR] Validation failed: {message}")
66
+ print(" Please fix the validation errors before packaging.")
67
+ return None
68
+ print(f"[OK] {message}\n")
69
+
70
+ skill_name = skill_path.name
71
+ if output_dir:
72
+ output_path = Path(output_dir).resolve()
73
+ output_path.mkdir(parents=True, exist_ok=True)
74
+ else:
75
+ output_path = Path.cwd()
76
+
77
+ skill_filename = output_path / f"{skill_name}.skill"
78
+
79
+ EXCLUDED_DIRS = {".git", ".svn", ".hg", "__pycache__", "node_modules"}
80
+
81
+ files_to_package = []
82
+ resolved_archive = skill_filename.resolve()
83
+
84
+ for file_path in skill_path.rglob("*"):
85
+ if file_path.is_symlink():
86
+ print(f"[ERROR] Symlink not allowed in packaged skill: {file_path}")
87
+ _cleanup_partial_archive(skill_filename)
88
+ return None
89
+
90
+ rel_parts = file_path.relative_to(skill_path).parts
91
+ if any(part in EXCLUDED_DIRS for part in rel_parts):
92
+ continue
93
+
94
+ if file_path.is_file():
95
+ resolved_file = file_path.resolve()
96
+ if not _is_within(resolved_file, skill_path):
97
+ print(f"[ERROR] File escapes skill root: {file_path}")
98
+ _cleanup_partial_archive(skill_filename)
99
+ return None
100
+ if resolved_file == resolved_archive:
101
+ print(f"[WARN] Skipping output archive: {file_path}")
102
+ continue
103
+ files_to_package.append(file_path)
104
+
105
+ try:
106
+ with zipfile.ZipFile(skill_filename, "w", zipfile.ZIP_DEFLATED) as zipf:
107
+ for file_path in files_to_package:
108
+ arcname = Path(skill_name) / file_path.relative_to(skill_path)
109
+ zipf.write(file_path, arcname)
110
+ print(f" Added: {arcname}")
111
+
112
+ print(f"\n[OK] Successfully packaged skill to: {skill_filename}")
113
+ return skill_filename
114
+
115
+ except Exception as e:
116
+ _cleanup_partial_archive(skill_filename)
117
+ print(f"[ERROR] Error creating .skill file: {e}")
118
+ return None
119
+
120
+
121
+ def main():
122
+ if len(sys.argv) < 2:
123
+ print("Usage: python package_skill.py <path/to/skill-folder> [output-directory]")
124
+ print("\nExample:")
125
+ print(" python package_skill.py skills/public/my-skill")
126
+ print(" python package_skill.py skills/public/my-skill ./dist")
127
+ sys.exit(1)
128
+
129
+ skill_path = sys.argv[1]
130
+ output_dir = sys.argv[2] if len(sys.argv) > 2 else None
131
+
132
+ print(f"Packaging skill: {skill_path}")
133
+ if output_dir:
134
+ print(f" Output directory: {output_dir}")
135
+ print()
136
+
137
+ result = package_skill(skill_path, output_dir)
138
+
139
+ if result:
140
+ sys.exit(0)
141
+ else:
142
+ sys.exit(1)
143
+
144
+
145
+ if __name__ == "__main__":
146
+ main()