@63klabs/cache-data 1.3.4 → 1.3.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.
@@ -0,0 +1,406 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Documentation Files Review Script
5
+ *
6
+ * Reviews all documentation files in docs/ directory to:
7
+ * - List all files
8
+ * - Check for broken links (internal file references)
9
+ * - Identify outdated content or examples
10
+ * - Note missing documentation for features
11
+ *
12
+ * Requirements: 3.1, 3.4, 4.1
13
+ */
14
+
15
+ import fs from 'fs';
16
+ import path from 'path';
17
+ import { fileURLToPath } from 'url';
18
+
19
+ const __filename = fileURLToPath(import.meta.url);
20
+ const __dirname = path.dirname(__filename);
21
+
22
+ // Configuration
23
+ const DOCS_DIR = path.join(__dirname, '..', 'docs');
24
+ const ROOT_DIR = path.join(__dirname, '..');
25
+ const OUTPUT_FILE = path.join(__dirname, '..', '.kiro', 'specs', '1-3-6-documentation-enhancement', 'docs-review-report.json');
26
+
27
+ /**
28
+ * Recursively get all markdown files in a directory
29
+ * @param {string} dir Directory to scan
30
+ * @param {Array<string>} fileList Accumulated file list
31
+ * @returns {Array<string>} List of file paths
32
+ */
33
+ function getMarkdownFiles(dir, fileList = []) {
34
+ try {
35
+ const files = fs.readdirSync(dir);
36
+
37
+ files.forEach(file => {
38
+ const filePath = path.join(dir, file);
39
+ const stat = fs.statSync(filePath);
40
+
41
+ if (stat.isDirectory()) {
42
+ getMarkdownFiles(filePath, fileList);
43
+ } else if (file.endsWith('.md')) {
44
+ fileList.push(filePath);
45
+ }
46
+ });
47
+ } catch (error) {
48
+ console.error(`Error reading directory ${dir}:`, error.message);
49
+ }
50
+
51
+ return fileList;
52
+ }
53
+
54
+ /**
55
+ * Extract links from markdown content
56
+ * @param {string} content Markdown content
57
+ * @returns {Array<Object>} Array of link objects with text and url
58
+ */
59
+ function extractLinks(content) {
60
+ const links = [];
61
+
62
+ // Match markdown links: [text](url)
63
+ const markdownLinkPattern = /\[([^\]]+)\]\(([^)]+)\)/g;
64
+ let match;
65
+
66
+ while ((match = markdownLinkPattern.exec(content)) !== null) {
67
+ links.push({
68
+ text: match[1],
69
+ url: match[2],
70
+ type: 'markdown'
71
+ });
72
+ }
73
+
74
+ // Match HTML links: <a href="url">text</a>
75
+ const htmlLinkPattern = /<a\s+(?:[^>]*?\s+)?href="([^"]*)"[^>]*>(.*?)<\/a>/gi;
76
+ while ((match = htmlLinkPattern.exec(content)) !== null) {
77
+ links.push({
78
+ text: match[2],
79
+ url: match[1],
80
+ type: 'html'
81
+ });
82
+ }
83
+
84
+ return links;
85
+ }
86
+
87
+ /**
88
+ * Check if a link is valid
89
+ * @param {string} url URL to check
90
+ * @param {string} sourceFile File containing the link
91
+ * @returns {Object} Validation result
92
+ */
93
+ function validateLink(url, sourceFile) {
94
+ // Skip external URLs (we'll just note them, not validate)
95
+ if (url.startsWith('http://') || url.startsWith('https://')) {
96
+ return {
97
+ valid: true,
98
+ type: 'external',
99
+ url
100
+ };
101
+ }
102
+
103
+ // Skip anchors within the same page
104
+ if (url.startsWith('#')) {
105
+ return {
106
+ valid: true,
107
+ type: 'anchor',
108
+ url,
109
+ note: 'Anchor links not validated'
110
+ };
111
+ }
112
+
113
+ // Handle relative paths
114
+ let targetPath = url;
115
+
116
+ // Remove anchor from URL
117
+ const anchorIndex = targetPath.indexOf('#');
118
+ if (anchorIndex !== -1) {
119
+ targetPath = targetPath.substring(0, anchorIndex);
120
+ }
121
+
122
+ // Resolve relative to source file
123
+ const sourceDir = path.dirname(sourceFile);
124
+ const resolvedPath = path.resolve(sourceDir, targetPath);
125
+
126
+ // Check if file exists
127
+ const exists = fs.existsSync(resolvedPath);
128
+
129
+ return {
130
+ valid: exists,
131
+ type: 'internal',
132
+ url,
133
+ resolvedPath,
134
+ exists
135
+ };
136
+ }
137
+
138
+ /**
139
+ * Check for outdated patterns in content
140
+ * @param {string} content File content
141
+ * @param {string} filePath File path
142
+ * @returns {Array<Object>} Array of potential issues
143
+ */
144
+ function checkForOutdatedContent(content, filePath) {
145
+ const issues = [];
146
+
147
+ // Check for old Node.js version references
148
+ const nodeVersionPattern = /node\.?js\s+(?:version\s+)?(\d+)\.(\d+)/gi;
149
+ let match;
150
+ while ((match = nodeVersionPattern.exec(content)) !== null) {
151
+ const major = parseInt(match[1]);
152
+ if (major < 20) {
153
+ issues.push({
154
+ type: 'outdated-node-version',
155
+ line: content.substring(0, match.index).split('\n').length,
156
+ text: match[0],
157
+ note: `References Node.js ${major}.x, but package.json requires >=20.0.0`
158
+ });
159
+ }
160
+ }
161
+
162
+ // Check for deprecated AWS SDK v2 references
163
+ if (content.includes('aws-sdk') && !content.includes('@aws-sdk')) {
164
+ const lines = content.split('\n');
165
+ lines.forEach((line, index) => {
166
+ if (line.includes('aws-sdk') && !line.includes('@aws-sdk')) {
167
+ issues.push({
168
+ type: 'deprecated-aws-sdk',
169
+ line: index + 1,
170
+ text: line.trim(),
171
+ note: 'References old aws-sdk package instead of @aws-sdk/*'
172
+ });
173
+ }
174
+ });
175
+ }
176
+
177
+ // Check for TODO or FIXME comments
178
+ const todoPattern = /(TODO|FIXME|XXX|HACK):\s*(.+)/gi;
179
+ while ((match = todoPattern.exec(content)) !== null) {
180
+ issues.push({
181
+ type: 'todo-comment',
182
+ line: content.substring(0, match.index).split('\n').length,
183
+ text: match[0],
184
+ note: 'Documentation contains TODO/FIXME comment'
185
+ });
186
+ }
187
+
188
+ // Check for broken code examples (missing closing backticks)
189
+ const codeBlockStarts = (content.match(/```/g) || []).length;
190
+ if (codeBlockStarts % 2 !== 0) {
191
+ issues.push({
192
+ type: 'malformed-code-block',
193
+ line: null,
194
+ text: null,
195
+ note: 'Unmatched code block delimiters (```)'
196
+ });
197
+ }
198
+
199
+ return issues;
200
+ }
201
+
202
+ /**
203
+ * Analyze a single documentation file
204
+ * @param {string} filePath Path to documentation file
205
+ * @returns {Object} Analysis results
206
+ */
207
+ function analyzeDocFile(filePath) {
208
+ const content = fs.readFileSync(filePath, 'utf-8');
209
+ const relativePath = path.relative(ROOT_DIR, filePath);
210
+
211
+ // Extract links
212
+ const links = extractLinks(content);
213
+
214
+ // Validate links
215
+ const brokenLinks = [];
216
+ const externalLinks = [];
217
+ const internalLinks = [];
218
+
219
+ links.forEach(link => {
220
+ const validation = validateLink(link.url, filePath);
221
+
222
+ if (validation.type === 'external') {
223
+ externalLinks.push({
224
+ text: link.text,
225
+ url: link.url
226
+ });
227
+ } else if (validation.type === 'internal') {
228
+ internalLinks.push({
229
+ text: link.text,
230
+ url: link.url,
231
+ valid: validation.valid
232
+ });
233
+
234
+ if (!validation.valid) {
235
+ brokenLinks.push({
236
+ text: link.text,
237
+ url: link.url,
238
+ resolvedPath: validation.resolvedPath
239
+ });
240
+ }
241
+ }
242
+ });
243
+
244
+ // Check for outdated content
245
+ const outdatedIssues = checkForOutdatedContent(content, filePath);
246
+
247
+ // Get file stats
248
+ const stats = fs.statSync(filePath);
249
+ const lineCount = content.split('\n').length;
250
+ const wordCount = content.split(/\s+/).length;
251
+
252
+ return {
253
+ filePath: relativePath,
254
+ stats: {
255
+ size: stats.size,
256
+ lastModified: stats.mtime.toISOString(),
257
+ lineCount,
258
+ wordCount
259
+ },
260
+ links: {
261
+ total: links.length,
262
+ external: externalLinks.length,
263
+ internal: internalLinks.length,
264
+ broken: brokenLinks.length
265
+ },
266
+ brokenLinks,
267
+ externalLinks,
268
+ outdatedIssues,
269
+ needsReview: brokenLinks.length > 0 || outdatedIssues.length > 0
270
+ };
271
+ }
272
+
273
+ /**
274
+ * Check for missing documentation based on source code
275
+ * @returns {Array<Object>} Missing documentation items
276
+ */
277
+ function checkMissingDocumentation() {
278
+ const missing = [];
279
+
280
+ // Check if main features are documented
281
+ const expectedDocs = [
282
+ { path: 'docs/features/cache/README.md', feature: 'Cache Module' },
283
+ { path: 'docs/features/endpoint/README.md', feature: 'Endpoint Module' },
284
+ { path: 'docs/features/tools/README.md', feature: 'Tools Module' },
285
+ { path: 'docs/00-quick-start-implementation/README.md', feature: 'Quick Start Guide' },
286
+ { path: 'docs/01-advanced-implementation-for-web-service/README.md', feature: 'Advanced Implementation Guide' },
287
+ { path: 'docs/00-example-implementation/README.md', feature: 'Example Implementation' },
288
+ { path: 'docs/lambda-optimization/README.md', feature: 'Lambda Optimization Guide' },
289
+ { path: 'docs/technical/in-memory-cache.md', feature: 'In-Memory Cache Technical Docs' }
290
+ ];
291
+
292
+ expectedDocs.forEach(doc => {
293
+ const fullPath = path.join(ROOT_DIR, doc.path);
294
+ if (!fs.existsSync(fullPath)) {
295
+ missing.push({
296
+ path: doc.path,
297
+ feature: doc.feature,
298
+ status: 'missing'
299
+ });
300
+ } else {
301
+ // Check if file is empty or very small
302
+ const stats = fs.statSync(fullPath);
303
+ if (stats.size < 100) {
304
+ missing.push({
305
+ path: doc.path,
306
+ feature: doc.feature,
307
+ status: 'stub',
308
+ size: stats.size
309
+ });
310
+ }
311
+ }
312
+ });
313
+
314
+ return missing;
315
+ }
316
+
317
+ /**
318
+ * Generate documentation review report
319
+ */
320
+ function generateReviewReport() {
321
+ console.log('Starting documentation files review...\n');
322
+
323
+ const files = getMarkdownFiles(DOCS_DIR);
324
+ console.log(`Found ${files.length} markdown files\n`);
325
+
326
+ const fileAnalyses = [];
327
+ let totalBrokenLinks = 0;
328
+ let totalOutdatedIssues = 0;
329
+ let filesNeedingReview = 0;
330
+
331
+ files.forEach(file => {
332
+ console.log(`Reviewing: ${path.relative(ROOT_DIR, file)}`);
333
+ const analysis = analyzeDocFile(file);
334
+ fileAnalyses.push(analysis);
335
+
336
+ totalBrokenLinks += analysis.brokenLinks.length;
337
+ totalOutdatedIssues += analysis.outdatedIssues.length;
338
+ if (analysis.needsReview) {
339
+ filesNeedingReview++;
340
+ }
341
+ });
342
+
343
+ // Check for missing documentation
344
+ const missingDocs = checkMissingDocumentation();
345
+
346
+ const report = {
347
+ reviewDate: new Date().toISOString(),
348
+ summary: {
349
+ totalFiles: files.length,
350
+ filesNeedingReview,
351
+ totalBrokenLinks,
352
+ totalOutdatedIssues,
353
+ missingDocumentation: missingDocs.length
354
+ },
355
+ missingDocumentation: missingDocs,
356
+ fileAnalyses
357
+ };
358
+
359
+ // Ensure output directory exists
360
+ const outputDir = path.dirname(OUTPUT_FILE);
361
+ if (!fs.existsSync(outputDir)) {
362
+ fs.mkdirSync(outputDir, { recursive: true });
363
+ }
364
+
365
+ // Write report to file
366
+ fs.writeFileSync(OUTPUT_FILE, JSON.stringify(report, null, 2));
367
+
368
+ // Print summary
369
+ console.log('\n' + '='.repeat(60));
370
+ console.log('DOCUMENTATION REVIEW SUMMARY');
371
+ console.log('='.repeat(60));
372
+ console.log(`Total Files Reviewed: ${report.summary.totalFiles}`);
373
+ console.log(`Files Needing Review: ${report.summary.filesNeedingReview}`);
374
+ console.log(`Broken Links: ${report.summary.totalBrokenLinks}`);
375
+ console.log(`Outdated Content Issues: ${report.summary.totalOutdatedIssues}`);
376
+ console.log(`Missing Documentation: ${report.summary.missingDocumentation}`);
377
+ console.log('='.repeat(60));
378
+
379
+ if (missingDocs.length > 0) {
380
+ console.log('\nMissing or Stub Documentation:');
381
+ missingDocs.forEach(doc => {
382
+ console.log(` - ${doc.feature} (${doc.path}) - ${doc.status}`);
383
+ });
384
+ }
385
+
386
+ if (totalBrokenLinks > 0) {
387
+ console.log('\nFiles with Broken Links:');
388
+ fileAnalyses.forEach(file => {
389
+ if (file.brokenLinks.length > 0) {
390
+ console.log(` - ${file.filePath} (${file.brokenLinks.length} broken links)`);
391
+ }
392
+ });
393
+ }
394
+
395
+ console.log(`\nDetailed report saved to: ${OUTPUT_FILE}\n`);
396
+
397
+ return report;
398
+ }
399
+
400
+ // Run the review
401
+ try {
402
+ generateReviewReport();
403
+ } catch (error) {
404
+ console.error('Error during review:', error);
405
+ process.exit(1);
406
+ }
@@ -0,0 +1,59 @@
1
+ #!/bin/bash
2
+
3
+ # Development Environment Setup Script
4
+ # This script sets up the development environment for contributors
5
+
6
+ set -e
7
+
8
+ echo "=========================================="
9
+ echo "Setting up development environment..."
10
+ echo "=========================================="
11
+ echo ""
12
+
13
+ # Check if we're in the right directory
14
+ if [ ! -f "package.json" ]; then
15
+ echo "❌ Error: package.json not found. Please run this script from the repository root."
16
+ exit 1
17
+ fi
18
+
19
+ # Install dependencies
20
+ echo "📦 Installing dependencies..."
21
+ npm install
22
+ echo "✅ Dependencies installed"
23
+ echo ""
24
+
25
+ # Make pre-commit hook executable
26
+ if [ -f ".git/hooks/pre-commit" ]; then
27
+ echo "🔧 Setting up pre-commit hook..."
28
+ chmod +x .git/hooks/pre-commit
29
+ echo "✅ Pre-commit hook is now executable"
30
+ echo ""
31
+
32
+ # Test the hook
33
+ echo "🧪 Testing pre-commit hook..."
34
+ if .git/hooks/pre-commit; then
35
+ echo "✅ Pre-commit hook test passed!"
36
+ else
37
+ echo "⚠️ Pre-commit hook test failed. Please check documentation validation."
38
+ fi
39
+ else
40
+ echo "⚠️ Warning: Pre-commit hook not found at .git/hooks/pre-commit"
41
+ fi
42
+
43
+ echo ""
44
+ echo "=========================================="
45
+ echo "Development environment setup complete!"
46
+ echo "=========================================="
47
+ echo ""
48
+ echo "Next steps:"
49
+ echo " 1. Review CONTRIBUTING.md for contribution guidelines"
50
+ echo " 2. Review .kiro/steering/documentation-standards.md for documentation requirements"
51
+ echo " 3. Run 'npm test' to ensure all tests pass"
52
+ echo " 4. Make your changes and commit (pre-commit hook will validate)"
53
+ echo ""
54
+ echo "Useful commands:"
55
+ echo " npm test - Run all tests"
56
+ echo " npm test -- test/documentation/ - Run documentation tests"
57
+ echo " node scripts/audit-documentation.mjs - Run documentation audit"
58
+ echo " .git/hooks/pre-commit - Test pre-commit hook manually"
59
+ echo ""
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Verification script for example implementation files
5
+ * Checks:
6
+ * - CloudFormation templates are syntactically valid YAML
7
+ * - Example code files use current API
8
+ * - Example code files have necessary imports
9
+ */
10
+
11
+ import fs from 'fs';
12
+ import path from 'path';
13
+ import yaml from 'js-yaml';
14
+ import { fileURLToPath } from 'url';
15
+
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = path.dirname(__filename);
18
+ const rootDir = path.resolve(__dirname, '..');
19
+
20
+ const results = {
21
+ cloudFormationTemplates: [],
22
+ exampleCodeFiles: [],
23
+ errors: [],
24
+ warnings: []
25
+ };
26
+
27
+ // CloudFormation template files to check
28
+ const cfTemplates = [
29
+ 'docs/00-example-implementation/example-template-lambda-function.yml',
30
+ 'docs/00-example-implementation/example-template-s3-and-dynamodb-cache-store.yml',
31
+ 'docs/00-example-implementation/example-template-parameters.yml'
32
+ ];
33
+
34
+ // Example code files to check
35
+ const exampleCodeFiles = [
36
+ 'docs/00-example-implementation/example-validations.js',
37
+ 'docs/00-example-implementation/example-buildspec.yml'
38
+ ];
39
+
40
+ console.log('Verifying example implementation files...\n');
41
+
42
+ // CloudFormation YAML schema with custom tags
43
+ const CF_SCHEMA = yaml.DEFAULT_SCHEMA.extend([
44
+ new yaml.Type('!Ref', { kind: 'scalar' }),
45
+ new yaml.Type('!GetAtt', { kind: 'scalar' }),
46
+ new yaml.Type('!Sub', { kind: 'scalar' }),
47
+ new yaml.Type('!Join', { kind: 'sequence' }),
48
+ new yaml.Type('!Select', { kind: 'sequence' }),
49
+ new yaml.Type('!Split', { kind: 'sequence' }),
50
+ new yaml.Type('!If', { kind: 'sequence' }),
51
+ new yaml.Type('!Equals', { kind: 'sequence' }),
52
+ new yaml.Type('!Not', { kind: 'sequence' }),
53
+ new yaml.Type('!And', { kind: 'sequence' }),
54
+ new yaml.Type('!Or', { kind: 'sequence' }),
55
+ new yaml.Type('!FindInMap', { kind: 'sequence' }),
56
+ new yaml.Type('!GetAZs', { kind: 'scalar' }),
57
+ new yaml.Type('!ImportValue', { kind: 'scalar' }),
58
+ new yaml.Type('!Base64', { kind: 'scalar' })
59
+ ]);
60
+
61
+ // Check CloudFormation templates
62
+ console.log('Checking CloudFormation templates:');
63
+ for (const templatePath of cfTemplates) {
64
+ const fullPath = path.join(rootDir, templatePath);
65
+ try {
66
+ const content = fs.readFileSync(fullPath, 'utf8');
67
+ yaml.load(content, { schema: CF_SCHEMA });
68
+ console.log(` ✓ ${templatePath} - Valid YAML`);
69
+ results.cloudFormationTemplates.push({
70
+ file: templatePath,
71
+ valid: true
72
+ });
73
+ } catch (error) {
74
+ console.log(` ✗ ${templatePath} - Invalid YAML: ${error.message}`);
75
+ results.cloudFormationTemplates.push({
76
+ file: templatePath,
77
+ valid: false,
78
+ error: error.message
79
+ });
80
+ results.errors.push(`CloudFormation template ${templatePath}: ${error.message}`);
81
+ }
82
+ }
83
+
84
+ console.log('\nChecking example code files:');
85
+ for (const codePath of exampleCodeFiles) {
86
+ const fullPath = path.join(rootDir, codePath);
87
+ try {
88
+ if (!fs.existsSync(fullPath)) {
89
+ console.log(` ! ${codePath} - File does not exist`);
90
+ results.warnings.push(`Example file ${codePath} does not exist`);
91
+ results.exampleCodeFiles.push({
92
+ file: codePath,
93
+ exists: false
94
+ });
95
+ continue;
96
+ }
97
+
98
+ const content = fs.readFileSync(fullPath, 'utf8');
99
+
100
+ // Check for necessary imports if it's a .js file
101
+ if (codePath.endsWith('.js')) {
102
+ const hasRequire = content.includes('require(');
103
+ const hasImport = content.includes('import ');
104
+ const hasCacheDataImport = content.includes('@63klabs/cache-data');
105
+
106
+ if (!hasRequire && !hasImport) {
107
+ console.log(` ! ${codePath} - No imports found`);
108
+ results.warnings.push(`Example file ${codePath} has no imports`);
109
+ } else if (hasCacheDataImport) {
110
+ console.log(` ✓ ${codePath} - Has cache-data import`);
111
+ } else {
112
+ console.log(` ✓ ${codePath} - Has imports (no cache-data import)`);
113
+ }
114
+
115
+ results.exampleCodeFiles.push({
116
+ file: codePath,
117
+ exists: true,
118
+ hasImports: hasRequire || hasImport,
119
+ hasCacheDataImport: hasCacheDataImport
120
+ });
121
+ } else {
122
+ // For YAML files, just check they're valid
123
+ yaml.load(content);
124
+ console.log(` ✓ ${codePath} - Valid YAML`);
125
+ results.exampleCodeFiles.push({
126
+ file: codePath,
127
+ exists: true,
128
+ valid: true
129
+ });
130
+ }
131
+ } catch (error) {
132
+ console.log(` ✗ ${codePath} - Error: ${error.message}`);
133
+ results.errors.push(`Example file ${codePath}: ${error.message}`);
134
+ results.exampleCodeFiles.push({
135
+ file: codePath,
136
+ exists: true,
137
+ error: error.message
138
+ });
139
+ }
140
+ }
141
+
142
+ // Check for missing example files that are referenced in README
143
+ const missingFiles = [
144
+ 'docs/00-example-implementation/example-config.js',
145
+ 'docs/00-example-implementation/example-handler.js'
146
+ ];
147
+
148
+ console.log('\nChecking for referenced but missing files:');
149
+ for (const filePath of missingFiles) {
150
+ const fullPath = path.join(rootDir, filePath);
151
+ if (!fs.existsSync(fullPath)) {
152
+ console.log(` ! ${filePath} - Referenced in README but does not exist`);
153
+ results.warnings.push(`Referenced file ${filePath} does not exist (noted in README as missing)`);
154
+ } else {
155
+ const content = fs.readFileSync(fullPath, 'utf8');
156
+ if (content.trim() === '') {
157
+ console.log(` ! ${filePath} - File exists but is empty`);
158
+ results.warnings.push(`Referenced file ${filePath} exists but is empty`);
159
+ } else {
160
+ console.log(` ✓ ${filePath} - Exists and has content`);
161
+ }
162
+ }
163
+ }
164
+
165
+ // Summary
166
+ console.log('\n' + '='.repeat(60));
167
+ console.log('VERIFICATION SUMMARY');
168
+ console.log('='.repeat(60));
169
+ console.log(`CloudFormation templates checked: ${results.cloudFormationTemplates.length}`);
170
+ console.log(`Example code files checked: ${results.exampleCodeFiles.length}`);
171
+ console.log(`Errors: ${results.errors.length}`);
172
+ console.log(`Warnings: ${results.warnings.length}`);
173
+
174
+ if (results.errors.length > 0) {
175
+ console.log('\nERRORS:');
176
+ results.errors.forEach(err => console.log(` - ${err}`));
177
+ }
178
+
179
+ if (results.warnings.length > 0) {
180
+ console.log('\nWARNINGS:');
181
+ results.warnings.forEach(warn => console.log(` - ${warn}`));
182
+ }
183
+
184
+ // Exit with error code if there are errors
185
+ if (results.errors.length > 0) {
186
+ console.log('\n❌ Verification failed with errors');
187
+ process.exit(1);
188
+ } else if (results.warnings.length > 0) {
189
+ console.log('\n⚠️ Verification completed with warnings');
190
+ process.exit(0);
191
+ } else {
192
+ console.log('\n✅ All verifications passed');
193
+ process.exit(0);
194
+ }