retestkit 1.4.1 → 1.5.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 (238) hide show
  1. package/README.md +59 -40
  2. package/dist/config.js +8 -8
  3. package/dist/config.js.map +1 -1
  4. package/dist/logger.js +1 -1
  5. package/dist/logger.js.map +1 -1
  6. package/dist/prompts/index.d.ts +1 -1
  7. package/dist/prompts/index.d.ts.map +1 -1
  8. package/dist/prompts/index.js +21 -21
  9. package/dist/prompts/index.js.map +1 -1
  10. package/dist/prompts/templates/mcp/retest-crawl.md +7 -0
  11. package/{src/prompts/templates/mcp/webtest-discover-flows.md → dist/prompts/templates/mcp/retest-discover-flows.md} +1 -1
  12. package/{src/prompts/templates/mcp/webtest-discover.md → dist/prompts/templates/mcp/retest-discover.md} +2 -2
  13. package/dist/prompts/templates/mcp/retest-full-workflow.md +12 -0
  14. package/{src/prompts/templates/mcp/webtest-generate-tests.md → dist/prompts/templates/mcp/retest-generate-tests.md} +1 -1
  15. package/{src/prompts/templates/mcp/webtest-run-test.md → dist/prompts/templates/mcp/retest-run-test.md} +1 -1
  16. package/{src/prompts/templates/mcp/webtest-start.md → dist/prompts/templates/mcp/retest-start.md} +1 -1
  17. package/{src → dist}/prompts/templates/sampling/system-prefix.md +1 -1
  18. package/dist/resources/index.js +7 -7
  19. package/dist/resources/index.js.map +1 -1
  20. package/dist/schemas/config.js +2 -2
  21. package/dist/schemas/config.js.map +1 -1
  22. package/dist/security/index.js +1 -1
  23. package/dist/security/index.js.map +1 -1
  24. package/dist/server.js +3 -3
  25. package/dist/server.js.map +1 -1
  26. package/dist/test-utils/mock-context.js +22 -22
  27. package/dist/test-utils/mock-context.js.map +1 -1
  28. package/dist/tools/index.d.ts +1 -1
  29. package/dist/tools/index.d.ts.map +1 -1
  30. package/dist/tools/index.js +5 -5
  31. package/dist/tools/index.js.map +1 -1
  32. package/dist/tools/retest/crawl.d.ts.map +1 -0
  33. package/dist/tools/{webtest → retest}/crawl.js +7 -7
  34. package/dist/tools/retest/crawl.js.map +1 -0
  35. package/dist/tools/retest/discover-features.d.ts.map +1 -0
  36. package/dist/tools/{webtest → retest}/discover-features.js +6 -6
  37. package/dist/tools/retest/discover-features.js.map +1 -0
  38. package/dist/tools/retest/discover-flows.d.ts.map +1 -0
  39. package/dist/tools/{webtest → retest}/discover-flows.js +6 -6
  40. package/dist/tools/retest/discover-flows.js.map +1 -0
  41. package/dist/tools/retest/generate-tests.d.ts.map +1 -0
  42. package/dist/tools/{webtest → retest}/generate-tests.js +5 -5
  43. package/dist/tools/retest/generate-tests.js.map +1 -0
  44. package/dist/tools/retest/index.d.ts.map +1 -0
  45. package/dist/tools/retest/index.js.map +1 -0
  46. package/dist/tools/retest/run-test-case.d.ts.map +1 -0
  47. package/dist/tools/{webtest → retest}/run-test-case.js +3 -3
  48. package/dist/tools/retest/run-test-case.js.map +1 -0
  49. package/dist/tools/retest/schemas.d.ts.map +1 -0
  50. package/dist/tools/retest/schemas.js.map +1 -0
  51. package/dist/tools/retest/start-analysis.d.ts.map +1 -0
  52. package/dist/tools/{webtest → retest}/start-analysis.js +5 -5
  53. package/dist/tools/retest/start-analysis.js.map +1 -0
  54. package/dist/workspace/index.js +8 -8
  55. package/dist/workspace/index.js.map +1 -1
  56. package/dist/workspace/types.d.ts +2 -2
  57. package/dist/workspace/types.d.ts.map +1 -1
  58. package/package.json +6 -2
  59. package/.claude/commands/openspec/apply.md +0 -23
  60. package/.claude/commands/openspec/archive.md +0 -27
  61. package/.claude/commands/openspec/proposal.md +0 -28
  62. package/.gemini/commands/openspec/apply.toml +0 -21
  63. package/.gemini/commands/openspec/archive.toml +0 -25
  64. package/.gemini/commands/openspec/proposal.toml +0 -26
  65. package/.github/prompts/openspec-apply.prompt.md +0 -22
  66. package/.github/prompts/openspec-archive.prompt.md +0 -26
  67. package/.github/prompts/openspec-proposal.prompt.md +0 -27
  68. package/.github/workflows/release.yml +0 -33
  69. package/.kilocode/workflows/openspec-apply.md +0 -17
  70. package/.kilocode/workflows/openspec-archive.md +0 -21
  71. package/.kilocode/workflows/openspec-proposal.md +0 -22
  72. package/.mcp.json +0 -23
  73. package/.opencode/command/openspec-apply.md +0 -25
  74. package/.opencode/command/openspec-archive.md +0 -28
  75. package/.opencode/command/openspec-proposal.md +0 -30
  76. package/.roo/commands/openspec-apply.md +0 -20
  77. package/.roo/commands/openspec-archive.md +0 -24
  78. package/.roo/commands/openspec-proposal.md +0 -25
  79. package/.vscode/mcp.json +0 -23
  80. package/AGENTS.md +0 -18
  81. package/CLAUDE.md +0 -18
  82. package/dist/tools/webtest/crawl.d.ts.map +0 -1
  83. package/dist/tools/webtest/crawl.js.map +0 -1
  84. package/dist/tools/webtest/discover-features.d.ts.map +0 -1
  85. package/dist/tools/webtest/discover-features.js.map +0 -1
  86. package/dist/tools/webtest/discover-flows.d.ts.map +0 -1
  87. package/dist/tools/webtest/discover-flows.js.map +0 -1
  88. package/dist/tools/webtest/generate-tests.d.ts.map +0 -1
  89. package/dist/tools/webtest/generate-tests.js.map +0 -1
  90. package/dist/tools/webtest/index.d.ts.map +0 -1
  91. package/dist/tools/webtest/index.js.map +0 -1
  92. package/dist/tools/webtest/run-test-case.d.ts.map +0 -1
  93. package/dist/tools/webtest/run-test-case.js.map +0 -1
  94. package/dist/tools/webtest/schemas.d.ts.map +0 -1
  95. package/dist/tools/webtest/schemas.js.map +0 -1
  96. package/dist/tools/webtest/start-analysis.d.ts.map +0 -1
  97. package/dist/tools/webtest/start-analysis.js.map +0 -1
  98. package/openspec/AGENTS.md +0 -456
  99. package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/proposal.md +0 -33
  100. package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/specs/webtest-resources/spec.md +0 -27
  101. package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/specs/webtest-tools/spec.md +0 -304
  102. package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/tasks.md +0 -43
  103. package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/design.md +0 -209
  104. package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/proposal.md +0 -41
  105. package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/specs/mcp-server-core/spec.md +0 -183
  106. package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/tasks.md +0 -112
  107. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/design.md +0 -333
  108. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/proposal.md +0 -66
  109. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/mcp-server-core/spec.md +0 -129
  110. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-lifecycle/spec.md +0 -138
  111. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-logging/spec.md +0 -211
  112. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-prompts/spec.md +0 -157
  113. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-resources/spec.md +0 -213
  114. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-sampling/spec.md +0 -257
  115. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-tools/spec.md +0 -501
  116. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/tasks.md +0 -264
  117. package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/proposal.md +0 -24
  118. package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/specs/webtest-tools/spec.md +0 -80
  119. package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/tasks.md +0 -8
  120. package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/design.md +0 -90
  121. package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/proposal.md +0 -28
  122. package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/specs/webtest-sampling/spec.md +0 -90
  123. package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/tasks.md +0 -33
  124. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/design.md +0 -558
  125. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/proposal.md +0 -119
  126. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/specs/webtest-resources/spec.md +0 -109
  127. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/specs/webtest-tools/spec.md +0 -121
  128. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/tasks.md +0 -133
  129. package/openspec/changes/extract-prompts-to-markdown/design.md +0 -86
  130. package/openspec/changes/extract-prompts-to-markdown/proposal.md +0 -50
  131. package/openspec/changes/extract-prompts-to-markdown/specs/webtest-prompts/spec.md +0 -74
  132. package/openspec/changes/extract-prompts-to-markdown/tasks.md +0 -40
  133. package/openspec/changes/refactor-webtest-naming/design.md +0 -95
  134. package/openspec/changes/refactor-webtest-naming/proposal.md +0 -66
  135. package/openspec/changes/refactor-webtest-naming/specs/webtest-prompts/spec.md +0 -79
  136. package/openspec/changes/refactor-webtest-naming/specs/webtest-resources/spec.md +0 -80
  137. package/openspec/changes/refactor-webtest-naming/specs/webtest-sampling/spec.md +0 -122
  138. package/openspec/changes/refactor-webtest-naming/specs/webtest-tools/spec.md +0 -113
  139. package/openspec/changes/refactor-webtest-naming/tasks.md +0 -119
  140. package/openspec/changes/rename-package-to-retest/proposal.md +0 -52
  141. package/openspec/changes/rename-package-to-retest/specs/mcp-server-core/spec.md +0 -53
  142. package/openspec/changes/rename-package-to-retest/specs/retest-lifecycle/spec.md +0 -68
  143. package/openspec/changes/rename-package-to-retest/specs/retest-logging/spec.md +0 -35
  144. package/openspec/changes/rename-package-to-retest/specs/retest-prompts/spec.md +0 -159
  145. package/openspec/changes/rename-package-to-retest/specs/retest-resources/spec.md +0 -251
  146. package/openspec/changes/rename-package-to-retest/specs/retest-sampling/spec.md +0 -99
  147. package/openspec/changes/rename-package-to-retest/specs/retest-tools/spec.md +0 -295
  148. package/openspec/changes/rename-package-to-retest/tasks.md +0 -71
  149. package/openspec/project.md +0 -31
  150. package/openspec/specs/mcp-server-core/spec.md +0 -178
  151. package/openspec/specs/webtest-lifecycle/spec.md +0 -136
  152. package/openspec/specs/webtest-logging/spec.md +0 -209
  153. package/openspec/specs/webtest-prompts/spec.md +0 -155
  154. package/openspec/specs/webtest-resources/spec.md +0 -248
  155. package/openspec/specs/webtest-sampling/spec.md +0 -344
  156. package/openspec/specs/webtest-tools/spec.md +0 -282
  157. package/release.config.js +0 -9
  158. package/src/config.test.ts +0 -96
  159. package/src/config.ts +0 -32
  160. package/src/elicitation/index.test.ts +0 -399
  161. package/src/elicitation/index.ts +0 -171
  162. package/src/elicitation/types.ts +0 -68
  163. package/src/index.ts +0 -83
  164. package/src/lifecycle/index.test.ts +0 -260
  165. package/src/lifecycle/index.ts +0 -101
  166. package/src/logger.redaction.test.ts +0 -322
  167. package/src/logger.test.ts +0 -123
  168. package/src/logger.ts +0 -229
  169. package/src/playwright-client/index.ts +0 -392
  170. package/src/playwright-client/types.ts +0 -99
  171. package/src/progress/index.test.ts +0 -327
  172. package/src/progress/index.ts +0 -170
  173. package/src/progress/types.ts +0 -25
  174. package/src/prompts/index.test.ts +0 -451
  175. package/src/prompts/index.ts +0 -246
  176. package/src/prompts/loader.test.ts +0 -100
  177. package/src/prompts/loader.ts +0 -59
  178. package/src/prompts/templates/mcp/webtest-crawl.md +0 -7
  179. package/src/prompts/templates/mcp/webtest-full-workflow.md +0 -12
  180. package/src/resources/index.ts +0 -250
  181. package/src/resources/subscriptions.ts +0 -37
  182. package/src/sampling/index.test.ts +0 -414
  183. package/src/sampling/index.ts +0 -286
  184. package/src/sampling/prompts.ts +0 -194
  185. package/src/sampling/types.ts +0 -60
  186. package/src/schemas/config.ts +0 -39
  187. package/src/security/index.test.ts +0 -441
  188. package/src/security/index.ts +0 -361
  189. package/src/security/security-scenarios.test.ts +0 -468
  190. package/src/server.ts +0 -211
  191. package/src/test-utils/index.ts +0 -6
  192. package/src/test-utils/mock-context.ts +0 -426
  193. package/src/test-utils/mock-playwright-client.ts +0 -422
  194. package/src/tools/index.ts +0 -11
  195. package/src/tools/webtest/crawl.test.ts +0 -834
  196. package/src/tools/webtest/crawl.ts +0 -901
  197. package/src/tools/webtest/discover-features.ts +0 -412
  198. package/src/tools/webtest/discover-flows.ts +0 -408
  199. package/src/tools/webtest/generate-tests.test.ts +0 -532
  200. package/src/tools/webtest/generate-tests.ts +0 -425
  201. package/src/tools/webtest/index.ts +0 -7
  202. package/src/tools/webtest/integration.test.ts +0 -536
  203. package/src/tools/webtest/run-test-case.test.ts +0 -659
  204. package/src/tools/webtest/run-test-case.ts +0 -508
  205. package/src/tools/webtest/schemas.ts +0 -201
  206. package/src/tools/webtest/start-analysis.test.ts +0 -151
  207. package/src/tools/webtest/start-analysis.ts +0 -158
  208. package/src/transports/http.ts +0 -19
  209. package/src/transports/index.ts +0 -30
  210. package/src/transports/stdio.ts +0 -7
  211. package/src/types/capabilities.test.ts +0 -193
  212. package/src/types/capabilities.ts +0 -50
  213. package/src/types/context.ts +0 -21
  214. package/src/types/tool.ts +0 -11
  215. package/src/workspace/index.ts +0 -945
  216. package/src/workspace/markdown.ts +0 -272
  217. package/src/workspace/types.ts +0 -186
  218. package/tests/integration/server.test.ts +0 -89
  219. package/tests/integration/tools.test.ts +0 -99
  220. package/tsconfig.json +0 -20
  221. package/vitest.config.ts +0 -9
  222. package/vitest.integration.config.ts +0 -10
  223. /package/{src → dist}/prompts/templates/sampling/crawl-action.md +0 -0
  224. /package/{src → dist}/prompts/templates/sampling/feature-discovery.md +0 -0
  225. /package/{src → dist}/prompts/templates/sampling/flow-discovery.md +0 -0
  226. /package/{src → dist}/prompts/templates/sampling/page-content-wrapper.md +0 -0
  227. /package/{src → dist}/prompts/templates/sampling/test-evaluation.md +0 -0
  228. /package/{src → dist}/prompts/templates/sampling/test-generation.md +0 -0
  229. /package/dist/tools/{webtest → retest}/crawl.d.ts +0 -0
  230. /package/dist/tools/{webtest → retest}/discover-features.d.ts +0 -0
  231. /package/dist/tools/{webtest → retest}/discover-flows.d.ts +0 -0
  232. /package/dist/tools/{webtest → retest}/generate-tests.d.ts +0 -0
  233. /package/dist/tools/{webtest → retest}/index.d.ts +0 -0
  234. /package/dist/tools/{webtest → retest}/index.js +0 -0
  235. /package/dist/tools/{webtest → retest}/run-test-case.d.ts +0 -0
  236. /package/dist/tools/{webtest → retest}/schemas.d.ts +0 -0
  237. /package/dist/tools/{webtest → retest}/schemas.js +0 -0
  238. /package/dist/tools/{webtest → retest}/start-analysis.d.ts +0 -0
