@veolab/discoverylab 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/.claude-plugin/marketplace.json +15 -0
  2. package/.claude-plugin/plugin.json +12 -0
  3. package/.mcp.json +6 -0
  4. package/README.md +214 -0
  5. package/assets/applab-discovery.jpeg +0 -0
  6. package/assets/backgrounds/abstract-colorful-gradient-orange-background.jpg +0 -0
  7. package/assets/backgrounds/blurred-colorful-luxury-gradient-rainbow-abstract.jpg +0 -0
  8. package/assets/backgrounds/glowing-neon-moving-continuously-looking-bright.jpg +0 -0
  9. package/assets/backgrounds/glowing-neon-moving-continuously-looking-bright2.jpg +0 -0
  10. package/assets/backgrounds/macos-big-sur-apple-layers-fluidic-colorful-wwdc-stock-4096x2304-1455.jpg +0 -0
  11. package/assets/backgrounds/macos-sierra-mountain-peak-sunset-evening-stock-5k-5120x3684-3987.jpg +0 -0
  12. package/assets/backgrounds/macos-tahoe-26-5120x2880-22674.jpg +0 -0
  13. package/assets/backgrounds/macos-tahoe-26-5120x2880-22675.jpg +0 -0
  14. package/assets/backgrounds/view-of-the-sea-from-the-window-of-an-airplane-2024-10-21-11-25-30-utc.jpg +0 -0
  15. package/assets/cursor/cursor-blue.png +0 -0
  16. package/assets/icons/android-head_3D.png +0 -0
  17. package/assets/icons/apple-logo.png +0 -0
  18. package/assets/icons/apple-logo.svg +4 -0
  19. package/assets/icons/claude-ai-icon.svg +1 -0
  20. package/assets/icons/icons8-apple-intelligence-48.png +0 -0
  21. package/assets/icons/icons8-apple-intelligence-96.png +0 -0
  22. package/dist/chunk-7IDQLLBW.js +311 -0
  23. package/dist/chunk-MLKGABMK.js +9 -0
  24. package/dist/chunk-MN6LCZHZ.js +1320 -0
  25. package/dist/chunk-PBHUHSC3.js +6002 -0
  26. package/dist/chunk-QJXXHOV7.js +205 -0
  27. package/dist/chunk-SSRXIO2V.js +6822 -0
  28. package/dist/chunk-VY3BLXBW.js +329 -0
  29. package/dist/chunk-W3WJGYR6.js +354 -0
  30. package/dist/cli.d.ts +1 -0
  31. package/dist/cli.js +120 -0
  32. package/dist/db-IWIL65EX.js +33 -0
  33. package/dist/gridCompositor-ENKLFPWR.js +409 -0
  34. package/dist/index.d.ts +1648 -0
  35. package/dist/index.js +869 -0
  36. package/dist/ocr-UTWC7537.js +21 -0
  37. package/dist/server-3FBHBA7L.js +15 -0
  38. package/dist/server-NM5CKDUU.js +13 -0
  39. package/dist/setup-27CQAX6K.js +17 -0
  40. package/dist/tools-75BAPCUM.js +177 -0
  41. package/package.json +84 -0
  42. package/skills/generate-assets/SKILL.md +44 -0
  43. package/skills/mobile-test/SKILL.md +33 -0
  44. package/skills/open-ui/SKILL.md +24 -0
  45. package/skills/quick-capture/SKILL.md +28 -0
  46. package/skills/task-hub/SKILL.md +44 -0
  47. package/skills/web-test/SKILL.md +41 -0
