@poolzin/pool-bot 2026.3.7 → 2026.3.9

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 (44) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/.buildstamp +1 -1
  3. package/dist/agents/error-classifier.js +302 -0
  4. package/dist/agents/skills/security.js +217 -0
  5. package/dist/build-info.json +3 -3
  6. package/dist/cli/lazy-commands.example.js +113 -0
  7. package/dist/cli/lazy-commands.js +329 -0
  8. package/dist/cli/program/command-registry.js +13 -0
  9. package/dist/cli/program/register.skills.js +4 -0
  10. package/dist/config/config.js +1 -0
  11. package/dist/config/secrets-integration.js +88 -0
  12. package/dist/context-engine/index.js +33 -0
  13. package/dist/context-engine/legacy.js +181 -0
  14. package/dist/context-engine/registry.js +86 -0
  15. package/dist/context-engine/summarizing.js +293 -0
  16. package/dist/context-engine/types.js +7 -0
  17. package/dist/infra/abort-pattern.js +106 -0
  18. package/dist/infra/retry.js +94 -0
  19. package/dist/secrets/index.js +28 -0
  20. package/dist/secrets/resolver.js +185 -0
  21. package/dist/secrets/runtime.js +142 -0
  22. package/dist/secrets/types.js +11 -0
  23. package/dist/security/dangerous-tools.js +80 -0
  24. package/dist/security/types.js +12 -0
  25. package/dist/skills/commands.js +351 -0
  26. package/dist/skills/index.js +167 -0
  27. package/dist/skills/loader.js +282 -0
  28. package/dist/skills/parser.js +461 -0
  29. package/dist/skills/registry.js +397 -0
  30. package/dist/skills/security.js +318 -0
  31. package/dist/skills/types.js +21 -0
  32. package/dist/test-utils/index.js +219 -0
  33. package/dist/tui/index.js +595 -0
  34. package/docs/INTEGRATION_PLAN.md +475 -0
  35. package/docs/INTEGRATION_SUMMARY.md +215 -0
  36. package/docs/integrations/HEXSTRIKE_PLAN.md +796 -0
  37. package/docs/integrations/INTEGRATION_PLAN.md +424 -0
  38. package/docs/integrations/PAGE_AGENT_PLAN.md +370 -0
  39. package/docs/integrations/XYOPS_PLAN.md +978 -0
  40. package/docs/skills/IMPLEMENTATION_SUMMARY.md +145 -0
  41. package/docs/skills/SKILL.md +524 -0
  42. package/docs/skills.md +405 -0
  43. package/package.json +1 -1
  44. package/skills/example-skill/SKILL.md +195 -0