@@ -1,412 +0,0 @@
1
- import { z } from "zod";
2
- import type { McpTool, ToolResult } from "../../types/tool.js";
3
- import type { ServerContext } from "../../types/context.js";
4
- import type { WorkspaceManager, Feature } from "../../workspace/index.js";
5
- import type { SamplingClient } from "../../sampling/index.js";
6
- import type { ResourceManager } from "../../resources/index.js";
7
- import { buildFeatureDiscoveryPrompt } from "../../sampling/prompts.js";
8
- import { FeaturesDiscoverySchema, AnalysisIdSchema, CrawlIdSchema, type FeaturesDiscovery } from "./schemas.js";
9
-
10
- export const discoverFeaturesInputSchema = z.object({
11
- analysisId: AnalysisIdSchema,
12
- crawlId: CrawlIdSchema
13
- .optional()
14
- .describe("Specific crawl to analyze. If not provided, uses the most recent crawl with captured pages."),
15
- manualDiscovery: z
16
- .object({
17
- appPurpose: z.string(),
18
- appType: z.string(),
19
- features: z.array(z.object({
20
- slug: z.string(),
21
- name: z.string(),
22
- description: z.string(),
23
- entities: z.array(z.string()),
24
- entryPoints: z.array(z.string()),
25
- })),
26
- securityObservations: z.array(z.string()).optional(),
27
- accessibilityObservations: z.array(z.string()).optional(),
28
- })
29
- .optional()
30
- .describe("Manual discovery result when sampling is unavailable. Provide this after executing the returned prompt."),
31
- });
32
-
33
- export type DiscoverFeaturesInput = z.infer<typeof discoverFeaturesInputSchema>;
34
-
35
- export function createDiscoverFeaturesTool(
36
- getContext: () => ServerContext & {
37
- workspaceManager: WorkspaceManager;
38
- samplingClient: SamplingClient;
39
- resourceManager: ResourceManager;
40
- }
41
- ): McpTool<DiscoverFeaturesInput> {
42
- return {
43
- name: "webtest_discover_features",
44
- description: `Discover application features/modules from crawl data.
45
-
46
- This tool uses AI to identify distinct features of the web application:
47
- - Identifies the application purpose and type
48
- - Discovers distinct features/modules (e.g., Authentication, Shopping Cart, Search)
49
- - Lists entities and entry points for each feature
50
- - Notes security and accessibility observations
51
-
52
- Outputs features.md at workspace root. After discovering features, use webtest_discover_flows to identify user flows within each feature.
53
-
54
- Requires a crawl with captured pages. If sampling is unavailable, returns a prompt for manual execution.`,
55
-
56
- inputSchema: discoverFeaturesInputSchema,
57
-
58
- async handler(input: DiscoverFeaturesInput): Promise<ToolResult> {
59
- const ctx = getContext();
60
- const { logger, workspaceManager, samplingClient, resourceManager } = ctx;
61
-
62
- const discoveryLogger = logger.withCorrelation({
63
- analysisId: input.analysisId,
64
- });
65
-
66
- discoveryLogger.info("Starting feature discovery", { crawlId: input.crawlId });
67
-
68
- try {
69
- // Validate workspace exists
70
- if (!(await workspaceManager.workspaceExists(input.analysisId))) {
71
- return {
72
- content: [
73
- {
74
- type: "text",
75
- text: `Error: Analysis workspace "${input.analysisId}" not found.`,
76
- },
77
- ],
78
- isError: true,
79
- };
80
- }
81
-
82
- const workspace = await workspaceManager.readWorkspaceIndex(input.analysisId);
83
-
84
- // Find the crawl to analyze
85
- let targetCrawlId = input.crawlId;
86
- let selectedCrawlRef: (typeof workspace.crawls)[0] | undefined;
87
-
88
- if (!targetCrawlId) {
89
- // Sort crawls by most recent first
90
- const sortedCrawls = [...workspace.crawls].sort(
91
- (a, b) =>
92
- new Date(b.completedAt || b.startedAt).getTime() -
93
- new Date(a.completedAt || a.startedAt).getTime()
94
- );
95
-
96
- // First try crawls with pagesVisited > 0 in workspace index
97
- const crawlsWithData = sortedCrawls.filter((c) => c.pagesVisited > 0);
98
-
99
- if (crawlsWithData.length > 0) {
100
- selectedCrawlRef = crawlsWithData[0];
101
- targetCrawlId = selectedCrawlRef.crawlId;
102
- } else if (sortedCrawls.length > 0) {
103
- // Fallback: check actual crawl indexes for pages (workspace index may be stale)
104
- for (const crawlRef of sortedCrawls) {
105
- try {
106
- const crawlIndex = await workspaceManager.readCrawlIndex(
107
- input.analysisId,
108
- crawlRef.crawlId
109
- );
110
- if (crawlIndex.pages.length > 0) {
111
- selectedCrawlRef = crawlRef;
112
- targetCrawlId = crawlRef.crawlId;
113
- discoveryLogger.info("Found crawl with data via index check", {
114
- crawlId: crawlRef.crawlId,
115
- pagesInIndex: crawlIndex.pages.length,
116
- pagesInWorkspace: crawlRef.pagesVisited,
117
- });
118
- break;
119
- }
120
- } catch {
121
- // Crawl index not readable, skip
122
- }
123
- }
124
- }
125
-
126
- if (!targetCrawlId) {
127
- return {
128
- content: [
129
- {
130
- type: "text",
131
- text: "Error: No crawls with captured data found. Run webtest_crawl_app first.",
132
- },
133
- ],
134
- isError: true,
135
- };
136
- }
137
- } else {
138
- selectedCrawlRef = workspace.crawls.find((c) => c.crawlId === targetCrawlId);
139
- }
140
-
141
- // Load crawl data
142
- const crawlIndex = await workspaceManager.readCrawlIndex(
143
- input.analysisId,
144
- targetCrawlId
145
- );
146
-
147
- if (crawlIndex.pages.length === 0) {
148
- return {
149
- content: [
150
- {
151
- type: "text",
152
- text: "Error: Crawl has no captured pages. The crawl may have failed.",
153
- },
154
- ],
155
- isError: true,
156
- };
157
- }
158
-
159
- discoveryLogger.info("Analyzing crawl data for features", {
160
- crawlId: targetCrawlId,
161
- pageCount: crawlIndex.pages.length,
162
- });
163
-
164
- // Build crawl summary
165
- const crawlSummary = `
166
- Target URL: ${workspace.url}
167
- Domain: ${workspace.domain}
168
- Focus: ${workspace.focus || "General exploration"}
169
- Goal: ${crawlIndex.goal}
170
- Strategy: ${crawlIndex.strategy}
171
- Pages visited: ${crawlIndex.pages.length}
172
- Actions taken: ${crawlIndex.actionHistory.length}
173
- Status: ${crawlIndex.status}
174
- `;
175
-
176
- // Load page snapshots (limited to avoid token limits)
177
- const pageSnapshots: Array<{ url: string; content: string }> = [];
178
-
179
- for (const page of crawlIndex.pages.slice(0, 10)) {
180
- try {
181
- const snapshotContent = await resourceManager.readResource(
182
- page.snapshotUri
183
- );
184
- if (snapshotContent.text) {
185
- const snapshot = JSON.parse(snapshotContent.text);
186
- pageSnapshots.push({
187
- url: page.url,
188
- content: snapshot.content || "",
189
- });
190
- }
191
- } catch (error) {
192
- discoveryLogger.warn("Failed to load page snapshot", {
193
- pageId: page.pageId,
194
- error: error instanceof Error ? error.message : "Unknown error",
195
- });
196
- }
197
- }
198
-
199
- // Determine discovery source: manual input, sampling, or fallback prompt
200
- let discovery: FeaturesDiscovery;
201
-
202
- if (input.manualDiscovery) {
203
- // Use manually provided discovery (from fallback mode)
204
- discoveryLogger.info("Using manual discovery input");
205
- discovery = input.manualDiscovery as FeaturesDiscovery;
206
- } else if (!samplingClient.hasSampling()) {
207
- // No sampling available and no manual input - return prompt for manual execution
208
- const prompt = buildFeatureDiscoveryPrompt({
209
- crawlSummary,
210
- pageSnapshots,
211
- });
212
-
213
- return {
214
- content: [
215
- {
216
- type: "text",
217
- text: JSON.stringify(
218
- {
219
- needsManualInput: true,
220
- analysisId: input.analysisId,
221
- crawlId: targetCrawlId,
222
- prompt,
223
- expectedResponseSchema: FeaturesDiscoverySchema._def,
224
- instructions:
225
- "Execute this prompt with your LLM, then call webtest_discover_features again with the result in the 'manualDiscovery' parameter",
226
- },
227
- null,
228
- 2
229
- ),
230
- },
231
- ],
232
- };
233
- } else {
234
- // Request discovery via sampling
235
- const samplingResult = await samplingClient.createMessage({
236
- systemPrompt: `You are analyzing a web application to identify its distinct features and modules.
237
- Each feature should be a cohesive capability that users interact with (e.g., Authentication, Shopping Cart, Search).
238
- Provide thorough feature identification that will help in discovering user flows within each feature.`,
239
- userPrompt: buildFeatureDiscoveryPrompt({
240
- crawlSummary,
241
- pageSnapshots,
242
- }),
243
- schema: FeaturesDiscoverySchema,
244
- maxTokens: 4096,
245
- });
246
-
247
- if (!samplingResult.success || !samplingResult.data) {
248
- discoveryLogger.error("Feature discovery sampling failed", {
249
- error: samplingResult.error,
250
- });
251
-
252
- return {
253
- content: [
254
- {
255
- type: "text",
256
- text: `Error during feature discovery: ${samplingResult.error}`,
257
- },
258
- ],
259
- isError: true,
260
- };
261
- }
262
-
263
- discovery = samplingResult.data;
264
- }
265
-
266
- // Generate markdown report
267
- const markdownReport = generateFeaturesMarkdown(discovery, workspace, crawlIndex);
268
-
269
- // Build frontmatter with features
270
- const frontmatter = {
271
- appPurpose: discovery.appPurpose,
272
- appType: discovery.appType,
273
- features: discovery.features,
274
- securityObservations: discovery.securityObservations,
275
- accessibilityObservations: discovery.accessibilityObservations,
276
- };
277
-
278
- // Save features
279
- const { featuresFilePath, featuresUri, featureCount } =
280
- await workspaceManager.saveFeatures(input.analysisId, {
281
- markdown: markdownReport,
282
- frontmatter: frontmatter as { features: Feature[] },
283
- });
284
-
285
- await resourceManager.notifyListChanged();
286
-
287
- // Build warning if crawl was incomplete
288
- const crawlStatus = selectedCrawlRef?.status;
289
- const isIncompleteCrawl = crawlStatus && crawlStatus !== "completed";
290
- const warning = isIncompleteCrawl
291
- ? `Note: Discovery based on incomplete crawl (status: ${crawlStatus}, pages: ${selectedCrawlRef?.pagesVisited || crawlIndex.pages.length}). Results may be partial.`
292
- : undefined;
293
-
294
- // Build next steps suggesting to discover flows for each feature
295
- const nextSteps = discovery.features.map(
296
- (f) => `Use webtest_discover_flows with analysisId="${input.analysisId}" featureSlug="${f.slug}" to discover flows for "${f.name}"`
297
- );
298
- nextSteps.push(`Review discovered features at ${featuresUri}`);
299
-
300
- const result = {
301
- analysisId: input.analysisId,
302
- crawlId: targetCrawlId,
303
- ...(warning && { warning }),
304
- featuresFilePath,
305
- featuresUri,
306
- summary: {
307
- appPurpose: discovery.appPurpose,
308
- appType: discovery.appType,
309
- featureCount,
310
- features: discovery.features.map((f) => ({
311
- slug: f.slug,
312
- name: f.name,
313
- })),
314
- crawlStatus: crawlStatus || "unknown",
315
- },
316
- nextSteps,
317
- };
318
-
319
- discoveryLogger.info("Feature discovery completed", {
320
- featureCount,
321
- features: discovery.features.map((f) => f.slug),
322
- });
323
-
324
- return {
325
- content: [
326
- {
327
- type: "text",
328
- text: JSON.stringify(result, null, 2),
329
- },
330
- ],
331
- };
332
- } catch (error) {
333
- const message = error instanceof Error ? error.message : "Unknown error";
334
- discoveryLogger.error("Feature discovery failed", { error: message });
335
-
336
- return {
337
- content: [
338
- {
339
- type: "text",
340
- text: `Error during feature discovery: ${message}`,
341
- },
342
- ],
343
- isError: true,
344
- };
345
- }
346
- },
347
- };
348
- }
349
-
350
- function generateFeaturesMarkdown(
351
- discovery: FeaturesDiscovery,
352
- workspace: { url: string; domain: string; focus?: string },
353
- crawl: { goal: string; pages: { url: string }[] }
354
- ): string {
355
- let md = `# Application Features: ${workspace.domain}
356
-
357
- ## Overview
358
-
359
- - **URL**: ${workspace.url}
360
- - **Purpose**: ${discovery.appPurpose}
361
- - **Type**: ${discovery.appType}
362
- - **Focus**: ${workspace.focus || "General"}
363
- - **Crawl Goal**: ${crawl.goal}
364
- - **Pages Analyzed**: ${crawl.pages.length}
365
-
366
- ## Discovered Features
367
-
368
- `;
369
-
370
- for (const feature of discovery.features) {
371
- md += `### ${feature.name}
372
-
373
- **Slug**: \`${feature.slug}\`
374
-
375
- ${feature.description}
376
-
377
- **Entities**: ${feature.entities.length > 0 ? feature.entities.join(", ") : "None identified"}
378
-
379
- **Entry Points**:
380
- `;
381
- for (const ep of feature.entryPoints) {
382
- md += `- ${ep}\n`;
383
- }
384
- md += "\n";
385
- }
386
-
387
- if (discovery.securityObservations && discovery.securityObservations.length > 0) {
388
- md += `## Security Observations
389
-
390
- `;
391
- for (const obs of discovery.securityObservations) {
392
- md += `- ${obs}\n`;
393
- }
394
- md += "\n";
395
- }
396
-
397
- if (discovery.accessibilityObservations && discovery.accessibilityObservations.length > 0) {
398
- md += `## Accessibility Observations
399
-
400
- `;
401
- for (const obs of discovery.accessibilityObservations) {
402
- md += `- ${obs}\n`;
403
- }
404
- md += "\n";
405
- }
406
-
407
- md += `---
408
- *Generated by testing-mcp*
409
- `;
410
-
411
- return md;
412
- }