package/dist/index.js ADDED
@@ -0,0 +1,869 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ analyzeTools,
4
+ canvasTools,
5
+ captureTools,
6
+ exportTools,
7
+ integrationTools,
8
+ projectTools,
9
+ taskHubTools,
10
+ testingTools,
11
+ uiTools
12
+ } from "./chunk-SSRXIO2V.js";
13
+ import "./chunk-W3WJGYR6.js";
14
+ import {
15
+ setupTools
16
+ } from "./chunk-7IDQLLBW.js";
17
+ import {
18
+ mcpServer
19
+ } from "./chunk-QJXXHOV7.js";
20
+ import {
21
+ startServer,
22
+ stopServer
23
+ } from "./chunk-PBHUHSC3.js";
24
+ import "./chunk-MN6LCZHZ.js";
25
+ import {
26
+ closeDatabase,
27
+ exportDestinations,
28
+ exportRules,
29
+ frames,
30
+ getDatabase,
31
+ projectExports,
32
+ projects,
33
+ settings
34
+ } from "./chunk-VY3BLXBW.js";
35
+ import "./chunk-MLKGABMK.js";
36
+
37
+ // src/core/protocol/types.ts
38
+ function isWSRequest(msg) {
39
+ return typeof msg === "object" && msg !== null && msg.type === "req" && typeof msg.id === "string" && typeof msg.method === "string";
40
+ }
41
+ function isWSResponse(msg) {
42
+ return typeof msg === "object" && msg !== null && msg.type === "res" && typeof msg.id === "string" && typeof msg.ok === "boolean";
43
+ }
44
+ function isWSEvent(msg) {
45
+ return typeof msg === "object" && msg !== null && msg.type === "event" && typeof msg.event === "string";
46
+ }
47
+ function isWSMessage(msg) {
48
+ return isWSRequest(msg) || isWSResponse(msg) || isWSEvent(msg);
49
+ }
50
+
51
+ // src/core/protocol/validator.ts
52
+ var schemas = {
53
+ request: {
54
+ type: "object",
55
+ required: ["type", "id", "method"],
56
+ properties: {
57
+ type: { const: "req" },
58
+ id: { type: "string" },
59
+ method: { type: "string" },
60
+ params: { type: "object" }
61
+ },
62
+ additionalProperties: false
63
+ },
64
+ response: {
65
+ type: "object",
66
+ required: ["type", "id", "ok"],
67
+ properties: {
68
+ type: { const: "res" },
69
+ id: { type: "string" },
70
+ ok: { type: "boolean" },
71
+ payload: {},
72
+ error: { type: "string" }
73
+ },
74
+ additionalProperties: false
75
+ },
76
+ event: {
77
+ type: "object",
78
+ required: ["type", "event", "payload"],
79
+ properties: {
80
+ type: { const: "event" },
81
+ event: { type: "string" },
82
+ payload: {},
83
+ seq: { type: "number" },
84
+ timestamp: { type: "number" }
85
+ },
86
+ additionalProperties: false
87
+ },
88
+ // Method-specific param schemas
89
+ methods: {
90
+ "ping": {
91
+ type: "object",
92
+ properties: {},
93
+ additionalProperties: false
94
+ },
95
+ "recorder.start": {
96
+ type: "object",
97
+ required: ["name", "url"],
98
+ properties: {
99
+ name: { type: "string" },
100
+ url: { type: "string" },
101
+ resolution: {
102
+ type: "object",
103
+ properties: {
104
+ width: { type: "number" },
105
+ height: { type: "number" }
106
+ }
107
+ }
108
+ },
109
+ additionalProperties: false
110
+ },
111
+ "recorder.stop": {
112
+ type: "object",
113
+ properties: {},
114
+ additionalProperties: false
115
+ },
116
+ "recorder.status": {
117
+ type: "object",
118
+ properties: {},
119
+ additionalProperties: false
120
+ },
121
+ "liveStream.start": {
122
+ type: "object",
123
+ required: ["platform"],
124
+ properties: {
125
+ platform: { enum: ["ios", "android"] },
126
+ deviceId: { type: "string" },
127
+ interactive: { type: "boolean" }
128
+ },
129
+ additionalProperties: false
130
+ },
131
+ "liveStream.stop": {
132
+ type: "object",
133
+ properties: {},
134
+ additionalProperties: false
135
+ },
136
+ "liveStream.tap": {
137
+ type: "object",
138
+ required: ["x", "y"],
139
+ properties: {
140
+ x: { type: "number" },
141
+ y: { type: "number" }
142
+ },
143
+ additionalProperties: false
144
+ },
145
+ "project.list": {
146
+ type: "object",
147
+ properties: {},
148
+ additionalProperties: false
149
+ },
150
+ "project.get": {
151
+ type: "object",
152
+ required: ["id"],
153
+ properties: {
154
+ id: { type: "string" }
155
+ },
156
+ additionalProperties: false
157
+ },
158
+ "project.create": {
159
+ type: "object",
160
+ required: ["name"],
161
+ properties: {
162
+ name: { type: "string" },
163
+ packageName: { type: "string" }
164
+ },
165
+ additionalProperties: false
166
+ },
167
+ "project.delete": {
168
+ type: "object",
169
+ required: ["id"],
170
+ properties: {
171
+ id: { type: "string" }
172
+ },
173
+ additionalProperties: false
174
+ }
175
+ }
176
+ };
177
+ function validateSchema(data, schema, path = "") {
178
+ const errors = [];
179
+ if (schema.const !== void 0) {
180
+ if (data !== schema.const) {
181
+ errors.push({
182
+ path: path || "root",
183
+ message: `Expected constant value`,
184
+ expected: String(schema.const),
185
+ received: String(data)
186
+ });
187
+ }
188
+ return errors;
189
+ }
190
+ if (schema.enum !== void 0) {
191
+ if (!schema.enum.includes(data)) {
192
+ errors.push({
193
+ path: path || "root",
194
+ message: `Value must be one of: ${schema.enum.join(", ")}`,
195
+ received: String(data)
196
+ });
197
+ }
198
+ return errors;
199
+ }
200
+ if (schema.type === "object") {
201
+ if (typeof data !== "object" || data === null || Array.isArray(data)) {
202
+ errors.push({
203
+ path: path || "root",
204
+ message: "Expected object",
205
+ expected: "object",
206
+ received: typeof data
207
+ });
208
+ return errors;
209
+ }
210
+ const obj = data;
211
+ if (schema.required) {
212
+ for (const key of schema.required) {
213
+ if (!(key in obj)) {
214
+ errors.push({
215
+ path: `${path}.${key}`,
216
+ message: `Missing required property: ${key}`
217
+ });
218
+ }
219
+ }
220
+ }
221
+ if (schema.properties) {
222
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
223
+ if (key in obj) {
224
+ const propErrors = validateSchema(obj[key], propSchema, `${path}.${key}`);
225
+ errors.push(...propErrors);
226
+ }
227
+ }
228
+ }
229
+ if (schema.additionalProperties === false && schema.properties) {
230
+ for (const key of Object.keys(obj)) {
231
+ if (!(key in schema.properties)) {
232
+ errors.push({
233
+ path: `${path}.${key}`,
234
+ message: `Unexpected property: ${key}`
235
+ });
236
+ }
237
+ }
238
+ }
239
+ } else if (schema.type === "string") {
240
+ if (typeof data !== "string") {
241
+ errors.push({
242
+ path: path || "root",
243
+ message: "Expected string",
244
+ expected: "string",
245
+ received: typeof data
246
+ });
247
+ }
248
+ } else if (schema.type === "number") {
249
+ if (typeof data !== "number") {
250
+ errors.push({
251
+ path: path || "root",
252
+ message: "Expected number",
253
+ expected: "number",
254
+ received: typeof data
255
+ });
256
+ }
257
+ } else if (schema.type === "boolean") {
258
+ if (typeof data !== "boolean") {
259
+ errors.push({
260
+ path: path || "root",
261
+ message: "Expected boolean",
262
+ expected: "boolean",
263
+ received: typeof data
264
+ });
265
+ }
266
+ }
267
+ return errors;
268
+ }
269
+ function validateRequest(msg) {
270
+ const errors = validateSchema(msg, schemas.request);
271
+ return { valid: errors.length === 0, errors };
272
+ }
273
+ function validateMethodParams(method, params) {
274
+ const schema = schemas.methods[method];
275
+ if (!schema) {
276
+ return {
277
+ valid: false,
278
+ errors: [{ path: "method", message: `Unknown method: ${method}` }]
279
+ };
280
+ }
281
+ const errors = validateSchema(params ?? {}, schema);
282
+ return { valid: errors.length === 0, errors };
283
+ }
284
+ function validateResponse(msg) {
285
+ const errors = validateSchema(msg, schemas.response);
286
+ return { valid: errors.length === 0, errors };
287
+ }
288
+ function validateEvent(msg) {
289
+ const errors = validateSchema(msg, schemas.event);
290
+ return { valid: errors.length === 0, errors };
291
+ }
292
+ function formatValidationErrors(errors) {
293
+ return errors.map((e) => {
294
+ let msg = `${e.path}: ${e.message}`;
295
+ if (e.expected) msg += ` (expected: ${e.expected})`;
296
+ if (e.received) msg += ` (received: ${e.received})`;
297
+ return msg;
298
+ }).join("\n");
299
+ }
300
+ var availableMethods = [
301
+ "ping",
302
+ "recorder.start",
303
+ "recorder.stop",
304
+ "recorder.status",
305
+ "liveStream.start",
306
+ "liveStream.stop",
307
+ "liveStream.tap",
308
+ "project.list",
309
+ "project.get",
310
+ "project.create",
311
+ "project.delete"
312
+ ];
313
+ var availableEvents = [
314
+ "action",
315
+ "screenshot",
316
+ "status",
317
+ "stopped",
318
+ "session",
319
+ "liveFrame",
320
+ "error",
321
+ "connected"
322
+ ];
323
+
324
+ // src/core/protocol/index.ts
325
+ import { randomUUID } from "crypto";
326
+ function createRequest(method, params) {
327
+ return {
328
+ type: "req",
329
+ id: randomUUID(),
330
+ method,
331
+ params
332
+ };
333
+ }
334
+ function createResponse(id, ok, payloadOrError) {
335
+ if (ok) {
336
+ return {
337
+ type: "res",
338
+ id,
339
+ ok: true,
340
+ payload: payloadOrError
341
+ };
342
+ } else {
343
+ return {
344
+ type: "res",
345
+ id,
346
+ ok: false,
347
+ error: payloadOrError
348
+ };
349
+ }
350
+ }
351
+ function createEvent(event, payload, seq) {
352
+ return {
353
+ type: "event",
354
+ event,
355
+ payload,
356
+ seq,
357
+ timestamp: Date.now()
358
+ };
359
+ }
360
+ var eventSequence = 0;
361
+ function nextEventSeq() {
362
+ return ++eventSequence;
363
+ }
364
+ function resetEventSeq() {
365
+ eventSequence = 0;
366
+ }
367
+
368
+ // src/core/skills/parser.ts
369
+ import { readFile } from "fs/promises";
370
+ function parseSimpleYaml(yaml) {
371
+ const result = {};
372
+ const lines = yaml.split("\n");
373
+ const stack = [{ obj: result, indent: -1 }];
374
+ for (const line of lines) {
375
+ if (!line.trim() || line.trim().startsWith("#")) continue;
376
+ const match = line.match(/^(\s*)([^:]+):\s*(.*)$/);
377
+ if (!match) continue;
378
+ const [, spaces, key, rawValue] = match;
379
+ const indent = spaces.length;
380
+ const trimmedKey = key.trim();
381
+ const value = rawValue.trim();
382
+ while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
383
+ stack.pop();
384
+ }
385
+ const current = stack[stack.length - 1].obj;
386
+ if (value === "") {
387
+ const nested = {};
388
+ current[trimmedKey] = nested;
389
+ stack.push({ obj: nested, indent });
390
+ } else if (value.startsWith("[") && value.endsWith("]")) {
391
+ const arrayContent = value.slice(1, -1);
392
+ current[trimmedKey] = arrayContent.split(",").map((s) => s.trim()).filter((s) => s.length > 0).map((s) => parseYamlValue(s));
393
+ } else {
394
+ current[trimmedKey] = parseYamlValue(value);
395
+ }
396
+ }
397
+ return result;
398
+ }
399
+ function parseYamlValue(value) {
400
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
401
+ return value.slice(1, -1);
402
+ }
403
+ if (value === "true") return true;
404
+ if (value === "false") return false;
405
+ if (value === "null" || value === "~") return null;
406
+ const num = Number(value);
407
+ if (!isNaN(num) && value !== "") return num;
408
+ return value;
409
+ }
410
+ var FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
411
+ function parseSkillMd(fileContent, filePath) {
412
+ const match = fileContent.match(FRONTMATTER_REGEX);
413
+ if (!match) {
414
+ throw new Error(`Invalid SKILL.md format: missing YAML frontmatter in ${filePath}`);
415
+ }
416
+ const [, yamlContent, markdownContent] = match;
417
+ let metadata;
418
+ try {
419
+ const parsed = parseSimpleYaml(yamlContent);
420
+ metadata = parsed;
421
+ } catch (error) {
422
+ const msg = error instanceof Error ? error.message : "Unknown error";
423
+ throw new Error(`Failed to parse YAML frontmatter in ${filePath}: ${msg}`);
424
+ }
425
+ if (!metadata.name) {
426
+ throw new Error(`Missing required field 'name' in ${filePath}`);
427
+ }
428
+ if (!metadata.description) {
429
+ throw new Error(`Missing required field 'description' in ${filePath}`);
430
+ }
431
+ if (metadata.requires) {
432
+ if (metadata.requires.bins && !Array.isArray(metadata.requires.bins)) {
433
+ metadata.requires.bins = [metadata.requires.bins];
434
+ }
435
+ if (metadata.requires.env && !Array.isArray(metadata.requires.env)) {
436
+ metadata.requires.env = [metadata.requires.env];
437
+ }
438
+ if (metadata.requires.packages && !Array.isArray(metadata.requires.packages)) {
439
+ metadata.requires.packages = [metadata.requires.packages];
440
+ }
441
+ }
442
+ if (metadata.os && !Array.isArray(metadata.os)) {
443
+ metadata.os = [metadata.os];
444
+ }
445
+ if (metadata.tools && !Array.isArray(metadata.tools)) {
446
+ metadata.tools = [metadata.tools];
447
+ }
448
+ if (metadata.tags && !Array.isArray(metadata.tags)) {
449
+ metadata.tags = [metadata.tags];
450
+ }
451
+ return {
452
+ metadata,
453
+ content: markdownContent.trim()
454
+ };
455
+ }
456
+ async function readSkillMd(filePath) {
457
+ const content = await readFile(filePath, "utf-8");
458
+ return parseSkillMd(content, filePath);
459
+ }
460
+ function generateSkillMd(metadata, content) {
461
+ const yaml = generateYamlFrontmatter(metadata);
462
+ return `---
463
+ ${yaml}---
464
+
465
+ ${content}`;
466
+ }
467
+ function generateYamlFrontmatter(metadata) {
468
+ const lines = [];
469
+ lines.push(`name: ${metadata.name}`);
470
+ lines.push(`description: "${metadata.description}"`);
471
+ if (metadata.emoji) {
472
+ lines.push(`emoji: "${metadata.emoji}"`);
473
+ }
474
+ if (metadata.version) {
475
+ lines.push(`version: "${metadata.version}"`);
476
+ }
477
+ if (metadata.author) {
478
+ lines.push(`author: "${metadata.author}"`);
479
+ }
480
+ if (metadata.category) {
481
+ lines.push(`category: ${metadata.category}`);
482
+ }
483
+ if (metadata.requires) {
484
+ lines.push("requires:");
485
+ if (metadata.requires.bins?.length) {
486
+ lines.push(` bins: [${metadata.requires.bins.join(", ")}]`);
487
+ }
488
+ if (metadata.requires.env?.length) {
489
+ lines.push(` env: [${metadata.requires.env.join(", ")}]`);
490
+ }
491
+ if (metadata.requires.packages?.length) {
492
+ lines.push(` packages: [${metadata.requires.packages.join(", ")}]`);
493
+ }
494
+ }
495
+ if (metadata.os?.length) {
496
+ lines.push(`os: [${metadata.os.join(", ")}]`);
497
+ }
498
+ if (metadata.always) {
499
+ lines.push("always: true");
500
+ }
501
+ if (metadata.install) {
502
+ lines.push("install:");
503
+ if (metadata.install.brew) {
504
+ lines.push(` brew: ${metadata.install.brew}`);
505
+ }
506
+ if (metadata.install.apt) {
507
+ lines.push(` apt: ${metadata.install.apt}`);
508
+ }
509
+ if (metadata.install.npm) {
510
+ lines.push(` npm: ${metadata.install.npm}`);
511
+ }
512
+ if (metadata.install.manual) {
513
+ lines.push(` manual: "${metadata.install.manual}"`);
514
+ }
515
+ }
516
+ if (metadata.tools?.length) {
517
+ lines.push(`tools: [${metadata.tools.join(", ")}]`);
518
+ }
519
+ if (metadata.tags?.length) {
520
+ lines.push(`tags: [${metadata.tags.join(", ")}]`);
521
+ }
522
+ return lines.join("\n") + "\n";
523
+ }
524
+
525
+ // src/core/skills/gating.ts
526
+ import { execSync } from "child_process";
527
+ import { platform } from "os";
528
+ function checkBinary(name) {
529
+ try {
530
+ const cmd = platform() === "win32" ? `where ${name}` : `which ${name}`;
531
+ execSync(cmd, { stdio: "ignore" });
532
+ return true;
533
+ } catch {
534
+ return false;
535
+ }
536
+ }
537
+ function checkBinaries(bins) {
538
+ return bins.filter((bin) => !checkBinary(bin));
539
+ }
540
+ function checkEnvVar(name) {
541
+ const value = process.env[name];
542
+ return value !== void 0 && value !== "";
543
+ }
544
+ function checkEnvVars(envs) {
545
+ return envs.filter((env) => !checkEnvVar(env));
546
+ }
547
+ function getCurrentOS() {
548
+ const os = platform();
549
+ if (os === "darwin" || os === "linux" || os === "win32") {
550
+ return os;
551
+ }
552
+ return "linux";
553
+ }
554
+ function checkOS(supportedOS) {
555
+ const currentOS = getCurrentOS();
556
+ return supportedOS.includes(currentOS);
557
+ }
558
+ function checkSkillRequirements(metadata) {
559
+ if (metadata.always) {
560
+ return {
561
+ satisfied: true,
562
+ missingBins: [],
563
+ missingEnv: [],
564
+ osUnsupported: false,
565
+ summary: "Skill is always available"
566
+ };
567
+ }
568
+ const missingBins = metadata.requires?.bins ? checkBinaries(metadata.requires.bins) : [];
569
+ const missingEnv = metadata.requires?.env ? checkEnvVars(metadata.requires.env) : [];
570
+ const osUnsupported = metadata.os ? !checkOS(metadata.os) : false;
571
+ const satisfied = missingBins.length === 0 && missingEnv.length === 0 && !osUnsupported;
572
+ const reasons = [];
573
+ if (missingBins.length > 0) {
574
+ reasons.push(`Missing binaries: ${missingBins.join(", ")}`);
575
+ }
576
+ if (missingEnv.length > 0) {
577
+ reasons.push(`Missing env vars: ${missingEnv.join(", ")}`);
578
+ }
579
+ if (osUnsupported) {
580
+ const currentOS = getCurrentOS();
581
+ reasons.push(`OS '${currentOS}' not supported (requires: ${metadata.os?.join(", ")})`);
582
+ }
583
+ const summary = satisfied ? "All requirements satisfied" : reasons.join("; ");
584
+ return {
585
+ satisfied,
586
+ missingBins,
587
+ missingEnv,
588
+ osUnsupported,
589
+ summary
590
+ };
591
+ }
592
+ function getInstallInstructions(metadata, result) {
593
+ const instructions = [];
594
+ if (result.missingBins.length > 0 && metadata.install) {
595
+ const os = getCurrentOS();
596
+ if (os === "darwin" && metadata.install.brew) {
597
+ instructions.push(`brew install ${metadata.install.brew}`);
598
+ } else if (os === "linux" && metadata.install.apt) {
599
+ instructions.push(`apt install ${metadata.install.apt}`);
600
+ } else if (metadata.install.npm) {
601
+ instructions.push(`npm install -g ${metadata.install.npm}`);
602
+ } else if (metadata.install.manual) {
603
+ instructions.push(metadata.install.manual);
604
+ }
605
+ }
606
+ if (result.missingEnv.length > 0) {
607
+ for (const env of result.missingEnv) {
608
+ instructions.push(`Set environment variable: export ${env}=<value>`);
609
+ }
610
+ }
611
+ return instructions;
612
+ }
613
+
614
+ // src/core/skills/loader.ts
615
+ import { readdir, access } from "fs/promises";
616
+ import { join, dirname } from "path";
617
+ import { homedir } from "os";
618
+ import { fileURLToPath } from "url";
619
+ var __dirname = dirname(fileURLToPath(import.meta.url));
620
+ var BUNDLED_SKILLS_DIR = join(__dirname, "../../mcp/tools");
621
+ var USER_SKILLS_DIR = join(homedir(), ".discoverylab", "skills");
622
+ var WORKSPACE_SKILLS_DIR = join(process.cwd(), ".discoverylab", "skills");
623
+ async function pathExists(path) {
624
+ try {
625
+ await access(path);
626
+ return true;
627
+ } catch {
628
+ return false;
629
+ }
630
+ }
631
+ async function findSkillFiles(baseDir) {
632
+ const skillFiles = [];
633
+ if (!await pathExists(baseDir)) {
634
+ return skillFiles;
635
+ }
636
+ try {
637
+ const entries = await readdir(baseDir, { withFileTypes: true });
638
+ for (const entry of entries) {
639
+ if (entry.isDirectory()) {
640
+ const skillPath = join(baseDir, entry.name, "SKILL.md");
641
+ if (await pathExists(skillPath)) {
642
+ skillFiles.push(skillPath);
643
+ }
644
+ }
645
+ }
646
+ } catch {
647
+ }
648
+ return skillFiles;
649
+ }
650
+ async function discoverSkillFiles() {
651
+ const [bundled, user, workspace] = await Promise.all([
652
+ findSkillFiles(BUNDLED_SKILLS_DIR),
653
+ findSkillFiles(USER_SKILLS_DIR),
654
+ findSkillFiles(WORKSPACE_SKILLS_DIR)
655
+ ]);
656
+ return { bundled, user, workspace };
657
+ }
658
+ async function loadSkill(skillPath) {
659
+ const { metadata, content } = await readSkillMd(skillPath);
660
+ const gatingResult = checkSkillRequirements(metadata);
661
+ const skill = {
662
+ metadata,
663
+ content,
664
+ path: skillPath,
665
+ available: gatingResult.satisfied
666
+ };
667
+ if (!gatingResult.satisfied) {
668
+ skill.unavailableReasons = [];
669
+ if (gatingResult.missingBins.length > 0) {
670
+ skill.unavailableReasons.push(`Missing binaries: ${gatingResult.missingBins.join(", ")}`);
671
+ }
672
+ if (gatingResult.missingEnv.length > 0) {
673
+ skill.unavailableReasons.push(`Missing env vars: ${gatingResult.missingEnv.join(", ")}`);
674
+ }
675
+ if (gatingResult.osUnsupported) {
676
+ skill.unavailableReasons.push(gatingResult.summary);
677
+ }
678
+ const instructions = getInstallInstructions(metadata, gatingResult);
679
+ if (instructions.length > 0) {
680
+ skill.unavailableReasons.push(`Install: ${instructions.join(" && ")}`);
681
+ }
682
+ }
683
+ return skill;
684
+ }
685
+ async function loadSkills() {
686
+ const result = {
687
+ loaded: [],
688
+ failed: [],
689
+ unavailable: []
690
+ };
691
+ const { bundled, user, workspace } = await discoverSkillFiles();
692
+ const allPaths = [...bundled, ...user, ...workspace];
693
+ const skillsByName = /* @__PURE__ */ new Map();
694
+ for (const path of allPaths) {
695
+ try {
696
+ const skill = await loadSkill(path);
697
+ skillsByName.set(skill.metadata.name, skill);
698
+ } catch (error) {
699
+ const msg = error instanceof Error ? error.message : "Unknown error";
700
+ result.failed.push({ path, error: msg });
701
+ }
702
+ }
703
+ for (const skill of skillsByName.values()) {
704
+ if (skill.available) {
705
+ result.loaded.push(skill);
706
+ } else {
707
+ result.unavailable.push(skill);
708
+ }
709
+ }
710
+ return result;
711
+ }
712
+ function createSkillRegistry() {
713
+ const skills = /* @__PURE__ */ new Map();
714
+ return {
715
+ skills,
716
+ getAvailable() {
717
+ return Array.from(skills.values()).filter((s) => s.available);
718
+ },
719
+ get(name) {
720
+ return skills.get(name);
721
+ },
722
+ isAvailable(name) {
723
+ const skill = skills.get(name);
724
+ return skill?.available ?? false;
725
+ },
726
+ async reload() {
727
+ skills.clear();
728
+ const result = await loadSkills();
729
+ for (const skill of [...result.loaded, ...result.unavailable]) {
730
+ skills.set(skill.metadata.name, skill);
731
+ }
732
+ return result;
733
+ }
734
+ };
735
+ }
736
+ var globalRegistry = null;
737
+ async function getSkillRegistry() {
738
+ if (!globalRegistry) {
739
+ globalRegistry = createSkillRegistry();
740
+ await globalRegistry.reload();
741
+ }
742
+ return globalRegistry;
743
+ }
744
+ async function reloadSkillRegistry() {
745
+ if (!globalRegistry) {
746
+ globalRegistry = createSkillRegistry();
747
+ }
748
+ return globalRegistry.reload();
749
+ }
750
+ function formatSkillInfo(skill) {
751
+ const lines = [];
752
+ const statusIcon = skill.available ? "\u2713" : "\u2717";
753
+ const emoji = skill.metadata.emoji || "\u{1F4E6}";
754
+ lines.push(`${emoji} ${skill.metadata.name} [${statusIcon}]`);
755
+ lines.push(` ${skill.metadata.description}`);
756
+ if (skill.metadata.version) {
757
+ lines.push(` Version: ${skill.metadata.version}`);
758
+ }
759
+ if (skill.metadata.requires) {
760
+ if (skill.metadata.requires.bins?.length) {
761
+ lines.push(` Requires: ${skill.metadata.requires.bins.join(", ")}`);
762
+ }
763
+ if (skill.metadata.requires.env?.length) {
764
+ lines.push(` Env vars: ${skill.metadata.requires.env.join(", ")}`);
765
+ }
766
+ }
767
+ if (!skill.available && skill.unavailableReasons?.length) {
768
+ lines.push(` \u26A0 ${skill.unavailableReasons.join("\n \u26A0 ")}`);
769
+ }
770
+ return lines.join("\n");
771
+ }
772
+ function formatSkillList(skills) {
773
+ if (skills.length === 0) {
774
+ return "No skills found";
775
+ }
776
+ const available = skills.filter((s) => s.available);
777
+ const unavailable = skills.filter((s) => !s.available);
778
+ const lines = [];
779
+ if (available.length > 0) {
780
+ lines.push("Available Skills:");
781
+ for (const skill of available) {
782
+ const emoji = skill.metadata.emoji || "\u{1F4E6}";
783
+ lines.push(` ${emoji} ${skill.metadata.name} - ${skill.metadata.description}`);
784
+ }
785
+ }
786
+ if (unavailable.length > 0) {
787
+ if (lines.length > 0) lines.push("");
788
+ lines.push("Unavailable Skills (missing dependencies):");
789
+ for (const skill of unavailable) {
790
+ const emoji = skill.metadata.emoji || "\u{1F4E6}";
791
+ lines.push(` ${emoji} ${skill.metadata.name} - ${skill.metadata.description}`);
792
+ if (skill.unavailableReasons?.length) {
793
+ lines.push(` \u26A0 ${skill.unavailableReasons[0]}`);
794
+ }
795
+ }
796
+ }
797
+ return lines.join("\n");
798
+ }
799
+
800
+ // src/index.ts
801
+ async function main() {
802
+ try {
803
+ getDatabase();
804
+ mcpServer.registerTools([
805
+ ...uiTools,
806
+ ...projectTools,
807
+ ...setupTools,
808
+ ...captureTools,
809
+ ...analyzeTools,
810
+ ...canvasTools,
811
+ ...exportTools,
812
+ ...testingTools,
813
+ ...integrationTools,
814
+ ...taskHubTools
815
+ ]);
816
+ await mcpServer.runStdio();
817
+ } catch (error) {
818
+ console.error("Failed to start MCP server:", error);
819
+ process.exit(1);
820
+ }
821
+ }
822
+ main();
823
+ export {
824
+ availableEvents,
825
+ availableMethods,
826
+ checkBinaries,
827
+ checkBinary,
828
+ checkEnvVar,
829
+ checkEnvVars,
830
+ checkOS,
831
+ checkSkillRequirements,
832
+ closeDatabase,
833
+ createEvent,
834
+ createRequest,
835
+ createResponse,
836
+ createSkillRegistry,
837
+ exportDestinations,
838
+ exportRules,
839
+ formatSkillInfo,
840
+ formatSkillList,
841
+ formatValidationErrors,
842
+ frames,
843
+ generateSkillMd,
844
+ getCurrentOS,
845
+ getDatabase,
846
+ getInstallInstructions,
847
+ getSkillRegistry,
848
+ isWSEvent,
849
+ isWSMessage,
850
+ isWSRequest,
851
+ isWSResponse,
852
+ loadSkills,
853
+ mcpServer,
854
+ nextEventSeq,
855
+ parseSkillMd,
856
+ projectExports,
857
+ projects,
858
+ readSkillMd,
859
+ reloadSkillRegistry,
860
+ resetEventSeq,
861
+ schemas,
862
+ settings,
863
+ startServer,
864
+ stopServer,
865
+ validateEvent,
866
+ validateMethodParams,
867
+ validateRequest,
868
+ validateResponse
869
+ };