snow-flow 8.37.27 ā 8.38.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.
- package/.snow-code/agent/deployment-specialist.md +346 -0
- package/.snow-code/agent/orchestrator.md +286 -0
- package/.snow-code/agent/risk-assessor.md +454 -0
- package/.snow-code/agent/solution-architect.md +582 -0
- package/.snow-code/agent/validator.md +503 -0
- package/.snow-code/opencode.json +49 -0
- package/README.md +16 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +93 -256
- package/dist/cli.js.map +1 -1
- package/dist/utils/sync-mcp-configs.d.ts +7 -5
- package/dist/utils/sync-mcp-configs.d.ts.map +1 -1
- package/dist/utils/sync-mcp-configs.js +19 -74
- package/dist/utils/sync-mcp-configs.js.map +1 -1
- package/package.json +2 -3
- package/scripts/check-binary-updates.js +0 -169
- package/scripts/check-npm-version.js +0 -92
- package/scripts/classify-all-tools.ts +0 -446
- package/scripts/classify-edge-cases.ts +0 -275
- package/scripts/classify-operations-tools.sh +0 -96
- package/scripts/cleanup-mcp-servers.js +0 -115
- package/scripts/diagnose-mcp.js +0 -299
- package/scripts/generate-mcp-config.js +0 -45
- package/scripts/mcp-server-manager.sh +0 -320
- package/scripts/postinstall.js +0 -75
- package/scripts/reset-mcp-servers.js +0 -266
- package/scripts/safe-mcp-cleanup.js +0 -151
- package/scripts/setup-mcp.js +0 -106
- package/scripts/start-mcp-proper.js +0 -76
- package/scripts/start-snowcode.sh +0 -123
- package/scripts/start-sysprops-mcp.js +0 -43
- package/scripts/sync-snow-code-version.js +0 -74
- package/scripts/test-auth-flow.js +0 -172
- package/scripts/test-auth-location-fix.js +0 -84
- package/scripts/test-mcp-manual.js +0 -140
- package/scripts/test-todowrite-timeout.js +0 -108
- package/scripts/update-dependencies.js +0 -90
- package/scripts/update-mcp-config.js +0 -96
- package/scripts/update-snow-code.js +0 -146
- package/scripts/verify-snowcode-fork.sh +0 -141
- package/templates/snow-code-package.json +0 -3
|
@@ -1,446 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Bulk Tool Classification Script
|
|
5
|
-
*
|
|
6
|
-
* Automatically classifies ALL 410+ tools in the unified MCP server
|
|
7
|
-
* as READ or WRITE based on intelligent name pattern analysis.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* npm install -g tsx # If not installed
|
|
11
|
-
* tsx scripts/classify-all-tools.ts
|
|
12
|
-
* tsx scripts/classify-all-tools.ts --dry-run # Preview without changes
|
|
13
|
-
* tsx scripts/classify-all-tools.ts --review-only # Only show tools needing manual review
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import * as fs from 'fs';
|
|
17
|
-
import * as path from 'path';
|
|
18
|
-
import { glob } from 'glob';
|
|
19
|
-
|
|
20
|
-
// Configuration
|
|
21
|
-
const TOOLS_BASE_DIR = path.join(__dirname, '../src/mcp/servicenow-mcp-unified/tools');
|
|
22
|
-
const DRY_RUN = process.argv.includes('--dry-run');
|
|
23
|
-
const REVIEW_ONLY = process.argv.includes('--review-only');
|
|
24
|
-
|
|
25
|
-
// Classification patterns
|
|
26
|
-
const READ_PATTERNS = [
|
|
27
|
-
/^snow_query_/,
|
|
28
|
-
/^snow_get_/,
|
|
29
|
-
/^snow_search_/,
|
|
30
|
-
/^snow_discover_/,
|
|
31
|
-
/^snow_find_/,
|
|
32
|
-
/^snow_list_/,
|
|
33
|
-
/^snow_view_/,
|
|
34
|
-
/^snow_show_/,
|
|
35
|
-
/^snow_analyze_/,
|
|
36
|
-
/^snow_check_/,
|
|
37
|
-
/^snow_test_/,
|
|
38
|
-
/^snow_validate_/,
|
|
39
|
-
/^snow_export_(?!.*import)/, // export is READ unless it's import_export
|
|
40
|
-
/^snow_fetch_/,
|
|
41
|
-
/^snow_lookup_/,
|
|
42
|
-
/^snow_inspect_/,
|
|
43
|
-
/^snow_monitor_/,
|
|
44
|
-
/_lookup$/,
|
|
45
|
-
/_search$/,
|
|
46
|
-
/_query$/,
|
|
47
|
-
/_details$/,
|
|
48
|
-
/_status$/,
|
|
49
|
-
/_metrics$/,
|
|
50
|
-
/_report$/,
|
|
51
|
-
/_analysis$/,
|
|
52
|
-
];
|
|
53
|
-
|
|
54
|
-
const WRITE_PATTERNS = [
|
|
55
|
-
/^snow_create_/,
|
|
56
|
-
/^snow_update_/,
|
|
57
|
-
/^snow_delete_/,
|
|
58
|
-
/^snow_remove_/,
|
|
59
|
-
/^snow_execute_/,
|
|
60
|
-
/^snow_deploy_/,
|
|
61
|
-
/^snow_manage_/,
|
|
62
|
-
/^snow_add_/,
|
|
63
|
-
/^snow_set_/,
|
|
64
|
-
/^snow_configure_/,
|
|
65
|
-
/^snow_trigger_/,
|
|
66
|
-
/^snow_schedule_/,
|
|
67
|
-
/^snow_enable_/,
|
|
68
|
-
/^snow_disable_/,
|
|
69
|
-
/^snow_activate_/,
|
|
70
|
-
/^snow_deactivate_/,
|
|
71
|
-
/^snow_install_/,
|
|
72
|
-
/^snow_uninstall_/,
|
|
73
|
-
/^snow_import_/,
|
|
74
|
-
/^snow_sync_/,
|
|
75
|
-
/^snow_push_/,
|
|
76
|
-
/^snow_pull_/, // pull modifies local state
|
|
77
|
-
/^snow_apply_/,
|
|
78
|
-
/^snow_assign_/,
|
|
79
|
-
/^snow_approve_/,
|
|
80
|
-
/^snow_reject_/,
|
|
81
|
-
/^snow_close_/,
|
|
82
|
-
/^snow_resolve_/,
|
|
83
|
-
/^snow_reopen_/,
|
|
84
|
-
/^snow_cancel_/,
|
|
85
|
-
/^snow_rollback_/,
|
|
86
|
-
/^snow_clone_/,
|
|
87
|
-
/^snow_backup_/,
|
|
88
|
-
/^snow_restore_/,
|
|
89
|
-
/^snow_migrate_/,
|
|
90
|
-
/^snow_transfer_/,
|
|
91
|
-
/^snow_retire_/,
|
|
92
|
-
/^snow_attach_/,
|
|
93
|
-
/^snow_upload_/,
|
|
94
|
-
/^snow_order_/,
|
|
95
|
-
/^snow_request_/,
|
|
96
|
-
/^snow_confirm_/,
|
|
97
|
-
/^snow_cleanup_/,
|
|
98
|
-
/_manage$/,
|
|
99
|
-
/_manager$/,
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
// Edge cases that need manual review
|
|
103
|
-
const AMBIGUOUS_PATTERNS = [
|
|
104
|
-
/^snow_record_manage$/, // Can be both READ and WRITE depending on action
|
|
105
|
-
/^snow_property_manager$/, // Can be both
|
|
106
|
-
/^snow_catalog_item_manager$/, // Can be both
|
|
107
|
-
];
|
|
108
|
-
|
|
109
|
-
interface ToolClassification {
|
|
110
|
-
filePath: string;
|
|
111
|
-
toolName: string;
|
|
112
|
-
currentPermission: string | null;
|
|
113
|
-
suggestedPermission: 'read' | 'write' | 'NEEDS_REVIEW';
|
|
114
|
-
suggestedRoles: string[];
|
|
115
|
-
confidence: 'high' | 'medium' | 'low';
|
|
116
|
-
reason: string;
|
|
117
|
-
alreadyClassified: boolean;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
interface ClassificationStats {
|
|
121
|
-
total: number;
|
|
122
|
-
alreadyClassified: number;
|
|
123
|
-
read: number;
|
|
124
|
-
write: number;
|
|
125
|
-
needsReview: number;
|
|
126
|
-
updated: number;
|
|
127
|
-
errors: number;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Classify a tool based on its name
|
|
132
|
-
*/
|
|
133
|
-
function classifyTool(toolName: string): ToolClassification['suggestedPermission'] {
|
|
134
|
-
// Check ambiguous patterns first
|
|
135
|
-
for (const pattern of AMBIGUOUS_PATTERNS) {
|
|
136
|
-
if (pattern.test(toolName)) {
|
|
137
|
-
return 'NEEDS_REVIEW';
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Check READ patterns
|
|
142
|
-
let readMatches = 0;
|
|
143
|
-
for (const pattern of READ_PATTERNS) {
|
|
144
|
-
if (pattern.test(toolName)) {
|
|
145
|
-
readMatches++;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Check WRITE patterns
|
|
150
|
-
let writeMatches = 0;
|
|
151
|
-
for (const pattern of WRITE_PATTERNS) {
|
|
152
|
-
if (pattern.test(toolName)) {
|
|
153
|
-
writeMatches++;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// If matches both, prefer WRITE (most restrictive)
|
|
158
|
-
if (writeMatches > 0) return 'write';
|
|
159
|
-
if (readMatches > 0) return 'read';
|
|
160
|
-
|
|
161
|
-
// No pattern matches - needs manual review
|
|
162
|
-
return 'NEEDS_REVIEW';
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Get classification reason
|
|
167
|
-
*/
|
|
168
|
-
function getClassificationReason(toolName: string, permission: ToolClassification['suggestedPermission']): string {
|
|
169
|
-
if (permission === 'NEEDS_REVIEW') {
|
|
170
|
-
return 'No clear pattern match - manual review required';
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (permission === 'read') {
|
|
174
|
-
if (toolName.includes('query')) return 'Query operation - only reads data';
|
|
175
|
-
if (toolName.includes('get')) return 'Get operation - retrieves data';
|
|
176
|
-
if (toolName.includes('search')) return 'Search operation - reads data';
|
|
177
|
-
if (toolName.includes('discover')) return 'Discovery operation - reads metadata';
|
|
178
|
-
if (toolName.includes('analyze')) return 'Analysis operation - reads data';
|
|
179
|
-
if (toolName.includes('export')) return 'Export operation - reads data';
|
|
180
|
-
return 'Read-only operation based on name pattern';
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (permission === 'write') {
|
|
184
|
-
if (toolName.includes('create')) return 'Create operation - modifies data';
|
|
185
|
-
if (toolName.includes('update')) return 'Update operation - modifies data';
|
|
186
|
-
if (toolName.includes('delete')) return 'Delete operation - modifies data';
|
|
187
|
-
if (toolName.includes('execute')) return 'Execution operation - can have side effects';
|
|
188
|
-
if (toolName.includes('deploy')) return 'Deployment operation - modifies state';
|
|
189
|
-
if (toolName.includes('manage')) return 'Management operation - modifies data';
|
|
190
|
-
return 'Write operation based on name pattern';
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return 'Unknown';
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Get confidence level
|
|
198
|
-
*/
|
|
199
|
-
function getConfidence(toolName: string, permission: ToolClassification['suggestedPermission']): 'high' | 'medium' | 'low' {
|
|
200
|
-
if (permission === 'NEEDS_REVIEW') return 'low';
|
|
201
|
-
|
|
202
|
-
// High confidence patterns
|
|
203
|
-
const highConfidencePatterns = [
|
|
204
|
-
/^snow_query_/,
|
|
205
|
-
/^snow_create_/,
|
|
206
|
-
/^snow_update_/,
|
|
207
|
-
/^snow_delete_/,
|
|
208
|
-
/^snow_execute_/,
|
|
209
|
-
];
|
|
210
|
-
|
|
211
|
-
for (const pattern of highConfidencePatterns) {
|
|
212
|
-
if (pattern.test(toolName)) return 'high';
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Medium confidence for other clear patterns
|
|
216
|
-
if (permission === 'read' && (toolName.includes('get') || toolName.includes('search'))) return 'high';
|
|
217
|
-
if (permission === 'write' && (toolName.includes('deploy') || toolName.includes('configure'))) return 'high';
|
|
218
|
-
|
|
219
|
-
return 'medium';
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Check if tool is already classified
|
|
224
|
-
*/
|
|
225
|
-
function isAlreadyClassified(fileContent: string): boolean {
|
|
226
|
-
return fileContent.includes('permission:') && fileContent.includes('allowedRoles:');
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Get current permission from file
|
|
231
|
-
*/
|
|
232
|
-
function getCurrentPermission(fileContent: string): string | null {
|
|
233
|
-
const match = fileContent.match(/permission:\s*['"](\w+)['"]/);
|
|
234
|
-
return match ? match[1] : null;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Add permission fields to tool file
|
|
239
|
-
*/
|
|
240
|
-
function addPermissionFields(
|
|
241
|
-
filePath: string,
|
|
242
|
-
permission: 'read' | 'write',
|
|
243
|
-
reason: string
|
|
244
|
-
): boolean {
|
|
245
|
-
try {
|
|
246
|
-
let content = fs.readFileSync(filePath, 'utf-8');
|
|
247
|
-
|
|
248
|
-
// Check if already has permission
|
|
249
|
-
if (isAlreadyClassified(content)) {
|
|
250
|
-
return false; // Already classified
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Find the insertion point (after frequency field)
|
|
254
|
-
const frequencyMatch = content.match(/frequency:\s*['"][\w-]+['"],?/);
|
|
255
|
-
|
|
256
|
-
if (!frequencyMatch) {
|
|
257
|
-
console.error(` ā ļø Could not find frequency field in ${path.basename(filePath)}`);
|
|
258
|
-
return false;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const roles = permission === 'read'
|
|
262
|
-
? "['developer', 'stakeholder', 'admin']"
|
|
263
|
-
: "['developer', 'admin']";
|
|
264
|
-
|
|
265
|
-
const permissionBlock = `\n\n // š Permission enforcement (Q1 2025)\n` +
|
|
266
|
-
` // Classification: ${permission.toUpperCase()} - ${reason}\n` +
|
|
267
|
-
` permission: '${permission}',\n` +
|
|
268
|
-
` allowedRoles: ${roles},`;
|
|
269
|
-
|
|
270
|
-
// Insert after frequency field
|
|
271
|
-
const insertIndex = frequencyMatch.index! + frequencyMatch[0].length;
|
|
272
|
-
content = content.slice(0, insertIndex) + permissionBlock + content.slice(insertIndex);
|
|
273
|
-
|
|
274
|
-
if (!DRY_RUN) {
|
|
275
|
-
fs.writeFileSync(filePath, content, 'utf-8');
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return true;
|
|
279
|
-
} catch (error) {
|
|
280
|
-
console.error(` ā Error updating ${path.basename(filePath)}:`, error);
|
|
281
|
-
return false;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Scan and classify all tools
|
|
287
|
-
*/
|
|
288
|
-
async function classifyAllTools(): Promise<void> {
|
|
289
|
-
console.log('š Scanning for tools in unified MCP server...\n');
|
|
290
|
-
|
|
291
|
-
// Find all tool files (excluding index.ts)
|
|
292
|
-
const toolFiles = await glob(`${TOOLS_BASE_DIR}/**/*.ts`, {
|
|
293
|
-
ignore: ['**/index.ts', '**/*.test.ts', '**/*.spec.ts']
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
console.log(`š¦ Found ${toolFiles.length} tool files\n`);
|
|
297
|
-
|
|
298
|
-
const classifications: ToolClassification[] = [];
|
|
299
|
-
const stats: ClassificationStats = {
|
|
300
|
-
total: toolFiles.length,
|
|
301
|
-
alreadyClassified: 0,
|
|
302
|
-
read: 0,
|
|
303
|
-
write: 0,
|
|
304
|
-
needsReview: 0,
|
|
305
|
-
updated: 0,
|
|
306
|
-
errors: 0
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
// Classify each tool
|
|
310
|
-
for (const filePath of toolFiles) {
|
|
311
|
-
const toolName = path.basename(filePath, '.ts');
|
|
312
|
-
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
313
|
-
|
|
314
|
-
const alreadyClassified = isAlreadyClassified(fileContent);
|
|
315
|
-
const currentPermission = getCurrentPermission(fileContent);
|
|
316
|
-
const suggestedPermission = classifyTool(toolName);
|
|
317
|
-
const confidence = getConfidence(toolName, suggestedPermission);
|
|
318
|
-
const reason = getClassificationReason(toolName, suggestedPermission);
|
|
319
|
-
|
|
320
|
-
const roles = suggestedPermission === 'read'
|
|
321
|
-
? ['developer', 'stakeholder', 'admin']
|
|
322
|
-
: suggestedPermission === 'write'
|
|
323
|
-
? ['developer', 'admin']
|
|
324
|
-
: [];
|
|
325
|
-
|
|
326
|
-
classifications.push({
|
|
327
|
-
filePath,
|
|
328
|
-
toolName,
|
|
329
|
-
currentPermission,
|
|
330
|
-
suggestedPermission,
|
|
331
|
-
suggestedRoles: roles,
|
|
332
|
-
confidence,
|
|
333
|
-
reason,
|
|
334
|
-
alreadyClassified
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
if (alreadyClassified) {
|
|
338
|
-
stats.alreadyClassified++;
|
|
339
|
-
} else {
|
|
340
|
-
if (suggestedPermission === 'read') stats.read++;
|
|
341
|
-
else if (suggestedPermission === 'write') stats.write++;
|
|
342
|
-
else stats.needsReview++;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// Sort by category
|
|
347
|
-
const needsReview = classifications.filter(c => !c.alreadyClassified && c.suggestedPermission === 'NEEDS_REVIEW');
|
|
348
|
-
const readTools = classifications.filter(c => !c.alreadyClassified && c.suggestedPermission === 'read');
|
|
349
|
-
const writeTools = classifications.filter(c => !c.alreadyClassified && c.suggestedPermission === 'write');
|
|
350
|
-
const alreadyDone = classifications.filter(c => c.alreadyClassified);
|
|
351
|
-
|
|
352
|
-
// Display results
|
|
353
|
-
if (REVIEW_ONLY) {
|
|
354
|
-
console.log('š Tools Needing Manual Review:\n');
|
|
355
|
-
for (const tool of needsReview) {
|
|
356
|
-
console.log(` ā ļø ${tool.toolName}`);
|
|
357
|
-
console.log(` Reason: ${tool.reason}`);
|
|
358
|
-
console.log(` File: ${path.relative(TOOLS_BASE_DIR, tool.filePath)}\n`);
|
|
359
|
-
}
|
|
360
|
-
console.log(`\nš ${needsReview.length} tools need manual review\n`);
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
console.log('š Classification Summary:\n');
|
|
365
|
-
console.log(` ā
Already classified: ${stats.alreadyClassified}`);
|
|
366
|
-
console.log(` š READ tools to classify: ${stats.read}`);
|
|
367
|
-
console.log(` āļø WRITE tools to classify: ${stats.write}`);
|
|
368
|
-
console.log(` ā ļø Need manual review: ${stats.needsReview}`);
|
|
369
|
-
console.log(`\n š¦ Total: ${stats.total} tools\n`);
|
|
370
|
-
|
|
371
|
-
if (needsReview.length > 0) {
|
|
372
|
-
console.log('ā ļø Tools Needing Manual Review:\n');
|
|
373
|
-
for (const tool of needsReview.slice(0, 10)) {
|
|
374
|
-
console.log(` ⢠${tool.toolName} - ${tool.reason}`);
|
|
375
|
-
}
|
|
376
|
-
if (needsReview.length > 10) {
|
|
377
|
-
console.log(` ... and ${needsReview.length - 10} more\n`);
|
|
378
|
-
}
|
|
379
|
-
console.log('');
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
if (DRY_RUN) {
|
|
383
|
-
console.log('š DRY RUN MODE - No files will be modified\n');
|
|
384
|
-
console.log('š Sample READ tools that would be classified:\n');
|
|
385
|
-
for (const tool of readTools.slice(0, 5)) {
|
|
386
|
-
console.log(` ā
${tool.toolName} (${tool.confidence} confidence)`);
|
|
387
|
-
console.log(` ${tool.reason}\n`);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
console.log('āļø Sample WRITE tools that would be classified:\n');
|
|
391
|
-
for (const tool of writeTools.slice(0, 5)) {
|
|
392
|
-
console.log(` ā
${tool.toolName} (${tool.confidence} confidence)`);
|
|
393
|
-
console.log(` ${tool.reason}\n`);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
console.log('š” Run without --dry-run to apply changes\n');
|
|
397
|
-
return;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Apply classifications
|
|
401
|
-
console.log('š Applying classifications...\n');
|
|
402
|
-
|
|
403
|
-
for (const tool of [...readTools, ...writeTools]) {
|
|
404
|
-
if (tool.suggestedPermission === 'NEEDS_REVIEW') continue;
|
|
405
|
-
|
|
406
|
-
const updated = addPermissionFields(
|
|
407
|
-
tool.filePath,
|
|
408
|
-
tool.suggestedPermission,
|
|
409
|
-
tool.reason
|
|
410
|
-
);
|
|
411
|
-
|
|
412
|
-
if (updated) {
|
|
413
|
-
stats.updated++;
|
|
414
|
-
console.log(` ā
${tool.toolName} ā ${tool.suggestedPermission.toUpperCase()}`);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
console.log(`\nā
Successfully classified ${stats.updated} tools!\n`);
|
|
419
|
-
|
|
420
|
-
if (needsReview.length > 0) {
|
|
421
|
-
console.log(`ā ļø ${needsReview.length} tools need manual review - see above\n`);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Generate summary report
|
|
425
|
-
const reportPath = path.join(__dirname, '../.snow-flow/classification-report.json');
|
|
426
|
-
const report = {
|
|
427
|
-
timestamp: new Date().toISOString(),
|
|
428
|
-
stats,
|
|
429
|
-
needsReview: needsReview.map(t => ({
|
|
430
|
-
name: t.toolName,
|
|
431
|
-
file: path.relative(TOOLS_BASE_DIR, t.filePath),
|
|
432
|
-
reason: t.reason
|
|
433
|
-
}))
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
fs.mkdirSync(path.dirname(reportPath), { recursive: true });
|
|
437
|
-
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
438
|
-
|
|
439
|
-
console.log(`š Full report saved to: ${reportPath}\n`);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// Run the script
|
|
443
|
-
classifyAllTools().catch(error => {
|
|
444
|
-
console.error('ā Fatal error:', error);
|
|
445
|
-
process.exit(1);
|
|
446
|
-
});
|