@veolab/discoverylab 1.4.4 → 1.6.5

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 (33) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +70 -211
  4. package/assets/applab-bundle-icon.png +0 -0
  5. package/assets/icons/icons8-claude-150.png +0 -0
  6. package/assets/icons/icons8-claude-500.png +0 -0
  7. package/dist/{chunk-CUBQRT5L.js → chunk-JAA53ES7.js} +111 -2
  8. package/dist/{chunk-HB3YPWF3.js → chunk-Q7Q3A2ZI.js} +301 -10
  9. package/dist/{chunk-XKX6NBHF.js → chunk-TWRWARU4.js} +52 -2
  10. package/dist/{chunk-2UUMLAVR.js → chunk-V6RREMYD.js} +332 -38
  11. package/dist/cli.js +164 -28
  12. package/dist/export/infographic-template.html +254 -0
  13. package/dist/import-W2JEW254.js +180 -0
  14. package/dist/index.d.ts +30 -6
  15. package/dist/index.html +473 -11
  16. package/dist/index.js +5 -5
  17. package/dist/infographic-GQAHEOAA.js +183 -0
  18. package/dist/mcpb/node_modules/@anthropic-ai/sdk/src/lib/.keep +4 -0
  19. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/better_sqlite3.node.d +1 -0
  20. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/better_sqlite3/src/better_sqlite3.o.d +133 -0
  21. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/deps/locate_sqlite3.stamp.d +1 -0
  22. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/sqlite3/gen/sqlite3/sqlite3.o.d +4 -0
  23. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/test_extension/deps/test_extension.o.d +7 -0
  24. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/sqlite3.a.d +1 -0
  25. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/test_extension.node.d +1 -0
  26. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/ba23eeee118cd63e16015df367567cb043fed872.intermediate.d +1 -0
  27. package/dist/{server-QFNKZCOJ.js → server-C2NZM2RV.js} +1 -1
  28. package/dist/{server-OVOACIOJ.js → server-WN6DCCUA.js} +1 -1
  29. package/dist/{setup-6JJYKKBS.js → setup-SMN7FJNZ.js} +5 -2
  30. package/dist/{tools-Q7OZO732.js → tools-VXU3JEQP.js} +6 -4
  31. package/doc/esvp-protocol.md +116 -0
  32. package/package.json +9 -3
  33. package/skills/knowledge-brain/SKILL.md +44 -43
@@ -1,3 +1,9 @@
1
+ import {
2
+ checkNotionAuth,
3
+ createNotionPage,
4
+ loginToNotion,
5
+ quickExportToNotion
6
+ } from "./chunk-34GGYFXX.js";
1
7
  import {
2
8
  detectKeyFrames,
3
9
  extractFrames,
@@ -8,14 +14,9 @@ import {
8
14
  import {
9
15
  createErrorResult,
10
16
  createJsonResult,
11
- createTextResult
12
- } from "./chunk-XKX6NBHF.js";
13
- import {
14
- checkNotionAuth,
15
- createNotionPage,
16
- loginToNotion,
17
- quickExportToNotion
18
- } from "./chunk-34GGYFXX.js";
17
+ createTextResult,
18
+ mcpServer
19
+ } from "./chunk-TWRWARU4.js";
19
20
  import {
20
21
  getAvailableTemplates,
21
22
  isTemplatesInstalled
@@ -3776,6 +3777,93 @@ var exportSequenceTool = {
3776
3777
  }, null, 2));
3777
3778
  }
3778
3779
  };
