@veolab/discoverylab 1.6.3 → 1.6.6

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 (29) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/assets/applab-bundle-icon.png +0 -0
  4. package/assets/icons/icons8-claude-150.png +0 -0
  5. package/assets/icons/icons8-claude-500.png +0 -0
  6. package/dist/{chunk-HFN6BTVO.js → chunk-JAA53ES7.js} +1 -1
  7. package/dist/{chunk-XKX6NBHF.js → chunk-TWRWARU4.js} +52 -2
  8. package/dist/{chunk-IVX2OSOJ.js → chunk-XDUFCPOC.js} +198 -69
  9. package/dist/{chunk-5AISGCS4.js → chunk-YFF3M76J.js} +162 -33
  10. package/dist/cli.js +29 -18
  11. package/dist/export/infographic-template.html +392 -128
  12. package/dist/index.d.ts +30 -6
  13. package/dist/index.html +75 -21
  14. package/dist/index.js +4 -4
  15. package/dist/{infographic-GQAHEOAA.js → infographic-OSDIJM5M.js} +119 -17
  16. package/dist/mcpb/node_modules/@anthropic-ai/sdk/src/lib/.keep +4 -0
  17. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/better_sqlite3.node.d +1 -0
  18. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/better_sqlite3/src/better_sqlite3.o.d +133 -0
  19. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/deps/locate_sqlite3.stamp.d +1 -0
  20. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/sqlite3/gen/sqlite3/sqlite3.o.d +4 -0
  21. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/obj.target/test_extension/deps/test_extension.o.d +7 -0
  22. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/sqlite3.a.d +1 -0
  23. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/Release/test_extension.node.d +1 -0
  24. package/dist/mcpb/node_modules/better-sqlite3/build/Release/.deps/ba23eeee118cd63e16015df367567cb043fed872.intermediate.d +1 -0
  25. package/dist/{server-W3JQ5RG7.js → server-GXNAKM4H.js} +1 -1
  26. package/dist/{server-OVOACIOJ.js → server-WN6DCCUA.js} +1 -1
  27. package/dist/{setup-F7MGEFIM.js → setup-SMN7FJNZ.js} +2 -2
  28. package/dist/{tools-VYFNRUS4.js → tools-6BTUMR3G.js} +3 -3
  29. package/package.json +8 -2