@@ -0,0 +1,397 @@
1
+ /**
2
+ * Skills registry
3
+ * Manages skill discovery, storage, and lifecycle
4
+ *
5
+ * @module skills/registry
6
+ */
7
+ import { readdir } from "node:fs/promises";
8
+ import { join } from "node:path";
9
+ import { EventEmitter } from "node:events";
10
+ import { SkillError, } from "./types.js";
11
+ import { parseSkill, isSkillFile } from "./parser.js";
12
+ import { scanSkill } from "./security.js";
13
+ // ============================================================================
14
+ // Default Configuration
15
+ // ============================================================================
16
+ const DEFAULT_CONFIG = {
17
+ localPaths: [],
18
+ autoScan: true,
19
+ strictSecurity: false,
20
+ cacheTtlMs: 5 * 60 * 1000, // 5 minutes
21
+ };
22
+ // ============================================================================
23
+ // Skills Registry
24
+ // ============================================================================
25
+ export class SkillsRegistry extends EventEmitter {
26
+ skills = new Map();
27
+ config;
28
+ scanCache = new Map();
29
+ constructor(config = {}) {
30
+ super();
31
+ this.config = { ...DEFAULT_CONFIG, ...config };
32
+ }
33
+ // ========================================================================
34
+ // Event Handling
35
+ // ========================================================================
36
+ emitEvent(type, skillId, data) {
37
+ const event = {
38
+ type,
39
+ skillId,
40
+ timestamp: new Date(),
41
+ data,
42
+ };
43
+ this.emit(type, event);
44
+ }
45
+ onSkillEvent(type, handler) {
46
+ this.on(type, handler);
47
+ }
48
+ // ========================================================================
49
+ // Skill Discovery
50
+ // ========================================================================
51
+ /**
52
+ * Scan all configured paths for skills
53
+ */
54
+ async scan() {
55
+ const errors = [];
56
+ let loaded = 0;
57
+ for (const path of this.config.localPaths) {
58
+ try {
59
+ const result = await this.scanPath(path);
60
+ loaded += result.loaded;
61
+ errors.push(...result.errors);
62
+ }
63
+ catch (error) {
64
+ errors.push(error);
65
+ }
66
+ }
67
+ return { loaded, errors };
68
+ }
69
+ /**
70
+ * Scan a single path for skills
71
+ */
72
+ async scanPath(basePath) {
73
+ const errors = [];
74
+ let loaded = 0;
75
+ try {
76
+ const entries = await readdir(basePath, { withFileTypes: true });
77
+ for (const entry of entries) {
78
+ if (!entry.isDirectory())
79
+ continue;
80
+ const skillPath = join(basePath.toString(), entry.name, "SKILL.md");
81
+ try {
82
+ if (await isSkillFile(skillPath)) {
83
+ const skill = await parseSkill(skillPath);
84
+ // Run security scan if auto-scan enabled
85
+ if (this.config.autoScan) {
86
+ skill.securityReport = await scanSkill(skill);
87
+ skill.verification = skill.securityReport.result;
88
+ // Auto-disable if strict security and failed
89
+ if (this.config.strictSecurity &&
90
+ skill.verification === "failed") {
91
+ skill.enabled = false;
92
+ skill.status = "disabled";
93
+ }
94
+ }
95
+ this.skills.set(skill.metadata.id, skill);
96
+ this.emitEvent("skill:loaded", skill.metadata.id);
97
+ loaded++;
98
+ }
99
+ }
100
+ catch (error) {
101
+ errors.push(new SkillError("LOAD_ERROR", `Failed to load skill from ${skillPath}: ${error}`, entry.name, error));
102
+ }
103
+ }
104
+ }
105
+ catch (error) {
106
+ errors.push(error);
107
+ }
108
+ return { loaded, errors };
109
+ }
110
+ /**
111
+ * Watch for skill changes (placeholder for future implementation)
112
+ */
113
+ async watch() {
114
+ // TODO: Implement file watching for hot-reload
115
+ // This would use fs.watch or chokidar
116
+ throw new SkillError("CONFIG_ERROR", "Watch mode not yet implemented");
117
+ }
118
+ // ========================================================================
119
+ // Skill CRUD
120
+ // ========================================================================
121
+ /**
122
+ * Get a skill by ID
123
+ */
124
+ get(id) {
125
+ return this.skills.get(id);
126
+ }
127
+ /**
128
+ * Get all skills
129
+ */
130
+ getAll() {
131
+ return Array.from(this.skills.values());
132
+ }
133
+ /**
134
+ * Get skill reference (lightweight)
135
+ */
136
+ getRef(id) {
137
+ const skill = this.skills.get(id);
138
+ if (!skill)
139
+ return undefined;
140
+ return {
141
+ id: skill.metadata.id,
142
+ name: skill.metadata.name,
143
+ description: skill.metadata.description,
144
+ category: skill.metadata.category,
145
+ tags: skill.metadata.tags,
146
+ version: skill.metadata.version,
147
+ status: skill.status,
148
+ verification: skill.verification,
149
+ enabled: skill.enabled,
150
+ };
151
+ }
152
+ /**
153
+ * Get all skill references
154
+ */
155
+ getAllRefs() {
156
+ return this.getAll().map((skill) => ({
157
+ id: skill.metadata.id,
158
+ name: skill.metadata.name,
159
+ description: skill.metadata.description,
160
+ category: skill.metadata.category,
161
+ tags: skill.metadata.tags,
162
+ version: skill.metadata.version,
163
+ status: skill.status,
164
+ verification: skill.verification,
165
+ enabled: skill.enabled,
166
+ }));
167
+ }
168
+ /**
169
+ * Add or update a skill
170
+ */
171
+ async add(skill) {
172
+ const exists = this.skills.has(skill.metadata.id);
173
+ this.skills.set(skill.metadata.id, skill);
174
+ this.emitEvent(exists ? "skill:updated" : "skill:loaded", skill.metadata.id);
175
+ }
176
+ /**
177
+ * Remove a skill
178
+ */
179
+ remove(id) {
180
+ const existed = this.skills.delete(id);
181
+ if (existed) {
182
+ this.emitEvent("skill:unloaded", id);
183
+ }
184
+ return existed;
185
+ }
186
+ /**
187
+ * Check if skill exists
188
+ */
189
+ has(id) {
190
+ return this.skills.has(id);
191
+ }
192
+ // ========================================================================
193
+ // Skill State Management
194
+ // ========================================================================
195
+ /**
196
+ * Enable a skill
197
+ */
198
+ enable(id) {
199
+ const skill = this.skills.get(id);
200
+ if (!skill)
201
+ return false;
202
+ if (skill.verification === "failed" && this.config.strictSecurity) {
203
+ throw new SkillError("SECURITY_VIOLATION", `Cannot enable skill ${id}: failed security scan`, id);
204
+ }
205
+ skill.enabled = true;
206
+ skill.status = "installed";
207
+ this.emitEvent("skill:enabled", id);
208
+ return true;
209
+ }
210
+ /**
211
+ * Disable a skill
212
+ */
213
+ disable(id) {
214
+ const skill = this.skills.get(id);
215
+ if (!skill)
216
+ return false;
217
+ skill.enabled = false;
218
+ skill.status = "disabled";
219
+ this.emitEvent("skill:disabled", id);
220
+ return true;
221
+ }
222
+ /**
223
+ * Toggle skill enabled state
224
+ */
225
+ toggle(id) {
226
+ const skill = this.skills.get(id);
227
+ if (!skill)
228
+ return false;
229
+ return skill.enabled ? this.disable(id) : this.enable(id);
230
+ }
231
+ // ========================================================================
232
+ // Search
233
+ // ========================================================================
234
+ /**
235
+ * Search skills with filters
236
+ */
237
+ search(filters = {}, page = 1, pageSize = 20) {
238
+ let results = this.getAll();
239
+ // Apply filters
240
+ if (filters.category) {
241
+ results = results.filter((s) => s.metadata.category === filters.category);
242
+ }
243
+ if (filters.tags?.length) {
244
+ results = results.filter((s) => filters.tags.some((tag) => s.metadata.tags.includes(tag)));
245
+ }
246
+ if (filters.status) {
247
+ results = results.filter((s) => s.status === filters.status);
248
+ }
249
+ if (filters.verification) {
250
+ results = results.filter((s) => s.verification === filters.verification);
251
+ }
252
+ if (filters.enabledOnly) {
253
+ results = results.filter((s) => s.enabled);
254
+ }
255
+ if (filters.query) {
256
+ const query = filters.query.toLowerCase();
257
+ results = results.filter((s) => s.metadata.name.toLowerCase().includes(query) ||
258
+ s.metadata.description.toLowerCase().includes(query) ||
259
+ s.metadata.tags.some((t) => t.toLowerCase().includes(query)));
260
+ }
261
+ // Pagination
262
+ const total = results.length;
263
+ const start = (page - 1) * pageSize;
264
+ const paginated = results.slice(start, start + pageSize);
265
+ return {
266
+ skills: paginated.map((s) => ({
267
+ id: s.metadata.id,
268
+ name: s.metadata.name,
269
+ description: s.metadata.description,
270
+ category: s.metadata.category,
271
+ tags: s.metadata.tags,
272
+ version: s.metadata.version,
273
+ status: s.status,
274
+ verification: s.verification,
275
+ enabled: s.enabled,
276
+ })),
277
+ total,
278
+ page,
279
+ pageSize,
280
+ };
281
+ }
282
+ /**
283
+ * Find skills by tag
284
+ */
285
+ findByTag(tag) {
286
+ return this.getAllRefs().filter((s) => s.tags.includes(tag));
287
+ }
288
+ /**
289
+ * Find skills by category
290
+ */
291
+ findByCategory(category) {
292
+ return this.getAllRefs().filter((s) => s.category === category);
293
+ }
294
+ // ========================================================================
295
+ // Statistics
296
+ // ========================================================================
297
+ /**
298
+ * Get registry statistics
299
+ */
300
+ getStats() {
301
+ const skills = this.getAll();
302
+ const byCategory = {};
303
+ const byVerification = {};
304
+ for (const skill of skills) {
305
+ // Count by category
306
+ byCategory[skill.metadata.category] =
307
+ (byCategory[skill.metadata.category] || 0) + 1;
308
+ // Count by verification
309
+ byVerification[skill.verification] =
310
+ (byVerification[skill.verification] || 0) + 1;
311
+ }
312
+ return {
313
+ total: skills.length,
314
+ enabled: skills.filter((s) => s.enabled).length,
315
+ disabled: skills.filter((s) => !s.enabled).length,
316
+ byCategory,
317
+ byVerification,
318
+ };
319
+ }
320
+ // ========================================================================
321
+ // Import/Export
322
+ // ========================================================================
323
+ /**
324
+ * Export all skills to JSON
325
+ */
326
+ export() {
327
+ const data = this.getAll().map((skill) => ({
328
+ metadata: skill.metadata,
329
+ status: skill.status,
330
+ verification: skill.verification,
331
+ enabled: skill.enabled,
332
+ userConfig: skill.userConfig,
333
+ }));
334
+ return JSON.stringify(data, null, 2);
335
+ }
336
+ /**
337
+ * Import skills from JSON
338
+ */
339
+ async import(json) {
340
+ const data = JSON.parse(json);
341
+ for (const partial of data) {
342
+ if (!partial.metadata?.id)
343
+ continue;
344
+ // Try to reload from source
345
+ const existing = this.skills.get(partial.metadata.id);
346
+ if (existing) {
347
+ // Update state only
348
+ if (partial.enabled !== undefined) {
349
+ existing.enabled = partial.enabled;
350
+ }
351
+ if (partial.userConfig) {
352
+ existing.userConfig = partial.userConfig;
353
+ }
354
+ }
355
+ }
356
+ }
357
+ // ========================================================================
358
+ // Cleanup
359
+ // ========================================================================
360
+ /**
361
+ * Clear all skills
362
+ */
363
+ clear() {
364
+ for (const id of this.skills.keys()) {
365
+ this.emitEvent("skill:unloaded", id);
366
+ }
367
+ this.skills.clear();
368
+ this.scanCache.clear();
369
+ }
370
+ /**
371
+ * Dispose of registry resources
372
+ */
373
+ dispose() {
374
+ this.clear();
375
+ this.removeAllListeners();
376
+ }
377
+ }
378
+ // ============================================================================
379
+ // Singleton Instance
380
+ // ============================================================================
381
+ let globalRegistry = null;
382
+ /**
383
+ * Get or create global registry instance
384
+ */
385
+ export function getRegistry(config) {
386
+ if (!globalRegistry) {
387
+ globalRegistry = new SkillsRegistry(config);
388
+ }
389
+ return globalRegistry;
390
+ }
391
+ /**
392
+ * Reset global registry (useful for testing)
393
+ */
394
+ export function resetRegistry() {
395
+ globalRegistry?.dispose();
396
+ globalRegistry = null;
397
+ }