3780
+ var exportInfographicTool = {
3781
+ name: "dlab.export.infographic",
3782
+ description: "Export a DiscoveryLab project as an interactive HTML infographic. Generates a self-contained offline HTML file with animated frame player, hotspots, annotations, and baseline status.",
3783
+ inputSchema: z6.object({
3784
+ projectId: z6.string().describe("Project ID"),
3785
+ open: z6.boolean().optional().describe("Open in browser after export"),
3786
+ outputPath: z6.string().optional().describe("Custom output directory")
3787
+ }),
3788
+ handler: async (params) => {
3789
+ try {
3790
+ const { projects: projectsTable, frames: framesTable, FRAMES_DIR, PROJECTS_DIR: PROJECTS_DIR3 } = await import("./db-5ECN3O7F.js");
3791
+ const { eq: eq4 } = await import("drizzle-orm");
3792
+ const { collectFrameImages, buildInfographicData, generateInfographicHtml } = await import("./infographic-GQAHEOAA.js");
3793
+ const db = getDatabase();
3794
+ const [project] = await db.select().from(projectsTable).where(eq4(projectsTable.id, params.projectId)).limit(1);
3795
+ if (!project) return createErrorResult(`Project not found: ${params.projectId}`);
3796
+ const dbFrames = await db.select().from(framesTable).where(eq4(framesTable.projectId, project.id)).orderBy(framesTable.frameNumber).limit(20);
3797
+ let frameFiles;
3798
+ let frameOcr;
3799
+ if (dbFrames.length > 0) {
3800
+ frameFiles = dbFrames.map((f) => f.imagePath);
3801
+ frameOcr = dbFrames;
3802
+ } else {
3803
+ frameFiles = collectFrameImages(
3804
+ path5.join(FRAMES_DIR, project.id),
3805
+ project.videoPath,
3806
+ PROJECTS_DIR3,
3807
+ project.id
3808
+ );
3809
+ frameOcr = frameFiles.map(() => ({ ocrText: null }));
3810
+ }
3811
+ if (frameFiles.length === 0) {
3812
+ return createErrorResult("No frames found. Run analyzer first.");
3813
+ }
3814
+ const data = buildInfographicData(project, frameFiles, frameOcr);
3815
+ const slug = (project.marketingTitle || project.name || project.id).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
3816
+ const outputFilePath = params.outputPath ? path5.join(params.outputPath, `${slug}-infographic.html`) : path5.join(EXPORTS_DIR, `${slug}-infographic.html`);
3817
+ const result = generateInfographicHtml(data, outputFilePath);
3818
+ if (!result.success) {
3819
+ return createErrorResult(`Export failed: ${result.error}`);
3820
+ }
3821
+ if (params.open) {
3822
+ const { exec: exec5 } = await import("child_process");
3823
+ exec5(`open "${result.outputPath}"`);
3824
+ }
3825
+ const sizeKb = ((result.size || 0) / 1024).toFixed(1);
3826
+ return createTextResult(`Infographic exported!
3827
+
3828
+ Path: ${result.outputPath}
3829
+ Size: ${sizeKb}KB
3830
+ Frames: ${result.frameCount}
3831
+
3832
+ Open in any browser to view.`);
3833
+ } catch (error) {
3834
+ return createErrorResult(`Export failed: ${error instanceof Error ? error.message : String(error)}`);
3835
+ }
3836
+ }
3837
+ };
3838
+ var importProjectTool = {
3839
+ name: "dlab.project.import",
3840
+ description: "Import a shared .applab project bundle file. Use this when someone shares a .applab file with you.",
3841
+ inputSchema: z6.object({
3842
+ filePath: z6.string().describe("Path to the .applab file")
3843
+ }),
3844
+ handler: async (params) => {
3845
+ try {
3846
+ const { projects: projectsTable, frames: framesTable, DATA_DIR: DATA_DIR2, FRAMES_DIR, PROJECTS_DIR: PROJECTS_DIR3 } = await import("./db-5ECN3O7F.js");
3847
+ const { importApplabBundle } = await import("./import-W2JEW254.js");
3848
+ const db = getDatabase();
3849
+ const result = await importApplabBundle(params.filePath, db, { projects: projectsTable, frames: framesTable }, {
3850
+ dataDir: DATA_DIR2,
3851
+ framesDir: FRAMES_DIR,
3852
+ projectsDir: PROJECTS_DIR3
3853
+ });
3854
+ if (!result.success) return createErrorResult(`Import failed: ${result.error}`);
3855
+ return createTextResult(`Project imported!
3856
+
3857
+ Name: ${result.projectName}
3858
+ ID: ${result.projectId}
3859
+ Frames: ${result.frameCount}
3860
+
3861
+ Use dlab.knowledge.search to query this project.`);
3862
+ } catch (error) {
3863
+ return createErrorResult(`Import failed: ${error instanceof Error ? error.message : String(error)}`);
3864
+ }
3865
+ }
3866
+ };
3779
3867
  var exportTools = [
3780
3868
  exportVideoTool,
3781
3869
  exportGifTool,
@@ -3788,7 +3876,9 @@ var exportTools = [
3788
3876
  exportInfoTool,
3789
3877
  exportClipboardTool,
3790
3878
  exportRevealTool,
3791
- exportSequenceTool
3879
+ exportSequenceTool,
3880
+ exportInfographicTool,
3881
+ importProjectTool
3792
3882
  ];
3793
3883
 
3794
3884
  // src/mcp/tools/testing.ts
@@ -6830,6 +6920,102 @@ var templateTools = [
6830
6920
  // src/mcp/tools/knowledge.ts
6831
6921
  import { z as z12 } from "zod";
6832
6922
  import { desc as desc2 } from "drizzle-orm";
6923
+ var KNOWLEDGE_VIEWER_RESOURCE_URI = "ui://discoverylab/knowledge-viewer.html";
6924
+ var KNOWLEDGE_VIEWER_MIME_TYPE = "text/html;profile=mcp-app";
6925
+ function buildViewerPlaceholderHtml(message) {
6926
+ const safeMessage = JSON.stringify(message);
6927
+ return `<!DOCTYPE html>
6928
+ <html lang="en">
6929
+ <head>
6930
+ <meta charset="UTF-8" />
6931
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6932
+ <title>DiscoveryLab Flow Viewer</title>
6933
+ <style>
6934
+ :root {
6935
+ color-scheme: dark;
6936
+ --bg: #0b1117;
6937
+ --panel: rgba(255, 255, 255, 0.04);
6938
+ --border: rgba(255, 255, 255, 0.1);
6939
+ --text: #eff5fb;
6940
+ --muted: #9fb0c2;
6941
+ --accent: #2dd4bf;
6942
+ }
6943
+
6944
+ * { box-sizing: border-box; }
6945
+
6946
+ body {
6947
+ margin: 0;
6948
+ min-height: 100vh;
6949
+ display: grid;
6950
+ place-items: center;
6951
+ padding: 24px;
6952
+ background:
6953
+ radial-gradient(circle at top left, rgba(45, 212, 191, 0.18), transparent 28%),
6954
+ linear-gradient(180deg, #101826, var(--bg));
6955
+ color: var(--text);
6956
+ font: 14px/1.5 "IBM Plex Sans", "Segoe UI", system-ui, sans-serif;
6957
+ }
6958
+
6959
+ .card {
6960
+ width: min(720px, 100%);
6961
+ border: 1px solid var(--border);
6962
+ border-radius: 24px;
6963
+ padding: 24px;
6964
+ background: var(--panel);
6965
+ box-shadow: 0 24px 60px rgba(0, 0, 0, 0.28);
6966
+ }
6967
+
6968
+ .eyebrow {
6969
+ display: inline-flex;
6970
+ align-items: center;
6971
+ gap: 8px;
6972
+ border: 1px solid var(--border);
6973
+ border-radius: 999px;
6974
+ padding: 6px 12px;
6975
+ color: var(--muted);
6976
+ font-size: 12px;
6977
+ text-transform: uppercase;
6978
+ letter-spacing: 0.03em;
6979
+ }
6980
+
6981
+ h1 {
6982
+ margin: 16px 0 8px;
6983
+ font: 700 32px/1.02 "Space Grotesk", "Avenir Next", system-ui, sans-serif;
6984
+ letter-spacing: -0.03em;
6985
+ }
6986
+
6987
+ p {
6988
+ margin: 0;
6989
+ color: var(--muted);
6990
+ }
6991
+ </style>
6992
+ </head>
6993
+ <body>
6994
+ <main class="card">
6995
+ <div class="eyebrow">DiscoveryLab Local Viewer</div>
6996
+ <h1>Open a flow in Claude Desktop</h1>
6997
+ <p id="message"></p>
6998
+ </main>
6999
+ <script>
7000
+ document.getElementById('message').textContent = ${safeMessage};
7001
+ </script>
7002
+ </body>
7003
+ </html>`;
7004
+ }
7005
+ mcpServer.registerResource({
7006
+ uri: KNOWLEDGE_VIEWER_RESOURCE_URI,
7007
+ name: "discoverylab-knowledge-viewer",
7008
+ title: "DiscoveryLab Flow Viewer",
7009
+ description: "Interactive local flow viewer for DiscoveryLab projects.",
7010
+ mimeType: KNOWLEDGE_VIEWER_MIME_TYPE,
7011
+ contents: [
7012
+ {
7013
+ uri: KNOWLEDGE_VIEWER_RESOURCE_URI,
7014
+ mimeType: KNOWLEDGE_VIEWER_MIME_TYPE,
7015
+ text: buildViewerPlaceholderHtml("Use dlab.knowledge.open to load a local project into the canvas.")
7016
+ }
7017
+ ]
7018
+ });
6833
7019
  var knowledgeSearchTool = {
6834
7020
  name: "dlab.knowledge.search",
6835
7021
  description: `Search across all DiscoveryLab projects for app flows, UI elements, screens, and behaviors. Use this when the user asks about how an app works, what a specific screen looks like, or any question about captured app flows. Returns relevant projects with their analysis, OCR text, and context.`,
@@ -6997,9 +7183,114 @@ Use \`dlab.knowledge.search\` with a query to find specific flows or screens.`;
6997
7183
  }
6998
7184
  }
6999
7185
  };
