@bryan-thompson/inspector-assessment-cli 1.26.6 → 1.26.7

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.
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Source File Loading
3
+ *
4
+ * Handles recursive source file discovery with gitignore support.
5
+ *
6
+ * @module cli/lib/assessment-runner/source-loader
7
+ */
8
+ import * as fs from "fs";
9
+ import * as path from "path";
10
+ /** Maximum file size (in characters) to include in source code analysis */
11
+ const MAX_SOURCE_FILE_SIZE = 100_000;
12
+ /**
13
+ * Load optional files from source code path
14
+ *
15
+ * @param sourcePath - Path to source code directory
16
+ * @returns Object containing loaded source files
17
+ */
18
+ export function loadSourceFiles(sourcePath) {
19
+ const result = {};
20
+ // Search for README in source directory and parent directories (up to 3 levels)
21
+ // This handles cases where --source points to a subdirectory but README is at repo root
22
+ const readmePaths = ["README.md", "readme.md", "Readme.md"];
23
+ let readmeFound = false;
24
+ // First try the source directory itself
25
+ for (const readmePath of readmePaths) {
26
+ const fullPath = path.join(sourcePath, readmePath);
27
+ if (fs.existsSync(fullPath)) {
28
+ result.readmeContent = fs.readFileSync(fullPath, "utf-8");
29
+ readmeFound = true;
30
+ break;
31
+ }
32
+ }
33
+ // If not found, search parent directories (up to 3 levels)
34
+ if (!readmeFound) {
35
+ let currentDir = sourcePath;
36
+ for (let i = 0; i < 3; i++) {
37
+ const parentDir = path.dirname(currentDir);
38
+ if (parentDir === currentDir)
39
+ break; // Reached filesystem root
40
+ for (const readmePath of readmePaths) {
41
+ const fullPath = path.join(parentDir, readmePath);
42
+ if (fs.existsSync(fullPath)) {
43
+ result.readmeContent = fs.readFileSync(fullPath, "utf-8");
44
+ readmeFound = true;
45
+ break;
46
+ }
47
+ }
48
+ if (readmeFound)
49
+ break;
50
+ currentDir = parentDir;
51
+ }
52
+ }
53
+ const packagePath = path.join(sourcePath, "package.json");
54
+ if (fs.existsSync(packagePath)) {
55
+ result.packageJson = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
56
+ }
57
+ const manifestPath = path.join(sourcePath, "manifest.json");
58
+ if (fs.existsSync(manifestPath)) {
59
+ result.manifestRaw = fs.readFileSync(manifestPath, "utf-8");
60
+ try {
61
+ result.manifestJson = JSON.parse(result.manifestRaw);
62
+ }
63
+ catch {
64
+ console.warn("[Assessment] Failed to parse manifest.json");
65
+ }
66
+ }
67
+ result.sourceCodeFiles = new Map();
68
+ // Include config files for portability analysis
69
+ const sourceExtensions = [
70
+ ".ts",
71
+ ".js",
72
+ ".py",
73
+ ".go",
74
+ ".rs",
75
+ ".json",
76
+ ".sh",
77
+ ".yaml",
78
+ ".yml",
79
+ ];
80
+ // Parse .gitignore patterns
81
+ const gitignorePatterns = [];
82
+ const gitignorePath = path.join(sourcePath, ".gitignore");
83
+ if (fs.existsSync(gitignorePath)) {
84
+ const gitignoreContent = fs.readFileSync(gitignorePath, "utf-8");
85
+ for (const line of gitignoreContent.split("\n")) {
86
+ const trimmed = line.trim();
87
+ if (!trimmed || trimmed.startsWith("#"))
88
+ continue;
89
+ // Convert gitignore pattern to regex
90
+ const pattern = trimmed
91
+ .replace(/\./g, "\\.")
92
+ .replace(/\*\*/g, ".*")
93
+ .replace(/\*/g, "[^/]*")
94
+ .replace(/\?/g, ".");
95
+ try {
96
+ gitignorePatterns.push(new RegExp(pattern));
97
+ }
98
+ catch {
99
+ // Skip invalid patterns
100
+ }
101
+ }
102
+ }
103
+ const isGitignored = (relativePath) => {
104
+ return gitignorePatterns.some((pattern) => pattern.test(relativePath));
105
+ };
106
+ const loadSourceDir = (dir, prefix = "") => {
107
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
108
+ for (const entry of entries) {
109
+ if (entry.name.startsWith(".") || entry.name === "node_modules")
110
+ continue;
111
+ const fullPath = path.join(dir, entry.name);
112
+ const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
113
+ // Skip gitignored files
114
+ if (isGitignored(relativePath))
115
+ continue;
116
+ if (entry.isDirectory()) {
117
+ loadSourceDir(fullPath, relativePath);
118
+ }
119
+ else if (sourceExtensions.some((ext) => entry.name.endsWith(ext))) {
120
+ try {
121
+ const content = fs.readFileSync(fullPath, "utf-8");
122
+ if (content.length < MAX_SOURCE_FILE_SIZE) {
123
+ result.sourceCodeFiles.set(relativePath, content);
124
+ }
125
+ }
126
+ catch {
127
+ // Skip unreadable files
128
+ }
129
+ }
130
+ }
131
+ };
132
+ try {
133
+ loadSourceDir(sourcePath);
134
+ }
135
+ catch (e) {
136
+ console.warn("[Assessment] Could not load source files:", e);
137
+ }
138
+ return result;
139
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Tool Call Wrapper
3
+ *
4
+ * Creates a wrapper around MCP client.callTool() for assessment context.
5
+ *
6
+ * @module cli/lib/assessment-runner/tool-wrapper
7
+ */
8
+ /**
9
+ * Create callTool wrapper for assessment context
10
+ *
11
+ * @param client - Connected MCP client
12
+ * @returns Wrapped callTool function
13
+ */
14
+ export function createCallToolWrapper(client) {
15
+ return async (name, params) => {
16
+ try {
17
+ const response = await client.callTool({
18
+ name,
19
+ arguments: params,
20
+ });
21
+ return {
22
+ content: response.content,
23
+ isError: response.isError || false,
24
+ structuredContent: response
25
+ .structuredContent,
26
+ };
27
+ }
28
+ catch (error) {
29
+ return {
30
+ content: [
31
+ {
32
+ type: "text",
33
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
34
+ },
35
+ ],
36
+ isError: true,
37
+ };
38
+ }
39
+ };
40
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Assessment Runner Types
3
+ *
4
+ * Shared type definitions for the assessment-runner module.
5
+ *
6
+ * @module cli/lib/assessment-runner/types
7
+ */
8
+ export {};