@@ -14,8 +14,9 @@ import {
14
14
  import {
15
15
  createErrorResult,
16
16
  createJsonResult,
17
- createTextResult
18
- } from "./chunk-XKX6NBHF.js";
17
+ createTextResult,
18
+ mcpServer
19
+ } from "./chunk-TWRWARU4.js";
19
20
  import {
20
21
  getAvailableTemplates,
21
22
  isTemplatesInstalled
@@ -3788,29 +3789,25 @@ var exportInfographicTool = {
3788
3789
  try {
3789
3790
  const { projects: projectsTable, frames: framesTable, FRAMES_DIR, PROJECTS_DIR: PROJECTS_DIR3 } = await import("./db-5ECN3O7F.js");
3790
3791
  const { eq: eq4 } = await import("drizzle-orm");
3791
- const { collectFrameImages, buildInfographicData, generateInfographicHtml } = await import("./infographic-GQAHEOAA.js");
3792
+ const { buildInfographicData, generateInfographicHtml, resolveInfographicFrameInputs } = await import("./infographic-OSDIJM5M.js");
3792
3793
  const db = getDatabase();
3793
3794
  const [project] = await db.select().from(projectsTable).where(eq4(projectsTable.id, params.projectId)).limit(1);
3794
3795
  if (!project) return createErrorResult(`Project not found: ${params.projectId}`);
3795
3796
  const dbFrames = await db.select().from(framesTable).where(eq4(framesTable.projectId, project.id)).orderBy(framesTable.frameNumber).limit(20);
3796
- let frameFiles;
3797
- let frameOcr;
3798
- if (dbFrames.length > 0) {
3799
- frameFiles = dbFrames.map((f) => f.imagePath);
3800
- frameOcr = dbFrames;
3801
- } else {
3802
- frameFiles = collectFrameImages(
3803
- path5.join(FRAMES_DIR, project.id),
3804
- project.videoPath,
3805
- PROJECTS_DIR3,
3806
- project.id
3807
- );
3808
- frameOcr = frameFiles.map(() => ({ ocrText: null }));
3797
+ const resolvedFrames = resolveInfographicFrameInputs(
3798
+ dbFrames,
3799
+ path5.join(FRAMES_DIR, project.id),
3800
+ project.videoPath,
3801
+ PROJECTS_DIR3,
3802
+ project.id
3803
+ );
3804
+ if (resolvedFrames.frameFiles.length === 0) {
3805
+ return createErrorResult(`No readable frames found for infographic export. Checked ${resolvedFrames.candidateCount} candidate(s) from ${resolvedFrames.source}.`);
3809
3806
  }
3810
- if (frameFiles.length === 0) {
3811
- return createErrorResult("No frames found. Run analyzer first.");
3807
+ const data = buildInfographicData(project, resolvedFrames.frameFiles, resolvedFrames.frameOcr);
3808
+ if (data.frames.length === 0) {
3809
+ return createErrorResult("Infographic export produced no embeddable frames.");
3812
3810
  }
3813
- const data = buildInfographicData(project, frameFiles, frameOcr);
3814
3811
  const slug = (project.marketingTitle || project.name || project.id).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
3815
3812
  const outputFilePath = params.outputPath ? path5.join(params.outputPath, `${slug}-infographic.html`) : path5.join(EXPORTS_DIR, `${slug}-infographic.html`);
3816
3813
  const result = generateInfographicHtml(data, outputFilePath);
@@ -6919,6 +6916,102 @@ var templateTools = [
6919
6916
  // src/mcp/tools/knowledge.ts
6920
6917
  import { z as z12 } from "zod";
6921
6918
  import { desc as desc2 } from "drizzle-orm";
6919
+ var KNOWLEDGE_VIEWER_RESOURCE_URI = "ui://discoverylab/knowledge-viewer.html";
6920
+ var KNOWLEDGE_VIEWER_MIME_TYPE = "text/html;profile=mcp-app";
6921
+ function buildViewerPlaceholderHtml(message) {
6922
+ const safeMessage = JSON.stringify(message);
6923
+ return `<!DOCTYPE html>
6924
+ <html lang="en">
6925
+ <head>
6926
+ <meta charset="UTF-8" />
6927
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6928
+ <title>DiscoveryLab Flow Viewer</title>
6929
+ <style>
6930
+ :root {
6931
+ color-scheme: dark;
6932
+ --bg: #0b1117;
6933
+ --panel: rgba(255, 255, 255, 0.04);
6934
+ --border: rgba(255, 255, 255, 0.1);
6935
+ --text: #eff5fb;
6936
+ --muted: #9fb0c2;
6937
+ --accent: #2dd4bf;
6938
+ }
6939
+
6940
+ * { box-sizing: border-box; }
6941
+
6942
+ body {
6943
+ margin: 0;
6944
+ min-height: 100vh;
6945
+ display: grid;
6946
+ place-items: center;
6947
+ padding: 24px;
6948
+ background:
6949
+ radial-gradient(circle at top left, rgba(45, 212, 191, 0.18), transparent 28%),
6950
+ linear-gradient(180deg, #101826, var(--bg));
6951
+ color: var(--text);
6952
+ font: 14px/1.5 "IBM Plex Sans", "Segoe UI", system-ui, sans-serif;
6953
+ }
6954
+
6955
+ .card {
6956
+ width: min(720px, 100%);
6957
+ border: 1px solid var(--border);
6958
+ border-radius: 24px;
6959
+ padding: 24px;
6960
+ background: var(--panel);
6961
+ box-shadow: 0 24px 60px rgba(0, 0, 0, 0.28);
6962
+ }
6963
+
6964
+ .eyebrow {
6965
+ display: inline-flex;
6966
+ align-items: center;
6967
+ gap: 8px;
6968
+ border: 1px solid var(--border);
6969
+ border-radius: 999px;
6970
+ padding: 6px 12px;
6971
+ color: var(--muted);
6972
+ font-size: 12px;
6973
+ text-transform: uppercase;
6974
+ letter-spacing: 0.03em;
6975
+ }
6976
+
6977
+ h1 {
6978
+ margin: 16px 0 8px;
6979
+ font: 700 32px/1.02 "Space Grotesk", "Avenir Next", system-ui, sans-serif;
6980
+ letter-spacing: -0.03em;
6981
+ }
6982
+
6983
+ p {
6984
+ margin: 0;
6985
+ color: var(--muted);
6986
+ }
6987
+ </style>
6988
+ </head>
6989
+ <body>
6990
+ <main class="card">
6991
+ <div class="eyebrow">DiscoveryLab Local Viewer</div>
6992
+ <h1>Open a flow in Claude Desktop</h1>
6993
+ <p id="message"></p>
6994
+ </main>
6995
+ <script>
6996
+ document.getElementById('message').textContent = ${safeMessage};
6997
+ </script>
6998
+ </body>
6999
+ </html>`;
7000
+ }
7001
+ mcpServer.registerResource({
7002
+ uri: KNOWLEDGE_VIEWER_RESOURCE_URI,
7003
+ name: "discoverylab-knowledge-viewer",
7004
+ title: "DiscoveryLab Flow Viewer",
7005
+ description: "Interactive local flow viewer for DiscoveryLab projects.",
7006
+ mimeType: KNOWLEDGE_VIEWER_MIME_TYPE,
7007
+ contents: [
7008
+ {
7009
+ uri: KNOWLEDGE_VIEWER_RESOURCE_URI,
7010
+ mimeType: KNOWLEDGE_VIEWER_MIME_TYPE,
7011
+ text: buildViewerPlaceholderHtml("Use dlab.knowledge.open to load a local project into the canvas.")
7012
+ }
7013
+ ]
7014
+ });
6922
7015
  var knowledgeSearchTool = {
6923
7016
  name: "dlab.knowledge.search",
6924
7017
  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.`,
@@ -7093,6 +7186,11 @@ var knowledgeOpenTool = {
7093
7186
  query: z12.string().optional().describe('Search query to find the project (e.g. "login flow", "onboarding")'),
7094
7187
  projectId: z12.string().optional().describe("Direct project ID if known")
7095
7188
  }),
7189
+ _meta: {
7190
+ ui: {
7191
+ resourceUri: KNOWLEDGE_VIEWER_RESOURCE_URI
7192
+ }
7193
+ },
7096
7194
  handler: async (params) => {
7097
7195
  try {
7098
7196
  const db = getDatabase();
@@ -7129,26 +7227,57 @@ var knowledgeOpenTool = {
7129
7227
  const { FRAMES_DIR, PROJECTS_DIR: PROJECTS_DIR3 } = await import("./db-5ECN3O7F.js");
7130
7228
  const { join: join12 } = await import("path");
7131
7229
  const dbFrames = await db.select().from(frames).where(eq4(frames.projectId, project.id)).orderBy(frames.frameNumber).limit(15);
7132
- let frameFiles;
7133
- let frameOcr;
7134
- if (dbFrames.length > 0) {
7135
- frameFiles = dbFrames.map((f) => f.imagePath);
7136
- frameOcr = dbFrames;
7137
- } else {
7138
- const { collectFrameImages } = await import("./infographic-GQAHEOAA.js");
7139
- frameFiles = collectFrameImages(join12(FRAMES_DIR, project.id), project.videoPath, PROJECTS_DIR3, project.id);
7140
- frameOcr = frameFiles.map(() => ({ ocrText: null }));
7230
+ const { resolveInfographicFrameInputs, buildInfographicData, generateInfographicHtmlString } = await import("./infographic-OSDIJM5M.js");
7231
+ const resolvedFrames = resolveInfographicFrameInputs(
7232
+ dbFrames,
7233
+ join12(FRAMES_DIR, project.id),
7234
+ project.videoPath,
7235
+ PROJECTS_DIR3,
7236
+ project.id
7237
+ );
7238
+ if (resolvedFrames.frameFiles.length === 0) {
7239
+ return createTextResult(`Project "${project.marketingTitle || project.name}" has no readable frames. Run the analyzer again, then try again.`);
7141
7240
  }
7142
- if (frameFiles.length === 0) {
7143
- return createTextResult(`Project "${project.marketingTitle || project.name}" has no frames. Run the analyzer first, then try again.`);
7241
+ const data = buildInfographicData(project, resolvedFrames.frameFiles, resolvedFrames.frameOcr);
7242
+ if (data.frames.length === 0) {
7243
+ return createErrorResult(`Project "${project.marketingTitle || project.name}" did not produce embeddable infographic frames.`);
7144
7244
  }
7145
- const { buildInfographicData, generateInfographicHtmlString } = await import("./infographic-GQAHEOAA.js");
7146
- const data = buildInfographicData(project, frameFiles, frameOcr);
7147
7245
  const html = generateInfographicHtmlString(data);
7148
7246
  if (!html) {
7149
7247
  return createErrorResult("Failed to generate infographic HTML (template not found)");
7150
7248
  }
7151
- return createTextResult(html);
7249
+ mcpServer.upsertResourceContents(KNOWLEDGE_VIEWER_RESOURCE_URI, {
7250
+ name: "discoverylab-knowledge-viewer",
7251
+ title: `DiscoveryLab Flow Viewer \xB7 ${project.marketingTitle || project.name}`,
7252
+ description: "Interactive local flow viewer for DiscoveryLab projects.",
7253
+ mimeType: KNOWLEDGE_VIEWER_MIME_TYPE,
7254
+ contents: [
7255
+ {
7256
+ uri: KNOWLEDGE_VIEWER_RESOURCE_URI,
7257
+ mimeType: KNOWLEDGE_VIEWER_MIME_TYPE,
7258
+ text: html
7259
+ }
7260
+ ]
7261
+ });
7262
+ return {
7263
+ content: [
7264
+ {
7265
+ type: "text",
7266
+ text: `Opened the local DiscoveryLab flow viewer for ${project.marketingTitle || project.name}.`
7267
+ }
7268
+ ],
7269
+ structuredContent: {
7270
+ projectId: project.id,
7271
+ name: project.marketingTitle || project.name,
7272
+ frameCount: resolvedFrames.frameFiles.length,
7273
+ platform: project.platform || "unknown"
7274
+ },
7275
+ _meta: {
7276
+ ui: {
7277
+ resourceUri: KNOWLEDGE_VIEWER_RESOURCE_URI
7278
+ }
7279
+ }
7280
+ };
7152
7281
  } catch (error) {
7153
7282
  return createErrorResult(`Failed to open flow: ${error instanceof Error ? error.message : String(error)}`);
7154
7283
  }
package/dist/cli.js CHANGED
@@ -11,6 +11,7 @@ import "./chunk-R5U7XKVJ.js";
11
11
  import { Command } from "commander";
12
12
  import chalk from "chalk";
13
13
  import open from "open";
14
+ import { fileURLToPath } from "url";
14
15
  var program = new Command();
15
16
  var binName = process.argv[1]?.replace(/.*[\\/]/, "").replace(/\.[^.]+$/, "") === "applab" ? "applab" : "discoverylab";
16
17
  program.name(binName).description("AI-powered app testing & evidence generator - Claude Code Plugin").version(APP_VERSION);
@@ -389,7 +390,7 @@ program.command("serve").alias("server").description("Start the DiscoveryLab web
389
390
  console.log(chalk.cyan("\n DiscoveryLab"));
390
391
  console.log(chalk.gray(" AI-powered app testing & evidence generator\n"));
391
392
  try {
392
- const { startServer } = await import("./server-W3JQ5RG7.js");
393
+ const { startServer } = await import("./server-GXNAKM4H.js");
393
394
  await startServer(port);
394
395
  console.log(chalk.green(` Server running at http://localhost:${port}`));
395
396
  console.log(chalk.gray(" Press Ctrl+C to stop\n"));
@@ -404,7 +405,7 @@ program.command("serve").alias("server").description("Start the DiscoveryLab web
404
405
  program.command("setup").description("Check and configure DiscoveryLab dependencies").action(async () => {
405
406
  console.log(chalk.cyan("\n DiscoveryLab Setup\n"));
406
407
  try {
407
- const { setupStatusTool } = await import("./setup-F7MGEFIM.js");
408
+ const { setupStatusTool } = await import("./setup-SMN7FJNZ.js");
408
409
  const result = await setupStatusTool.handler({});
409
410
  if (result.isError) {
410
411
  console.error(chalk.red(" Setup check failed"));
@@ -459,7 +460,11 @@ program.command("install").description("Install DiscoveryLab as MCP server for C
459
460
  const { existsSync, readFileSync, writeFileSync, mkdirSync } = await import("fs");
460
461
  const { join, dirname } = await import("path");
461
462
  const home = homedir();
462
- const mcpEntry = {
463
+ const localMcpEntrypoint = fileURLToPath(new URL("./index.js", import.meta.url));
464
+ const mcpEntry = existsSync(localMcpEntrypoint) ? {
465
+ command: process.execPath,
466
+ args: [localMcpEntrypoint]
467
+ } : {
463
468
  command: "npx",
464
469
  args: ["-y", "@veolab/discoverylab@latest", "mcp"]
465
470
  };
@@ -531,7 +536,7 @@ program.command("mcp").description("Run as MCP server (for Claude Code integrati
531
536
  try {
532
537
  const { getDatabase } = await import("./db-5ECN3O7F.js");
533
538
  getDatabase();
534
- const { mcpServer } = await import("./server-OVOACIOJ.js");
539
+ const { mcpServer } = await import("./server-WN6DCCUA.js");
535
540
  const {
536
541
  uiTools,
537
542
  projectTools,
@@ -545,7 +550,7 @@ program.command("mcp").description("Run as MCP server (for Claude Code integrati
545
550
  taskHubTools,
546
551
  esvpTools,
547
552
  knowledgeTools
548
- } = await import("./tools-VYFNRUS4.js");
553
+ } = await import("./tools-6BTUMR3G.js");
549
554
  mcpServer.registerTools([
550
555
  ...uiTools,
551
556
  ...projectTools,
@@ -592,7 +597,7 @@ program.command("export").description("Export project in various formats").argum
592
597
  const { join: pathJoin } = await import("path");
593
598
  const { getDatabase, projects, frames: framesTable, FRAMES_DIR, EXPORTS_DIR, PROJECTS_DIR } = await import("./db-5ECN3O7F.js");
594
599
  const { eq } = await import("drizzle-orm");
595
- const { collectFrameImages, buildInfographicData, generateInfographicHtml } = await import("./infographic-GQAHEOAA.js");
600
+ const { buildInfographicData, generateInfographicHtml, resolveInfographicFrameInputs } = await import("./infographic-OSDIJM5M.js");
596
601
  const db = getDatabase();
597
602
  const allProjects = await db.select().from(projects);
598
603
  const project = allProjects.find((p) => p.id === projectId || p.id.startsWith(projectId) || p.name.toLowerCase().includes(projectId.toLowerCase()));
@@ -606,21 +611,27 @@ program.command("export").description("Export project in various formats").argum
606
611
  }
607
612
  console.log(chalk.green(` \u2714 Found project: ${project.marketingTitle || project.name}`));
608
613
  const dbFrames = await db.select().from(framesTable).where(eq(framesTable.projectId, project.id)).orderBy(framesTable.frameNumber).limit(20);
609
- let frameFiles;
610
- let frameOcr;
611
- if (dbFrames.length > 0) {
612
- frameFiles = dbFrames.map((f) => f.imagePath);
613
- frameOcr = dbFrames;
614
- } else {
615
- frameFiles = collectFrameImages(pathJoin(FRAMES_DIR, project.id), project.videoPath, PROJECTS_DIR, project.id);
616
- frameOcr = frameFiles.map(() => ({ ocrText: null }));
614
+ const resolvedFrames = resolveInfographicFrameInputs(
615
+ dbFrames,
616
+ pathJoin(FRAMES_DIR, project.id),
617
+ project.videoPath,
618
+ PROJECTS_DIR,
619
+ project.id
620
+ );
621
+ console.log(
622
+ chalk.green(
623
+ ` \u2714 ${resolvedFrames.frameFiles.length} readable frames found` + (resolvedFrames.candidateCount > 0 ? ` (from ${resolvedFrames.candidateCount} candidates via ${resolvedFrames.source})` : "")
624
+ )
625
+ );
626
+ if (resolvedFrames.frameFiles.length === 0) {
627
+ console.log(chalk.red(" No readable frames found. Run analyzer first or refresh project screenshots."));
628
+ return;
617
629
  }
618
- console.log(chalk.green(` \u2714 ${frameFiles.length} frames found`));
619
- if (frameFiles.length === 0) {
620
- console.log(chalk.red(" No frames found. Run analyzer first."));
630
+ const data = buildInfographicData(project, resolvedFrames.frameFiles, resolvedFrames.frameOcr);
631
+ if (data.frames.length === 0) {
632
+ console.log(chalk.red(" Export failed: infographic payload did not produce embeddable frames."));
621
633
  return;
622
634
  }
623
- const data = buildInfographicData(project, frameFiles, frameOcr);
624
635
  console.log(chalk.green(` \u2714 ${project.aiSummary ? "AI analysis loaded" : "No analysis (basic labels)"}`));
625
636
  const slug = (project.marketingTitle || project.name || project.id).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
626
637
  const outputPath = opts.output ? pathJoin(opts.output, `${slug}-infographic.html`) : pathJoin(EXPORTS_DIR, `${slug}-infographic.html`);