7186
+ var knowledgeOpenTool = {
7187
+ name: "dlab.knowledge.open",
7188
+ description: `Open an interactive visual infographic of an app flow. Returns self-contained HTML that Claude Desktop renders as a canvas/artifact. Use this when the user wants to SEE a flow visually, not just read about it. The HTML includes animated frame player, annotations, and navigation.`,
7189
+ inputSchema: z12.object({
7190
+ query: z12.string().optional().describe('Search query to find the project (e.g. "login flow", "onboarding")'),
7191
+ projectId: z12.string().optional().describe("Direct project ID if known")
7192
+ }),
7193
+ _meta: {
7194
+ ui: {
7195
+ resourceUri: KNOWLEDGE_VIEWER_RESOURCE_URI
7196
+ }
7197
+ },
7198
+ handler: async (params) => {
7199
+ try {
7200
+ const db = getDatabase();
7201
+ let project = null;
7202
+ if (params.projectId) {
7203
+ const { eq: eq5 } = await import("drizzle-orm");
7204
+ const [p] = await db.select().from(projects).where(eq5(projects.id, params.projectId)).limit(1);
7205
+ project = p;
7206
+ } else if (params.query) {
7207
+ const allProjects = await db.select().from(projects).orderBy(desc2(projects.updatedAt));
7208
+ const query = params.query.toLowerCase();
7209
+ const queryTerms = query.split(/\s+/).filter((t) => t.length > 1);
7210
+ let bestScore = 0;
7211
+ for (const p of allProjects) {
7212
+ let score = 0;
7213
+ const fields = [p.name, p.marketingTitle, p.aiSummary, p.ocrText, p.tags, p.linkedTicket].filter(Boolean);
7214
+ for (const field of fields) {
7215
+ const text = field.toLowerCase();
7216
+ for (const term of queryTerms) {
7217
+ if (text.includes(term)) score += 1;
7218
+ }
7219
+ if (text.includes(query)) score += 3;
7220
+ }
7221
+ if (score > bestScore) {
7222
+ bestScore = score;
7223
+ project = p;
7224
+ }
7225
+ }
7226
+ }
7227
+ if (!project) {
7228
+ return createErrorResult(`No project found${params.query ? ` for "${params.query}"` : ""}. Use dlab.knowledge.summary to see available projects.`);
7229
+ }
7230
+ const { eq: eq4 } = await import("drizzle-orm");
7231
+ const { FRAMES_DIR, PROJECTS_DIR: PROJECTS_DIR3 } = await import("./db-5ECN3O7F.js");
7232
+ const { join: join12 } = await import("path");
7233
+ const dbFrames = await db.select().from(frames).where(eq4(frames.projectId, project.id)).orderBy(frames.frameNumber).limit(15);
7234
+ let frameFiles;
7235
+ let frameOcr;
7236
+ if (dbFrames.length > 0) {
7237
+ frameFiles = dbFrames.map((f) => f.imagePath);
7238
+ frameOcr = dbFrames;
7239
+ } else {
7240
+ const { collectFrameImages } = await import("./infographic-GQAHEOAA.js");
7241
+ frameFiles = collectFrameImages(join12(FRAMES_DIR, project.id), project.videoPath, PROJECTS_DIR3, project.id);
7242
+ frameOcr = frameFiles.map(() => ({ ocrText: null }));
7243
+ }
7244
+ if (frameFiles.length === 0) {
7245
+ return createTextResult(`Project "${project.marketingTitle || project.name}" has no frames. Run the analyzer first, then try again.`);
7246
+ }
7247
+ const { buildInfographicData, generateInfographicHtmlString } = await import("./infographic-GQAHEOAA.js");
7248
+ const data = buildInfographicData(project, frameFiles, frameOcr);
7249
+ const html = generateInfographicHtmlString(data);
7250
+ if (!html) {
7251
+ return createErrorResult("Failed to generate infographic HTML (template not found)");
7252
+ }
7253
+ mcpServer.upsertResourceContents(KNOWLEDGE_VIEWER_RESOURCE_URI, {
7254
+ name: "discoverylab-knowledge-viewer",
7255
+ title: `DiscoveryLab Flow Viewer \xB7 ${project.marketingTitle || project.name}`,
7256
+ description: "Interactive local flow viewer for DiscoveryLab projects.",
7257
+ mimeType: KNOWLEDGE_VIEWER_MIME_TYPE,
7258
+ contents: [
7259
+ {
7260
+ uri: KNOWLEDGE_VIEWER_RESOURCE_URI,
7261
+ mimeType: KNOWLEDGE_VIEWER_MIME_TYPE,
7262
+ text: html
7263
+ }
7264
+ ]
7265
+ });
7266
+ return {
7267
+ content: [
7268
+ {
7269
+ type: "text",
7270
+ text: `Opened the local DiscoveryLab flow viewer for ${project.marketingTitle || project.name}.`
7271
+ }
7272
+ ],
7273
+ structuredContent: {
7274
+ projectId: project.id,
7275
+ name: project.marketingTitle || project.name,
7276
+ frameCount: frameFiles.length,
7277
+ platform: project.platform || "unknown"
7278
+ },
7279
+ _meta: {
7280
+ ui: {
7281
+ resourceUri: KNOWLEDGE_VIEWER_RESOURCE_URI
7282
+ }
7283
+ }
7284
+ };
7285
+ } catch (error) {
7286
+ return createErrorResult(`Failed to open flow: ${error instanceof Error ? error.message : String(error)}`);
7287
+ }
7288
+ }
7289
+ };
7000
7290
  var knowledgeTools = [
7001
7291
  knowledgeSearchTool,
7002
- knowledgeSummaryTool
7292
+ knowledgeSummaryTool,
7293
+ knowledgeOpenTool
7003
7294
  ];
