@bryan-thompson/inspector-assessment 1.5.0 → 1.7.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 (77) hide show
  1. package/cli/build/assess-full.js +528 -0
  2. package/cli/build/assess-security.js +342 -0
  3. package/cli/build/cli.js +10 -1
  4. package/client/dist/assets/{OAuthCallback-TeTvKfWE.js → OAuthCallback-Xo9zS7pv.js} +1 -1
  5. package/client/dist/assets/{OAuthDebugCallback-DwA2sKy9.js → OAuthDebugCallback-CaIey8K_.js} +1 -1
  6. package/client/dist/assets/{index-BwAoxcvr.js → index-nCPw6E-c.js} +4 -4
  7. package/client/dist/index.html +1 -1
  8. package/client/lib/lib/assessmentTypes.d.ts +670 -0
  9. package/client/lib/lib/assessmentTypes.d.ts.map +1 -0
  10. package/client/lib/lib/assessmentTypes.js +220 -0
  11. package/client/lib/lib/aupPatterns.d.ts +63 -0
  12. package/client/lib/lib/aupPatterns.d.ts.map +1 -0
  13. package/client/lib/lib/aupPatterns.js +344 -0
  14. package/client/lib/lib/prohibitedLibraries.d.ts +76 -0
  15. package/client/lib/lib/prohibitedLibraries.d.ts.map +1 -0
  16. package/client/lib/lib/prohibitedLibraries.js +364 -0
  17. package/client/lib/lib/securityPatterns.d.ts +64 -0
  18. package/client/lib/lib/securityPatterns.d.ts.map +1 -0
  19. package/client/lib/lib/securityPatterns.js +453 -0
  20. package/client/lib/services/assessment/AssessmentOrchestrator.d.ts +88 -0
  21. package/client/lib/services/assessment/AssessmentOrchestrator.d.ts.map +1 -0
  22. package/client/lib/services/assessment/AssessmentOrchestrator.js +418 -0
  23. package/client/lib/services/assessment/ResponseValidator.d.ts +69 -0
  24. package/client/lib/services/assessment/ResponseValidator.d.ts.map +1 -0
  25. package/client/lib/services/assessment/ResponseValidator.js +1038 -0
  26. package/client/lib/services/assessment/TestDataGenerator.d.ts +86 -0
  27. package/client/lib/services/assessment/TestDataGenerator.d.ts.map +1 -0
  28. package/client/lib/services/assessment/TestDataGenerator.js +669 -0
  29. package/client/lib/services/assessment/TestScenarioEngine.d.ts +91 -0
  30. package/client/lib/services/assessment/TestScenarioEngine.d.ts.map +1 -0
  31. package/client/lib/services/assessment/TestScenarioEngine.js +505 -0
  32. package/client/lib/services/assessment/ToolClassifier.d.ts +61 -0
  33. package/client/lib/services/assessment/ToolClassifier.d.ts.map +1 -0
  34. package/client/lib/services/assessment/ToolClassifier.js +349 -0
  35. package/client/lib/services/assessment/lib/claudeCodeBridge.d.ts +160 -0
  36. package/client/lib/services/assessment/lib/claudeCodeBridge.d.ts.map +1 -0
  37. package/client/lib/services/assessment/lib/claudeCodeBridge.js +357 -0
  38. package/client/lib/services/assessment/modules/AUPComplianceAssessor.d.ts +100 -0
  39. package/client/lib/services/assessment/modules/AUPComplianceAssessor.d.ts.map +1 -0
  40. package/client/lib/services/assessment/modules/AUPComplianceAssessor.js +474 -0
  41. package/client/lib/services/assessment/modules/BaseAssessor.d.ts +71 -0
  42. package/client/lib/services/assessment/modules/BaseAssessor.d.ts.map +1 -0
  43. package/client/lib/services/assessment/modules/BaseAssessor.js +171 -0
  44. package/client/lib/services/assessment/modules/DocumentationAssessor.d.ts +45 -0
  45. package/client/lib/services/assessment/modules/DocumentationAssessor.d.ts.map +1 -0
  46. package/client/lib/services/assessment/modules/DocumentationAssessor.js +355 -0
  47. package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts +25 -0
  48. package/client/lib/services/assessment/modules/ErrorHandlingAssessor.d.ts.map +1 -0
  49. package/client/lib/services/assessment/modules/ErrorHandlingAssessor.js +564 -0
  50. package/client/lib/services/assessment/modules/FunctionalityAssessor.d.ts +20 -0
  51. package/client/lib/services/assessment/modules/FunctionalityAssessor.d.ts.map +1 -0
  52. package/client/lib/services/assessment/modules/FunctionalityAssessor.js +253 -0
  53. package/client/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts +70 -0
  54. package/client/lib/services/assessment/modules/MCPSpecComplianceAssessor.d.ts.map +1 -0
  55. package/client/lib/services/assessment/modules/MCPSpecComplianceAssessor.js +508 -0
  56. package/client/lib/services/assessment/modules/ManifestValidationAssessor.d.ts +70 -0
  57. package/client/lib/services/assessment/modules/ManifestValidationAssessor.d.ts.map +1 -0
  58. package/client/lib/services/assessment/modules/ManifestValidationAssessor.js +430 -0
  59. package/client/lib/services/assessment/modules/PortabilityAssessor.d.ts +43 -0
  60. package/client/lib/services/assessment/modules/PortabilityAssessor.d.ts.map +1 -0
  61. package/client/lib/services/assessment/modules/PortabilityAssessor.js +347 -0
  62. package/client/lib/services/assessment/modules/ProhibitedLibrariesAssessor.d.ts +41 -0
  63. package/client/lib/services/assessment/modules/ProhibitedLibrariesAssessor.d.ts.map +1 -0
  64. package/client/lib/services/assessment/modules/ProhibitedLibrariesAssessor.js +256 -0
  65. package/client/lib/services/assessment/modules/SecurityAssessor.d.ts +176 -0
  66. package/client/lib/services/assessment/modules/SecurityAssessor.d.ts.map +1 -0
  67. package/client/lib/services/assessment/modules/SecurityAssessor.js +1333 -0
  68. package/client/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts +96 -0
  69. package/client/lib/services/assessment/modules/ToolAnnotationAssessor.d.ts.map +1 -0
  70. package/client/lib/services/assessment/modules/ToolAnnotationAssessor.js +593 -0
  71. package/client/lib/services/assessment/modules/UsabilityAssessor.d.ts +21 -0
  72. package/client/lib/services/assessment/modules/UsabilityAssessor.d.ts.map +1 -0
  73. package/client/lib/services/assessment/modules/UsabilityAssessor.js +241 -0
  74. package/client/lib/services/assessment/modules/index.d.ts +33 -0
  75. package/client/lib/services/assessment/modules/index.d.ts.map +1 -0
  76. package/client/lib/services/assessment/modules/index.js +35 -0
  77. package/package.json +7 -2
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Base Assessor Class
3
+ * Provides common functionality for all assessment modules
4
+ */
5
+ export class BaseAssessor {
6
+ config;
7
+ testCount = 0;
8
+ constructor(config) {
9
+ this.config = config;
10
+ }
11
+ /**
12
+ * Common method to determine status based on pass rate
13
+ */
14
+ determineStatus(passed, total, threshold = 0.8) {
15
+ if (total === 0)
16
+ return "NEED_MORE_INFO";
17
+ const passRate = passed / total;
18
+ if (passRate >= threshold)
19
+ return "PASS";
20
+ if (passRate >= threshold * 0.5)
21
+ return "NEED_MORE_INFO";
22
+ return "FAIL";
23
+ }
24
+ /**
25
+ * Log assessment progress
26
+ */
27
+ log(message) {
28
+ console.log(`[${this.constructor.name}] ${message}`);
29
+ }
30
+ /**
31
+ * Log error
32
+ */
33
+ logError(message, error) {
34
+ console.error(`[${this.constructor.name}] ${message}`, error);
35
+ }
36
+ /**
37
+ * Get test count for this assessor
38
+ */
39
+ getTestCount() {
40
+ return this.testCount;
41
+ }
42
+ /**
43
+ * Reset test count
44
+ */
45
+ resetTestCount() {
46
+ this.testCount = 0;
47
+ }
48
+ /**
49
+ * Check if a feature is enabled in configuration
50
+ */
51
+ isFeatureEnabled(feature) {
52
+ return this.config.assessmentCategories?.[feature] ?? false;
53
+ }
54
+ /**
55
+ * Sleep for specified milliseconds (useful for rate limiting)
56
+ */
57
+ async sleep(ms) {
58
+ return new Promise((resolve) => setTimeout(resolve, ms));
59
+ }
60
+ /**
61
+ * Execute with timeout
62
+ */
63
+ async executeWithTimeout(promise, timeoutMs = this.config.testTimeout) {
64
+ const timeoutPromise = new Promise((_, reject) => {
65
+ setTimeout(() => reject(new Error(`Operation timed out after ${timeoutMs}ms`)), timeoutMs);
66
+ });
67
+ return Promise.race([promise, timeoutPromise]);
68
+ }
69
+ /**
70
+ * Safe JSON parse with error handling
71
+ */
72
+ safeJsonParse(text) {
73
+ try {
74
+ return JSON.parse(text);
75
+ }
76
+ catch (error) {
77
+ this.logError(`Failed to parse JSON: ${text}`, error);
78
+ return null;
79
+ }
80
+ }
81
+ /**
82
+ * Extract error message from various error types
83
+ */
84
+ extractErrorMessage(error) {
85
+ if (typeof error === "string")
86
+ return error;
87
+ if (error?.message)
88
+ return error.message;
89
+ if (error?.error)
90
+ return this.extractErrorMessage(error.error);
91
+ if (error?.content) {
92
+ if (Array.isArray(error.content)) {
93
+ return error.content
94
+ .map((c) => c.text || c.content || "")
95
+ .join(" ");
96
+ }
97
+ return error.content;
98
+ }
99
+ return JSON.stringify(error);
100
+ }
101
+ /**
102
+ * Check if a response indicates an error
103
+ * Handles various MCP response formats
104
+ *
105
+ * @param response - The response to check
106
+ * @param strictMode - If true, only check explicit error indicators (default: false)
107
+ */
108
+ isErrorResponse(response, strictMode = false) {
109
+ if (!response)
110
+ return false;
111
+ // Check explicit error flag first (always check these)
112
+ if (response.isError === true || response.error !== undefined) {
113
+ return true;
114
+ }
115
+ // In strict mode, only rely on explicit error indicators
116
+ // Used by FunctionalityAssessor where we expect valid responses
117
+ if (strictMode) {
118
+ return false;
119
+ }
120
+ // In non-strict mode, also check content for error patterns
121
+ // Used by ErrorHandlingAssessor where we're deliberately triggering errors
122
+ if (response.content) {
123
+ if (typeof response.content === "string") {
124
+ const lower = response.content.toLowerCase();
125
+ // Only flag if error appears at start or with strong indicators
126
+ return (lower.startsWith("error:") ||
127
+ lower.startsWith("error ") ||
128
+ lower.includes("error occurred") ||
129
+ lower.includes("failed to") ||
130
+ lower.includes("exception:"));
131
+ }
132
+ else if (Array.isArray(response.content)) {
133
+ // Check if any text content starts with error indicators
134
+ return response.content.some((c) => {
135
+ if (c.type !== "text" || !c.text)
136
+ return false;
137
+ const lower = c.text.toLowerCase();
138
+ return (lower.startsWith("error:") ||
139
+ lower.startsWith("error ") ||
140
+ lower.includes("error occurred") ||
141
+ lower.includes("failed to") ||
142
+ lower.includes("exception:"));
143
+ });
144
+ }
145
+ }
146
+ return false;
147
+ }
148
+ /**
149
+ * Extract error information from a response
150
+ */
151
+ extractErrorInfo(response) {
152
+ if (!response)
153
+ return {};
154
+ // Extract text from content array if present
155
+ let contentText;
156
+ if (Array.isArray(response.content)) {
157
+ const textContent = response.content.find((c) => c.type === "text");
158
+ contentText = textContent?.text;
159
+ }
160
+ else if (typeof response.content === "string") {
161
+ contentText = response.content;
162
+ }
163
+ return {
164
+ code: response.errorCode || response.code || response.error?.code,
165
+ message: response.errorMessage ||
166
+ response.message ||
167
+ response.error?.message ||
168
+ contentText,
169
+ };
170
+ }
171
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Documentation Assessor Module
3
+ * Evaluates documentation quality and completeness
4
+ */
5
+ import { DocumentationAssessment } from "../../../lib/assessmentTypes.js";
6
+ import { BaseAssessor } from "./BaseAssessor.js";
7
+ import { AssessmentContext } from "../AssessmentOrchestrator.js";
8
+ export declare class DocumentationAssessor extends BaseAssessor {
9
+ assess(context: AssessmentContext): Promise<DocumentationAssessment>;
10
+ private analyzeDocumentation;
11
+ /**
12
+ * Extract functional example prompts from documentation.
13
+ * Only counts user-facing examples, not configuration or installation code.
14
+ */
15
+ private extractFunctionalExamples;
16
+ /**
17
+ * Check if a code block is non-functional (config, install, implementation).
18
+ */
19
+ private isNonFunctionalCodeBlock;
20
+ /**
21
+ * Score functional example quality (returns true if meets minimum threshold).
22
+ */
23
+ private scoreFunctionalExample;
24
+ /**
25
+ * Get line number for a position in content.
26
+ */
27
+ private getLineNumber;
28
+ /**
29
+ * Remove duplicate examples based on similarity.
30
+ */
31
+ private deduplicateExamples;
32
+ /**
33
+ * @deprecated Use extractFunctionalExamples() instead.
34
+ * This method counts ALL code blocks including configs and install commands.
35
+ */
36
+ private extractCodeExamples;
37
+ private checkInstallInstructions;
38
+ private checkUsageGuide;
39
+ private checkAPIReference;
40
+ private extractSection;
41
+ private determineDocumentationStatus;
42
+ private generateExplanation;
43
+ private generateRecommendations;
44
+ }
45
+ //# sourceMappingURL=DocumentationAssessor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocumentationAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/DocumentationAssessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EAIxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,qBAAa,qBAAsB,SAAQ,YAAY;IAC/C,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAkB1E,OAAO,CAAC,oBAAoB;IAsF5B;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAuEjC;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAiBhC;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAmC9B;;OAEG;IACH,OAAO,CAAC,aAAa;IAKrB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAqB3B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAyC3B,OAAO,CAAC,wBAAwB;IAchC,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,4BAA4B;IAmBpC,OAAO,CAAC,mBAAmB;IAyB3B,OAAO,CAAC,uBAAuB;CA+BhC"}
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Documentation Assessor Module
3
+ * Evaluates documentation quality and completeness
4
+ */
5
+ import { BaseAssessor } from "./BaseAssessor.js";
6
+ export class DocumentationAssessor extends BaseAssessor {
7
+ async assess(context) {
8
+ this.log("Starting documentation assessment");
9
+ const readmeContent = context.readmeContent || "";
10
+ const metrics = this.analyzeDocumentation(readmeContent, context.tools);
11
+ const status = this.determineDocumentationStatus(metrics);
12
+ const explanation = this.generateExplanation(metrics);
13
+ const recommendations = this.generateRecommendations(metrics);
14
+ return {
15
+ metrics,
16
+ status,
17
+ explanation,
18
+ recommendations,
19
+ };
20
+ }
21
+ analyzeDocumentation(content, tools) {
22
+ const hasReadme = content.length > 0;
23
+ // Use new functional examples method that filters out configs/installs
24
+ const functionalExamples = this.extractFunctionalExamples(content);
25
+ // Keep old method for backward compatibility (extractedExamples field)
26
+ const allCodeExamples = this.extractCodeExamples(content);
27
+ const hasInstallInstructions = this.checkInstallInstructions(content);
28
+ const hasUsageGuide = this.checkUsageGuide(content);
29
+ const hasAPIReference = this.checkAPIReference(content);
30
+ // Check which tools are documented
31
+ const missingExamples = [];
32
+ let documentedToolsCount = 0;
33
+ if (tools && tools.length > 0) {
34
+ // Check each tool for documentation
35
+ for (const tool of tools) {
36
+ const toolName = tool.name;
37
+ const hasDescription = tool.description && tool.description.trim().length > 0;
38
+ // Check if tool is mentioned in headings (any level) or code examples
39
+ const headingRegex = new RegExp(`^#{1,6}\\s+${toolName}`, "mi");
40
+ const mentionRegex = new RegExp(`\\b${toolName}\\b`, "i");
41
+ const hasHeading = headingRegex.test(content);
42
+ const hasMention = mentionRegex.test(content);
43
+ // Count as documented if mentioned in README
44
+ if (hasHeading || hasMention) {
45
+ documentedToolsCount++;
46
+ }
47
+ // Tool is missing if it has no description AND not documented in README
48
+ if (!hasDescription && !hasHeading && !hasMention) {
49
+ missingExamples.push(toolName);
50
+ }
51
+ }
52
+ }
53
+ else {
54
+ // Fallback to generic example checking if no tools provided
55
+ if (functionalExamples.length < 1)
56
+ missingExamples.push("Basic usage example");
57
+ if (!functionalExamples.some((e) => e.description?.includes("error")) &&
58
+ !allCodeExamples.some((e) => e.description?.includes("error"))) {
59
+ missingExamples.push("Error handling example");
60
+ }
61
+ if (!functionalExamples.some((e) => e.description?.includes("config")) &&
62
+ !allCodeExamples.some((e) => e.description?.includes("config"))) {
63
+ missingExamples.push("Configuration example");
64
+ }
65
+ }
66
+ // Required examples: 3 minimum (Anthropic's standard)
67
+ const requiredExamples = 3;
68
+ // Count functional examples only (not configs/installs)
69
+ // For servers with tools, also count documented tools
70
+ const functionalExampleCount = functionalExamples.length +
71
+ (tools && tools.length > 0 ? documentedToolsCount : 0);
72
+ return {
73
+ hasReadme,
74
+ exampleCount: functionalExampleCount, // Use functional examples instead of all code blocks
75
+ requiredExamples,
76
+ missingExamples,
77
+ hasInstallInstructions,
78
+ hasUsageGuide,
79
+ hasAPIReference,
80
+ extractedExamples: allCodeExamples, // Keep for backward compatibility
81
+ installInstructions: hasInstallInstructions
82
+ ? this.extractSection(content, "install")
83
+ : undefined,
84
+ usageInstructions: hasUsageGuide
85
+ ? this.extractSection(content, "usage")
86
+ : undefined,
87
+ };
88
+ }
89
+ /**
90
+ * Extract functional example prompts from documentation.
91
+ * Only counts user-facing examples, not configuration or installation code.
92
+ */
93
+ extractFunctionalExamples(content) {
94
+ const functionalExamples = [];
95
+ // Pattern 1: Look for standalone lines with tool triggers (use context7, use library, with X)
96
+ const standalonePromptRegex = /^[ \t]*([A-Z][^\n]{10,300}?(?:use (?:context7|library|@?[\w-]+\/[\w-]+)|with \S+)[^\n]*?)[ \t]*$/gim;
97
+ let standaloneMatch;
98
+ while ((standaloneMatch = standalonePromptRegex.exec(content)) !== null) {
99
+ const prompt = standaloneMatch[1].trim();
100
+ // Filter out non-functional examples
101
+ if (!this.isNonFunctionalCodeBlock(prompt) &&
102
+ this.scoreFunctionalExample(prompt)) {
103
+ functionalExamples.push({
104
+ code: prompt,
105
+ language: "prompt",
106
+ description: "Functional example prompt",
107
+ lineNumber: this.getLineNumber(content, standaloneMatch.index),
108
+ });
109
+ }
110
+ }
111
+ // Pattern 2: Look for bulleted examples
112
+ const bulletedExampleRegex = /^[ \t]*[-*][ \t]+([A-Z][^\n]{10,300}?)[ \t]*$/gim;
113
+ let bulletMatch;
114
+ while ((bulletMatch = bulletedExampleRegex.exec(content)) !== null) {
115
+ const prompt = bulletMatch[1].trim();
116
+ // Must have tool trigger or action verb + technical term
117
+ if (!this.isNonFunctionalCodeBlock(prompt) &&
118
+ this.scoreFunctionalExample(prompt)) {
119
+ functionalExamples.push({
120
+ code: prompt,
121
+ language: "prompt",
122
+ description: "Bulleted example",
123
+ lineNumber: this.getLineNumber(content, bulletMatch.index),
124
+ });
125
+ }
126
+ }
127
+ // Pattern 3: Look for inline examples with labels
128
+ const inlineExampleRegex = /(?:Try|Example|Usage):\s*["`']([^"`']{10,300}?)["`']/gi;
129
+ let inlineMatch;
130
+ while ((inlineMatch = inlineExampleRegex.exec(content)) !== null) {
131
+ const prompt = inlineMatch[1].trim();
132
+ if (!this.isNonFunctionalCodeBlock(prompt) &&
133
+ this.scoreFunctionalExample(prompt)) {
134
+ functionalExamples.push({
135
+ code: prompt,
136
+ language: "prompt",
137
+ description: "Inline example",
138
+ lineNumber: this.getLineNumber(content, inlineMatch.index),
139
+ });
140
+ }
141
+ }
142
+ // Remove duplicates based on similar content
143
+ return this.deduplicateExamples(functionalExamples);
144
+ }
145
+ /**
146
+ * Check if a code block is non-functional (config, install, implementation).
147
+ */
148
+ isNonFunctionalCodeBlock(text) {
149
+ const excludePatterns = [
150
+ /^\s*{\s*["']mcpServers["']/i, // JSON config
151
+ /^\s*{\s*["']command["']/i, // MCP config
152
+ /^\s*(npx|npm|yarn|pnpm|docker|git)\s+/i, // Install commands
153
+ /^\s*FROM\s+\w+:/i, // Dockerfile
154
+ /^\s*import\s+.*\s+from/i, // Import statements
155
+ /^\s*\[.*\]\s*=\s*["']/i, // TOML/config assignments
156
+ /^\s*<\w+>/i, // HTML/XML tags
157
+ /^\s*(export|const|let|var|function)\s+/i, // Code declarations
158
+ /^\s*\/\//i, // Code comments
159
+ /^\s*#\s*\w+/i, // Shell comments or headers
160
+ ];
161
+ return excludePatterns.some((pattern) => pattern.test(text));
162
+ }
163
+ /**
164
+ * Score functional example quality (returns true if meets minimum threshold).
165
+ */
166
+ scoreFunctionalExample(text) {
167
+ let score = 0;
168
+ // Has clear action verb (create, configure, implement, show, etc.)
169
+ if (/\b(create|configure|implement|show|generate|build|write|add|get|set|use|run|start)\b/i.test(text)) {
170
+ score += 2;
171
+ }
172
+ // Includes tool trigger ("use context7", "with library", "use library")
173
+ if (/\b(?:use|with)\s+(?:context7|library|@?\w+\/\w+)\b/i.test(text)) {
174
+ score += 2;
175
+ }
176
+ // Is a complete sentence (starts with capital, ends with punctuation or is substantial)
177
+ if (/^[A-Z].{10,}/.test(text)) {
178
+ score += 1;
179
+ }
180
+ // Has technical context (mentions framework/library/technology)
181
+ if (/\b(Next\.js|React|Vue|Angular|PostgreSQL|MySQL|MongoDB|Redis|AWS|Cloudflare|API|HTTP|REST|GraphQL|TypeScript|JavaScript|Python|Docker|Kubernetes|Supabase|Firebase|Auth|JWT|OAuth)\b/i.test(text)) {
182
+ score += 1;
183
+ }
184
+ // Minimum score: 4 out of 6 points
185
+ return score >= 4;
186
+ }
187
+ /**
188
+ * Get line number for a position in content.
189
+ */
190
+ getLineNumber(content, position) {
191
+ const beforeMatch = content.substring(0, position);
192
+ return beforeMatch.split("\n").length;
193
+ }
194
+ /**
195
+ * Remove duplicate examples based on similarity.
196
+ */
197
+ deduplicateExamples(examples) {
198
+ const seen = new Set();
199
+ const unique = [];
200
+ for (const example of examples) {
201
+ // Create a normalized version for comparison
202
+ const normalized = example.code
203
+ .toLowerCase()
204
+ .replace(/[^\w\s]/g, "") // Remove punctuation
205
+ .replace(/\s+/g, " ") // Normalize multiple spaces to single space
206
+ .trim();
207
+ if (!seen.has(normalized)) {
208
+ seen.add(normalized);
209
+ unique.push(example);
210
+ }
211
+ }
212
+ return unique;
213
+ }
214
+ /**
215
+ * @deprecated Use extractFunctionalExamples() instead.
216
+ * This method counts ALL code blocks including configs and install commands.
217
+ */
218
+ extractCodeExamples(content) {
219
+ const examples = [];
220
+ const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
221
+ let match;
222
+ let lineNumber = 0;
223
+ const lines = content.split("\n");
224
+ while ((match = codeBlockRegex.exec(content)) !== null) {
225
+ const language = match[1] || "plaintext";
226
+ const code = match[2].trim();
227
+ // Find line number
228
+ const position = match.index;
229
+ const beforeMatch = content.substring(0, position);
230
+ lineNumber = beforeMatch.split("\n").length;
231
+ // Try to find description from preceding lines
232
+ let description = "";
233
+ const lineIndex = lines.findIndex((_, i) => lines.slice(0, i + 1).join("\n").length >= position);
234
+ if (lineIndex > 0) {
235
+ const prevLine = lines[lineIndex - 1].trim();
236
+ if (prevLine && !prevLine.startsWith("#")) {
237
+ description = prevLine;
238
+ }
239
+ }
240
+ examples.push({
241
+ code,
242
+ language,
243
+ description,
244
+ lineNumber,
245
+ });
246
+ }
247
+ return examples;
248
+ }
249
+ checkInstallInstructions(content) {
250
+ const installKeywords = [
251
+ "install",
252
+ "npm install",
253
+ "yarn add",
254
+ "pip install",
255
+ "setup",
256
+ "getting started",
257
+ ];
258
+ const contentLower = content.toLowerCase();
259
+ return installKeywords.some((keyword) => contentLower.includes(keyword));
260
+ }
261
+ checkUsageGuide(content) {
262
+ const usageKeywords = [
263
+ "usage",
264
+ "how to use",
265
+ "example",
266
+ "quick start",
267
+ "tutorial",
268
+ ];
269
+ const contentLower = content.toLowerCase();
270
+ return usageKeywords.some((keyword) => contentLower.includes(keyword));
271
+ }
272
+ checkAPIReference(content) {
273
+ const apiKeywords = [
274
+ "api",
275
+ "reference",
276
+ "methods",
277
+ "functions",
278
+ "parameters",
279
+ "returns",
280
+ "endpoints",
281
+ ];
282
+ const contentLower = content.toLowerCase();
283
+ return apiKeywords.some((keyword) => contentLower.includes(keyword));
284
+ }
285
+ extractSection(content, section) {
286
+ const sectionRegex = new RegExp(`#+\\s*${section}[\\s\\S]*?(?=\\n#|$)`, "gi");
287
+ const match = content.match(sectionRegex);
288
+ return match ? match[0].trim() : "";
289
+ }
290
+ determineDocumentationStatus(metrics) {
291
+ let score = 0;
292
+ const maxScore = 5;
293
+ if (metrics.hasReadme)
294
+ score++;
295
+ if (metrics.hasInstallInstructions)
296
+ score++;
297
+ if (metrics.hasUsageGuide)
298
+ score++;
299
+ if (metrics.hasAPIReference)
300
+ score++;
301
+ if (metrics.exampleCount >= metrics.requiredExamples)
302
+ score++;
303
+ const percentage = (score / maxScore) * 100;
304
+ if (percentage >= 80)
305
+ return "PASS";
306
+ if (percentage >= 50)
307
+ return "NEED_MORE_INFO";
308
+ return "FAIL";
309
+ }
310
+ generateExplanation(metrics) {
311
+ const parts = [];
312
+ if (!metrics.hasReadme) {
313
+ parts.push("No README documentation found.");
314
+ }
315
+ else {
316
+ parts.push(`README contains ${metrics.exampleCount} code examples.`);
317
+ const features = [];
318
+ if (metrics.hasInstallInstructions)
319
+ features.push("installation");
320
+ if (metrics.hasUsageGuide)
321
+ features.push("usage guide");
322
+ if (metrics.hasAPIReference)
323
+ features.push("API reference");
324
+ if (features.length > 0) {
325
+ parts.push(`Documentation includes: ${features.join(", ")}.`);
326
+ }
327
+ if (metrics.missingExamples.length > 0) {
328
+ parts.push(`Missing examples: ${metrics.missingExamples.join(", ")}.`);
329
+ }
330
+ }
331
+ return parts.join(" ");
332
+ }
333
+ generateRecommendations(metrics) {
334
+ const recommendations = [];
335
+ if (!metrics.hasReadme) {
336
+ recommendations.push("Create a comprehensive README.md file");
337
+ }
338
+ if (!metrics.hasInstallInstructions) {
339
+ recommendations.push("Add clear installation instructions");
340
+ }
341
+ if (!metrics.hasUsageGuide) {
342
+ recommendations.push("Include a usage guide with examples");
343
+ }
344
+ if (!metrics.hasAPIReference) {
345
+ recommendations.push("Document all available tools and parameters");
346
+ }
347
+ if (metrics.exampleCount < metrics.requiredExamples) {
348
+ recommendations.push(`Add ${metrics.requiredExamples - metrics.exampleCount} more code examples`);
349
+ }
350
+ metrics.missingExamples.forEach((missing) => {
351
+ recommendations.push(`Add ${missing}`);
352
+ });
353
+ return recommendations;
354
+ }
355
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Error Handling Assessor Module
3
+ * Tests error handling and input validation
4
+ */
5
+ import { ErrorHandlingAssessment } from "../../../lib/assessmentTypes.js";
6
+ import { BaseAssessor } from "./BaseAssessor.js";
7
+ import { AssessmentContext } from "../AssessmentOrchestrator.js";
8
+ export declare class ErrorHandlingAssessor extends BaseAssessor {
9
+ assess(context: AssessmentContext): Promise<ErrorHandlingAssessment>;
10
+ private selectToolsForTesting;
11
+ private testToolErrorHandling;
12
+ private testMissingParameters;
13
+ private testWrongTypes;
14
+ private testInvalidValues;
15
+ private testExcessiveInput;
16
+ private getToolSchema;
17
+ private generateWrongTypeParams;
18
+ private generateInvalidValueParams;
19
+ private generateParamsWithValue;
20
+ private calculateMetrics;
21
+ private determineErrorHandlingStatus;
22
+ private generateExplanation;
23
+ private generateRecommendations;
24
+ }
25
+ //# sourceMappingURL=ErrorHandlingAssessor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorHandlingAssessor.d.ts","sourceRoot":"","sources":["../../../../src/services/assessment/modules/ErrorHandlingAssessor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,uBAAuB,EAIxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,qBAAa,qBAAsB,SAAQ,YAAY;IAC/C,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAyC1E,OAAO,CAAC,qBAAqB;YA8Cf,qBAAqB;YAuBrB,qBAAqB;YAmGrB,cAAc;YAmFd,iBAAiB;YA8DjB,kBAAkB;IA6DhC,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,uBAAuB;IAgC/B,OAAO,CAAC,0BAA0B;IAgClC,OAAO,CAAC,uBAAuB;IA4B/B,OAAO,CAAC,gBAAgB;IAoGxB,OAAO,CAAC,4BAA4B;IAapC,OAAO,CAAC,mBAAmB;IAuE3B,OAAO,CAAC,uBAAuB;CA4ChC"}