7004
7295
 
7005
7296
  export {
@@ -6,6 +6,7 @@ import {
6
6
  import { z } from "zod";
7
7
  var MCPServer = class {
8
8
  tools = /* @__PURE__ */ new Map();
9
+ resources = /* @__PURE__ */ new Map();
9
10
  serverInfo = {
10
11
  name: "discoverylab",
11
12
  version: APP_VERSION
@@ -18,6 +19,15 @@ var MCPServer = class {
18
19
  this.registerTool(tool);
19
20
  }
20
21
  }
22
+ registerResource(resource) {
23
+ this.resources.set(resource.uri, resource);
24
+ }
25
+ upsertResourceContents(uri, resource) {
26
+ this.resources.set(uri, {
27
+ uri,
28
+ ...resource
29
+ });
30
+ }
21
31
  async handleRequest(request) {
22
32
  const { id, method, params } = request;
23
33
  try {
@@ -28,6 +38,10 @@ var MCPServer = class {
28
38
  return this.handleToolsList(id);
29
39
  case "tools/call":
30
40
  return this.handleToolCall(id, params);
41
+ case "resources/list":
42
+ return this.handleResourcesList(id);
43
+ case "resources/read":
44
+ return this.handleResourcesRead(id, params);
31
45
  case "ping":
32
46
  return { jsonrpc: "2.0", id, result: { pong: true } };
33
47
  default:
@@ -54,7 +68,8 @@ var MCPServer = class {
54
68
  protocolVersion: "2024-11-05",
55
69
  serverInfo: this.serverInfo,
56
70
  capabilities: {
57
- tools: {}
71
+ tools: {},
72
+ resources: {}
58
73
  }
59
74
  }
60
75
  };
@@ -63,7 +78,8 @@ var MCPServer = class {
63
78
  const tools = Array.from(this.tools.values()).map((tool) => ({
64
79
  name: tool.name,
65
80
  description: tool.description,
66
- inputSchema: this.zodToJsonSchema(tool.inputSchema)
81
+ inputSchema: this.zodToJsonSchema(tool.inputSchema),
82
+ ...tool._meta ? { _meta: tool._meta } : {}
67
83
  }));
68
84
  return {
69
85
  jsonrpc: "2.0",
@@ -71,6 +87,40 @@ var MCPServer = class {
71
87
  result: { tools }
72
88
  };
73
89
  }
90
+ handleResourcesList(id) {
91
+ const resources = Array.from(this.resources.values()).map((resource) => ({
92
+ uri: resource.uri,
93
+ name: resource.name,
94
+ ...resource.title ? { title: resource.title } : {},
95
+ ...resource.description ? { description: resource.description } : {},
96
+ mimeType: resource.mimeType
97
+ }));
98
+ return {
99
+ jsonrpc: "2.0",
100
+ id,
101
+ result: { resources }
102
+ };
103
+ }
104
+ handleResourcesRead(id, params) {
105
+ const uri = typeof params?.uri === "string" ? params.uri : "";
106
+ const resource = this.resources.get(uri);
107
+ if (!resource) {
108
+ return {
109
+ jsonrpc: "2.0",
110
+ id,
111
+ error: { code: -32602, message: `Resource not found: ${uri}` }
112
+ };
113
+ }
114
+ const contents = Array.isArray(resource.contents) && resource.contents.length > 0 ? resource.contents : [{
115
+ uri: resource.uri,
116
+ mimeType: resource.mimeType
117
+ }];
118
+ return {
119
+ jsonrpc: "2.0",
120
+ id,
121
+ result: { contents }
122
+ };
123
+ }
74
124
  async handleToolCall(id, params) {
75
125
  const { name, arguments: args } = params;
76
126
  const tool = this.tools.get(name);